Skip to content
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

NonReturnValveController created, test in pipeflow_internals added and docu updated #118

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion doc/source/controller/controller_classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
: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:
1 change: 1 addition & 0 deletions pandapipes/control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
118 changes: 118 additions & 0 deletions pandapipes/control/controller/non_return_valve_controller.py
Original file line number Diff line number Diff line change
@@ -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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we changed the attribute from initial_powerflow to initial_run. Please update your pandapower and pandapipes version!

**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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have q-values in pandapipes. Q-values stands for reactive power.


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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do a pipeflow within a controller. Do it with the flag initial_run


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be solved without a loop which would make it much more performant! Should me something like:
rel_valve = self.net.valve.loc[self.element_index, :]
rel_valve.loc[rel_valve["opened] and rel_valve['v_mean_m_per_s']<0, 'opened] = False

Furthermore, I think, in the initial step you should only do one thing: Open all non return valves. Following reason: Right now I don't think the controller acts the way it should. Imagine following thing. Right now you only consider the valves which are open. But if you run it once, you close all valves which have a v_mean <0. Now you change something in your grid and run it again. Now, the valves you closed in the first run are not considered anymore. However, I think they still should. I would rather do it like this: All valves covered in the element_index should be considered.


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: converged = np.all(self.net.valve.loc[self.element_index, 'v_mean_m_per_s')>=0)


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing what I was thinking about: I would return to the initial state, meaning all valves should have the same values before you used the controller. Meaning, if the user set some non return valves on open and you closed the now, I would in a finalizing step return to a open valve, as only if you really want to use the controller the controller should also have an effect.

Original file line number Diff line number Diff line change
@@ -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'])