-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Leakage Controller added, creating test in pipeflow_internals and documentation updated #115
base: develop
Are you sure you want to change the base?
Changes from 4 commits
fd92380
5e46f45
004349b
b07091b
1d4c21e
b0a0e8f
4d87d41
060c71e
0bf1713
01861a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# 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 LeakageController(Controller): | ||
""" | ||
Controller to consider a leak at pipes, valves or heat exchangers. | ||
|
||
:param net: The net in which the controller resides | ||
:type net: pandapipesNet | ||
:param element: Element (first only "pipe", "valve", "heat_exchanger") | ||
:type element: string | ||
:param element_index: IDs of controlled elements | ||
:type element_index: int[] | ||
:param output_area_m2: Size of the leakage in m^2 | ||
:type output_area_m2: float[] | ||
: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} | ||
>>> LeakageController(net, element='pipe', element_index=0, output_area_m2=1, **kwargs) | ||
>>> run_control(net) | ||
|
||
""" | ||
|
||
def __init__(self, net, element, element_index, output_area_m2, 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. intial_run instead of initial_pipeflow. Please update pandapipes. |
||
**kwargs): | ||
|
||
if element not in ["pipe", "valve", "heat_exchanger"]: | ||
raise Exception("Only 'pipe', 'valve' or 'heat_exchanger' is allowed as element.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice it we extend it also for junctions. |
||
|
||
if matching_params is None: | ||
matching_params = {"element": element, "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": element, "element_index": element_index} | ||
if numpy.isscalar(element_index): | ||
self.element_index = [element_index] | ||
self.output_area_m2 = [output_area_m2] | ||
else: | ||
self.element_index = element_index | ||
self.output_area_m2 = output_area_m2 | ||
self.element = element | ||
self.values = None | ||
self.profile_name = profile_name | ||
self.scale_factor = scale_factor | ||
self.initial_pipeflow = initial_pipeflow | ||
self.kwargs = kwargs | ||
self.rho_kg_per_m3 = [] # densities for the calculation of leakage mass flows | ||
self.v_m_per_s = [] # current flow velocities at pipes | ||
self.mass_flow_kg_per_s_init = [] # initial/ previous leakage mass flows | ||
self.mass_flow_kg_per_s = [] # current leakage mass flows | ||
self.leakage_index = [] # index of the sinks for leakages | ||
|
||
if set_q_from_cosphi: | ||
logger.error("Parameter set_q_from_cosphi deprecated!") | ||
raise ValueError | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't have reactive power in pandapipes |
||
|
||
def initialize_control(self): | ||
""" | ||
First calculation of a pipeflow without leakage. \n | ||
Then define the initial values and create the sinks representing the leaks. | ||
""" | ||
pp.pipeflow(self.net, self.kwargs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't do a pipeflow in intialize_control, use the flag initial_run instead |
||
|
||
for i in range(len(self.element_index)): | ||
self.init_values(self.element_index[i]) | ||
|
||
index = pp.create_sink(self.net, self.net[self.element].loc[self.element_index[i], "to_junction"], | ||
mdot_kg_per_s=0, name="leakage"+str(i)) | ||
self.leakage_index.append(index) | ||
self.mass_flow_kg_per_s_init.append(0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please prevent loops |
||
|
||
def init_values(self, index): | ||
""" | ||
Initialize the flow velocity and density for the individual control steps. | ||
""" | ||
self.v_m_per_s.append(self.net["res_"+self.element].loc[index, "v_mean_m_per_s"]) | ||
|
||
temp_1 = self.net.res_junction.loc[self.net[self.element].loc[index, "from_junction"], "t_k"] | ||
temp_2 = self.net.res_junction.loc[self.net[self.element].loc[index, "to_junction"], "t_k"] | ||
self.rho_kg_per_m3.append((self.net.fluid.get_density(temp_1) + self.net.fluid.get_density(temp_2)) / 2) | ||
|
||
def is_converged(self): | ||
""" | ||
Convergence Condition: Difference between mass flows smaller than 1e-5 = 0.00001 \n | ||
Delete the sinks that represented the leaks. | ||
""" | ||
if not self.mass_flow_kg_per_s: | ||
return False | ||
|
||
for i in range(len(self.element_index)): | ||
if abs(self.mass_flow_kg_per_s_init[i] - self.mass_flow_kg_per_s[i]) > 1e-5: | ||
return False | ||
|
||
self.net.sink = self.net.sink.drop(labels=self.leakage_index) | ||
self.net.res_sink = self.net.res_sink.drop(labels=self.leakage_index) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should happen in finalize_control |
||
|
||
return True | ||
|
||
def control_step(self): | ||
""" | ||
Calculate the new mass flow for each leak using the density, flow velocity and outlet area. | ||
""" | ||
pp.pipeflow(self.net, self.kwargs) | ||
self.mass_flow_kg_per_s = [] | ||
self.v_m_per_s = [] | ||
self.rho_kg_per_m3 = [] | ||
|
||
for i in range(len(self.element_index)): | ||
self.init_values(self.element_index[i]) | ||
|
||
self.net.sink.loc[self.leakage_index[i], "mdot_kg_per_s"] = self.rho_kg_per_m3[i] * self.v_m_per_s[i] * \ | ||
self.output_area_m2[i] | ||
self.mass_flow_kg_per_s.append(self.net.sink.loc[self.leakage_index[i], "mdot_kg_per_s"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prevent loops due to performance issues. |
||
|
||
self.mass_flow_kg_per_s_init = self.mass_flow_kg_per_s |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# 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 LeakageController, run_control | ||
|
||
|
||
def test_one_pipe_one_leakage(): | ||
net = pandapipes.create_empty_network("net", fluid="water", add_stdtypes=True) | ||
|
||
j0 = pandapipes.create_junction(net, pn_bar=5, tfluid_k=293.15) | ||
j1 = pandapipes.create_junction(net, pn_bar=5, tfluid_k=293.15) | ||
|
||
pandapipes.create_ext_grid(net, j0, p_bar=5, t_k=293.15, type="pt") | ||
|
||
pandapipes.create_sink(net, j1, mdot_kg_per_s=1) | ||
|
||
pandapipes.create_pipe_from_parameters(net, j0, j1, diameter_m=0.75, k_mm=0.1, length_km=2) | ||
|
||
kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'colebrook', | ||
'mode': 'hydraulics', 'only_update_hydraulic_matrix': False} | ||
|
||
LeakageController(net, element='pipe', element_index=0, output_area_m2=1, **kwargs) | ||
|
||
run_control(net) | ||
|
||
|
||
def test_two_pipes_two_leakages(): | ||
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) | ||
|
||
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, j2, mdot_kg_per_s=0.5) | ||
|
||
pandapipes.create_pipe_from_parameters(net, j0, j1, diameter_m=0.75, k_mm=0.1, length_km=2) | ||
pandapipes.create_pipe_from_parameters(net, j1, j2, diameter_m=0.6, k_mm=0.1, length_km=3) | ||
|
||
kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'colebrook', | ||
'mode': 'hydraulics', 'only_update_hydraulic_matrix': False} | ||
|
||
LeakageController(net, element='pipe', element_index=[0, 1], output_area_m2=[1, 2.5], **kwargs) | ||
|
||
run_control(net) | ||
|
||
|
||
def test_one_valve_one_leakage(): | ||
net = pandapipes.create_empty_network("net", fluid="water", add_stdtypes=True) | ||
|
||
j0 = pandapipes.create_junction(net, pn_bar=2, tfluid_k=293.15) | ||
j1 = pandapipes.create_junction(net, pn_bar=2, tfluid_k=293.15) | ||
|
||
pandapipes.create_ext_grid(net, j0, p_bar=2, t_k=293.15, type="pt") | ||
|
||
pandapipes.create_sink(net, j1, mdot_kg_per_s=0.5) | ||
|
||
pandapipes.create_valve(net, j0, j1, diameter_m=0.1, opened=True) | ||
|
||
kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'nikuradse', | ||
'mode': 'hydraulics', 'only_update_hydraulic_matrix': False} | ||
|
||
LeakageController(net, element='valve', element_index=0, output_area_m2=0.5, **kwargs) | ||
|
||
run_control(net) | ||
|
||
|
||
def test_one_heat_exchanger_one_leakage(): | ||
net = pandapipes.create_empty_network("net", fluid="water", add_stdtypes=True) | ||
|
||
j0 = pandapipes.create_junction(net, pn_bar=1, tfluid_k=293.15) | ||
j1 = pandapipes.create_junction(net, pn_bar=1, tfluid_k=293.15) | ||
|
||
pandapipes.create_ext_grid(net, j0, p_bar=1, t_k=350, type="pt") | ||
|
||
pandapipes.create_sink(net, j1, mdot_kg_per_s=0.5) | ||
|
||
pandapipes.create_heat_exchanger(net, j0, j1, diameter_m=0.8, qext_w=20000) | ||
|
||
kwargs = {'stop_condition': 'tol', 'iter': 100, 'tol_p': 1e-7, 'tol_v': 1e-7, 'friction_model': 'colebrook', | ||
'mode': 'all', 'only_update_hydraulic_matrix': False} | ||
|
||
LeakageController(net, element='heat_exchanger', element_index=0, output_area_m2=0.1, **kwargs) | ||
|
||
run_control(net) | ||
|
||
|
||
if __name__ == "__main__": | ||
pytest.main([r'pandapipes/test/pipeflow_internals/test_leakage_controller.py']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also be able to use something like kg_per_m_s meaning mass per meter and second.