From 4066fbe21ce66ff08a147420d08a5ed10bd2f6c0 Mon Sep 17 00:00:00 2001 From: Tabea Trummel Date: Thu, 13 Aug 2020 16:02:45 +0200 Subject: [PATCH 1/4] NonReturnValveController added and test created in pipeflow_internals --- pandapipes/control/__init__.py | 1 + .../controller/non_return_valve_controller.py | 118 ++++++++++++++++++ .../test_non_return_valve_controller.py | 44 +++++++ 3 files changed, 163 insertions(+) create mode 100644 pandapipes/control/controller/non_return_valve_controller.py create mode 100644 pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py diff --git a/pandapipes/control/__init__.py b/pandapipes/control/__init__.py index a2cd8e5a..a5b339df 100644 --- a/pandapipes/control/__init__.py +++ b/pandapipes/control/__init__.py @@ -3,3 +3,4 @@ # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. from pandapipes.control.run_control import run_control +from pandapipes.control.controller.non_return_valve_controller import NonReturnValveController diff --git a/pandapipes/control/controller/non_return_valve_controller.py b/pandapipes/control/controller/non_return_valve_controller.py new file mode 100644 index 00000000..5674bc8f --- /dev/null +++ b/pandapipes/control/controller/non_return_valve_controller.py @@ -0,0 +1,118 @@ +# 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 +import numpy +from pandapower.control.basic_controller import Controller + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + + +class NonReturnValveController(Controller): + """ + Controller for implementing a non-return valve. + + :param net: The net in which the controller resides + :type net: pandapipesNet + :param element_index: IDs of controlled valves + :type element_index: int[] + :param in_service: Indicates if the controller is currently in_service + :type in_service: bool, default True + :param recycle: Re-use of internal-data + :type recycle: bool, default True + :param drop_same_existing_ctrl: Indicates if already existing controllers of the same type and with the same matching parameters (e.g. at same element) should be dropped + :type drop_same_existing_ctrl: bool, default False + :param kwargs: Parameters for pipeflow + :type kwargs: dict + + :Example: + >>> kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'colebrook', + >>> 'mode': 'hydraulics', 'only_update_hydraulic_matrix': False} + >>> NonReturnValveController(net, element_index=[0, 1, 3], **kwargs) + >>> run_control(net) + + """ + + def __init__(self, net, element_index, profile_name=None, + scale_factor=1.0, in_service=True, recycle=True, order=0, level=0, + drop_same_existing_ctrl=False, set_q_from_cosphi=False, matching_params=None, initial_pipeflow=False, + **kwargs): + + if matching_params is None: + matching_params = {"element_index": element_index} + + # just calling init of the parent + super().__init__(net, in_service=in_service, recycle=recycle, order=order, level=level, + drop_same_existing_ctrl=drop_same_existing_ctrl, + matching_params=matching_params, initial_powerflow=initial_pipeflow, + **kwargs) + + self.matching_params = {"element_index": element_index} + if numpy.isscalar(element_index): + self.element_index = [element_index] + else: + self.element_index = element_index + self.values = None + self.profile_name = profile_name + self.scale_factor = scale_factor + self.initial_pipeflow = initial_pipeflow + self.kwargs = kwargs + self.v_m_per_s = [] # current flow velocities at valves + self.opened = [] # remember original user-defined values of opened + + if set_q_from_cosphi: + logger.error("Parameter set_q_from_cosphi deprecated!") + raise ValueError + + def initialize_control(self): + """ + First calculation of a pipeflow. \n + Saving the user-defined values, determine valves with negative flow velocities, + set opened to False for these. + """ + pp.pipeflow(self.net, self.kwargs) + + self.opened = self.net.valve.loc[self.element_index, "opened"] + + j = 0 + for i in self.element_index: + self.v_m_per_s.append(self.net.res_valve.loc[i, "v_mean_m_per_s"]) + + if self.net.valve.loc[i, "opened"] and self.v_m_per_s[j] < 0: + # use the element indices, where opened = True, otherwise NaN would be in self.v_m_per_s + self.net.valve.loc[i, "opened"] = False + j += 1 + + def is_converged(self): + """ + Convergence Condition: If all flow velocities at the non-return valves are >= 0 or opened equal False. \n + Resetting the variable opened to user defaults. + """ + + for i in range(len(self.element_index)): + if self.net.valve.loc[self.element_index[i], "opened"] and self.v_m_per_s[i] < 0: + return False + + self.net.valve.loc[self.element_index, "opened"] = self.opened + return True + + def control_step(self): + """ + Check whether negative flow velocities are still present at non-return valves. + """ + pp.pipeflow(self.net, self.kwargs) + + j = 0 + for i in self.element_index: + self.v_m_per_s.append(self.net.res_valve.loc[i, "v_mean_m_per_s"]) + + if self.net.valve.loc[i, "opened"] and self.v_m_per_s[j] < 0: + # use the element indices, where opened = True, otherwise NaN would be in self.v_m_per_s + self.net.valve.loc[i, "opened"] = False + j += 1 diff --git a/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py b/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py new file mode 100644 index 00000000..b92665b6 --- /dev/null +++ b/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py @@ -0,0 +1,44 @@ +# 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 +import pytest +from pandapipes.control import NonReturnValveController, run_control + + +def test_nrv(): + net = pandapipes.create_empty_network("net", fluid="water", add_stdtypes=True) + + j0 = pandapipes.create_junction(net, pn_bar=3, tfluid_k=293.15) + j1 = pandapipes.create_junction(net, pn_bar=3, tfluid_k=293.15) + j2 = pandapipes.create_junction(net, pn_bar=3, tfluid_k=293.15) + j3 = pandapipes.create_junction(net, pn_bar=3, tfluid_k=283.15) + j4 = pandapipes.create_junction(net, pn_bar=3, tfluid_k=283.15) + + pandapipes.create_ext_grid(net, j0, p_bar=3, t_k=293.15, type="pt") + + pandapipes.create_sink(net, j1, mdot_kg_per_s=1) + pandapipes.create_sink(net, j4, mdot_kg_per_s=0.5) + + pandapipes.create_source(net, j2, mdot_kg_per_s=0.05) + pandapipes.create_source(net, j3, mdot_kg_per_s=1) + + pandapipes.create_pipe_from_parameters(net, j1, j0, diameter_m=0.75, k_mm=0.1, length_km=15) + pandapipes.create_pipe_from_parameters(net, j4, j2, diameter_m=0.1, k_mm=0.1, length_km=10) + + pandapipes.create_valve(net, j4, j3, diameter_m=0.1, opened=True) + pandapipes.create_valve(net, j1, j3, diameter_m=0.07, opened=True) + pandapipes.create_valve(net, j1, j2, diameter_m=0.05, opened=True) + pandapipes.create_valve(net, j3, j0, diameter_m=0.01, opened=True) + + kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'colebrook', + 'mode': 'hydraulics', 'only_update_hydraulic_matrix': False} + + NonReturnValveController(net, element_index=[1, 3], **kwargs) + + run_control(net) + + +if __name__ == "__main__": + pytest.main([r'pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py']) \ No newline at end of file From 8e1e22b2439ab511107e6c51a366ebfb494b3e5c Mon Sep 17 00:00:00 2001 From: Tabea Trummel Date: Thu, 13 Aug 2020 16:40:12 +0200 Subject: [PATCH 2/4] add NonReturnValveController to documentation --- doc/source/controller/controller_classes.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/source/controller/controller_classes.rst b/doc/source/controller/controller_classes.rst index 7e394853..f99682f1 100644 --- a/doc/source/controller/controller_classes.rst +++ b/doc/source/controller/controller_classes.rst @@ -22,4 +22,15 @@ This is used to read the data from a DataSource and write it to a network. .. _ConstControl: .. autoclass:: pandapower.control.controller.const_control.ConstControl - :members: \ No newline at end of file + :members: + +NonReturnValveController +======================== + +The NonReturnValveController makes it possible to implement a valve +which only allows flow in the connection direction. In the backward +direction the valve can be regarded as ideally closed. + +.. _NonReturnValveController: +.. autoclass:: pandapipes.control.controller.non_return_valve_controller.NonReturnValveController + :members: From e7d6e16f84a720a2103402615e834fc58175bbe4 Mon Sep 17 00:00:00 2001 From: Tabea Trummel Date: Tue, 25 Aug 2020 14:23:39 +0200 Subject: [PATCH 3/4] - loops removed - improvement made --- .../controller/non_return_valve_controller.py | 61 ++++++++----------- .../test_non_return_valve_controller.py | 2 +- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/pandapipes/control/controller/non_return_valve_controller.py b/pandapipes/control/controller/non_return_valve_controller.py index 5674bc8f..42a3ee44 100644 --- a/pandapipes/control/controller/non_return_valve_controller.py +++ b/pandapipes/control/controller/non_return_valve_controller.py @@ -41,7 +41,7 @@ class NonReturnValveController(Controller): def __init__(self, net, element_index, profile_name=None, scale_factor=1.0, in_service=True, recycle=True, order=0, level=0, - drop_same_existing_ctrl=False, set_q_from_cosphi=False, matching_params=None, initial_pipeflow=False, + drop_same_existing_ctrl=False, matching_params=None, initial_run=False, **kwargs): if matching_params is None: @@ -50,7 +50,7 @@ def __init__(self, net, element_index, profile_name=None, # just calling init of the parent super().__init__(net, in_service=in_service, recycle=recycle, order=order, level=level, drop_same_existing_ctrl=drop_same_existing_ctrl, - matching_params=matching_params, initial_powerflow=initial_pipeflow, + matching_params=matching_params, initial_run=initial_run, **kwargs) self.matching_params = {"element_index": element_index} @@ -61,58 +61,49 @@ def __init__(self, net, element_index, profile_name=None, self.values = None self.profile_name = profile_name self.scale_factor = scale_factor - self.initial_pipeflow = initial_pipeflow + self.initial_run = initial_run self.kwargs = kwargs self.v_m_per_s = [] # current flow velocities at valves self.opened = [] # remember original user-defined values of opened - if set_q_from_cosphi: - logger.error("Parameter set_q_from_cosphi deprecated!") - raise ValueError - def initialize_control(self): """ - First calculation of a pipeflow. \n - Saving the user-defined values, determine valves with negative flow velocities, - set opened to False for these. + Saving the user-defined values and adapt types. """ - pp.pipeflow(self.net, self.kwargs) - self.opened = self.net.valve.loc[self.element_index, "opened"] - - j = 0 - for i in self.element_index: - self.v_m_per_s.append(self.net.res_valve.loc[i, "v_mean_m_per_s"]) - - if self.net.valve.loc[i, "opened"] and self.v_m_per_s[j] < 0: - # use the element indices, where opened = True, otherwise NaN would be in self.v_m_per_s - self.net.valve.loc[i, "opened"] = False - j += 1 + self.net.valve.loc[self.element_index, "type"] = "non-return valve" def is_converged(self): """ - Convergence Condition: If all flow velocities at the non-return valves are >= 0 or opened equal False. \n - Resetting the variable opened to user defaults. + Convergence Condition: If all flow velocities at the non-return valves are >= 0 or opened equal False. """ + if numpy.array(self.v_m_per_s).size == 0: + return False - for i in range(len(self.element_index)): - if self.net.valve.loc[self.element_index[i], "opened"] and self.v_m_per_s[i] < 0: - return False + if numpy.array(self.net.valve.loc[self.element_index, "opened"]).any() and \ + numpy.array(self.v_m_per_s).any() < 0: + return False - self.net.valve.loc[self.element_index, "opened"] = self.opened return True def control_step(self): """ - Check whether negative flow velocities are still present at non-return valves. + Check whether negative flow velocities are present at non-return valves, + set opened to False for these. """ pp.pipeflow(self.net, self.kwargs) - j = 0 - for i in self.element_index: - self.v_m_per_s.append(self.net.res_valve.loc[i, "v_mean_m_per_s"]) + self.v_m_per_s = numpy.array(self.net.res_valve.loc[self.element_index, "v_mean_m_per_s"]) + + ind_opened = numpy.where(self.net.valve.loc[self.element_index, "opened"]) + # use the element indices, where opened = True, otherwise NaN would be in self.v_m_per_s + + ind_negative_v = numpy.where(self.v_m_per_s[ind_opened[0]] < 0) + + self.net.valve.loc[numpy.array(self.element_index)[ind_negative_v[0]], "opened"] = False - if self.net.valve.loc[i, "opened"] and self.v_m_per_s[j] < 0: - # use the element indices, where opened = True, otherwise NaN would be in self.v_m_per_s - self.net.valve.loc[i, "opened"] = False - j += 1 + def finalize_control(self): + """ + Resetting the variable opened to user defaults. + """ + self.net.valve.loc[self.element_index, "opened"] = self.opened diff --git a/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py b/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py index b92665b6..989b06dd 100644 --- a/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py +++ b/pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py @@ -41,4 +41,4 @@ def test_nrv(): if __name__ == "__main__": - pytest.main([r'pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py']) \ No newline at end of file + pytest.main([r'pandapipes/test/pipeflow_internals/test_non_return_valve_controller.py']) From f00af59b426e46ed7e531850bb4b7cdbf027b64a Mon Sep 17 00:00:00 2001 From: Tabea Trummel Date: Wed, 26 Aug 2020 09:37:52 +0200 Subject: [PATCH 4/4] set non-return valve openings to true before calculations --- pandapipes/control/controller/non_return_valve_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandapipes/control/controller/non_return_valve_controller.py b/pandapipes/control/controller/non_return_valve_controller.py index 42a3ee44..2a9f40df 100644 --- a/pandapipes/control/controller/non_return_valve_controller.py +++ b/pandapipes/control/controller/non_return_valve_controller.py @@ -71,6 +71,7 @@ def initialize_control(self): Saving the user-defined values and adapt types. """ self.opened = self.net.valve.loc[self.element_index, "opened"] + self.net.valve.loc[self.element_index, "opened"] = True self.net.valve.loc[self.element_index, "type"] = "non-return valve" def is_converged(self):