From fb7261eb844521d175a60f155f2027816d85ffc1 Mon Sep 17 00:00:00 2001 From: Laszlo Starost Date: Tue, 28 Jul 2020 15:22:51 +0200 Subject: [PATCH 01/11] Added component: reservoir. Create: e.g. create_reservoir(net, junction=0, h_m=3, name=reservoir) --- .../component_models/reservoir_component.py | 36 +++++++++++++ pandapipes/constants.py | 1 + pandapipes/create.py | 51 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 pandapipes/component_models/reservoir_component.py diff --git a/pandapipes/component_models/reservoir_component.py b/pandapipes/component_models/reservoir_component.py new file mode 100644 index 00000000..6aff08bb --- /dev/null +++ b/pandapipes/component_models/reservoir_component.py @@ -0,0 +1,36 @@ +from numpy import dtype + +from pandapipes.component_models.ext_grid_component import ExtGrid + + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + + +class Reservoir(ExtGrid): + """ + + """ + + @classmethod + def table_name(cls): + return "reservoir" + + @classmethod + def get_component_input(cls): + """ + + :return: + """ + + return [("name", dtype(object)), + ("junction", "u4"), + ("h_m", "f8"), + ("p_bar", "f8"), + ("t_k", "f8"), + ("in_service", "bool"), + ('type', dtype(object))] diff --git a/pandapipes/constants.py b/pandapipes/constants.py index aa4d808a..709bcec3 100644 --- a/pandapipes/constants.py +++ b/pandapipes/constants.py @@ -8,6 +8,7 @@ NORMAL_PRESSURE = 1.01325 # pressure under normal conditions (at sea level) in bar MOLAR_MASS_AIR = 0.02896 # molar mass of air in m³/mol R_UNIVERSAL = 8.314 # universal gas constant (molar) in J/(mol * K) +WATER_DENSITY = 997 # kg/m³ # specific constants HEIGHT_EXPONENT = 5.255 # exponent for height correction (typical value of international equation) diff --git a/pandapipes/create.py b/pandapipes/create.py index e9d3456f..85a6ad44 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -14,6 +14,8 @@ from pandapipes.std_types.std_type_toolbox import regression_function from pandapipes.component_models import Junction, Sink, Source, Pump, Pipe, ExtGrid, \ HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass +from pandapipes.component_models.reservoir_component import Reservoir +from pandapipes.constants import GRAVITATION_CONSTANT, WATER_DENSITY, P_CONVERSION try: import pplog as logging @@ -303,6 +305,55 @@ def create_ext_grid(net, junction, p_bar, t_k, name=None, in_service=True, index return index +def create_reservoir(net, junction, h_m, t_k=293, name=None, in_service=True, index=None): + """ + + :param net: The net that the reservoir should be connected to + :type net: pandapipesNet + :param junction: The junction to which the reservoir grid is connected + :type junction: int + :param h_m: The height of the reservoir + :type h_m: float + :param t_k: The fixed temperature of the water in the reservoir + :type t_k: float, default 293 + :param name: A name tg for this reservoir + :type name: str, default None + :param in_service: True for in service, false for out of service + :type in_service: bool, default True + :param index: Force a specified ID if it is available. If None, the index is set one higher than the \ + highest already existing index is selected. + :return: index - The unique ID of the created element + :rtype: int + + :Example: create_reservoir(net, junction1, h_m=3, name="Grid reservoir") + + """ + add_new_component(net, Reservoir) + + if junction not in net["junction"].index.values: + raise UserWarning("Cannot attach to junction %s, junction does not exist" % junction) + + if index is not None and index in net["reservoir"].index: + raise UserWarning("An external grid with with index %s already exists" % index) + + if index is None: + index = get_free_id(net["reservoir"]) + + dtypes = net.reservoir.dtypes + + p_bar = WATER_DENSITY * h_m * GRAVITATION_CONSTANT * P_CONVERSION + + # external grid with fixed pressure --> the node acts as a slack node for the mass flow. + type = "p" + + net.reservoir.loc[index, ["name", "junction", "h_m", "p_bar", "t_k", "in_service", "type"]] = \ + [name, junction, h_m, p_bar, t_k, bool(in_service), type] + + # and preserve dtypes + _preserve_dtypes(net.reservoir, dtypes) + return index + + def create_heat_exchanger(net, from_junction, to_junction, diameter_m, qext_w, loss_coefficient=0, name=None, index=None, in_service=True, type="heat_exchanger", **kwargs): """ From 1a086461d2ed64dad43a745ed297ab1519ea4de9 Mon Sep 17 00:00:00 2001 From: Laszlo Starost Date: Tue, 28 Jul 2020 15:37:24 +0200 Subject: [PATCH 02/11] Added test for reservoir. Needs to be change! --- .../api/test_components/test_reservoir.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pandapipes/test/api/test_components/test_reservoir.py diff --git a/pandapipes/test/api/test_components/test_reservoir.py b/pandapipes/test/api/test_components/test_reservoir.py new file mode 100644 index 00000000..d3854550 --- /dev/null +++ b/pandapipes/test/api/test_components/test_reservoir.py @@ -0,0 +1,22 @@ +import pandapipes as pp + + +def test_reservoir(): + """ + + :return: + """ + net = pp.create_empty_network(fluid="water") + + junction1 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Connection to External Grid") + junction2 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Junction 2") + + pp.create_reservoir(net, junction1, h_m=3, name="Grid reservoir") + + pp.create_pipe_from_parameters(net, from_junction=junction1, to_junction=junction2, length_km=10, diameter_m=0.3, name="Pipe 1") + + pp.create_sink(net, junction=junction2, mdot_kg_per_s=0.545, name="Sink 1") + + pp.pipeflow(net) + + assert 1 == 1 From c4467d1afda3575edcedfab9de3e239db2617aa5 Mon Sep 17 00:00:00 2001 From: Laszlo Starost Date: Tue, 28 Jul 2020 16:35:10 +0200 Subject: [PATCH 03/11] removed density from constants and read it from net.fluid for pressure calculation of reservoir --- pandapipes/constants.py | 1 - pandapipes/create.py | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pandapipes/constants.py b/pandapipes/constants.py index 709bcec3..aa4d808a 100644 --- a/pandapipes/constants.py +++ b/pandapipes/constants.py @@ -8,7 +8,6 @@ NORMAL_PRESSURE = 1.01325 # pressure under normal conditions (at sea level) in bar MOLAR_MASS_AIR = 0.02896 # molar mass of air in m³/mol R_UNIVERSAL = 8.314 # universal gas constant (molar) in J/(mol * K) -WATER_DENSITY = 997 # kg/m³ # specific constants HEIGHT_EXPONENT = 5.255 # exponent for height correction (typical value of international equation) diff --git a/pandapipes/create.py b/pandapipes/create.py index 85a6ad44..8121c57b 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -15,7 +15,7 @@ from pandapipes.component_models import Junction, Sink, Source, Pump, Pipe, ExtGrid, \ HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass from pandapipes.component_models.reservoir_component import Reservoir -from pandapipes.constants import GRAVITATION_CONSTANT, WATER_DENSITY, P_CONVERSION +from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION try: import pplog as logging @@ -341,7 +341,9 @@ def create_reservoir(net, junction, h_m, t_k=293, name=None, in_service=True, in dtypes = net.reservoir.dtypes - p_bar = WATER_DENSITY * h_m * GRAVITATION_CONSTANT * P_CONVERSION + density = net.fluid.get_density(t_k).item() + + p_bar = density * h_m * GRAVITATION_CONSTANT * P_CONVERSION # external grid with fixed pressure --> the node acts as a slack node for the mass flow. type = "p" From d31f8372194a262cc9b201cb91e1bbe7a9d57af3 Mon Sep 17 00:00:00 2001 From: sdrauz Date: Mon, 3 Aug 2020 10:39:36 +0200 Subject: [PATCH 04/11] adaption reservoir: - renamed reservoir in water tower - adaption of create and component - adaption of test --- pandapipes/component_models/__init__.py | 1 + .../component_models/reservoir_component.py | 36 ----- .../component_models/water_tower_component.py | 139 ++++++++++++++++++ pandapipes/create.py | 60 ++++---- .../api/test_components/test_reservoir.py | 22 --- .../api/test_components/test_water_tower.py | 45 ++++++ .../data/test_water_tower.csv | 3 + 7 files changed, 217 insertions(+), 89 deletions(-) delete mode 100644 pandapipes/component_models/reservoir_component.py create mode 100644 pandapipes/component_models/water_tower_component.py delete mode 100644 pandapipes/test/api/test_components/test_reservoir.py create mode 100644 pandapipes/test/api/test_components/test_water_tower.py create mode 100644 pandapipes/test/pipeflow_internals/data/test_water_tower.csv diff --git a/pandapipes/component_models/__init__.py b/pandapipes/component_models/__init__.py index 3f3c3861..033c4f9b 100644 --- a/pandapipes/component_models/__init__.py +++ b/pandapipes/component_models/__init__.py @@ -12,3 +12,4 @@ from pandapipes.component_models.pump_component import * from pandapipes.component_models.circulation_pump_mass_component import * from pandapipes.component_models.circulation_pump_pressure_component import * +from pandapipes.component_models.water_tower_component import * \ No newline at end of file diff --git a/pandapipes/component_models/reservoir_component.py b/pandapipes/component_models/reservoir_component.py deleted file mode 100644 index 6aff08bb..00000000 --- a/pandapipes/component_models/reservoir_component.py +++ /dev/null @@ -1,36 +0,0 @@ -from numpy import dtype - -from pandapipes.component_models.ext_grid_component import ExtGrid - - -try: - import pplog as logging -except ImportError: - import logging - -logger = logging.getLogger(__name__) - - -class Reservoir(ExtGrid): - """ - - """ - - @classmethod - def table_name(cls): - return "reservoir" - - @classmethod - def get_component_input(cls): - """ - - :return: - """ - - return [("name", dtype(object)), - ("junction", "u4"), - ("h_m", "f8"), - ("p_bar", "f8"), - ("t_k", "f8"), - ("in_service", "bool"), - ('type', dtype(object))] diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py new file mode 100644 index 00000000..67b9874a --- /dev/null +++ b/pandapipes/component_models/water_tower_component.py @@ -0,0 +1,139 @@ +# Copyright (c) 2020 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +import numpy as np +from numpy import dtype + +from pandapipes.component_models.abstract_models import NodeElementComponent +from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION +from pandapipes.idx_branch import FROM_NODE, TO_NODE, LOAD_VEC_NODES +from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE +from pandapipes.internals_toolbox import _sum_by_group +from pandapipes.pipeflow_setup import get_lookup + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + + +class WaterTower(NodeElementComponent): + """ + + """ + + @classmethod + def table_name(cls): + return "water_tower" + + @classmethod + def sign(cls): + return 1. + + @classmethod + def create_pit_node_entries(cls, net, node_pit, node_name): + """ + Function which creates pit node entries. + + :param net: The pandapipes network + :type net: pandapipesNet + :param node_pit: + :type node_pit: + :return: No Output. + """ + water_towers = net[cls.table_name()] + density = net.fluid.get_density(water_towers.t_k.values) + press = density * water_towers.height_m.values * GRAVITATION_CONSTANT / P_CONVERSION * \ + water_towers.in_service.values + junction_idx_lookups = get_lookup(net, "node", "index")[node_name] + junction = cls.get_connected_junction(net) + juncts_p, press_sum, number = _sum_by_group(junction.values, press, + np.ones_like(press, dtype=np.int32)) + index_p = junction_idx_lookups[juncts_p] + node_pit[index_p, PINIT] = press_sum / number + node_pit[index_p, NODE_TYPE] = P + node_pit[index_p, EXT_GRID_OCCURENCE] += number + + net["_lookups"]["water_tower"] = \ + np.array(list(set(np.concatenate([net["_lookups"]["water_tower"], index_p])))) if \ + "water_tower" in net['_lookups'] else index_p + return water_towers, press + + @classmethod + def extract_results(cls, net, options, node_name): + """ + Function that extracts certain results. + + :param net: The pandapipes network + :type net: pandapipesNet + :param options: + :type options: + :return: No Output. + """ + water_towers = net[cls.table_name()] + + if len(water_towers) == 0: + return + + res_table = super().extract_results(net, options, node_name) + + branch_pit = net['_pit']['branch'] + node_pit = net["_pit"]["node"] + + junction = cls.get_connected_junction(net) + index_juncts = np.array(junction.values[junction]) + junct_uni = np.array(list(set(index_juncts))) + index_nodes = get_lookup(net, "node", "index")[node_name][junct_uni] + eg_from_branches = np.isin(branch_pit[:, FROM_NODE], index_nodes) + eg_to_branches = np.isin(branch_pit[:, TO_NODE], index_nodes) + from_nodes = branch_pit[eg_from_branches, FROM_NODE] + to_nodes = branch_pit[eg_to_branches, TO_NODE] + mass_flow_from = branch_pit[eg_from_branches, LOAD_VEC_NODES] + mass_flow_to = branch_pit[eg_to_branches, LOAD_VEC_NODES] + press = node_pit[index_nodes, PINIT] + loads = node_pit[index_nodes, LOAD] + counts = node_pit[index_nodes, EXT_GRID_OCCURENCE] + all_index_nodes = np.concatenate([from_nodes, to_nodes, index_nodes]) + all_mass_flows = np.concatenate([-mass_flow_from, mass_flow_to, -loads]) + nodes, sum_mass_flows = _sum_by_group(all_index_nodes, all_mass_flows) + + # positive results mean that the ext_grid feeds in, negative means that the ext grid + # extracts (like a load) + res_table["mdot_kg_per_s"].values[:] = np.repeat(cls.sign() * sum_mass_flows / counts, + counts.astype(int)) + res_table["p_bar"].values[:] = press + return res_table, water_towers, index_nodes, node_pit, branch_pit + + @classmethod + def get_connected_junction(cls, net): + junction = net[cls.table_name()].junction + return junction + + @classmethod + def get_component_input(cls): + """ + + :return: + """ + + return [("name", dtype(object)), + ("junction", "u4"), + ("height_m", "f8"), + ("t_k", "f8"), + ("in_service", "bool"), + ('type', dtype(object))] + + @classmethod + def get_result_table(cls, net): + """ + + :param net: The pandapipes network + :type net: pandapipesNet + :return: (columns, all_float) - the column names and whether they are all float type. Only + if False, returns columns as tuples also specifying the dtypes + :rtype: (list, bool) + """ + return ["mdot_kg_per_s", "p_bar"], True diff --git a/pandapipes/create.py b/pandapipes/create.py index 8121c57b..58baf8ba 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -4,18 +4,17 @@ import numpy as np import pandas as pd +from pandapower.auxiliary import get_free_id, _preserve_dtypes + +from pandapipes.component_models import Junction, Sink, Source, Pump, Pipe, ExtGrid, \ + HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass, WaterTower from pandapipes.component_models.auxiliaries.component_toolbox import add_new_component from pandapipes.pandapipes_net import pandapipesNet, get_default_pandapipes_structure from pandapipes.properties import call_lib, add_fluid_to_net -from pandapower.auxiliary import get_free_id, _preserve_dtypes from pandapipes.properties.fluids import Fluid from pandapipes.std_types.std_type import PumpStdType, add_basic_std_types, add_pump_std_type, \ load_std_type from pandapipes.std_types.std_type_toolbox import regression_function -from pandapipes.component_models import Junction, Sink, Source, Pump, Pipe, ExtGrid, \ - HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass -from pandapipes.component_models.reservoir_component import Reservoir -from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION try: import pplog as logging @@ -305,54 +304,53 @@ def create_ext_grid(net, junction, p_bar, t_k, name=None, in_service=True, index return index -def create_reservoir(net, junction, h_m, t_k=293, name=None, in_service=True, index=None): +def create_water_tower(net, junction, height_m, t_k=293.15, name=None, in_service=True, index=None, + type='water_tower', **kwargs): """ - :param net: The net that the reservoir should be connected to + :param net: The net that the water tower should be connected to :type net: pandapipesNet - :param junction: The junction to which the reservoir grid is connected + :param junction: The junction to which the water tower is connected to :type junction: int - :param h_m: The height of the reservoir + :param h_m: The height of the water tower :type h_m: float - :param t_k: The fixed temperature of the water in the reservoir - :type t_k: float, default 293 - :param name: A name tg for this reservoir + :param t_k: The fixed temperature of the water in the water tower + :type t_k: float, default 293.15 + :param name: A name tag for this water tower :type name: str, default None - :param in_service: True for in service, false for out of service + :param in_service: True for in service, False for out of service :type in_service: bool, default True :param index: Force a specified ID if it is available. If None, the index is set one higher than the \ highest already existing index is selected. :return: index - The unique ID of the created element :rtype: int - :Example: create_reservoir(net, junction1, h_m=3, name="Grid reservoir") + :Example: create_water_tower(net, junction1, h_m=3, name="Grid reservoir") """ - add_new_component(net, Reservoir) + add_new_component(net, WaterTower) if junction not in net["junction"].index.values: raise UserWarning("Cannot attach to junction %s, junction does not exist" % junction) - if index is not None and index in net["reservoir"].index: - raise UserWarning("An external grid with with index %s already exists" % index) + if index is not None and index in net["water_tower"].index: + raise UserWarning("An water tower with index %s already exists" % index) if index is None: - index = get_free_id(net["reservoir"]) - - dtypes = net.reservoir.dtypes - - density = net.fluid.get_density(t_k).item() - - p_bar = density * h_m * GRAVITATION_CONSTANT * P_CONVERSION + index = get_free_id(net["water_tower"]) - # external grid with fixed pressure --> the node acts as a slack node for the mass flow. - type = "p" + # store dtypes + dtypes = net.water_tower.dtypes - net.reservoir.loc[index, ["name", "junction", "h_m", "p_bar", "t_k", "in_service", "type"]] = \ - [name, junction, h_m, p_bar, t_k, bool(in_service), type] + cols = ["name", "junction", "height_m", "t_k", "in_service", "type"] + vals = [name, junction, height_m, t_k, bool(in_service), type] + all_values = {col: val for col, val in zip(cols, vals)} + all_values.update(**kwargs) + for col, val in all_values.items(): + net.water_tower.at[index, col] = val # and preserve dtypes - _preserve_dtypes(net.reservoir, dtypes) + _preserve_dtypes(net.water_tower, dtypes) return index @@ -876,8 +874,8 @@ def create_circ_pump_const_pressure(net, from_junction, to_junction, p_bar, plif for b in [from_junction, to_junction]: if b not in net["junction"].index.values: raise UserWarning( - "CirculationPumpPressure %s tries to attach to non-existing junction %s" - % (name, b)) + "CirculationPumpPressure %s tries to attach to non-existing junction %s" + % (name, b)) if index is None: index = get_free_id(net["circ_pump_pressure"]) diff --git a/pandapipes/test/api/test_components/test_reservoir.py b/pandapipes/test/api/test_components/test_reservoir.py deleted file mode 100644 index d3854550..00000000 --- a/pandapipes/test/api/test_components/test_reservoir.py +++ /dev/null @@ -1,22 +0,0 @@ -import pandapipes as pp - - -def test_reservoir(): - """ - - :return: - """ - net = pp.create_empty_network(fluid="water") - - junction1 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Connection to External Grid") - junction2 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Junction 2") - - pp.create_reservoir(net, junction1, h_m=3, name="Grid reservoir") - - pp.create_pipe_from_parameters(net, from_junction=junction1, to_junction=junction2, length_km=10, diameter_m=0.3, name="Pipe 1") - - pp.create_sink(net, junction=junction2, mdot_kg_per_s=0.545, name="Sink 1") - - pp.pipeflow(net) - - assert 1 == 1 diff --git a/pandapipes/test/api/test_components/test_water_tower.py b/pandapipes/test/api/test_components/test_water_tower.py new file mode 100644 index 00000000..989c691e --- /dev/null +++ b/pandapipes/test/api/test_components/test_water_tower.py @@ -0,0 +1,45 @@ +# Copyright (c) 2020 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +import os + +import numpy as np +import pandas as pd + +import pandapipes as pp +from pandapipes.test.pipeflow_internals import internals_data_path + + +def test_reservoir(): + """ + + :rtype: + """ + net = pp.create_empty_network(fluid="water") + + junction1 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Connection to Water Tower") + junction2 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Junction 2") + + pp.create_water_tower(net, junction1, height_m=30, name="Water Tower") + + pp.create_pipe_from_parameters(net, from_junction=junction1, to_junction=junction2, length_km=10, diameter_m=0.075, + name="Pipe 1") + + pp.create_sink(net, junction=junction2, mdot_kg_per_s=0.545, name="Sink 1") + + pp.pipeflow(net, stop_condition="tol", iter=3, friction_model="nikuradse", + mode="hydraulics", transient=False, nonlinear_method="automatic", + tol_p=1e-4, + tol_v=1e-4) + + data = pd.read_csv(os.path.join(internals_data_path, "test_water_tower.csv"), sep=';') + + res_junction = net.res_junction.p_bar.values + res_pipe = net.res_pipe.v_mean_m_per_s.values + + p_diff = np.abs(1 - res_junction / data['p'].dropna().values) + v_diff = np.abs(1 - res_pipe / data['v'].dropna().values) + + assert np.all(p_diff < 0.01) + assert np.all(v_diff < 0.01) diff --git a/pandapipes/test/pipeflow_internals/data/test_water_tower.csv b/pandapipes/test/pipeflow_internals/data/test_water_tower.csv new file mode 100644 index 00000000..aaeba0a1 --- /dev/null +++ b/pandapipes/test/pipeflow_internals/data/test_water_tower.csv @@ -0,0 +1,3 @@ +v;p +0.123588;2.937630 +;2.442159 From 02b28d8a38c009f999a9551f1657ebb62c7d5e52 Mon Sep 17 00:00:00 2001 From: sdrauz Date: Mon, 3 Aug 2020 16:19:27 +0200 Subject: [PATCH 05/11] bugfix in water tower --- pandapipes/component_models/water_tower_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py index 67b9874a..abc07a92 100644 --- a/pandapipes/component_models/water_tower_component.py +++ b/pandapipes/component_models/water_tower_component.py @@ -84,7 +84,7 @@ def extract_results(cls, net, options, node_name): node_pit = net["_pit"]["node"] junction = cls.get_connected_junction(net) - index_juncts = np.array(junction.values[junction]) + index_juncts =junction.values junct_uni = np.array(list(set(index_juncts))) index_nodes = get_lookup(net, "node", "index")[node_name][junct_uni] eg_from_branches = np.isin(branch_pit[:, FROM_NODE], index_nodes) From 40601d33b47f6eaf685b76f92f50b8e5fbc6dc19 Mon Sep 17 00:00:00 2001 From: sdrauz Date: Tue, 4 Aug 2020 13:07:37 +0200 Subject: [PATCH 06/11] bugfix --- pandapipes/component_models/water_tower_component.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py index abc07a92..8da8c1fc 100644 --- a/pandapipes/component_models/water_tower_component.py +++ b/pandapipes/component_models/water_tower_component.py @@ -8,7 +8,7 @@ from pandapipes.component_models.abstract_models import NodeElementComponent from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION from pandapipes.idx_branch import FROM_NODE, TO_NODE, LOAD_VEC_NODES -from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE +from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE, HEIGHT from pandapipes.internals_toolbox import _sum_by_group from pandapipes.pipeflow_setup import get_lookup @@ -46,10 +46,11 @@ def create_pit_node_entries(cls, net, node_pit, node_name): """ water_towers = net[cls.table_name()] density = net.fluid.get_density(water_towers.t_k.values) - press = density * water_towers.height_m.values * GRAVITATION_CONSTANT / P_CONVERSION * \ - water_towers.in_service.values junction_idx_lookups = get_lookup(net, "node", "index")[node_name] junction = cls.get_connected_junction(net) + index_wt = junction_idx_lookups[junction] + press = density * (water_towers.height_m.values - node_pit[index_wt, HEIGHT]) * \ + GRAVITATION_CONSTANT / P_CONVERSION * water_towers.in_service.values juncts_p, press_sum, number = _sum_by_group(junction.values, press, np.ones_like(press, dtype=np.int32)) index_p = junction_idx_lookups[juncts_p] @@ -84,7 +85,7 @@ def extract_results(cls, net, options, node_name): node_pit = net["_pit"]["node"] junction = cls.get_connected_junction(net) - index_juncts =junction.values + index_juncts = junction.values junct_uni = np.array(list(set(index_juncts))) index_nodes = get_lookup(net, "node", "index")[node_name][junct_uni] eg_from_branches = np.isin(branch_pit[:, FROM_NODE], index_nodes) From abf213d53102c27562580c7c04c5bc87ce12db88 Mon Sep 17 00:00:00 2001 From: sdrauz Date: Tue, 4 Aug 2020 17:44:48 +0200 Subject: [PATCH 07/11] only considering height of the water column --- pandapipes/component_models/water_tower_component.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py index 8da8c1fc..3a9cf4cd 100644 --- a/pandapipes/component_models/water_tower_component.py +++ b/pandapipes/component_models/water_tower_component.py @@ -48,8 +48,7 @@ def create_pit_node_entries(cls, net, node_pit, node_name): density = net.fluid.get_density(water_towers.t_k.values) junction_idx_lookups = get_lookup(net, "node", "index")[node_name] junction = cls.get_connected_junction(net) - index_wt = junction_idx_lookups[junction] - press = density * (water_towers.height_m.values - node_pit[index_wt, HEIGHT]) * \ + press = density * water_towers.height_m.values * \ GRAVITATION_CONSTANT / P_CONVERSION * water_towers.in_service.values juncts_p, press_sum, number = _sum_by_group(junction.values, press, np.ones_like(press, dtype=np.int32)) From 5e99548697d5a7351997d91b6b5c5f3d285a198e Mon Sep 17 00:00:00 2001 From: sdrauz Date: Tue, 4 Aug 2020 18:03:46 +0200 Subject: [PATCH 08/11] adaptions water tower: adding an explaination of the water tower model, renaming the test and adapting the desciption in the create function. --- pandapipes/component_models/water_tower_component.py | 3 ++- pandapipes/create.py | 2 +- pandapipes/test/api/test_components/test_water_tower.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py index 3a9cf4cd..d107d5af 100644 --- a/pandapipes/component_models/water_tower_component.py +++ b/pandapipes/component_models/water_tower_component.py @@ -22,7 +22,8 @@ class WaterTower(NodeElementComponent): """ - + The water tower is similiar to an external grid. Based on the water column height the relative pressure + is determined. This pressure is fixed, i.e. the capacity of the water tower is infinitem and its level is constant. """ @classmethod diff --git a/pandapipes/create.py b/pandapipes/create.py index 58baf8ba..da181149 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -312,7 +312,7 @@ def create_water_tower(net, junction, height_m, t_k=293.15, name=None, in_servic :type net: pandapipesNet :param junction: The junction to which the water tower is connected to :type junction: int - :param h_m: The height of the water tower + :param h_m: The height of the water column relative to its surrounding :type h_m: float :param t_k: The fixed temperature of the water in the water tower :type t_k: float, default 293.15 diff --git a/pandapipes/test/api/test_components/test_water_tower.py b/pandapipes/test/api/test_components/test_water_tower.py index 989c691e..a6a85769 100644 --- a/pandapipes/test/api/test_components/test_water_tower.py +++ b/pandapipes/test/api/test_components/test_water_tower.py @@ -11,7 +11,7 @@ from pandapipes.test.pipeflow_internals import internals_data_path -def test_reservoir(): +def test_water_tower(): """ :rtype: From 7e0176b2933bddc33f83db4109e9634f8460e8c3 Mon Sep 17 00:00:00 2001 From: sdrauz Date: Wed, 5 Aug 2020 07:59:21 +0200 Subject: [PATCH 09/11] PEP 8 --- pandapipes/component_models/water_tower_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py index d107d5af..2ecb6806 100644 --- a/pandapipes/component_models/water_tower_component.py +++ b/pandapipes/component_models/water_tower_component.py @@ -8,7 +8,7 @@ from pandapipes.component_models.abstract_models import NodeElementComponent from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION from pandapipes.idx_branch import FROM_NODE, TO_NODE, LOAD_VEC_NODES -from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE, HEIGHT +from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE from pandapipes.internals_toolbox import _sum_by_group from pandapipes.pipeflow_setup import get_lookup From 5363a9873f3a2ca2f60c5e5ecaecb2db33d2dfea Mon Sep 17 00:00:00 2001 From: sdrauz Date: Mon, 10 Aug 2020 14:36:32 +0200 Subject: [PATCH 10/11] removing water tower as own component and introducing a toolbox to convert a water tower into an ext grid --- pandapipes/component_models/__init__.py | 1 - .../component_models/water_tower_component.py | 140 ------------------ pandapipes/create.py | 52 +------ pandapipes/create_toolbox.py | 15 ++ .../api/test_components/test_water_tower.py | 6 +- 5 files changed, 19 insertions(+), 195 deletions(-) delete mode 100644 pandapipes/component_models/water_tower_component.py create mode 100644 pandapipes/create_toolbox.py diff --git a/pandapipes/component_models/__init__.py b/pandapipes/component_models/__init__.py index 033c4f9b..3f3c3861 100644 --- a/pandapipes/component_models/__init__.py +++ b/pandapipes/component_models/__init__.py @@ -12,4 +12,3 @@ from pandapipes.component_models.pump_component import * from pandapipes.component_models.circulation_pump_mass_component import * from pandapipes.component_models.circulation_pump_pressure_component import * -from pandapipes.component_models.water_tower_component import * \ No newline at end of file diff --git a/pandapipes/component_models/water_tower_component.py b/pandapipes/component_models/water_tower_component.py deleted file mode 100644 index 2ecb6806..00000000 --- a/pandapipes/component_models/water_tower_component.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2020 by Fraunhofer Institute for Energy Economics -# and Energy System Technology (IEE), Kassel. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. - -import numpy as np -from numpy import dtype - -from pandapipes.component_models.abstract_models import NodeElementComponent -from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION -from pandapipes.idx_branch import FROM_NODE, TO_NODE, LOAD_VEC_NODES -from pandapipes.idx_node import PINIT, LOAD, NODE_TYPE, P, EXT_GRID_OCCURENCE -from pandapipes.internals_toolbox import _sum_by_group -from pandapipes.pipeflow_setup import get_lookup - -try: - import pplog as logging -except ImportError: - import logging - -logger = logging.getLogger(__name__) - - -class WaterTower(NodeElementComponent): - """ - The water tower is similiar to an external grid. Based on the water column height the relative pressure - is determined. This pressure is fixed, i.e. the capacity of the water tower is infinitem and its level is constant. - """ - - @classmethod - def table_name(cls): - return "water_tower" - - @classmethod - def sign(cls): - return 1. - - @classmethod - def create_pit_node_entries(cls, net, node_pit, node_name): - """ - Function which creates pit node entries. - - :param net: The pandapipes network - :type net: pandapipesNet - :param node_pit: - :type node_pit: - :return: No Output. - """ - water_towers = net[cls.table_name()] - density = net.fluid.get_density(water_towers.t_k.values) - junction_idx_lookups = get_lookup(net, "node", "index")[node_name] - junction = cls.get_connected_junction(net) - press = density * water_towers.height_m.values * \ - GRAVITATION_CONSTANT / P_CONVERSION * water_towers.in_service.values - juncts_p, press_sum, number = _sum_by_group(junction.values, press, - np.ones_like(press, dtype=np.int32)) - index_p = junction_idx_lookups[juncts_p] - node_pit[index_p, PINIT] = press_sum / number - node_pit[index_p, NODE_TYPE] = P - node_pit[index_p, EXT_GRID_OCCURENCE] += number - - net["_lookups"]["water_tower"] = \ - np.array(list(set(np.concatenate([net["_lookups"]["water_tower"], index_p])))) if \ - "water_tower" in net['_lookups'] else index_p - return water_towers, press - - @classmethod - def extract_results(cls, net, options, node_name): - """ - Function that extracts certain results. - - :param net: The pandapipes network - :type net: pandapipesNet - :param options: - :type options: - :return: No Output. - """ - water_towers = net[cls.table_name()] - - if len(water_towers) == 0: - return - - res_table = super().extract_results(net, options, node_name) - - branch_pit = net['_pit']['branch'] - node_pit = net["_pit"]["node"] - - junction = cls.get_connected_junction(net) - index_juncts = junction.values - junct_uni = np.array(list(set(index_juncts))) - index_nodes = get_lookup(net, "node", "index")[node_name][junct_uni] - eg_from_branches = np.isin(branch_pit[:, FROM_NODE], index_nodes) - eg_to_branches = np.isin(branch_pit[:, TO_NODE], index_nodes) - from_nodes = branch_pit[eg_from_branches, FROM_NODE] - to_nodes = branch_pit[eg_to_branches, TO_NODE] - mass_flow_from = branch_pit[eg_from_branches, LOAD_VEC_NODES] - mass_flow_to = branch_pit[eg_to_branches, LOAD_VEC_NODES] - press = node_pit[index_nodes, PINIT] - loads = node_pit[index_nodes, LOAD] - counts = node_pit[index_nodes, EXT_GRID_OCCURENCE] - all_index_nodes = np.concatenate([from_nodes, to_nodes, index_nodes]) - all_mass_flows = np.concatenate([-mass_flow_from, mass_flow_to, -loads]) - nodes, sum_mass_flows = _sum_by_group(all_index_nodes, all_mass_flows) - - # positive results mean that the ext_grid feeds in, negative means that the ext grid - # extracts (like a load) - res_table["mdot_kg_per_s"].values[:] = np.repeat(cls.sign() * sum_mass_flows / counts, - counts.astype(int)) - res_table["p_bar"].values[:] = press - return res_table, water_towers, index_nodes, node_pit, branch_pit - - @classmethod - def get_connected_junction(cls, net): - junction = net[cls.table_name()].junction - return junction - - @classmethod - def get_component_input(cls): - """ - - :return: - """ - - return [("name", dtype(object)), - ("junction", "u4"), - ("height_m", "f8"), - ("t_k", "f8"), - ("in_service", "bool"), - ('type', dtype(object))] - - @classmethod - def get_result_table(cls, net): - """ - - :param net: The pandapipes network - :type net: pandapipesNet - :return: (columns, all_float) - the column names and whether they are all float type. Only - if False, returns columns as tuples also specifying the dtypes - :rtype: (list, bool) - """ - return ["mdot_kg_per_s", "p_bar"], True diff --git a/pandapipes/create.py b/pandapipes/create.py index da181149..5f800f06 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -7,7 +7,7 @@ from pandapower.auxiliary import get_free_id, _preserve_dtypes from pandapipes.component_models import Junction, Sink, Source, Pump, Pipe, ExtGrid, \ - HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass, WaterTower + HeatExchanger, Valve, CirculationPumpPressure, CirculationPumpMass from pandapipes.component_models.auxiliaries.component_toolbox import add_new_component from pandapipes.pandapipes_net import pandapipesNet, get_default_pandapipes_structure from pandapipes.properties import call_lib, add_fluid_to_net @@ -304,56 +304,6 @@ def create_ext_grid(net, junction, p_bar, t_k, name=None, in_service=True, index return index -def create_water_tower(net, junction, height_m, t_k=293.15, name=None, in_service=True, index=None, - type='water_tower', **kwargs): - """ - - :param net: The net that the water tower should be connected to - :type net: pandapipesNet - :param junction: The junction to which the water tower is connected to - :type junction: int - :param h_m: The height of the water column relative to its surrounding - :type h_m: float - :param t_k: The fixed temperature of the water in the water tower - :type t_k: float, default 293.15 - :param name: A name tag for this water tower - :type name: str, default None - :param in_service: True for in service, False for out of service - :type in_service: bool, default True - :param index: Force a specified ID if it is available. If None, the index is set one higher than the \ - highest already existing index is selected. - :return: index - The unique ID of the created element - :rtype: int - - :Example: create_water_tower(net, junction1, h_m=3, name="Grid reservoir") - - """ - add_new_component(net, WaterTower) - - if junction not in net["junction"].index.values: - raise UserWarning("Cannot attach to junction %s, junction does not exist" % junction) - - if index is not None and index in net["water_tower"].index: - raise UserWarning("An water tower with index %s already exists" % index) - - if index is None: - index = get_free_id(net["water_tower"]) - - # store dtypes - dtypes = net.water_tower.dtypes - - cols = ["name", "junction", "height_m", "t_k", "in_service", "type"] - vals = [name, junction, height_m, t_k, bool(in_service), type] - all_values = {col: val for col, val in zip(cols, vals)} - all_values.update(**kwargs) - for col, val in all_values.items(): - net.water_tower.at[index, col] = val - - # and preserve dtypes - _preserve_dtypes(net.water_tower, dtypes) - return index - - def create_heat_exchanger(net, from_junction, to_junction, diameter_m, qext_w, loss_coefficient=0, name=None, index=None, in_service=True, type="heat_exchanger", **kwargs): """ diff --git a/pandapipes/create_toolbox.py b/pandapipes/create_toolbox.py new file mode 100644 index 00000000..5af808d3 --- /dev/null +++ b/pandapipes/create_toolbox.py @@ -0,0 +1,15 @@ +# Copyright (c) 2020 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +import pandapipes as pp +from pandapipes.constants import GRAVITATION_CONSTANT, P_CONVERSION + + +def transform_water_tower_into_ext_grid(net, junction, height_m, t_k=293.15, name='water_tower', in_service=True, + index=None, type='pt'): + density = net.fluid.get_density(t_k).item() + + p_bar = density * height_m * GRAVITATION_CONSTANT / P_CONVERSION + + return pp.create_ext_grid(net, junction, p_bar, t_k, name, in_service, index, type) diff --git a/pandapipes/test/api/test_components/test_water_tower.py b/pandapipes/test/api/test_components/test_water_tower.py index a6a85769..8b5667d6 100644 --- a/pandapipes/test/api/test_components/test_water_tower.py +++ b/pandapipes/test/api/test_components/test_water_tower.py @@ -3,6 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. import os +from pandapipes.create_toolbox import transform_water_tower_into_ext_grid import numpy as np import pandas as pd @@ -21,7 +22,7 @@ def test_water_tower(): junction1 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Connection to Water Tower") junction2 = pp.create_junction(net, pn_bar=1.0, tfluid_k=293.15, name="Junction 2") - pp.create_water_tower(net, junction1, height_m=30, name="Water Tower") + transform_water_tower_into_ext_grid(net, junction1, height_m=30) pp.create_pipe_from_parameters(net, from_junction=junction1, to_junction=junction2, length_km=10, diameter_m=0.075, name="Pipe 1") @@ -30,8 +31,7 @@ def test_water_tower(): pp.pipeflow(net, stop_condition="tol", iter=3, friction_model="nikuradse", mode="hydraulics", transient=False, nonlinear_method="automatic", - tol_p=1e-4, - tol_v=1e-4) + tol_p=1e-4, tol_v=1e-4) data = pd.read_csv(os.path.join(internals_data_path, "test_water_tower.csv"), sep=';') From e044b085c89ce3defbe076e9a2ec09e8a17728ba Mon Sep 17 00:00:00 2001 From: sdrauz Date: Mon, 10 Aug 2020 14:42:09 +0200 Subject: [PATCH 11/11] small bugfix --- pandapipes/create_toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapipes/create_toolbox.py b/pandapipes/create_toolbox.py index 5af808d3..972ca967 100644 --- a/pandapipes/create_toolbox.py +++ b/pandapipes/create_toolbox.py @@ -8,7 +8,7 @@ def transform_water_tower_into_ext_grid(net, junction, height_m, t_k=293.15, name='water_tower', in_service=True, index=None, type='pt'): - density = net.fluid.get_density(t_k).item() + density = net.fluid.get_density([t_k]) p_bar = density * height_m * GRAVITATION_CONSTANT / P_CONVERSION