diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b37c29ab..30a65978 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,17 +75,18 @@ jobs: needs: upload strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] os: [ ubuntu-latest, windows-latest ] steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest igraph pytest-split numba + python -m pip install pytest igraph pytest-split + if ${{ matrix.python-version != '3.11' }}; then python -m pip install numba; fi - name: Install pandapipes from TestPyPI if: ${{ inputs.upload_server == 'testpypi'}} run: | diff --git a/.github/workflows/run_tests_develop.yml b/.github/workflows/run_tests_develop.yml index 287eedfa..a172d407 100644 --- a/.github/workflows/run_tests_develop.yml +++ b/.github/workflows/run_tests_develop.yml @@ -18,17 +18,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest igraph pytest-split numba + python -m pip install pytest igraph pytest-split + if ${{ matrix.python-version != '3.11' }}; then python -m pip install numba; fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python -m pip install git+https://github.com/e2nIEE/pandapower@develop#egg=pandapower pip install . @@ -55,12 +56,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8'] + python-version: ['3.10'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -86,17 +87,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest nbmake pytest-xdist pytest-split igraph numba + python -m pip install pytest nbmake pytest-xdist pytest-split igraph + if ${{ matrix.python-version != '3.11' }}; then python -m pip install numba; fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python -m pip install git+https://github.com/e2nIEE/pandapower@develop#egg=pandapower pip install . @@ -111,11 +113,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8' ] + python-version: [ '3.10' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Check docs for Python ${{ matrix.python-version }} diff --git a/.github/workflows/run_tests_master.yml b/.github/workflows/run_tests_master.yml index 7d3240e3..ad856db1 100644 --- a/.github/workflows/run_tests_master.yml +++ b/.github/workflows/run_tests_master.yml @@ -17,18 +17,19 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest igraph pytest-split numba + python -m pip install pytest igraph pytest-split + if ${{ matrix.python-version != '3.11' }}; then python -m pip install numba; fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python -m pip install git+https://github.com/e2nIEE/pandapower@master#egg=pandapower; pip install . @@ -54,17 +55,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest nbmake pytest-xdist pytest-split igraph numba + python -m pip install pytest nbmake pytest-xdist pytest-split igraph + if ${{ matrix.python-version != '3.11' }}; then python -m pip install numba; fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python -m pip install git+https://github.com/e2nIEE/pandapower@master#egg=pandapower pip install . @@ -79,11 +81,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8' ] + python-version: [ '3.10' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Check docs for Python ${{ matrix.python-version }} diff --git a/.readthedocs.yml b/.readthedocs.yml index 9fcbb7ac..b6911a40 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,9 +1,10 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.9" + python: - version: 3.7 install: - - method: pip - path: . - extra_requirements: - - docs + - requirements: doc/requirements.txt \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8b911be3..58ff9bc6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,18 @@ Change Log ============= +[upcoming release] - 2023-..-.. +------------------------------- +- [ADDED] multiple creation of heat exchanger +- [ADDED] support Python 3.11 (now included in test pipeline) +- [CHANGED] dropped support for Python 3.7 (no longer included in test pipeline) +- [CHANGED] connectivity check now separated by hydraulics and heat_transfer calculation, so that also results can differ in some rows (NaN or not) +- [CHANGED] dynamic creation of lookups for getting pit as pandas tables +- [CHANGED] components can have their own internal arrays for specific calculations (e.g. for compressor pressure ratio), so that the pit does not need to include such component specific entries +- [CHANGED] .readthedocs.yml due to deprecation +- [FIXED] in STANET converter: bug fix for heat exchanger creation and external temperatures of pipes added +- [REMOVED] broken travis badge removed from readme + [0.8.5] - 2023-06-19 ------------------------------- - [FIXED] consider ambient pressure in calculation of compression power for pumps/compressors diff --git a/README.rst b/README.rst index 361db9b2..6a388649 100644 --- a/README.rst +++ b/README.rst @@ -18,10 +18,6 @@ :target: http://pandapipes.readthedocs.io/ :alt: docs -.. image:: https://travis-ci.org/e2nIEE/pandapipes.svg?branch=master - :target: https://travis-ci.org/e2nIEE/pandapipes/branches - :alt: travis - .. image:: https://codecov.io/gh/e2nIEE/pandapipes/branch/master/graph/badge.svg :target: https://codecov.io/github/e2nIEE/pandapipes?branch=master :alt: codecov diff --git a/doc/source/about/license.rst b/doc/source/about/license.rst index ee323bc7..cc6c655d 100644 --- a/doc/source/about/license.rst +++ b/doc/source/about/license.rst @@ -7,32 +7,7 @@ License .. highlight:: none -pandapipes is published under the following 3-clause BSD license: :: +pandapipes is published under the following 3-clause BSD license: - - Copyright (c) 2020-2023 by Fraunhofer Institute for Energy Economics and - Energy System Technology (IEE) Kassel and individual contributors - (see AUTHORS file for details). All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided - with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be used to - endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + .. literalinclude:: ../../../LICENSE diff --git a/doc/source/components/heat_exchanger/heat_exchanger_component.rst b/doc/source/components/heat_exchanger/heat_exchanger_component.rst index a853f442..7ad146af 100644 --- a/doc/source/components/heat_exchanger/heat_exchanger_component.rst +++ b/doc/source/components/heat_exchanger/heat_exchanger_component.rst @@ -7,9 +7,15 @@ Create Function .. _create_heat_exchanger: +For creating a single heat exchanger unit: .. autofunction:: pandapipes.create_heat_exchanger +For creating multiple heat exchangers at once: + +.. autofunction:: pandapipes.create_heat_exchangers + + Component Table Data ==================== diff --git a/doc/source/fluid_properties/composition.csv b/doc/source/fluid_properties/composition.csv new file mode 100644 index 00000000..6f88aa3f --- /dev/null +++ b/doc/source/fluid_properties/composition.csv @@ -0,0 +1,9 @@ +;**Biomethane pure**;**Biomethane treated** +methane; 96.15% ; 90.94% +nitrogen; 0.75 % ;0.69 % +carbon dioxide; 2.9 % ;2.68 % +oxygen; 0.2 % ; 0.19 % +propane; 0 % ; 5 % +butane; 0 %; 0.5 % +Wobbe-Index (normal conditions) :math:`[\frac{KWh}{m^3}]` ; 13.9 ; 14.9 +HHV (normal conditions) :math:`[\frac{KWh}{m^3}]`; 10.6 ; 11.6 \ No newline at end of file diff --git a/doc/source/fluid_properties/create_fluids.rst b/doc/source/fluid_properties/create_fluids.rst index 3c5dd9d8..ec2fbd1b 100644 --- a/doc/source/fluid_properties/create_fluids.rst +++ b/doc/source/fluid_properties/create_fluids.rst @@ -10,8 +10,16 @@ Fluid from Library ================== In the fluid library some default fluids are already implemented. Currently it is -possible to work with the default fluids high and low calorific natural -gas (hgas and lgas), hydrogen, methane, water and air. The values are loaded from txt-files in +possible to work with the default fluids: + +- ``hgas`` and ``lgas`` (high and low calorific natural gas), +- ``hydrogen``, +- ``methane``, +- ``water``, +- ``biomethane_pure`` and ``biomathane_treated`` (see `here `_ for the compositions), +- ``air``. + +The values are loaded from txt-files in the 'pandapipes/properties/[fluid name]' folder. One of these default fluids can be created and added to an existing network by calling the following function. diff --git a/doc/source/fluid_properties/fluids.rst b/doc/source/fluid_properties/fluids.rst index 2689b004..d3e63a03 100644 --- a/doc/source/fluid_properties/fluids.rst +++ b/doc/source/fluid_properties/fluids.rst @@ -4,9 +4,29 @@ Fluid Properties in pandapipes The pipelines in the pandapipes network are run with a certain fluid. This can be chosen individually for the net from the fluid library or by own creation. The fluids -are defined by certain properties. In the fluid library currently high and low calorific natural -gas (hgas and lgas), hydrogen, methane, water and air are implemented with default properties. +are defined by certain properties. +The following fluids currently exist in the library: + +- ``hgas`` and ``lgas`` (high and low calorific natural gas), +- ``hydrogen``, +- ``methane``, +- ``water``, +- ``biomethane_pure`` and ``biomathane_treated`` (compositions below) +- ``air``. + + +.. Note:: + **Biomethane(s)**: + A particularity of injecting biomethane in the gas grid in Germany is the addition of LPG to enhance + the Wobbe-Index and the heating value of the gas and thus make it equivalent to the (high-calorific) natural gas transported + in the grid. This addition is done in a gas treatment unit (Biogaseinspeiseanlage) upstream of the biomethane + feed-in junction. The properties of the biomethane(s) were computed based on the package `CoolProp `_. + + .. csv-table:: + :file: composition.csv + :delim: ; + :widths: 10, 10, 10 The Fluid Class =============== diff --git a/pandapipes/component_models/abstract_models/base_component.py b/pandapipes/component_models/abstract_models/base_component.py index cdfb0f09..105035f3 100644 --- a/pandapipes/component_models/abstract_models/base_component.py +++ b/pandapipes/component_models/abstract_models/base_component.py @@ -33,7 +33,7 @@ def init_results(cls, net): return res_table @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. @@ -43,10 +43,8 @@ def extract_results(cls, net, options, branch_results, nodes_connected, branches :type options: :param branch_results: :type branch_results: - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: + :param mode: + :type mode: :return: No Output. """ raise NotImplementedError @@ -80,6 +78,15 @@ def adaption_before_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lo def adaption_after_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): pass + @classmethod + def adaption_before_derivatives_thermal(cls, net, branch_pit, node_pit, idx_lookups, options): + pass + + @classmethod + def adaption_after_derivatives_thermal(cls, net, branch_pit, node_pit, idx_lookups, options): + pass + + @classmethod def create_node_lookups(cls, net, ft_lookups, table_lookup, idx_lookups, current_start, current_table, internal_nodes_lookup): @@ -128,65 +135,43 @@ def create_branch_lookups(cls, net, ft_lookups, table_lookup, idx_lookups, curre return current_start, current_table @classmethod - def create_pit_node_entries(cls, net, node_pit): + def create_component_array(cls, net, component_pits): """ - Function which creates pit branch entries. + Function which creates an internal array of the component in analogy to the pit, but with + component specific entries, that are not needed in the pit. :param net: The pandapipes network :type net: pandapipesNet - :param node_pit: - :type node_pit: - :return: No Output. + :param component_pits: dictionary of component specific arrays + :type component_pits: dict + :return: + :rtype: """ pass @classmethod - def create_pit_branch_entries(cls, net, branch_pit): + def create_pit_node_entries(cls, net, node_pit): """ Function which creates pit branch entries. :param net: The pandapipes network :type net: pandapipesNet - :param branch_pit: - :type branch_pit: - :return: No Output. - """ - pass - - @classmethod - def calculate_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): - """ - Function which creates derivatives. - - :param net: The pandapipes network - :type net: pandapipesNet - :param branch_pit: - :type branch_pit: :param node_pit: :type node_pit: - :param idx_lookups: - :type idx_lookups: - :param options: - :type options: :return: No Output. """ pass @classmethod - def calculate_derivatives_thermal(cls, net, branch_pit, node_pit, idx_lookups, options): + def create_pit_branch_entries(cls, net, branch_pit): """ - Function which creates derivatives. + Function which creates pit branch entries. :param net: The pandapipes network :type net: pandapipesNet :param branch_pit: :type branch_pit: - :param node_pit: - :type node_pit: - :param idx_lookups: - :type idx_lookups: - :param options: - :type options: :return: No Output. """ pass + diff --git a/pandapipes/component_models/abstract_models/branch_models.py b/pandapipes/component_models/abstract_models/branch_models.py index 3ee41146..f9d98d6e 100644 --- a/pandapipes/component_models/abstract_models/branch_models.py +++ b/pandapipes/component_models/abstract_models/branch_models.py @@ -5,10 +5,7 @@ import numpy as np from pandapipes.component_models.abstract_models.base_component import Component -from pandapipes.idx_branch import LENGTH, D, AREA, RHO, VINIT, ALPHA, QEXT, TEXT, branch_cols, \ - T_OUT, CP, VINIT_T, FROM_NODE_T, TL, \ - JAC_DERIV_DT, JAC_DERIV_DT1, JAC_DERIV_DT_NODE, LOAD_VEC_BRANCHES_T, LOAD_VEC_NODES_T -from pandapipes.idx_node import TINIT as TINIT_NODE +from pandapipes.idx_branch import VINIT, branch_cols from pandapipes.pf.pipeflow_setup import get_table_number, get_lookup try: @@ -95,69 +92,5 @@ def create_pit_branch_entries(cls, net, branch_pit): return branch_component_pit, node_pit, from_nodes, to_nodes @classmethod - def calculate_derivatives_thermal(cls, net, branch_pit, node_pit, idx_lookups, options): - """ - Function which creates derivatives of the temperature. - - :param net: - :type net: - :param branch_pit: - :type branch_pit: - :param node_pit: - :type node_pit: - :param idx_lookups: - :type idx_lookups: - :param options: - :type options: - :return: No Output. - """ - f, t = idx_lookups[cls.table_name()] - branch_component_pit = branch_pit[f:t, :] - cp = branch_component_pit[:, CP] - rho = branch_component_pit[:, RHO] - v_init = branch_component_pit[:, VINIT_T] - from_nodes = branch_component_pit[:, FROM_NODE_T].astype(np.int32) - t_init_i = node_pit[from_nodes, TINIT_NODE] - t_init_i1 = branch_component_pit[:, T_OUT] - t_amb = branch_component_pit[:, TEXT] - area = branch_component_pit[:, AREA] - length = branch_component_pit[:, LENGTH] - alpha = branch_component_pit[:, ALPHA] * np.pi * branch_component_pit[:, D] - cls.calculate_temperature_lift(net, branch_component_pit, node_pit) - tl = branch_component_pit[:, TL] - qext = branch_component_pit[:, QEXT] - t_m = (t_init_i1 + t_init_i) / 2 - - branch_component_pit[:, LOAD_VEC_BRANCHES_T] = \ - -(rho * area * cp * v_init * (-t_init_i + t_init_i1 - tl) - - alpha * (t_amb - t_m) * length + qext) - - branch_component_pit[:, JAC_DERIV_DT] = - rho * area * cp * v_init + alpha / 2 * length - branch_component_pit[:, JAC_DERIV_DT1] = rho * area * cp * v_init + alpha / 2 * length - - branch_component_pit[:, JAC_DERIV_DT_NODE] = rho * v_init * branch_component_pit[:, AREA] - branch_component_pit[:, LOAD_VEC_NODES_T] = rho * v_init * branch_component_pit[:, AREA] \ - * t_init_i1 - - @classmethod - def adaption_before_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): - pass - - @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): - """ - - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: - """ - raise NotImplementedError - - @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError diff --git a/pandapipes/component_models/abstract_models/branch_w_internals_models.py b/pandapipes/component_models/abstract_models/branch_w_internals_models.py index 6861223e..0478254a 100644 --- a/pandapipes/component_models/abstract_models/branch_w_internals_models.py +++ b/pandapipes/component_models/abstract_models/branch_w_internals_models.py @@ -6,7 +6,7 @@ from pandapipes.component_models.abstract_models.branch_models import BranchComponent from pandapipes.component_models.component_toolbox import set_entry_check_repeat -from pandapipes.idx_branch import ACTIVE, FROM_NODE, TO_NODE, TINIT, RHO, ETA, CP, ELEMENT_IDX +from pandapipes.idx_branch import ACTIVE, FROM_NODE, TO_NODE, RHO, ETA, CP, ELEMENT_IDX, TOUTINIT from pandapipes.idx_node import L, node_cols, TINIT as TINIT_NODE from pandapipes.pf.pipeflow_setup import add_table_lookup, get_lookup, get_table_number from pandapipes.properties.fluids import get_fluid @@ -196,13 +196,12 @@ def create_pit_branch_entries(cls, net, branch_pit): internal_pipe_number, has_internals) branch_w_internals_pit[:, FROM_NODE] = from_nodes branch_w_internals_pit[:, TO_NODE] = to_nodes - branch_w_internals_pit[:, TINIT] = (node_pit[from_nodes, TINIT_NODE] + node_pit[ - to_nodes, TINIT_NODE]) / 2 + branch_w_internals_pit[:, TOUTINIT] = node_pit[to_nodes, TINIT_NODE] + tm = (node_pit[from_nodes, TINIT_NODE] + branch_w_internals_pit[:, TOUTINIT]) / 2 fluid = get_fluid(net) - branch_w_internals_pit[:, RHO] = fluid.get_density(branch_w_internals_pit[:, TINIT]) - branch_w_internals_pit[:, ETA] = fluid.get_viscosity(branch_w_internals_pit[:, TINIT]) - branch_w_internals_pit[:, CP] = fluid.get_heat_capacity(branch_w_internals_pit[:, TINIT]) - + branch_w_internals_pit[:, RHO] = fluid.get_density(tm) + branch_w_internals_pit[:, ETA] = fluid.get_viscosity(tm) + branch_w_internals_pit[:, CP] = fluid.get_heat_capacity(tm) return branch_w_internals_pit, internal_pipe_number @classmethod @@ -217,7 +216,7 @@ def get_internal_pipe_number(cls, net): return np.array(net[cls.table_name()].sections.values) @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError @classmethod diff --git a/pandapipes/component_models/abstract_models/branch_wo_internals_models.py b/pandapipes/component_models/abstract_models/branch_wo_internals_models.py index 0efa325a..2867c241 100644 --- a/pandapipes/component_models/abstract_models/branch_wo_internals_models.py +++ b/pandapipes/component_models/abstract_models/branch_wo_internals_models.py @@ -4,7 +4,7 @@ from pandapipes.component_models.abstract_models.branch_models import BranchComponent -from pandapipes.idx_branch import FROM_NODE, TO_NODE, TINIT, ELEMENT_IDX, RHO, ETA, CP, ACTIVE +from pandapipes.idx_branch import FROM_NODE, TO_NODE, TOUTINIT, ELEMENT_IDX, RHO, ETA, CP, ACTIVE from pandapipes.idx_node import TINIT as TINIT_NODE from pandapipes.pf.pipeflow_setup import add_table_lookup @@ -82,12 +82,12 @@ def create_pit_branch_entries(cls, net, branch_pit): branch_wo_internals_pit[:, ELEMENT_IDX] = net[cls.table_name()].index.values branch_wo_internals_pit[:, FROM_NODE] = from_nodes branch_wo_internals_pit[:, TO_NODE] = to_nodes - branch_wo_internals_pit[:, TINIT] = (node_pit[from_nodes, TINIT_NODE] - + node_pit[to_nodes, TINIT_NODE]) / 2 + branch_wo_internals_pit[:, TOUTINIT] = node_pit[to_nodes, TINIT_NODE] + tm = (node_pit[from_nodes, TINIT_NODE] + branch_wo_internals_pit[:, TOUTINIT]) / 2 fluid = get_fluid(net) - branch_wo_internals_pit[:, RHO] = fluid.get_density(branch_wo_internals_pit[:, TINIT]) - branch_wo_internals_pit[:, ETA] = fluid.get_viscosity(branch_wo_internals_pit[:, TINIT]) - branch_wo_internals_pit[:, CP] = fluid.get_heat_capacity(branch_wo_internals_pit[:, TINIT]) + branch_wo_internals_pit[:, RHO] = fluid.get_density(tm) + branch_wo_internals_pit[:, ETA] = fluid.get_viscosity(tm) + branch_wo_internals_pit[:, CP] = fluid.get_heat_capacity(tm) branch_wo_internals_pit[:, ACTIVE] = net[cls.table_name()][cls.active_identifier()].values return branch_wo_internals_pit @@ -100,5 +100,5 @@ def get_connected_node_type(cls): raise NotImplementedError @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError diff --git a/pandapipes/component_models/abstract_models/branch_wzerolength_models.py b/pandapipes/component_models/abstract_models/branch_wzerolength_models.py index dc224f8a..b9da3028 100644 --- a/pandapipes/component_models/abstract_models/branch_wzerolength_models.py +++ b/pandapipes/component_models/abstract_models/branch_wzerolength_models.py @@ -61,7 +61,7 @@ def create_pit_branch_entries(cls, net, branch_pit): return branch_wzerolength_pit @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError @classmethod diff --git a/pandapipes/component_models/abstract_models/circulation_pump.py b/pandapipes/component_models/abstract_models/circulation_pump.py index cbcbfb60..3c3e338d 100644 --- a/pandapipes/component_models/abstract_models/circulation_pump.py +++ b/pandapipes/component_models/abstract_models/circulation_pump.py @@ -93,18 +93,12 @@ def create_pit_branch_entries(cls, net, branch_pit): circ_pump_pit[:, ACTIVE] = False @classmethod - def calculate_temperature_lift(cls, net, pipe_pit, node_pit): - raise NotImplementedError - - @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: + :param mode: + :type mode: :param branch_results: :type branch_results: :param net: The pandapipes network diff --git a/pandapipes/component_models/abstract_models/const_flow_models.py b/pandapipes/component_models/abstract_models/const_flow_models.py index c49a58ec..a10a4d0e 100644 --- a/pandapipes/component_models/abstract_models/const_flow_models.py +++ b/pandapipes/component_models/abstract_models/const_flow_models.py @@ -51,14 +51,12 @@ def create_pit_node_entries(cls, net, node_pit): node_pit[index, LOAD] += loads_sum @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: + :param mode: + :type mode: :param branch_results: :type branch_results: :param net: The pandapipes network @@ -74,8 +72,8 @@ def extract_results(cls, net, options, branch_results, nodes_connected, branches is_loads = loads.in_service.values fj, tj = get_lookup(net, "node", "from_to")[cls.get_connected_node_type().table_name()] junct_pit = net["_pit"]["node"][fj:tj, :] - nodes_connected = get_lookup(net, "node", "active")[fj:tj] - is_juncts = np.isin(loads.junction.values, junct_pit[nodes_connected, ELEMENT_IDX]) + nodes_connected_hyd = get_lookup(net, "node", "active_hydraulics")[fj:tj] + is_juncts = np.isin(loads.junction.values, junct_pit[nodes_connected_hyd, ELEMENT_IDX]) is_calc = is_loads & is_juncts res_table["mdot_kg_per_s"].values[is_calc] = loads.mdot_kg_per_s.values[is_calc] \ diff --git a/pandapipes/component_models/abstract_models/node_element_models.py b/pandapipes/component_models/abstract_models/node_element_models.py index 04e594e2..0792fa53 100644 --- a/pandapipes/component_models/abstract_models/node_element_models.py +++ b/pandapipes/component_models/abstract_models/node_element_models.py @@ -51,5 +51,5 @@ def create_pit_node_entries(cls, net, node_pit): raise NotImplementedError @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError diff --git a/pandapipes/component_models/abstract_models/node_models.py b/pandapipes/component_models/abstract_models/node_models.py index 5a917bef..0ce18e4a 100644 --- a/pandapipes/component_models/abstract_models/node_models.py +++ b/pandapipes/component_models/abstract_models/node_models.py @@ -66,5 +66,5 @@ def get_result_table(cls, net): raise NotImplementedError @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): raise NotImplementedError diff --git a/pandapipes/component_models/circulation_pump_mass_component.py b/pandapipes/component_models/circulation_pump_mass_component.py index d18e299d..699d1e41 100644 --- a/pandapipes/component_models/circulation_pump_mass_component.py +++ b/pandapipes/component_models/circulation_pump_mass_component.py @@ -72,6 +72,3 @@ def create_pit_node_entries(cls, net, node_pit): index = junction_idx_lookups[juncts] node_pit[index, LOAD] += loads_sum - @classmethod - def calculate_temperature_lift(cls, net, pipe_pit, node_pit): - pass diff --git a/pandapipes/component_models/circulation_pump_pressure_component.py b/pandapipes/component_models/circulation_pump_pressure_component.py index c1ef5abd..6cfbf2eb 100644 --- a/pandapipes/component_models/circulation_pump_pressure_component.py +++ b/pandapipes/component_models/circulation_pump_pressure_component.py @@ -63,7 +63,3 @@ def create_pit_node_entries(cls, net, node_pit): p_in = press - circ_pump.plift_bar.values set_fixed_node_entries(net, node_pit, junction, circ_pump.type.values, p_in, None, cls.get_connected_node_type(), "p") - - @classmethod - def calculate_temperature_lift(cls, net, pipe_pit, node_pit): - pass diff --git a/pandapipes/component_models/component_toolbox.py b/pandapipes/component_models/component_toolbox.py index 04f357b3..30526143 100644 --- a/pandapipes/component_models/component_toolbox.py +++ b/pandapipes/component_models/component_toolbox.py @@ -5,6 +5,7 @@ import numpy as np import pandas as pd +from pandapipes import get_fluid from pandapipes.constants import NORMAL_PRESSURE, TEMP_GRADIENT_KPM, AVG_TEMPERATURE_K, \ HEIGHT_EXPONENT from pandapipes.idx_branch import LOAD_VEC_NODES, FROM_NODE, TO_NODE @@ -177,3 +178,45 @@ def get_mass_flow_at_nodes(net, node_pit, branch_pit, eg_nodes, comp): raise UserWarning("In component %s: Something went wrong with the mass flow balance. " "Please report this error at github." % comp.__name__) return sum_mass_flows, inverse_nodes, counts + + +def standard_branch_wo_internals_result_lookup(net): + required_results_hyd = [ + ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("mdot_to_kg_per_s", "mf_to"), + ("mdot_from_kg_per_s", "mf_from"), ("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), + ("reynolds", "reynolds") + ] + required_results_ht = [("t_from_k", "temp_from"), ("t_to_k", "temp_to")] + + if get_fluid(net).is_gas: + required_results_hyd.extend([ + ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), + ("v_mean_m_per_s", "v_gas_mean"), ("normfactor_from", "normfactor_from"), + ("normfactor_to", "normfactor_to") + ]) + else: + required_results_hyd.extend([("v_mean_m_per_s", "v_mps")]) + + return required_results_hyd, required_results_ht + + +def get_component_array(net, component_name, component_type="branch", only_active=True): + """ + Returns the internal array of a component. + + :param net: The pandapipes network + :type net: pandapipesNet + :param component_name: Table name of the component for which to extract internal array + :type component_name: str + :param component_type: Type of component that is considered ("branch" or "node") + :type component_type: str, default "branch" + :param only_active: If True, only return entries of active elements (included in _active_pit) + :type only_active: bool + :return: component_array - internal array of the component + :rtype: numpy.ndarray + """ + f_all, t_all = get_lookup(net, component_type, "from_to")[component_name] + if not only_active: + return net["_pit"]["components"][component_name] + in_service_elm = get_lookup(net, component_type, "active_hydraulics")[f_all:t_all] + return net["_pit"]["components"][component_name][in_service_elm] diff --git a/pandapipes/component_models/compressor_component.py b/pandapipes/component_models/compressor_component.py index 7c09300a..d9d5e811 100644 --- a/pandapipes/component_models/compressor_component.py +++ b/pandapipes/component_models/compressor_component.py @@ -5,10 +5,10 @@ import numpy as np from numpy import dtype +from pandapipes.component_models.component_toolbox import get_component_array from pandapipes.component_models.junction_component import Junction from pandapipes.component_models.pump_component import Pump -from pandapipes.idx_branch import VINIT, D, AREA, LOSS_COEFFICIENT as LC, FROM_NODE, PL,\ - PRESSURE_RATIO +from pandapipes.idx_branch import VINIT, D, AREA, LOSS_COEFFICIENT as LC, FROM_NODE, PL from pandapipes.idx_node import PINIT, PAMB @@ -16,6 +16,9 @@ class Compressor(Pump): """ """ + PRESSURE_RATIO = 0 + + internal_cols = 1 @classmethod def table_name(cls): @@ -41,23 +44,42 @@ def create_pit_branch_entries(cls, net, branch_pit): compressor_pit[:, D] = 0.1 compressor_pit[:, AREA] = compressor_pit[:, D] ** 2 * np.pi / 4 compressor_pit[:, LC] = 0 - compressor_pit[:, PRESSURE_RATIO] = net[cls.table_name()].pressure_ratio.values + + @classmethod + def create_component_array(cls, net, component_pits): + """ + Function which creates an internal array of the component in analogy to the pit, but with + component specific entries, that are not needed in the pit. + + :param net: The pandapipes network + :type net: pandapipesNet + :param component_pits: dictionary of component specific arrays + :type component_pits: dict + :return: + :rtype: + """ + tbl = net[cls.table_name()] + compr_array = np.zeros(shape=(len(tbl), cls.internal_cols), dtype=np.float64) + compr_array[:, cls.PRESSURE_RATIO] = net[cls.table_name()].pressure_ratio.values + component_pits[cls.table_name()] = compr_array @classmethod def adaption_before_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): # calculation of pressure lift f, t = idx_lookups[cls.table_name()] - compressor_pit = branch_pit[f:t, :] + compressor_branch_pit = branch_pit[f:t, :] + compressor_array = get_component_array(net, cls.table_name()) - from_nodes = compressor_pit[:, FROM_NODE].astype(np.int32) + from_nodes = compressor_branch_pit[:, FROM_NODE].astype(np.int32) p_from = node_pit[from_nodes, PAMB] + node_pit[from_nodes, PINIT] - p_to_calc = p_from * compressor_pit[:, PRESSURE_RATIO] + + p_to_calc = p_from * compressor_array[:, cls.PRESSURE_RATIO] pl_abs = p_to_calc - p_from - v_mps = compressor_pit[:, VINIT] + v_mps = compressor_branch_pit[:, VINIT] pl_abs[v_mps < 0] = 0 # force pressure lift = 0 for reverse flow - compressor_pit[:, PL] = pl_abs + compressor_branch_pit[:, PL] = pl_abs @classmethod def get_component_input(cls): diff --git a/pandapipes/component_models/ext_grid_component.py b/pandapipes/component_models/ext_grid_component.py index 9570f51e..d7802832 100644 --- a/pandapipes/component_models/ext_grid_component.py +++ b/pandapipes/component_models/ext_grid_component.py @@ -62,20 +62,18 @@ def create_pit_node_entries(cls, net, node_pit): return ext_grids, press @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: :param branch_results: :type branch_results: :param net: The pandapipes network :type net: pandapipesNet :param options: :type options: + :param mode: + :type mode: :return: No Output. """ ext_grids = net[cls.table_name()] diff --git a/pandapipes/component_models/flow_control_component.py b/pandapipes/component_models/flow_control_component.py index d42f5815..67e25487 100644 --- a/pandapipes/component_models/flow_control_component.py +++ b/pandapipes/component_models/flow_control_component.py @@ -5,10 +5,12 @@ import numpy as np from numpy import dtype -from pandapipes.component_models.junction_component import Junction from pandapipes.component_models.abstract_models import BranchWZeroLengthComponent, get_fluid -from pandapipes.idx_branch import D, AREA, TL, JAC_DERIV_DP, JAC_DERIV_DP1, JAC_DERIV_DV, VINIT, \ - RHO, LOAD_VEC_BRANCHES, ELEMENT_IDX +from pandapipes.component_models.component_toolbox import \ + standard_branch_wo_internals_result_lookup, get_component_array +from pandapipes.component_models.junction_component import Junction +from pandapipes.idx_branch import D, AREA, JAC_DERIV_DP, JAC_DERIV_DP1, JAC_DERIV_DV, VINIT, \ + RHO, LOAD_VEC_BRANCHES from pandapipes.pf.result_extraction import extract_branch_results_without_internals @@ -16,6 +18,9 @@ class FlowControlComponent(BranchWZeroLengthComponent): """ """ + CONTROL_ACTIVE = 0 + + internal_cols = 1 @classmethod def table_name(cls): @@ -43,64 +48,50 @@ def create_pit_branch_entries(cls, net, branch_pit): :type branch_pit: :return: No Output. """ - fc_pit = super().create_pit_branch_entries(net, branch_pit) - fc_pit[:, D] = net[cls.table_name()].diameter_m.values - fc_pit[:, AREA] = fc_pit[:, D] ** 2 * np.pi / 4 - fc_pit[:, VINIT] = net[cls.table_name()].controlled_mdot_kg_per_s.values / \ - (fc_pit[:, AREA] * fc_pit[:, RHO]) - - @classmethod - def adaption_before_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): - pass - - @classmethod - def adaption_after_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): - # set all pressure derivatives to 0 and velocity to 1; load vector must be 0, as no change - # of velocity is allowed during the pipeflow iteration - f, t = idx_lookups[cls.table_name()] - fc_pit = branch_pit[f:t, :] - in_service_elm = np.isin(net[cls.table_name()].index.values, - fc_pit[:, ELEMENT_IDX].astype(np.int32)) - active = net[cls.table_name()].control_active.values[in_service_elm] - fc_pit[active, JAC_DERIV_DP] = 0 - fc_pit[active, JAC_DERIV_DP1] = 0 - fc_pit[active, JAC_DERIV_DV] = 1 - fc_pit[active, LOAD_VEC_BRANCHES] = 0 + fc_branch_pit = super().create_pit_branch_entries(net, branch_pit) + fc_branch_pit[:, D] = net[cls.table_name()].diameter_m.values + fc_branch_pit[:, AREA] = fc_branch_pit[:, D] ** 2 * np.pi / 4 + fc_branch_pit[:, VINIT] = net[cls.table_name()].controlled_mdot_kg_per_s.values / \ + (fc_branch_pit[:, AREA] * fc_branch_pit[:, RHO]) @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): + def create_component_array(cls, net, component_pits): """ + Function which creates an internal array of the component in analogy to the pit, but with + component specific entries, that are not needed in the pit. - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: + :param net: The pandapipes network + :type net: pandapipesNet + :param component_pits: dictionary of component specific arrays + :type component_pits: dict :return: :rtype: """ - branch_component_pit[:, TL] = 0 + tbl = net[cls.table_name()] + fc_pit = np.zeros(shape=(len(tbl), cls.internal_cols), dtype=np.float64) + fc_pit[:, cls.CONTROL_ACTIVE] = tbl.control_active.values + component_pits[cls.table_name()] = fc_pit + @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): - required_results = [ - ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("t_from_k", "temp_from"), - ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to"), ("mdot_from_kg_per_s", "mf_from"), - ("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), ("reynolds", "reynolds") - ] + def adaption_after_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): + # set all pressure derivatives to 0 and velocity to 1; load vector must be 0, as no change + # of velocity is allowed during the pipeflow iteration + f, t = idx_lookups[cls.table_name()] + fc_branch_pit = branch_pit[f:t, :] + fc_array = get_component_array(net, cls.table_name()) + active = fc_array[:, cls.CONTROL_ACTIVE].astype(np.bool_) + fc_branch_pit[active, JAC_DERIV_DP] = 0 + fc_branch_pit[active, JAC_DERIV_DP1] = 0 + fc_branch_pit[active, JAC_DERIV_DV] = 1 + fc_branch_pit[active, LOAD_VEC_BRANCHES] = 0 - if get_fluid(net).is_gas: - required_results.extend([ - ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), - ("v_mean_m_per_s", "v_gas_mean"), ("normfactor_from", "normfactor_from"), - ("normfactor_to", "normfactor_to") - ]) - else: - required_results.extend([("v_mean_m_per_s", "v_mps")]) + @classmethod + def extract_results(cls, net, options, branch_results, mode): + required_results_hyd, required_results_ht = standard_branch_wo_internals_result_lookup(net) - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) + extract_branch_results_without_internals(net, branch_results, required_results_hyd, + required_results_ht, cls.table_name(), mode) @classmethod def get_component_input(cls): diff --git a/pandapipes/component_models/heat_exchanger_component.py b/pandapipes/component_models/heat_exchanger_component.py index abbb2259..d958fda9 100644 --- a/pandapipes/component_models/heat_exchanger_component.py +++ b/pandapipes/component_models/heat_exchanger_component.py @@ -5,10 +5,11 @@ import numpy as np from numpy import dtype +from pandapipes.component_models import standard_branch_wo_internals_result_lookup from pandapipes.component_models.abstract_models.branch_wzerolength_models import \ BranchWZeroLengthComponent from pandapipes.component_models.junction_component import Junction -from pandapipes.idx_branch import TL, ALPHA, TEXT, QEXT, T_OUT, D, AREA, LOSS_COEFFICIENT as LC +from pandapipes.idx_branch import ALPHA, TEXT, QEXT, D, AREA, LOSS_COEFFICIENT as LC, TOUTINIT from pandapipes.pf.pipeflow_setup import get_fluid from pandapipes.pf.result_extraction import extract_branch_results_without_internals @@ -57,42 +58,29 @@ def create_pit_branch_entries(cls, net, branch_pit): heat_exchanger_pit[:, ALPHA] = 0 heat_exchanger_pit[:, QEXT] = net[cls.table_name()].qext_w.values heat_exchanger_pit[:, TEXT] = 293.15 - heat_exchanger_pit[:, T_OUT] = 307 + heat_exchanger_pit[:, TOUTINIT] = 307 @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): - required_results = [ - ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("t_from_k", "temp_from"), - ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to"), ("mdot_from_kg_per_s", "mf_from"), - ("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), ("reynolds", "reynolds") - ] - - if get_fluid(net).is_gas: - required_results.extend([ - ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), - ("v_mean_m_per_s", "v_gas_mean"), ("normfactor_from", "normfactor_from"), - ("normfactor_to", "normfactor_to") - ]) - else: - required_results.extend([("v_mean_m_per_s", "v_mps")]) - - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) - - @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): + def extract_results(cls, net, options, branch_results, mode): """ + Class method to extract pipeflow results from the internal structure into the results table. - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: + :param net: The pandapipes network + :type net: pandapipesNet + :param options: pipeflow options + :type options: dict + :param branch_results: important branch results + :type branch_results: dict + :param mode: simulation mode + :type mode: str + :return: No Output. + :rtype: None """ - branch_component_pit[:, TL] = 0 + required_results_hyd, required_results_ht = standard_branch_wo_internals_result_lookup(net) + + extract_branch_results_without_internals(net, branch_results, required_results_hyd, + required_results_ht, cls.table_name(), mode) + @classmethod def get_component_input(cls): diff --git a/pandapipes/component_models/junction_component.py b/pandapipes/component_models/junction_component.py index 3426fae0..fdf2a22a 100644 --- a/pandapipes/component_models/junction_component.py +++ b/pandapipes/component_models/junction_component.py @@ -88,37 +88,44 @@ def create_pit_node_entries(cls, net, node_pit): junction_pit[:, ACTIVE_ND] = junctions.in_service.values @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: - :param branch_results: - :type branch_results: + :param mode: + :type mode: :param net: The pandapipes network :type net: pandapipesNet :param options: :type options: + :param branch_results: + :type branch_results: + :param mode: + :type mode: :return: No Output. """ res_table = net["res_" + cls.table_name()] f, t = get_lookup(net, "node", "from_to")[cls.table_name()] - fa, ta = get_lookup(net, "node", "from_to_active")[cls.table_name()] - - junction_pit = net["_active_pit"]["node"][fa:ta, :] - junctions_active = get_lookup(net, "node", "active")[f:t] - - if np.any(junction_pit[:, PINIT] < 0): - warn(UserWarning('Pipeflow converged, however, the results are physically incorrect ' - 'as pressure is negative at nodes %s' - % junction_pit[junction_pit[:, PINIT] < 0, ELEMENT_IDX])) - - res_table["p_bar"].values[junctions_active] = junction_pit[:, PINIT] - res_table["t_k"].values[junctions_active] = junction_pit[:, TINIT] + junction_pit = net["_pit"]["node"][f:t, :] + + if mode in ["hydraulics", "all"]: + junctions_connected_hydraulic = get_lookup(net, "node", "active_hydraulics")[f:t] + + if np.any(junction_pit[junctions_connected_hydraulic, PINIT] < 0): + warn(UserWarning('Pipeflow converged, however, the results are physically incorrect ' + 'as pressure is negative at nodes %s' + % junction_pit[junction_pit[:, PINIT] < 0, ELEMENT_IDX])) + + # res_table["p_bar"].values[junctions_connected_hydraulic] = junction_pit[:, PINIT] + # if mode == "hydraulics": + # res_table["t_k"].values[junctions_connected_hydraulic] = junction_pit[:, TINIT] + # + # if mode in ["heat", "all"]: + # junctions_connected_ht = get_lookup(net, "node", "active_heat_transfer")[f:t] + # res_table["t_k"].values[junctions_connected_ht] = junction_pit[:, TINIT] + res_table["p_bar"].values[:] = junction_pit[:, PINIT] + res_table["t_k"].values[:] = junction_pit[:, TINIT] @classmethod def get_component_input(cls): diff --git a/pandapipes/component_models/pipe_component.py b/pandapipes/component_models/pipe_component.py index f8f38aa1..99b2f098 100644 --- a/pandapipes/component_models/pipe_component.py +++ b/pandapipes/component_models/pipe_component.py @@ -11,7 +11,7 @@ from pandapipes.component_models.junction_component import Junction from pandapipes.constants import NORMAL_TEMPERATURE, NORMAL_PRESSURE from pandapipes.idx_branch import FROM_NODE, TO_NODE, LENGTH, D, AREA, K, \ - VINIT, ALPHA, QEXT, TEXT, LOSS_COEFFICIENT as LC, T_OUT, TL + VINIT, ALPHA, QEXT, TEXT, LOSS_COEFFICIENT as LC from pandapipes.idx_node import PINIT, HEIGHT, TINIT as TINIT_NODE, \ RHO as RHO_NODES, PAMB, ACTIVE as ACTIVE_ND from pandapipes.pf.pipeflow_setup import get_fluid, get_lookup @@ -151,47 +151,38 @@ def create_pit_branch_entries(cls, net, branch_pit): set_entry_check_repeat( pipe_pit, LC, net[tbl].loss_coefficient.values, internal_pipe_number, has_internals) - pipe_pit[:, T_OUT] = 293 pipe_pit[:, AREA] = pipe_pit[:, D] ** 2 * np.pi / 4 @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): - """ - - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: - """ - branch_component_pit[:, TL] = 0 - - @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): - res_nodes_from = [("p_from_bar", "p_from"), ("t_from_k", "temp_from"), - ("mdot_from_kg_per_s", "mf_from")] - res_nodes_to = [("p_to_bar", "p_to"), ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to")] - res_mean = [("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), ("reynolds", "reynolds")] + def extract_results(cls, net, options, branch_results, mode): + res_nodes_from_hyd = [("p_from_bar", "p_from"), ("mdot_from_kg_per_s", "mf_from")] + res_nodes_from_ht = [("t_from_k", "temp_from")] + res_nodes_to_hyd = [("p_to_bar", "p_to"), ("mdot_to_kg_per_s", "mf_to")] + res_nodes_to_ht = [("t_to_k", "temp_to")] + res_mean_hyd = [("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), + ("reynolds", "reynolds")] if get_fluid(net).is_gas: - res_nodes_from.extend( - [("v_from_m_per_s", "v_gas_from"), ("normfactor_from", "normfactor_from")]) - res_nodes_to.extend([("v_to_m_per_s", "v_gas_to"), ("normfactor_to", "normfactor_to")]) - res_mean.extend([("v_mean_m_per_s", "v_gas_mean")]) + res_nodes_from_hyd.extend([("v_from_m_per_s", "v_gas_from"), + ("normfactor_from", "normfactor_from")]) + res_nodes_to_hyd.extend([("v_to_m_per_s", "v_gas_to"), + ("normfactor_to", "normfactor_to")]) + res_mean_hyd.extend([("v_mean_m_per_s", "v_gas_mean")]) else: - res_mean.extend([("v_mean_m_per_s", "v_mps")]) + res_mean_hyd.extend([("v_mean_m_per_s", "v_mps")]) if np.any(cls.get_internal_pipe_number(net) > 1): extract_branch_results_with_internals( - net, branch_results, cls.table_name(), res_nodes_from, res_nodes_to, res_mean, - cls.get_connected_node_type().table_name(), branches_connected) + net, branch_results, cls.table_name(), res_nodes_from_hyd, res_nodes_from_ht, + res_nodes_to_hyd, res_nodes_to_ht, res_mean_hyd, [], + cls.get_connected_node_type().table_name(), mode) else: - required_results = res_nodes_from + res_nodes_to + res_mean - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) + required_results_hyd = res_nodes_from_hyd + res_nodes_to_hyd + res_mean_hyd + required_results_ht = res_nodes_from_ht + res_nodes_to_ht + extract_branch_results_without_internals( + net, branch_results, required_results_hyd, required_results_ht, cls.table_name(), + mode + ) @classmethod def get_internal_results(cls, net, pipe): diff --git a/pandapipes/component_models/pressure_control_component.py b/pandapipes/component_models/pressure_control_component.py index abd4b247..e582925b 100644 --- a/pandapipes/component_models/pressure_control_component.py +++ b/pandapipes/component_models/pressure_control_component.py @@ -8,7 +8,7 @@ from pandapipes.component_models.abstract_models.branch_wzerolength_models import \ BranchWZeroLengthComponent from pandapipes.component_models.junction_component import Junction -from pandapipes.idx_branch import D, AREA, TL, \ +from pandapipes.idx_branch import D, AREA, \ JAC_DERIV_DP, JAC_DERIV_DP1, JAC_DERIV_DV, BRANCH_TYPE, LOSS_COEFFICIENT as LC from pandapipes.idx_node import PINIT, NODE_TYPE, PC from pandapipes.pf.pipeflow_setup import get_lookup @@ -80,29 +80,12 @@ def adaption_after_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_loo press_pit[pc_branch, JAC_DERIV_DV] = 0 @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): - """ - - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: - """ - branch_component_pit[:, TL] = 0 - - @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: + :param mode: + :type mode: :param branch_results: :type branch_results: :param net: The pandapipes network @@ -111,22 +94,22 @@ def extract_results(cls, net, options, branch_results, nodes_connected, branches :type options: :return: No Output. """ - required_results = [ - ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("t_from_k", "temp_from"), - ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to"), ("mdot_from_kg_per_s", "mf_from"), - ("vdot_norm_m3_per_s", "vf") + required_results_hyd = [ + ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("mdot_from_kg_per_s", "mf_from"), + ("mdot_to_kg_per_s", "mf_to"), ("vdot_norm_m3_per_s", "vf") ] + required_results_ht = [("t_from_k", "temp_from"), ("t_to_k", "temp_to")] if get_fluid(net).is_gas: - required_results.extend([ + required_results_hyd.extend([ ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), ("normfactor_from", "normfactor_from"), ("normfactor_to", "normfactor_to") ]) else: - required_results.extend([("v_mean_m_per_s", "v_mps")]) + required_results_hyd.extend([("v_mean_m_per_s", "v_mps")]) - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) + extract_branch_results_without_internals(net, branch_results, required_results_hyd, + required_results_ht, cls.table_name(), mode) res_table = net["res_" + cls.table_name()] f, t = get_lookup(net, "branch", "from_to")[cls.table_name()] diff --git a/pandapipes/component_models/pump_component.py b/pandapipes/component_models/pump_component.py index 9cee8f91..715332f6 100644 --- a/pandapipes/component_models/pump_component.py +++ b/pandapipes/component_models/pump_component.py @@ -7,12 +7,12 @@ import numpy as np from numpy import dtype -from pandapipes.component_models.junction_component import Junction from pandapipes.component_models.abstract_models.branch_wzerolength_models import \ BranchWZeroLengthComponent +from pandapipes.component_models.component_toolbox import get_component_array +from pandapipes.component_models.junction_component import Junction from pandapipes.constants import NORMAL_TEMPERATURE, NORMAL_PRESSURE, R_UNIVERSAL, P_CONVERSION -from pandapipes.idx_branch import STD_TYPE, VINIT, D, AREA, TL, LOSS_COEFFICIENT as LC, FROM_NODE, \ - TINIT, PL +from pandapipes.idx_branch import VINIT, D, AREA, LOSS_COEFFICIENT as LC, FROM_NODE, PL from pandapipes.idx_node import PINIT, PAMB, TINIT as TINIT_NODE from pandapipes.pf.pipeflow_setup import get_fluid, get_net_option, get_lookup from pandapipes.pf.result_extraction import extract_branch_results_without_internals @@ -29,6 +29,9 @@ class Pump(BranchWZeroLengthComponent): """ """ + STD_TYPE = 0 + + internal_cols = 1 @classmethod def from_to_node_cols(cls): @@ -58,93 +61,97 @@ def create_pit_branch_entries(cls, net, branch_pit): :return: No Output. """ pump_pit = super().create_pit_branch_entries(net, branch_pit) - std_types_lookup = np.array(list(net.std_types[cls.table_name()].keys())) - std_type, pos = np.where(net[cls.table_name()]['std_type'].values - == std_types_lookup[:, np.newaxis]) - pump_pit[pos, STD_TYPE] = std_type pump_pit[:, D] = 0.1 pump_pit[:, AREA] = pump_pit[:, D] ** 2 * np.pi / 4 pump_pit[:, LC] = 0 + @classmethod + def create_component_array(cls, net, component_pits): + """ + Function which creates an internal array of the component in analogy to the pit, but with + component specific entries, that are not needed in the pit. + + :param net: The pandapipes network + :type net: pandapipesNet + :param component_pits: dictionary of component specific arrays + :type component_pits: dict + :return: + :rtype: + """ + tbl = net[cls.table_name()] + pump_array = np.zeros(shape=(len(tbl), cls.internal_cols), dtype=np.float64) + std_types_lookup = get_std_type_lookup(net, cls.table_name()) + std_type, pos = np.where(net[cls.table_name()]['std_type'].values + == std_types_lookup[:, np.newaxis]) + pump_array[pos, cls.STD_TYPE] = std_type + component_pits[cls.table_name()] = pump_array + @classmethod def adaption_before_derivatives_hydraulic(cls, net, branch_pit, node_pit, idx_lookups, options): # calculation of pressure lift f, t = idx_lookups[cls.table_name()] - pump_pit = branch_pit[f:t, :] - area = pump_pit[:, AREA] - idx = pump_pit[:, STD_TYPE].astype(int) - std_types = np.array(list(net.std_types['pump'].keys()))[idx] - from_nodes = pump_pit[:, FROM_NODE].astype(np.int32) - # to_nodes = pump_pit[:, TO_NODE].astype(np.int32) + pump_branch_pit = branch_pit[f:t, :] + area = pump_branch_pit[:, AREA] + + pump_array = get_component_array(net, cls.table_name()) + idx = pump_array[:, cls.STD_TYPE].astype(np.int32) + std_types = get_std_type_lookup(net, cls.table_name())[idx] + + from_nodes = pump_branch_pit[:, FROM_NODE].astype(np.int32) + # to_nodes = pump_branch_pit[:, TO_NODE].astype(np.int32) fluid = get_fluid(net) p_from = node_pit[from_nodes, PAMB] + node_pit[from_nodes, PINIT] # p_to = node_pit[to_nodes, PAMB] + node_pit[to_nodes, PINIT] - numerator = NORMAL_PRESSURE * pump_pit[:, TINIT] - v_mps = pump_pit[:, VINIT] + t_from = node_pit[from_nodes, TINIT_NODE] + numerator_from = NORMAL_PRESSURE * t_from + v_mps = pump_branch_pit[:, VINIT] if fluid.is_gas: # consider volume flow at inlet - normfactor_from = numerator * fluid.get_property("compressibility", p_from) \ + normfactor_from = numerator_from * fluid.get_property("compressibility", p_from) \ / (p_from * NORMAL_TEMPERATURE) - v_mean = v_mps * normfactor_from + v_from = v_mps * normfactor_from else: - v_mean = v_mps - vol = v_mean * area + v_from = v_mps + vol = v_from * area if len(std_types): fcts = itemgetter(*std_types)(net['std_types']['pump']) fcts = [fcts] if not isinstance(fcts, tuple) else fcts pl = np.array(list(map(lambda x, y: x.get_pressure(y), fcts, vol))) - pump_pit[:, PL] = pl + pump_branch_pit[:, PL] = pl @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): - """ - - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: - """ - branch_component_pit[:, TL] = 0 - - @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): + def extract_results(cls, net, options, branch_results, mode): """ Function that extracts certain results. - :param nodes_connected: - :type nodes_connected: - :param branches_connected: - :type branches_connected: :param branch_results: :type branch_results: :param net: The pandapipes network :type net: pandapipesNet :param options: :type options: + :param mode: + :type mode: :return: No Output. """ calc_compr_pow = options['calc_compression_power'] - required_results = [ - ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("t_from_k", "temp_from"), - ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to"), ("mdot_from_kg_per_s", "mf_from"), - ("vdot_norm_m3_per_s", "vf"), ("deltap_bar", "pl") + required_results_hyd = [ + ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("mdot_to_kg_per_s", "mf_to"), + ("mdot_from_kg_per_s", "mf_from"), ("vdot_norm_m3_per_s", "vf"), ("deltap_bar", "pl"), ] + required_results_ht = [("t_from_k", "temp_from"), ("t_to_k", "temp_to")] if get_fluid(net).is_gas: - required_results.extend([ + required_results_hyd.extend([ ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), ("normfactor_from", "normfactor_from"), ("normfactor_to", "normfactor_to") ]) else: - required_results.extend([("v_mean_m_per_s", "v_mps")]) + required_results_hyd.extend([("v_mean_m_per_s", "v_mps")]) - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) + extract_branch_results_without_internals(net, branch_results, required_results_hyd, + required_results_ht, cls.table_name(), mode) if calc_compr_pow: f, t = get_lookup(net, "branch", "from_to")[cls.table_name()] @@ -222,3 +229,7 @@ def get_result_table(cls, net): output += ["compr_power_mw"] return output, True + + +def get_std_type_lookup(net, table_name): + return np.array(list(net.std_types[table_name].keys())) diff --git a/pandapipes/component_models/valve_component.py b/pandapipes/component_models/valve_component.py index b63173e8..ff2b9451 100644 --- a/pandapipes/component_models/valve_component.py +++ b/pandapipes/component_models/valve_component.py @@ -5,10 +5,11 @@ import numpy as np from numpy import dtype +from pandapipes.component_models.component_toolbox import standard_branch_wo_internals_result_lookup from pandapipes.component_models.abstract_models.branch_wzerolength_models import \ BranchWZeroLengthComponent from pandapipes.component_models.junction_component import Junction -from pandapipes.idx_branch import D, AREA, LOSS_COEFFICIENT as LC, TL +from pandapipes.idx_branch import D, AREA, LOSS_COEFFICIENT as LC from pandapipes.pf.result_extraction import extract_branch_results_without_internals from pandapipes.properties.fluids import get_fluid @@ -51,21 +52,6 @@ def create_pit_branch_entries(cls, net, branch_pit): valve_pit[:, AREA] = valve_pit[:, D] ** 2 * np.pi / 4 valve_pit[:, LC] = net[cls.table_name()].loss_coefficient.values - @classmethod - def calculate_temperature_lift(cls, net, branch_component_pit, node_pit): - """ - - :param net: - :type net: - :param branch_component_pit: - :type branch_component_pit: - :param node_pit: - :type node_pit: - :return: - :rtype: - """ - branch_component_pit[:, TL] = 0 - @classmethod def get_component_input(cls): """ @@ -82,24 +68,11 @@ def get_component_input(cls): ("type", dtype(object))] @classmethod - def extract_results(cls, net, options, branch_results, nodes_connected, branches_connected): - required_results = [ - ("p_from_bar", "p_from"), ("p_to_bar", "p_to"), ("t_from_k", "temp_from"), - ("t_to_k", "temp_to"), ("mdot_to_kg_per_s", "mf_to"), ("mdot_from_kg_per_s", "mf_from"), - ("vdot_norm_m3_per_s", "vf"), ("lambda", "lambda"), ("reynolds", "reynolds") - ] - - if get_fluid(net).is_gas: - required_results.extend([ - ("v_from_m_per_s", "v_gas_from"), ("v_to_m_per_s", "v_gas_to"), - ("v_mean_m_per_s", "v_gas_mean"), ("normfactor_from", "normfactor_from"), - ("normfactor_to", "normfactor_to") - ]) - else: - required_results.extend([("v_mean_m_per_s", "v_mps")]) + def extract_results(cls, net, options, branch_results, mode): + required_results_hyd, required_results_ht = standard_branch_wo_internals_result_lookup(net) - extract_branch_results_without_internals(net, branch_results, required_results, - cls.table_name(), branches_connected) + extract_branch_results_without_internals(net, branch_results, required_results_hyd, + required_results_ht, cls.table_name(), mode) @classmethod def get_result_table(cls, net): diff --git a/pandapipes/converter/stanet/stanet2pandapipes.py b/pandapipes/converter/stanet/stanet2pandapipes.py index 3ff7d837..a0a4f687 100644 --- a/pandapipes/converter/stanet/stanet2pandapipes.py +++ b/pandapipes/converter/stanet/stanet2pandapipes.py @@ -9,7 +9,7 @@ connection_pipe_section_table, get_stanet_raw_data, create_meter_table, create_house_table from pandapipes.converter.stanet.table_creation import create_junctions_from_nodes, \ create_valve_and_pipe, create_pumps, create_junctions_from_connections, \ - create_pipes_from_connections, create_heat_exchangers, create_slider_valves, \ + create_pipes_from_connections, create_heat_exchangers_stanet, create_slider_valves, \ create_pipes_from_remaining_pipe_table, create_nodes_house_connections, \ create_sinks_meters, create_sinks_from_nodes, create_control_components, \ create_sinks_from_customers, create_pipes_house_connections @@ -92,7 +92,8 @@ def stanet_to_pandapipes(stanet_path, name="net", remove_unused_household_connec create_pipes_from_remaining_pipe_table(net, stored_data, connections, index_mapping, pipe_geodata, add_layers) - create_heat_exchangers(net, stored_data, connections, index_mapping, add_layers) + create_heat_exchangers_stanet(net, stored_data, index_mapping, add_layers, + add_flow=kwargs.pop("add_heat_exchanger_flow", False)) # valves always have a length in STANET, therefore, they are created as valve with pipe in # pandapipes diff --git a/pandapipes/converter/stanet/table_creation.py b/pandapipes/converter/stanet/table_creation.py index 4c02c80e..553a0c3f 100644 --- a/pandapipes/converter/stanet/table_creation.py +++ b/pandapipes/converter/stanet/table_creation.py @@ -65,7 +65,8 @@ def create_junctions_from_nodes(net, stored_data, net_params, index_mapping, add add_info = {"stanet_id": node_table.STANETID.astype(str).values if "STANETID" in node_table.columns else knams, "p_stanet": node_table.PRECH.values.astype(np.float64), - "stanet_valid": ~node_table.CALCBAD.values.astype(np.bool_)} + "stanet_valid": ~node_table.CALCBAD.values.astype(np.bool_), + "t_stanet": node_table.TEMP.values.astype(np.float64)} if hasattr(node_table, "KFAK"): add_info["K_stanet"] = node_table.KFAK.values.astype(np.float64) if add_layers: @@ -152,11 +153,14 @@ def create_valve_and_pipe(net, stored_data, index_mapping, net_params, stanet_li stanet_nr=-999, stanet_id='aux_' + j_ref['stanet_id'], p_stanet=np.NaN, stanet_active=bool(row.ISACTIVE), **add_info ) + text_k = 293 + if hasattr(row, "TU"): + text_k = row.TU + 273.15 pandapipes.create_pipe_from_parameters( net, node_mapping[from_stanet_nr], j_aux, length_km=row.RORL / 1000, diameter_m=float(row.DM / 1000), k_mm=row.RAU, loss_coefficient=row.ZETA, name="pipe_%s_%s" % (str(row.ANFNAM), 'aux_' + str(row.ENDNAM)), - in_service=bool(row.ISACTIVE), stanet_nr=-999, + text_k=text_k, in_service=bool(row.ISACTIVE), stanet_nr=-999, stanet_id='pipe_valve_' + str(row.STANETID), v_stanet=row.VM, stanet_active=bool(row.ISACTIVE), stanet_valid=False, **add_info ) @@ -559,10 +563,14 @@ def create_geodata_sections(row): if add_layers: add_info["stanet_layer"] = pipes.LAYER.values.astype(str) # TODO: v_stanet might have to be extended by house connections VMA and VMB + text_k = 293 + if "TU" in pipes.columns: + text_k = pipes.TU.values.astype(np.float64) + 273.15 pandapipes.create_pipes_from_parameters( net, pipe_sections.fj.values, pipe_sections.tj.values, pipe_sections.length.values / 1000, pipes.DM.values / 1000, pipes.RAU.values, pipes.ZETA.values, type="main_pipe", - stanet_std_type=pipes.ROHRTYP.values, in_service=pipes.ISACTIVE.values, + stanet_std_type=pipes.ROHRTYP.values, in_service=pipes.ISACTIVE.values, text_k=text_k, + alpha_w_per_m2k=pipes.WDZAHL.values.astype(np.float64), name=["pipe_%s_%s_%s" % (nf, nt, sec) for nf, nt, sec in zip( pipes.ANFNAM.values, pipes.ENDNAM.values, pipe_sections.section_no.values)], stanet_nr=pipes.RECNO.values, stanet_id=pipes.STANETID.values, @@ -574,27 +582,26 @@ def create_geodata_sections(row): ) -def create_heat_exchangers(net, stored_data, connection_table, index_mapping, add_layers): +def create_heat_exchangers_stanet(net, stored_data, index_mapping, add_layers, add_flow=False): """ Creates pandapipes heat exchangers from STANET connections. :param net: :type net: :param stored_data: :type stored_data: - :param connection_table: - :type connection_table: :param index_mapping: :type index_mapping: :param add_layers: :type add_layers: + :param add_flow: + :type add_flow: :return: :rtype: """ if "heat_exchangers" not in stored_data: return - heat_ex_table = stored_data["heat_exchangers"] + heat_exchanger = stored_data["heat_exchangers"] logger.info("Creating all heat exchangers.") - heat_exchanger = heat_ex_table.loc[~heat_ex_table.RECNO.isin(connection_table.SNUM.values)] node_mapping = index_mapping["nodes"] for row in heat_exchanger.itertuples(): @@ -616,9 +623,14 @@ def create_heat_exchangers(net, stored_data, connection_table, index_mapping, ad add_info = dict() if add_layers: add_info["stanet_layer"] = str(row.LAYER) + if add_flow: + add_info["flow_stanet"] = row.FLUSS + qext = 0 + if hasattr(row, "WAERMECALC"): + qext = getattr(row, "WAERMECALC") * (-1000) # TODO: there is no qext given!!! pandapipes.create_heat_exchanger( - net, node_mapping[from_stanet_nr], node_mapping[to_stanet_nr], qext_w=0, + net, node_mapping[from_stanet_nr], node_mapping[to_stanet_nr], qext_w=qext, diameter_m=float(row.DM / 1000), loss_coefficient=row.ZETA, std_type=row.ROHRTYP, in_service=bool(row.ISACTIVE), name="heat_exchanger_%s_%s" % (row.ANFNAM, row.ENDNAM), stanet_nr=int(row.RECNO), stanet_id=str(row.STANETID), v_stanet=row.VM, @@ -679,11 +691,15 @@ def create_pipes_from_remaining_pipe_table(net, stored_data, connection_table, i add_info = dict() if add_layers: add_info["stanet_layer"] = p_tbl.LAYER.values.astype(str) + text_k = 293 + if "TU" in p_tbl.columns: + text_k = p_tbl.TU.values.astype(np.float64) + 273.15 pandapipes.create_pipes_from_parameters( net, from_junctions, to_junctions, length_km=p_tbl.RORL.values.astype(np.float64) / 1000, type="main_pipe", diameter_m=p_tbl.DM.values.astype(np.float64) / 1000, loss_coefficient=p_tbl.ZETA.values, stanet_std_type=p_tbl.ROHRTYP.values, k_mm=p_tbl.RAU.values, in_service=p_tbl.ISACTIVE.values.astype(np.bool_), + alpha_w_per_m2k=p_tbl.WDZAHL.values.astype(np.float64), text_k=text_k, name=["pipe_%s_%s" % (anf, end) for anf, end in zip(from_names[valid], to_names[valid])], stanet_nr=p_tbl.RECNO.values.astype(np.int32), stanet_id=p_tbl.STANETID.values.astype(str), v_stanet=p_tbl.VM.values, geodata=geodata, @@ -990,11 +1006,15 @@ def create_geodata_sections(row): if add_layers: add_info["stanet_layer"] = hp_data.LAYER.values.astype(str) # TODO: v_stanet might have to be extended by house connections VMA and VMB + text_k = 293 + if "TU" in hp_data.columns: + text_k = hp_data.TU.values.astype(np.float64) + 273.15 pandapipes.create_pipes_from_parameters( net, hp_data.fj.values, hp_data.tj.values, hp_data.length.values / 1000, hp_data.DM.values / 1000, hp_data.RAU.values, hp_data.ZETA.values, type="house_pipe", stanet_std_type=hp_data.ROHRTYP.values, - in_service=hp_data.ISACTIVE.values if houses_in_calculation else False, + in_service=hp_data.ISACTIVE.values if houses_in_calculation else False, text_k=text_k, + alpha_w_per_m2k=hp_data.WDZAHL.values.astype(np.float64), name=["pipe_%s_%s_%s" % (nf, nt, sec) for nf, nt, sec in zip( hp_data.CLIENTID.values, hp_data.CLIENT2ID.values, hp_data.section_no.values)], stanet_nr=hp_data.RECNO.values, stanet_id=hp_data.STANETID.values, diff --git a/pandapipes/create.py b/pandapipes/create.py index d9f67f38..dd3746ff 100644 --- a/pandapipes/create.py +++ b/pandapipes/create.py @@ -204,6 +204,7 @@ def create_source(net, junction, mdot_kg_per_s, scaling=1., name=None, index=Non return index + def create_mass_storage(net, junction, mdot_kg_per_s, init_m_stored_kg=0, min_m_stored_kg=0., max_m_stored_kg=np.inf, scaling=1., name=None, index=None, in_service=True, type="mass_storage", **kwargs): @@ -1502,7 +1503,7 @@ def create_pressure_controls(net, from_junctions, to_junctions, controlled_junct _set_multiple_entries(net, "press_control", index, **entries, **kwargs) controlled_elsewhere = (controlled_junctions != from_junctions) \ - & (controlled_junctions != to_junctions) + & (controlled_junctions != to_junctions) if np.any(controlled_elsewhere): controllers_warn = index[controlled_elsewhere] logger.warning("The pressure controllers %s control the pressure at junctions that they are" @@ -1572,11 +1573,64 @@ def create_flow_controls(net, from_junctions, to_junctions, controlled_mdot_kg_p return index +def create_heat_exchangers(net, from_junctions, to_junctions, diameter_m, qext_w, loss_coefficient=0, + name=None, index=None, in_service=True, type="heat_exchanger", **kwargs): + """ + Convenience function for creating many heat exchangers at once. Parameters 'from_junctions'\ + and 'to_junctions' must be arrays of equal length. Other parameters may be either arrays of the\ + same length or single values. + + :param net: The net for which the heat exchangers should be created + :type net: pandapipesNet + :param from_junctions: ID of the junctions on one side the heat exchangers will be\ + connected with + :type from_junctions: Iterable(int) + :param to_junctions: ID of the junctions on the other side the heat exchangers will be\ + connected with + :type to_junctions: Iterable(int) + :param diameter_m: The heat exchangers inner diameter in [m] + :type diameter_m: Iterable(float) or float + :param qext_w: External heat flux in [W]. If positive, heat is derived from the network. If + negative, heat is being fed into the network from a heat source. + :type qext_w: Iterable(float) or float + :param loss_coefficient: An additional pressure loss coefficient, introduced by e.g. bends + :type loss_coefficient: Iterable(float) or float + :param name: The name of the heat exchangers + :type name: str, default None + :param index: Force a specified ID if it is available. If None, the index one higher than the\ + highest already existing index is selected. + :type index: Iterable(str) or str, default None + :param in_service: True if the heat exchangers are in service or False if they are out of service + :type in_service: Iterable(bool) or bool, default True + :param type: Not used yet + :type type: Iterable(str) or str, default "heat exchanger" + :param kwargs: Additional keyword arguments will be added as further columns to the\ + net["heat_exchanger"] table + :return: index - The unique IDs of the created heat exchangers + :rtype: Iterable(int), default None + + :Example: + >>> create_heat_exchangers(net, from_junctions=[0,1], to_junctions=[2,3], + >>> diameter_m=40e-3, qext_w=2000) + """ + add_new_component(net, HeatExchanger) + + index = _get_multiple_index_with_check(net, "heat_exchanger", index, len(from_junctions)) + _check_branches(net, from_junctions, to_junctions, "heat_exchanger") + + entries = {"name": name, "from_junction": from_junctions, "to_junction": to_junctions, + "diameter_m": diameter_m, "qext_w": qext_w, "loss_coefficient": loss_coefficient, + "in_service": bool(in_service), "type": type} + _set_multiple_entries(net, "heat_exchanger", index, **entries, **kwargs) + + return index + + def create_fluid_from_lib(net, name, overwrite=True): """ Creates a fluid from library (if there is an entry) and sets net["fluid"] to this value. - Currently, existing fluids in the library are: "hgas", "lgas", "hydrogen", "methane", "water", - "air". + Currently, existing fluids in the library are: "hgas", "lgas", "hydrogen", "methane", "water","biomethane_pure", + "biomethane_treated", "air". :param net: The net for which this fluid should be created :type net: pandapipesNet diff --git a/pandapipes/idx_branch.py b/pandapipes/idx_branch.py index a2846aa2..c4e3c163 100644 --- a/pandapipes/idx_branch.py +++ b/pandapipes/idx_branch.py @@ -2,6 +2,10 @@ # and Energy System Technology (IEE), Kassel, and University of Kassel. All rights reserved. # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. +# branch types +# no types defined + +# branch indices TABLE_IDX = 0 # number of the table that this branch belongs to ELEMENT_IDX = 1 # index of the element that this branch belongs to (within the given table) FROM_NODE = 2 # f, from bus number @@ -13,34 +17,31 @@ RHO = 8 # Density in [kg/m^3 ETA = 9 # Dynamic viscosity in [Pas] K = 10 # Pipe roughness in [m] -TINIT = 11 # Temperature in [K] -VINIT = 12 # velocity in [m/s] -RE = 13 # Reynolds number -LAMBDA = 14 # Lambda -JAC_DERIV_DV = 15 # Slot for the derivative by velocity -JAC_DERIV_DP = 16 # Slot for the derivative by pressure from_node -JAC_DERIV_DP1 = 17 # Slot for the derivative by pressure to_node -LOAD_VEC_BRANCHES = 18 # Slot for the load vector for the branches -JAC_DERIV_DV_NODE = 19 # Slot for the derivative by velocity for the nodes connected to branch -LOAD_VEC_NODES = 20 # Slot for the load vector of the nodes connected to branch -LOSS_COEFFICIENT = 21 -CP = 22 # Slot for fluid heat capacity values -ALPHA = 23 # Slot for heat transfer coefficient -JAC_DERIV_DT = 24 -JAC_DERIV_DT1 = 25 -LOAD_VEC_BRANCHES_T = 26 -T_OUT = 27 # Internal slot for outlet pipe temperature -JAC_DERIV_DT_NODE = 28 # Slot for the derivative fpr T for the nodes connected to branch -LOAD_VEC_NODES_T = 29 -VINIT_T = 30 -FROM_NODE_T = 31 -TO_NODE_T = 32 -QEXT = 33 # heat input in [W] -TEXT = 34 -STD_TYPE = 35 -PL = 36 -TL = 37 # Temperature lift [K] -BRANCH_TYPE = 38 # branch type relevant for the pressure controller -PRESSURE_RATIO = 39 # boost ratio for compressors with proportional pressure lift +VINIT = 11 # velocity in [m/s] +RE = 12 # Reynolds number +LAMBDA = 13 # Lambda +JAC_DERIV_DV = 14 # Slot for the derivative by velocity +JAC_DERIV_DP = 15 # Slot for the derivative by pressure from_node +JAC_DERIV_DP1 = 16 # Slot for the derivative by pressure to_node +LOAD_VEC_BRANCHES = 17 # Slot for the load vector for the branches +JAC_DERIV_DV_NODE = 18 # Slot for the derivative by velocity for the nodes connected to branch +LOAD_VEC_NODES = 19 # Slot for the load vector of the nodes connected to branch +LOSS_COEFFICIENT = 20 +CP = 21 # Slot for fluid heat capacity values +ALPHA = 22 # Slot for heat transfer coefficient +JAC_DERIV_DT = 23 +JAC_DERIV_DT1 = 24 +LOAD_VEC_BRANCHES_T = 25 +TOUTINIT = 26 # Internal slot for outlet pipe temperature +JAC_DERIV_DT_NODE = 27 # Slot for the derivative fpr T for the nodes connected to branch +LOAD_VEC_NODES_T = 28 +VINIT_T = 29 +FROM_NODE_T = 30 +TO_NODE_T = 31 +QEXT = 32 # heat input in [W] +TEXT = 33 +PL = 34 +TL = 35 # Temperature lift [K] +BRANCH_TYPE = 36 # branch type relevant for the pressure controller -branch_cols = 40 +branch_cols = 37 diff --git a/pandapipes/idx_node.py b/pandapipes/idx_node.py index 6a4abef2..0cf71b5a 100644 --- a/pandapipes/idx_node.py +++ b/pandapipes/idx_node.py @@ -2,14 +2,14 @@ # and Energy System Technology (IEE), Kassel, and University of Kassel. All rights reserved. # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. -# define bus types +# node types P = 1 # Reference node, pressure is fixed L = 2 # All other nodes T = 10 # Reference node with fixed temperature, otherwise 0 PC = 20 # Controlled node with fixed pressure p NONE = 3 # None -# define the indices +# node indices TABLE_IDX = 0 # number of the table that this node belongs to ELEMENT_IDX = 1 # index of the element that this node belongs to (within the given table) NODE_TYPE = 2 # junction type diff --git a/pandapipes/io/file_io.py b/pandapipes/io/file_io.py index 97a3a61a..a1aa0a86 100644 --- a/pandapipes/io/file_io.py +++ b/pandapipes/io/file_io.py @@ -150,8 +150,7 @@ def from_json_string(json_string, convert=False, encryption_key=None): if encryption_key is not None: json_string = decrypt_string(json_string, encryption_key) - net = json.loads(json_string, cls=PPJSONDecoder, - object_hook=partial(pp_hook, registry_class=FromSerializableRegistryPpipe)) + net = json.loads(json_string, cls=PPJSONDecoder, registry_class=FromSerializableRegistryPpipe) if convert and isinstance(net, pandapipesNet): convert_format(net) diff --git a/pandapipes/pf/derivative_calculation.py b/pandapipes/pf/derivative_calculation.py index 80083437..7298adbd 100644 --- a/pandapipes/pf/derivative_calculation.py +++ b/pandapipes/pf/derivative_calculation.py @@ -1,8 +1,10 @@ import numpy as np -from pandapipes.idx_branch import LENGTH, ETA, RHO, D, K, RE, LAMBDA, TINIT, LOAD_VEC_BRANCHES, \ +from pandapipes.idx_branch import LENGTH, ETA, RHO, D, K, RE, LAMBDA, LOAD_VEC_BRANCHES, \ JAC_DERIV_DV, JAC_DERIV_DP, JAC_DERIV_DP1, LOAD_VEC_NODES, JAC_DERIV_DV_NODE, VINIT, \ - FROM_NODE, TO_NODE + FROM_NODE, TO_NODE, CP, VINIT_T, FROM_NODE_T, TOUTINIT, TEXT, AREA, ALPHA, TL, QEXT, LOAD_VEC_NODES_T, \ + LOAD_VEC_BRANCHES_T, JAC_DERIV_DT, JAC_DERIV_DT1, JAC_DERIV_DT_NODE +from pandapipes.idx_node import TINIT as TINIT_NODE from pandapipes.properties.fluids import get_fluid @@ -35,7 +37,6 @@ def calculate_derivatives_hydraulic(net, branch_pit, node_pit, options): to_nodes = branch_pit[:, TO_NODE].astype(np.int32) tinit_branch, height_difference, p_init_i_abs, p_init_i1_abs = \ get_derived_values(node_pit, from_nodes, to_nodes, options["use_numba"]) - branch_pit[:, TINIT] = tinit_branch if not gas_mode: if options["use_numba"]: @@ -62,7 +63,7 @@ def calculate_derivatives_hydraulic(net, branch_pit, node_pit, options): der_comp = fluid.get_der_compressibility() * der_p_m der_comp1 = fluid.get_der_compressibility() * der_p_m1 load_vec, load_vec_nodes, df_dv, df_dv_nodes, df_dp, df_dp1 = derivatives_hydraulic_comp( - branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, height_difference, + node_pit, branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, height_difference, comp_fact, der_comp, der_comp1) branch_pit[:, LOAD_VEC_BRANCHES] = load_vec @@ -73,6 +74,33 @@ def calculate_derivatives_hydraulic(net, branch_pit, node_pit, options): branch_pit[:, JAC_DERIV_DV_NODE] = df_dv_nodes +def calculate_derivatives_thermal(net, branch_pit, node_pit, options): + cp = branch_pit[:, CP] + rho = branch_pit[:, RHO] + v_init = branch_pit[:, VINIT_T] + from_nodes = branch_pit[:, FROM_NODE_T].astype(np.int32) + t_init_i = node_pit[from_nodes, TINIT_NODE] + t_init_i1 = branch_pit[:, TOUTINIT] + t_amb = branch_pit[:, TEXT] + area = branch_pit[:, AREA] + length = branch_pit[:, LENGTH] + alpha = branch_pit[:, ALPHA] * np.pi * branch_pit[:, D] + tl = branch_pit[:, TL] + qext = branch_pit[:, QEXT] + t_m = (t_init_i1 + t_init_i) / 2 + + branch_pit[:, LOAD_VEC_BRANCHES_T] = \ + -(rho * area * cp * v_init * (-t_init_i + t_init_i1 - tl) + - alpha * (t_amb - t_m) * length + qext) + + branch_pit[:, JAC_DERIV_DT] = - rho * area * cp * v_init + alpha / 2 * length + branch_pit[:, JAC_DERIV_DT1] = rho * area * cp * v_init + alpha / 2 * length + + branch_pit[:, JAC_DERIV_DT_NODE] = rho * v_init * branch_pit[:, AREA] + branch_pit[:, LOAD_VEC_NODES_T] = rho * v_init * branch_pit[:, AREA] \ + * t_init_i1 + + def get_derived_values(node_pit, from_nodes, to_nodes, use_numba): if use_numba: from pandapipes.pf.derivative_toolbox_numba import calc_derived_values_numba @@ -110,11 +138,11 @@ def calc_lambda(v, eta, rho, d, k, gas_mode, friction_model, lengths, options): """ if options["use_numba"]: from pandapipes.pf.derivative_toolbox_numba import calc_lambda_nikuradse_incomp_numba as \ - calc_lambda_nikuradse_incomp, colebrook_numba as colebrook,\ + calc_lambda_nikuradse_incomp, colebrook_numba as colebrook, \ calc_lambda_nikuradse_comp_numba as calc_lambda_nikuradse_comp else: from pandapipes.pf.derivative_toolbox import calc_lambda_nikuradse_incomp_np as \ - calc_lambda_nikuradse_incomp, colebrook_np as colebrook,\ + calc_lambda_nikuradse_incomp, colebrook_np as colebrook, \ calc_lambda_nikuradse_comp_np as calc_lambda_nikuradse_comp if gas_mode: re, lambda_laminar, lambda_nikuradse = calc_lambda_nikuradse_comp(v, d, k, eta, rho) @@ -134,7 +162,7 @@ def calc_lambda(v, eta, rho, d, k, gas_mode, friction_model, lengths, options): "argument to the pipeflow.") return lambda_colebrook, re elif friction_model == "swamee-jain": - lambda_swamee_jain = 0.25 / ((np.log10(k/(3.7*d) + 5.74/(re**0.9)))**2) + lambda_swamee_jain = 0.25 / ((np.log10(k / (3.7 * d) + 5.74 / (re ** 0.9))) ** 2) return lambda_swamee_jain, re else: # lambda_tot = np.where(re > 2300, lambda_laminar + lambda_nikuradse, lambda_laminar) @@ -182,7 +210,7 @@ def calc_der_lambda(v, eta, rho, d, k, friction_model, lambda_pipe): return lambda_colebrook_der elif friction_model == "swamee-jain": - param = k/(3.7*d) + 5.74 * (np.abs(eta))**0.9 / ((np.abs(rho*v_corr*d))**0.9) + param = k / (3.7 * d) + 5.74 * (np.abs(eta)) ** 0.9 / ((np.abs(rho * v_corr * d)) ** 0.9) # 0.5 / (log(10) * log(param)^3 * param) * 5.166 * abs(eta)^0.9 / (abs(rho * d)^0.9 # * abs(v_corr)^1.9) lambda_swamee_jain_der = 0.5 / np.log(10) / (np.log(param) ** 3) / param * 5.166 \ diff --git a/pandapipes/pf/derivative_toolbox.py b/pandapipes/pf/derivative_toolbox.py index e681c71f..8ee43571 100644 --- a/pandapipes/pf/derivative_toolbox.py +++ b/pandapipes/pf/derivative_toolbox.py @@ -7,8 +7,8 @@ from pandapipes.constants import P_CONVERSION, GRAVITATION_CONSTANT, NORMAL_PRESSURE, \ NORMAL_TEMPERATURE -from pandapipes.idx_branch import LENGTH, LAMBDA, D, LOSS_COEFFICIENT as LC, RHO, PL, AREA, TINIT, \ - VINIT +from pandapipes.idx_branch import LENGTH, LAMBDA, D, LOSS_COEFFICIENT as LC, RHO, PL, AREA, \ + VINIT, TOUTINIT, FROM_NODE from pandapipes.idx_node import HEIGHT, PINIT, PAMB, TINIT as TINIT_NODE @@ -32,7 +32,7 @@ def derivatives_hydraulic_incomp_np(branch_pit, der_lambda, p_init_i_abs, p_init return load_vec, load_vec_nodes, df_dv, df_dv_nodes, df_dp, df_dp1 -def derivatives_hydraulic_comp_np(branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, +def derivatives_hydraulic_comp_np(node_pit, branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, height_difference, comp_fact, der_comp, der_comp1): # Formulas for gas pressure loss according to laminar version v_init_abs = np.abs(branch_pit[:, VINIT]) @@ -40,12 +40,13 @@ def derivatives_hydraulic_comp_np(branch_pit, lambda_, der_lambda, p_init_i_abs, p_diff = p_init_i_abs - p_init_i1_abs p_sum = p_init_i_abs + p_init_i1_abs p_sum_div = np.divide(1, p_sum) - - const_lambda = np.divide(NORMAL_PRESSURE * branch_pit[:, RHO] * branch_pit[:, TINIT], + from_nodes = branch_pit[:, FROM_NODE].astype(np.int32) + tm = (node_pit[from_nodes, TINIT_NODE] + branch_pit[:, TOUTINIT]) / 2 + const_lambda = np.divide(NORMAL_PRESSURE * branch_pit[:, RHO] * tm, NORMAL_TEMPERATURE * P_CONVERSION) const_height = np.divide( branch_pit[:, RHO] * NORMAL_TEMPERATURE * GRAVITATION_CONSTANT * height_difference, - 2 * NORMAL_PRESSURE * branch_pit[:, TINIT] * P_CONVERSION) + 2 * NORMAL_PRESSURE * tm * P_CONVERSION) friction_term = np.divide(lambda_ * branch_pit[:, LENGTH], branch_pit[:, D]) + branch_pit[:, LC] load_vec = p_diff + branch_pit[:, PL] + const_height * p_sum \ diff --git a/pandapipes/pf/derivative_toolbox_numba.py b/pandapipes/pf/derivative_toolbox_numba.py index a3b7f3eb..3e125a11 100644 --- a/pandapipes/pf/derivative_toolbox_numba.py +++ b/pandapipes/pf/derivative_toolbox_numba.py @@ -3,8 +3,8 @@ from pandapipes.constants import P_CONVERSION, GRAVITATION_CONSTANT, NORMAL_PRESSURE, \ NORMAL_TEMPERATURE -from pandapipes.idx_branch import LENGTH, LAMBDA, D, LOSS_COEFFICIENT as LC, RHO, PL, AREA, TINIT, \ - VINIT +from pandapipes.idx_branch import LENGTH, LAMBDA, D, LOSS_COEFFICIENT as LC, RHO, PL, AREA, \ + VINIT, FROM_NODE, TO_NODE from pandapipes.idx_node import HEIGHT, PAMB, PINIT, TINIT as TINIT_NODE try: @@ -43,9 +43,9 @@ def derivatives_hydraulic_incomp_numba(branch_pit, der_lambda, p_init_i_abs, p_i return load_vec, load_vec_nodes, df_dv, df_dv_nodes, df_dp, df_dp1 -@jit((float64[:, :], float64[:], float64[:], float64[:], float64[:], float64[:], float64[:], +@jit((float64[:, :], float64[:, :], float64[:], float64[:], float64[:], float64[:], float64[:], float64[:], float64[:], float64[:]), nopython=True, cache=False) -def derivatives_hydraulic_comp_numba(branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, +def derivatives_hydraulic_comp_numba(node_pit, branch_pit, lambda_, der_lambda, p_init_i_abs, p_init_i1_abs, height_difference, comp_fact, der_comp, der_comp1): le = lambda_.shape[0] load_vec = np.zeros_like(lambda_) @@ -54,6 +54,8 @@ def derivatives_hydraulic_comp_numba(branch_pit, lambda_, der_lambda, p_init_i_a df_dp1 = np.zeros_like(lambda_) * (-1) load_vec_nodes = np.zeros_like(der_lambda) df_dv_nodes = np.zeros_like(der_lambda) + from_nodes = branch_pit[:, FROM_NODE].astype(np.int32) + to_nodes = branch_pit[:, TO_NODE].astype(np.int32) # Formulas for gas pressure loss according to laminar version for i in range(le): @@ -63,12 +65,15 @@ def derivatives_hydraulic_comp_numba(branch_pit, lambda_, der_lambda, p_init_i_a p_diff = p_init_i_abs[i] - p_init_i1_abs[i] p_sum = p_init_i_abs[i] + p_init_i1_abs[i] p_sum_div = np.divide(1, p_sum) + fn = from_nodes[i] + tn = to_nodes[i] + tm = (node_pit[fn, TINIT_NODE] + node_pit[tn, TINIT_NODE]) / 2 - const_lambda = np.divide(NORMAL_PRESSURE * branch_pit[i][RHO] * branch_pit[i][TINIT], + const_lambda = np.divide(NORMAL_PRESSURE * branch_pit[i][RHO] * tm, NORMAL_TEMPERATURE * P_CONVERSION) const_height = np.divide( branch_pit[i][RHO] * NORMAL_TEMPERATURE * GRAVITATION_CONSTANT * height_difference[i], - 2 * NORMAL_PRESSURE * branch_pit[i][TINIT] * P_CONVERSION) + 2 * NORMAL_PRESSURE * tm * P_CONVERSION) friction_term = np.divide(lambda_[i] * branch_pit[i][LENGTH], branch_pit[i][D]) + branch_pit[i][LC] diff --git a/pandapipes/pf/pipeflow_setup.py b/pandapipes/pf/pipeflow_setup.py index 4dac0133..713f05be 100644 --- a/pandapipes/pf/pipeflow_setup.py +++ b/pandapipes/pf/pipeflow_setup.py @@ -9,9 +9,10 @@ from scipy.sparse import coo_matrix, csgraph from pandapipes.idx_branch import FROM_NODE, TO_NODE, branch_cols, \ - ACTIVE as ACTIVE_BR + ACTIVE as ACTIVE_BR, VINIT from pandapipes.idx_node import NODE_TYPE, P, NODE_TYPE_T, node_cols, T, ACTIVE as ACTIVE_ND, \ TABLE_IDX as TABLE_IDX_ND, ELEMENT_IDX as ELEMENT_IDX_ND +from pandapipes.pf.internals_toolbox import _sum_by_group from pandapipes.properties.fluids import get_fluid try: @@ -161,8 +162,9 @@ def get_lookup(net, pit_type="node", lookup_type="index"): """ pit_type = pit_type.lower() lookup_type = lookup_type.lower() - all_lookup_types = ["index", "table", "from_to", "active", "length", "from_to_active", - "index_active"] + all_lookup_types = ["index", "table", "from_to", "active_hydraulics", "active_heat_transfer", + "length", "from_to_active_hydraulics", "from_to_active_heat_transfer", + "index_active_hydraulics", "index_active_heat_transfer"] if lookup_type not in all_lookup_types: type_names = "', '".join(all_lookup_types) logger.error("No lookup type '%s' exists. Please choose one of '%s'." @@ -187,7 +189,7 @@ def set_user_pf_options(net, reset=False, **kwargs): :type net: pandapipesNet :param reset: Specifies whether the user_pf_options is removed before setting new options :type reset: bool, default False - :param kwargs: pipeflow options that shall be set, e. g. tol_v = 1e-7 + :param kwargs: pipeflow options that shall be set, e.g. tol_v = 1e-7 :return: No output """ if reset or 'user_pf_options' not in net.keys(): @@ -239,7 +241,7 @@ def init_options(net, local_parameters): automatically with respect to the convergence behaviour. - **mode** (str): "hydraulics" - Define the calculation mode: what shall be calculated - \ - solely hydraulics ('hydraulic'), solely heat transfer('heat') or both combined \ + solely hydraulics ('hydraulics'), solely heat transfer('heat') or both combined \ ('all'). - **only_update_hydraulic_matrix** (bool): False - If True, the system matrix is not \ @@ -350,6 +352,7 @@ def initialize_pit(net): for comp in net['component_list']: comp.create_pit_node_entries(net, pit["node"]) comp.create_pit_branch_entries(net, pit["branch"]) + comp.create_component_array(net, pit["components"]) return pit["node"], pit["branch"] @@ -373,7 +376,8 @@ def create_empty_pit(net): branch_length = get_lookup(net, "branch", "length") # init empty pit pit = {"node": np.empty((node_length, node_cols), dtype=np.float64), - "branch": np.empty((branch_length, branch_cols), dtype=np.float64)} + "branch": np.empty((branch_length, branch_cols), dtype=np.float64), + "components": {}} net["_pit"] = pit return pit @@ -435,7 +439,82 @@ def create_lookups(net): "internal_nodes_lookup": internal_nodes_lookup} -def check_connectivity(net, branch_pit, node_pit, check_heat): +def identify_active_nodes_branches(net, branch_pit, node_pit, hydraulic=True): + """ + Function that creates the connectivity lookup for nodes and branches. If the option \ + "check_connectivity" is set, a full connectivity check is performed based on a sparse matrix \ + graph search. Otherwise, only the nodes and branches are identified that are inactive, which \ + means:\ + - in case of hydraulics, just use the "ACTIVE" identifier of the respective components\ + - in case of heat transfer, use the hydraulic result to check which branches are traversed \ + by the fluid and a simple rule to make sure that active nodes are connected to at least one\ + traversed branch\ + The result of this connectivity search is stored in the lookups (e.g. as \ + net["_lookups"]["node_active_hydraulics"]) + + :param net: the pandapipes net for which to identify the connectivity + :type net: pandapipes.pandapipesNet + :param branch_pit: Internal array with branch entries + :type branch_pit: np.array + :param node_pit: Internal array with node entries + :type node_pit: np.array + :param hydraulic: flag for the mode (if True, do the check for the hydraulic simulation, \ + otherwise for the heat transfer simulation with other considerations) + :type hydraulic: bool, default True + :return: No output + """ + if hydraulic: + # connectivity check for hydraulic simulation + if get_net_option(net, "check_connectivity"): + nodes_connected, branches_connected = check_connectivity(net, branch_pit, node_pit) + else: + # if connectivity check is switched off, still consider oos elements + nodes_connected = node_pit[:, ACTIVE_ND].astype(np.bool_) + branches_connected = branch_pit[:, ACTIVE_BR].astype(np.bool_) + else: + # connectivity check for heat simulation (needs to consider branches with 0 velocity as + # well) + if get_net_option(net, "check_connectivity"): + # full connectivity check for hydraulic simulation + nodes_connected, branches_connected = check_connectivity(net, branch_pit, node_pit, + mode="heat_transfer") + else: + # if no full connectivity check is performed, all nodes that are not connected to the + # rest of the network wrt. flow can be identified by a more performant sum_by_group_call + # check for branches that are not traversed (for temperature calculation, this means + # that they are "out of service") + branches_connected = get_lookup(net, "branch", "active_hydraulics") \ + & branches_connected_flow(branch_pit) + fn = branch_pit[:, FROM_NODE].astype(np.int32) + tn = branch_pit[:, TO_NODE].astype(np.int32) + fn_tn, flow = _sum_by_group( + get_net_option(net, "use_numba"), np.concatenate([fn, tn]), + np.concatenate([branches_connected, branches_connected]).astype(np.int32) + ) + nodes_connected = np.copy(get_lookup(net, "node", "active_hydraulics")) + # set nodes oos that are not connected to any branches with flow > 0 (0.1 is arbitrary + # here, any value between 0 and 1 should work, excluding 0 and 1) + nodes_connected[fn_tn] = nodes_connected[fn_tn] & (flow > 0.1) + mode = "hydraulics" if hydraulic else "heat_transfer" + net["_lookups"]["node_active_" + mode] = nodes_connected + net["_lookups"]["branch_active_" + mode] = branches_connected + + +def branches_connected_flow(branch_pit): + """ + Simple function to identify branches with flow based on the calculated velocity. + + :param branch_pit: The pandapipes internal table of the network (including hydraulics results) + :type branch_pit: np.array + :return: branches_connected_flow - lookup array if branch is connected wrt. flow + :rtype: np.array + """ + # TODO: is this formulation correct or could there be any caveats? + return ~np.isnan(branch_pit[:, VINIT]) \ + & ~np.isclose(branch_pit[:, VINIT], 0, rtol=1e-10, atol=1e-10) + + +def check_connectivity(net, branch_pit, node_pit, mode="hydraulics"): """ Perform a connectivity check which means that network nodes are identified that don't have any connection to an external grid component. Quick overview over the steps of this function: @@ -461,40 +540,30 @@ def check_connectivity(net, branch_pit, node_pit, check_heat): :type branch_pit: np.array :param node_pit: Internal array with node entries :type node_pit: np.array - :param check_heat: Flag which determines whether to also check for connectivity to heat \ - external grids - :type check_heat: bool - :return: (nodes_connected_hyd, branches_connected) - Lookups of np.arrays stating which of the + :return: (nodes_connected, branches_connected) - Lookups of np.arrays stating which of the internal nodes and branches are reachable from any of the hyd_slacks (np mask). :rtype: tuple(np.array) """ - active_branch_lookup = branch_pit[:, ACTIVE_BR].astype(bool) - active_node_lookup = node_pit[:, ACTIVE_ND].astype(bool) - from_nodes = branch_pit[:, FROM_NODE].astype(np.int32) - to_nodes = branch_pit[:, TO_NODE].astype(np.int32) - hyd_slacks = np.where((node_pit[:, NODE_TYPE] == P) & active_node_lookup)[0] - # hyd_slacks = np.where(((node_pit[:, NODE_TYPE] == P) | (node_pit[:, NODE_TYPE] == PC)) - # & active_node_lookup)[0] - - nodes_connected, branches_connected = perform_connectivity_search( - net, node_pit, hyd_slacks, from_nodes, to_nodes, active_node_lookup, active_branch_lookup, - mode="hydraulics") - if not check_heat: - return nodes_connected, branches_connected - - heat_slacks = np.where((node_pit[:, NODE_TYPE_T] == T) & nodes_connected)[0] - if len(heat_slacks) == len(hyd_slacks) and np.all(heat_slacks == hyd_slacks): - return nodes_connected, branches_connected - - nodes_connected, branches_connected = perform_connectivity_search( - net, node_pit, heat_slacks, from_nodes, to_nodes, nodes_connected, branches_connected, - mode="heat transfer") - return nodes_connected, branches_connected + if mode == "hydraulics": + active_branch_lookup = branch_pit[:, ACTIVE_BR].astype(np.bool_) + active_node_lookup = node_pit[:, ACTIVE_ND].astype(np.bool_) + slacks = np.where((node_pit[:, NODE_TYPE] == P) & active_node_lookup)[0] + else: + active_branch_lookup = branches_connected_flow(branch_pit) \ + & get_lookup(net, "branch", "active_hydraulics") + active_node_lookup = node_pit[:, ACTIVE_ND].astype(np.bool_)\ + & get_lookup(net, "node", "active_hydraulics") + slacks = np.where((node_pit[:, NODE_TYPE_T] == T) & active_node_lookup)[0] + + return perform_connectivity_search(net, node_pit, branch_pit, slacks, active_node_lookup, + active_branch_lookup, mode=mode) -def perform_connectivity_search(net, node_pit, slack_nodes, from_nodes, to_nodes, +def perform_connectivity_search(net, node_pit, branch_pit, slack_nodes, active_node_lookup, active_branch_lookup, mode="hydraulics"): len_nodes = len(node_pit) + from_nodes = branch_pit[:, FROM_NODE].astype(np.int32) + to_nodes = branch_pit[:, TO_NODE].astype(np.int32) nobranch = np.sum(active_branch_lookup) active_from_nodes = from_nodes[active_branch_lookup] active_to_nodes = to_nodes[active_branch_lookup] @@ -569,7 +638,7 @@ def get_table_index_list(net, pit_array, pit_indices, pit_type="node"): for tbl in tables] -def reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected): +def reduce_pit(net, node_pit, branch_pit, mode="hydraulics"): """ Create an internal ("active") pit with all nodes and branches that are actually in_service. This is also done for different lookups (e.g. the from_to indices for this pit and the node index @@ -582,44 +651,46 @@ def reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected): :type node_pit: np.array :param branch_pit: The internal structure branch array :type branch_pit: np.array - :param nodes_connected: A mask array stating which nodes are actually connected to the rest of\ - the net - :type nodes_connected: np.array - :param branches_connected: A mask array stating which branches are actually connected to the \ - rest of the net - :type branches_connected: np.array + :param mode: the mode of the calculation (either "hydraulics" or "heat_transfer") for storing /\ + retrieving correct lookups + :type mode: str, default "hydraulics" :return: No output """ active_pit = dict() els = dict() reduced_node_lookup = None + nodes_connected = get_lookup(net, "node", "active_" + mode) + branches_connected = get_lookup(net, "branch", "active_" + mode) if np.alltrue(nodes_connected): - net["_lookups"]["node_from_to_active"] = copy.deepcopy(get_lookup(net, "node", "from_to")) - net["_lookups"]["node_index_active"] = copy.deepcopy(get_lookup(net, "node", "index")) + net["_lookups"]["node_from_to_active_" + mode] = copy.deepcopy( + get_lookup(net, "node", "from_to")) + net["_lookups"]["node_index_active_" + mode] = copy.deepcopy( + get_lookup(net, "node", "index")) active_pit["node"] = np.copy(node_pit) else: active_pit["node"] = np.copy(node_pit[nodes_connected, :]) reduced_node_lookup = np.cumsum(nodes_connected) - 1 node_idx_lookup = get_lookup(net, "node", "index") - net["_lookups"]["node_index_active"] = { + net["_lookups"]["node_index_active_" + mode] = { tbl: reduced_node_lookup[idx_lookup[idx_lookup != -1]] for tbl, idx_lookup in node_idx_lookup.items()} els["node"] = nodes_connected if np.alltrue(branches_connected): - net["_lookups"]["branch_from_to_active"] = copy.deepcopy(get_lookup(net, "branch", - "from_to")) + net["_lookups"]["branch_from_to_active_" + mode] = copy.deepcopy( + get_lookup(net, "branch", "from_to")) active_pit["branch"] = np.copy(branch_pit) - net["_lookups"]["branch_index_active"] = copy.deepcopy(get_lookup(net, "branch", "index")) + net["_lookups"]["branch_index_active_" + mode] = copy.deepcopy( + get_lookup(net, "branch", "index")) else: active_pit["branch"] = np.copy(branch_pit[branches_connected, :]) branch_idx_lookup = get_lookup(net, "branch", "index") if len(branch_idx_lookup): reduced_branch_lookup = np.cumsum(branches_connected) - 1 - net["_lookups"]["branch_index_active"] = { + net["_lookups"]["branch_index_active_" + mode] = { tbl: reduced_branch_lookup[idx_lookup[idx_lookup != -1]] for tbl, idx_lookup in branch_idx_lookup.items()} else: - net["_lookups"]["branch_index_active"] = dict() + net["_lookups"]["branch_index_active_" + mode] = dict() els["branch"] = branches_connected if reduced_node_lookup is not None: active_pit["branch"][:, FROM_NODE] = reduced_node_lookup[ @@ -627,8 +698,6 @@ def reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected): active_pit["branch"][:, TO_NODE] = reduced_node_lookup[ branch_pit[branches_connected, TO_NODE].astype(np.int32)] net["_active_pit"] = active_pit - net["_lookups"]["node_active"] = nodes_connected - net["_lookups"]["branch_active"] = branches_connected for el, connected_els in els.items(): ft_lookup = get_lookup(net, el, "from_to") @@ -639,4 +708,4 @@ def reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected): for table, (_, _, len_new) in sorted(aux_lookup.items(), key=lambda x: x[1][0]): from_to_active_lookup[table] = (count, count + len_new) count += len_new - net["_lookups"]["%s_from_to_active" % el] = from_to_active_lookup + net["_lookups"]["%s_from_to_active_%s" % (el, mode)] = from_to_active_lookup diff --git a/pandapipes/pf/result_extraction.py b/pandapipes/pf/result_extraction.py index c453c9f5..037ffc79 100644 --- a/pandapipes/pf/result_extraction.py +++ b/pandapipes/pf/result_extraction.py @@ -2,7 +2,7 @@ from pandapipes.constants import NORMAL_PRESSURE, NORMAL_TEMPERATURE from pandapipes.idx_branch import ELEMENT_IDX, FROM_NODE, TO_NODE, LOAD_VEC_NODES, VINIT, RE, \ - LAMBDA, TINIT, FROM_NODE_T, TO_NODE_T, PL + LAMBDA, FROM_NODE_T, TO_NODE_T, PL, TOUTINIT from pandapipes.idx_node import TABLE_IDX as TABLE_IDX_NODE, PINIT, PAMB, TINIT as TINIT_NODE from pandapipes.pf.internals_toolbox import _sum_by_group from pandapipes.pf.pipeflow_setup import get_table_number, get_lookup, get_net_option @@ -14,13 +14,15 @@ from pandapower.pf.no_numba import jit -def extract_all_results(net, nodes_connected, branches_connected): +def extract_all_results(net, calculation_mode): """ Extract results from branch pit and node pit and write them to the different tables of the net,\ as defined by the component models. :param net: pandapipes net for which to extract results into net.res_xy :type net: pandapipesNet + :param net: mode of the simulation (e.g. "hydraulics" or "heat" or "all") + :type net: str :return: No output """ @@ -49,8 +51,7 @@ def extract_all_results(net, nodes_connected, branches_connected): } branch_results.update(gas_branch_results) for comp in net['component_list']: - comp.extract_results(net, net["_options"], branch_results, nodes_connected, - branches_connected) + comp.extract_results(net, net["_options"], branch_results, calculation_mode) def get_basic_branch_results(net, branch_pit, node_pit): @@ -76,9 +77,15 @@ def get_branch_results_gas(net, branch_pit, node_pit, from_nodes, to_nodes, v_mp / (p_abs_from[mask] ** 2 - p_abs_to[mask] ** 2) fluid = get_fluid(net) - numerator = NORMAL_PRESSURE * branch_pit[:, TINIT] / NORMAL_TEMPERATURE - normfactor_from = numerator * fluid.get_property("compressibility", p_abs_from) / p_abs_from - normfactor_to = numerator * fluid.get_property("compressibility", p_abs_to) / p_abs_to + t_from = node_pit[from_nodes, TINIT_NODE] + t_to = branch_pit[:, TOUTINIT] + tm = (t_from + t_to) / 2 + numerator_from = NORMAL_PRESSURE * t_from / NORMAL_TEMPERATURE + numerator_to = NORMAL_PRESSURE * t_to / NORMAL_TEMPERATURE + numerator = NORMAL_PRESSURE * tm / NORMAL_TEMPERATURE + + normfactor_from = numerator_from * fluid.get_property("compressibility", p_abs_from) / p_abs_from + normfactor_to = numerator_to * fluid.get_property("compressibility", p_abs_to) / p_abs_to normfactor_mean = numerator * fluid.get_property("compressibility", p_abs_mean) / p_abs_mean v_gas_from = v_mps * normfactor_from @@ -100,7 +107,7 @@ def get_branch_results_gas_numba(net, branch_pit, node_pit, from_nodes, to_nodes comp_mean = fluid.get_property("compressibility", p_abs_mean) v_gas_from, v_gas_to, v_gas_mean, normfactor_from, normfactor_to, normfactor_mean = \ - get_gas_vel_numba(branch_pit, comp_from, comp_to, comp_mean, p_abs_from, p_abs_to, + get_gas_vel_numba(node_pit, branch_pit, comp_from, comp_to, comp_mean, p_abs_from, p_abs_to, p_abs_mean, v_mps) return v_gas_from, v_gas_to, v_gas_mean, p_abs_from, p_abs_to, p_abs_mean, normfactor_from, \ @@ -124,15 +131,19 @@ def get_pressures_numba(node_pit, from_nodes, to_nodes, v_mps, p_from, p_to): @jit(nopython=True) -def get_gas_vel_numba(branch_pit, comp_from, comp_to, comp_mean, p_abs_from, p_abs_to, p_abs_mean, - v_mps): +def get_gas_vel_numba(node_pit, branch_pit, comp_from, comp_to, comp_mean, p_abs_from, p_abs_to, p_abs_mean, v_mps): v_gas_from, v_gas_to, v_gas_mean, normfactor_from, normfactor_to, normfactor_mean = \ [np.empty_like(v_mps) for _ in range(6)] - + from_nodes = branch_pit[:, FROM_NODE].astype(np.int32) for i in range(len(v_mps)): - numerator = np.divide(NORMAL_PRESSURE * branch_pit[i, TINIT], NORMAL_TEMPERATURE) - normfactor_from[i] = np.divide(numerator * comp_from[i], p_abs_from[i]) - normfactor_to[i] = np.divide(numerator * comp_to[i], p_abs_to[i]) + t_from = node_pit[from_nodes[i], TINIT_NODE] + t_to = branch_pit[i, TOUTINIT] + tm = (t_from + t_to) / 2 + numerator_from = np.divide(NORMAL_PRESSURE * t_from, NORMAL_TEMPERATURE) + numerator_to = np.divide(NORMAL_PRESSURE * t_to, NORMAL_TEMPERATURE) + numerator = np.divide(NORMAL_PRESSURE * tm, NORMAL_TEMPERATURE) + normfactor_from[i] = np.divide(numerator_from * comp_from[i], p_abs_from[i]) + normfactor_to[i] = np.divide(numerator_to * comp_to[i], p_abs_to[i]) normfactor_mean[i] = np.divide(numerator * comp_mean[i], p_abs_mean[i]) v_gas_from[i] = v_mps[i] * normfactor_from[i] v_gas_to[i] = v_mps[i] * normfactor_to[i] @@ -141,8 +152,11 @@ def get_gas_vel_numba(branch_pit, comp_from, comp_to, comp_mean, p_abs_from, p_a return v_gas_from, v_gas_to, v_gas_mean, normfactor_from, normfactor_to, normfactor_mean -def extract_branch_results_with_internals(net, branch_results, table_name, res_nodes_from, - res_nodes_to, res_mean, node_name, branches_connected): +def extract_branch_results_with_internals(net, branch_results, table_name, + res_nodes_from_hydraulics, res_nodes_from_heat, + res_nodes_to_hydraulics, res_nodes_to_heat, + res_mean_hydraulics, res_mean_heat, node_name, + simulation_mode): # the result table to write results to res_table = net["res_" + table_name] @@ -155,101 +169,137 @@ def extract_branch_results_with_internals(net, branch_results, table_name, res_n # respective table), the placement of the indices mus be known to allocate the values correctly placement_table = np.argsort(net[table_name].index.values) idx_pit = branch_pit[f:t, ELEMENT_IDX] - comp_connected = branches_connected[f:t] node_pit = net["_pit"]["node"] # the id of the external node table inside the node_pit (mostly this is "junction": 0) ext_node_tbl_idx = get_table_number(get_lookup(net, "node", "table"), node_name) - if len(res_nodes_from) > 0: - # results that relate to the from_node --> in case of many internal nodes, only the single - # from_node that is the exterior node (e.g. junction vs. internal pipe_node) result has to - # be extracted from the node_pit - from_nodes = branch_results["from_nodes"][f:t] - from_nodes_external = node_pit[from_nodes, TABLE_IDX_NODE] == ext_node_tbl_idx - considered = from_nodes_external & comp_connected - external_active = comp_connected[from_nodes_external] - for res_name, entry in res_nodes_from: - res_table[res_name].values[external_active] = branch_results[entry][f:t][considered] - - if len(res_nodes_to) > 0: - # results that relate to the to_node --> in case of many internal nodes, only the single - # to_node that is the exterior node (e.g. junction vs. internal pipe_node) result has to - # be extracted from the node_pit - to_nodes = branch_results["to_nodes"][f:t] - to_nodes_external = node_pit[to_nodes, TABLE_IDX_NODE] == ext_node_tbl_idx - considered = to_nodes_external & comp_connected - external_active = comp_connected[to_nodes_external] - for res_name, entry in res_nodes_to: - res_table[res_name].values[external_active] = branch_results[entry][f:t][considered] - - if len(res_mean) > 0: - # results that relate to the whole branch and shall be averaged (by summing up all values - # and dividing by number of internal sections) - use_numba = get_net_option(net, "use_numba") - res = _sum_by_group(use_numba, idx_pit, np.ones_like(idx_pit), - comp_connected.astype(np.int32), - *[branch_results[rn[1]][f:t] for rn in res_mean]) - connected_ind = res[2] > 0.99 - num_internals = res[1][connected_ind] - # hint: idx_pit[placement_table] should result in the indices as ordered in the table - placement_table = placement_table[connected_ind] - - for i, (res_name, entry) in enumerate(res_mean): - res_table[res_name].values[placement_table] = res[i + 3][connected_ind] / num_internals - - -def extract_branch_results_without_internals(net, branch_results, required_results, table_name, - branches_connected): + for (result_mode, res_nodes_from, res_nodes_to, res_mean) in [ + ("hydraulics", res_nodes_from_hydraulics, res_nodes_to_hydraulics, res_mean_hydraulics), + ("heat", res_nodes_from_heat, res_nodes_to_heat, res_mean_heat) + ]: + if result_mode == "hydraulics" and simulation_mode == "heat": + continue + lookup_name = "hydraulics" + if result_mode == "heat" and simulation_mode in ["heat", "all"]: + lookup_name = "heat_transfer" + comp_connected = get_lookup(net, "branch", "active_" + lookup_name)[f:t] + for (res_ext, node_name) in ((res_nodes_from, "from_nodes"), (res_nodes_to, "to_nodes")): + if len(res_ext) == 0: + continue + # results that relate to the from_node --> in case of many internal nodes, only the + # single from_node that is the exterior node (e.g. junction vs. internal pipe_node) + # result has to be extracted from the node_pit + end_nodes = branch_results[node_name][f:t] + end_nodes_external = node_pit[end_nodes, TABLE_IDX_NODE] == ext_node_tbl_idx + considered = end_nodes_external & comp_connected + external_active = comp_connected[end_nodes_external] + for res_name, entry in res_ext: + res_table[res_name].values[external_active] = branch_results[entry][f:t][considered] + if len(res_mean) > 0: + # results that relate to the whole branch and shall be averaged (by summing up all + # values and dividing by number of internal sections) + use_numba = get_net_option(net, "use_numba") + res = _sum_by_group(use_numba, idx_pit, np.ones_like(idx_pit), + comp_connected.astype(np.int32), + *[branch_results[rn[1]][f:t] for rn in res_mean]) + connected_ind = res[2] > 0.99 + num_internals = res[1][connected_ind] + + # hint: idx_pit[placement_table] should result in the indices as ordered in the table + pt = placement_table[connected_ind] + + for i, (res_name, entry) in enumerate(res_mean_hydraulics): + res_table[res_name].values[pt] = res[i + 3][connected_ind] / num_internals + + +def extract_branch_results_without_internals(net, branch_results, required_results_hydraulic, + required_results_heat, table_name, simulation_mode): + """ + Extract the results from the branch result array derived from the pit to the result table of the + net (only for branch components without internal nodes). Here, we need to consider which results + exist for hydraulic calculation and for heat transfer calculation (wrt. connectivity). + + :param net: The pandapipes net that the internal structure belongs to + :type net: pandapipesNet + :param branch_results: Important branch results from the internal pit structure + :type branch_results: dict[np.ndarray] + :param required_results_hydraulic: The entries that should be extracted for the respective \ + component for hydraulic calculation + :type required_results_hydraulic: list[tuple] + :param required_results_heat: The entries that should be extracted for the respective \ + component for heat transfer calculation + :type required_results_heat: list[tuple] + :param table_name: The name of the table that the results should be written to + :type table_name: str + :param simulation_mode: simulation mode (e.g. "hydraulics", "heat", "all"); defines whether results from \ + hydraulic or temperature calculation are transferred + :type simulation_mode: str + :return: No output + :rtype: None + """ res_table = net["res_" + table_name] f, t = get_lookup(net, "branch", "from_to")[table_name] - comp_connected = branches_connected[f:t] - - for res_name, entry in required_results: - res_table[res_name].values[:][comp_connected] = branch_results[entry][f:t][comp_connected] - -def extract_results_active_pit(net, node_pit, branch_pit, nodes_connected, branches_connected): + # extract hydraulic results + if simulation_mode in ["hydraulics", "all"]: + # lookup for connected branch elements (hydraulic results) + comp_connected_hyd = get_lookup(net, "branch", "active_hydraulics")[f:t] + for res_name, entry in required_results_hydraulic: + res_table[res_name].values[:][comp_connected_hyd] = \ + branch_results[entry][f:t][comp_connected_hyd] + if simulation_mode == "hydraulics": + for res_name, entry in required_results_heat: + res_table[res_name].values[:][comp_connected_hyd] = \ + branch_results[entry][f:t][comp_connected_hyd] + + # extract heat transfer results + if simulation_mode in ["heat", "all"]: + # lookup for connected branch elements (heat transfer results) + comp_connected_ht = get_lookup(net, "branch", "active_heat_transfer")[f:t] + for res_name, entry in required_results_heat: + res_table[res_name].values[:][comp_connected_ht] = \ + branch_results[entry][f:t][comp_connected_ht] + + +def extract_results_active_pit(net, mode="hydraulics"): """ Extract the pipeflow results from the internal pit structure ("_active_pit") to the general pit structure. :param net: The pandapipes net that the internal structure belongs to :type net: pandapipesNet - :param node_pit: The internal structure node array - :type node_pit: np.array - :param branch_pit: The internal structure branch array - :type branch_pit: np.array - :param nodes_connected: A mask array stating which nodes are actually connected to the rest of\ - the net - :type nodes_connected: np.array - :param branches_connected: A mask array stating which branches are actually connected to the \ - rest of the net - :type branches_connected: np.array + :param mode: defines whether results from hydraulic or temperature calculation are transferred + :type mode: str, default "hydraulics" :return: No output """ - all_nodes_connected = np.alltrue(nodes_connected) - if not all_nodes_connected: - node_pit[~nodes_connected, PINIT] = np.NaN - node_pit[nodes_connected, :] = net["_active_pit"]["node"] - cols_br = np.array([i for i in range(branch_pit.shape[1]) - if i not in [FROM_NODE, TO_NODE, FROM_NODE_T, TO_NODE_T]]) - else: - net["_pit"]["node"] = np.copy(net["_active_pit"]["node"]) - cols_br = None - - if not np.alltrue(branches_connected): - branch_pit[~branches_connected, VINIT] = np.NaN - rows_active_br = np.where(branches_connected)[0] - if all_nodes_connected: - branch_pit[rows_active_br, :] = net["_active_pit"]["branch"][:, :] - else: - branch_pit[rows_active_br[:, np.newaxis], cols_br[np.newaxis, :]] = \ - net["_active_pit"]["branch"][:, cols_br] - else: - if all_nodes_connected: - net["_pit"]["branch"] = np.copy(net["_active_pit"]["branch"]) - else: - net["_pit"]["branch"][:, cols_br] = net["_active_pit"]["branch"][:, cols_br] + nodes_connected = get_lookup(net, "node", "active_" + mode) + branches_connected = get_lookup(net, "branch", "active_" + mode) + result_node_col = PINIT if mode == "hydraulics" else TINIT_NODE + not_affected_node_col = TINIT_NODE if mode == "hydraulics" else PINIT + copied_node_cols = np.array([i for i in range(net["_pit"]["node"].shape[1]) + if i not in [not_affected_node_col]]) + rows_nodes = np.arange(net["_pit"]["node"].shape[0])[nodes_connected] + + result_branch_col = VINIT if mode == "hydraulics" else TOUTINIT + not_affected_branch_col = TOUTINIT if mode == "hydraulics" else VINIT + copied_branch_cols = np.array([i for i in range(net["_pit"]["branch"].shape[1]) + if i not in [FROM_NODE, TO_NODE, FROM_NODE_T, TO_NODE_T, + not_affected_branch_col]]) + rows_branches = np.arange(net["_pit"]["branch"].shape[0])[branches_connected] + + net["_pit"]["node"][~nodes_connected, result_node_col] = np.NaN + net["_pit"]["node"][rows_nodes[:, np.newaxis], copied_node_cols[np.newaxis, :]] = \ + net["_active_pit"]["node"][:, copied_node_cols] + net["_pit"]["branch"][~branches_connected, result_branch_col] = np.NaN + net["_pit"]["branch"][rows_branches[:, np.newaxis], copied_branch_cols[np.newaxis, :]] = \ + net["_active_pit"]["branch"][:, copied_branch_cols] + + +def consider_heat(mode, results=None): + consider_ = mode in ["heat", "all"] + if results is None: + return consider_ + return consider_ and any(r[2] for r in results) diff --git a/pandapipes/pipeflow.py b/pandapipes/pipeflow.py index b91b2679..aa9f4994 100644 --- a/pandapipes/pipeflow.py +++ b/pandapipes/pipeflow.py @@ -7,15 +7,14 @@ from pandapower.auxiliary import ppException from scipy.sparse.linalg import spsolve -from pandapipes.idx_branch import ACTIVE as ACTIVE_BR, FROM_NODE, TO_NODE, FROM_NODE_T, \ - TO_NODE_T, VINIT, T_OUT, VINIT_T -from pandapipes.idx_node import PINIT, TINIT, ACTIVE as ACTIVE_ND +from pandapipes.idx_branch import FROM_NODE, TO_NODE, FROM_NODE_T, TO_NODE_T, VINIT, TOUTINIT, VINIT_T +from pandapipes.idx_node import PINIT, TINIT from pandapipes.pf.build_system_matrix import build_system_matrix -from pandapipes.pf.derivative_calculation import calculate_derivatives_hydraulic +from pandapipes.pf.derivative_calculation import calculate_derivatives_hydraulic, calculate_derivatives_thermal from pandapipes.pf.pipeflow_setup import get_net_option, get_net_options, set_net_option, \ init_options, create_internal_results, write_internal_results, get_lookup, create_lookups, \ - initialize_pit, check_connectivity, reduce_pit, \ - set_user_pf_options, init_all_result_tables + initialize_pit, reduce_pit, set_user_pf_options, init_all_result_tables, \ + identify_active_nodes_branches from pandapipes.pf.result_extraction import extract_all_results, extract_results_active_pit try: @@ -79,37 +78,36 @@ def pipeflow(net, sol_vec=None, **kwargs): calculate_hydraulics = calculation_mode in ["hydraulics", "all"] calculate_heat = calculation_mode in ["heat", "all"] - if get_net_option(net, "check_connectivity"): - nodes_connected, branches_connected = check_connectivity( - net, branch_pit, node_pit, check_heat=calculate_heat) - else: - nodes_connected = node_pit[:, ACTIVE_ND].astype(np.bool_) - branches_connected = branch_pit[:, ACTIVE_BR].astype(np.bool_) - - reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected) + identify_active_nodes_branches(net, branch_pit, node_pit) - if calculation_mode == "heat" and not net.user_pf_options["hyd_flag"]: - raise UserWarning("Converged flag not set. Make sure that hydraulic calculation results " - "are available.") - elif calculation_mode == "heat" and net.user_pf_options["hyd_flag"]: - net["_active_pit"]["node"][:, PINIT] = sol_vec[:len(node_pit)] - net["_active_pit"]["branch"][:, VINIT] = sol_vec[len(node_pit):] + if calculation_mode == "heat": + if not net.user_pf_options["hyd_flag"]: + raise UserWarning("Converged flag not set. Make sure that hydraulic calculation " + "results are available.") + else: + net["_pit"]["node"][:, PINIT] = sol_vec[:len(node_pit)] + net["_pit"]["branch"][:, VINIT] = sol_vec[len(node_pit):] if calculate_hydraulics: + reduce_pit(net, node_pit, branch_pit, mode="hydraulics") converged, _ = hydraulics(net) if not converged: raise PipeflowNotConverged("The hydraulic calculation did not converge to a solution.") + extract_results_active_pit(net, mode="hydraulics") if calculate_heat: + node_pit, branch_pit = net["_pit"]["node"], net["_pit"]["branch"] + identify_active_nodes_branches(net, branch_pit, node_pit, False) + reduce_pit(net, node_pit, branch_pit, mode="heat_transfer") converged, _ = heat_transfer(net) if not converged: raise PipeflowNotConverged("The heat transfer calculation did not converge to a " "solution.") + extract_results_active_pit(net, mode="heat_transfer") elif not calculate_hydraulics: raise UserWarning("No proper calculation mode chosen.") - extract_results_active_pit(net, node_pit, branch_pit, nodes_connected, branches_connected) - extract_all_results(net, nodes_connected, branches_connected) + extract_all_results(net, calculation_mode) def hydraulics(net): @@ -191,7 +189,7 @@ def heat_transfer(net): error_t_out.append(linalg.norm(delta_t_out) / (len(delta_t_out))) finalize_iteration(net, niter, error_t, error_t_out, residual_norm, nonlinear_method, tol_t, - tol_t, tol_res, t_init_old, t_out_old, hyraulic_mode=True) + tol_t, tol_res, t_init_old, t_out_old, hydraulic_mode=False) logger.debug("F: %s" % epsilon.round(4)) logger.debug("T_init_: %s" % t_init.round(4)) logger.debug("T_out_: %s" % t_out.round(4)) @@ -222,7 +220,7 @@ def solve_hydraulics(net): branch_pit = net["_active_pit"]["branch"] node_pit = net["_active_pit"]["node"] - branch_lookups = get_lookup(net, "branch", "from_to_active") + branch_lookups = get_lookup(net, "branch", "from_to_active_hydraulics") for comp in net['component_list']: comp.adaption_before_derivatives_hydraulic( net, branch_pit, node_pit, branch_lookups, options) @@ -258,7 +256,7 @@ def solve_temperature(net): options = net["_options"] branch_pit = net["_active_pit"]["branch"] node_pit = net["_active_pit"]["node"] - branch_lookups = get_lookup(net, "branch", "from_to_active") + branch_lookups = get_lookup(net, "branch", "from_to_active_heat_transfer") # Negative velocity values are turned to positive ones (including exchange of from_node and # to_node for temperature calculation @@ -271,17 +269,22 @@ def solve_temperature(net): branch_pit[mask, TO_NODE_T] = branch_pit[mask, FROM_NODE] for comp in net['component_list']: - comp.calculate_derivatives_thermal(net, branch_pit, node_pit, branch_lookups, options) + comp.adaption_before_derivatives_thermal( + net, branch_pit, node_pit, branch_lookups, options) + calculate_derivatives_thermal(net, branch_pit, node_pit, options) + for comp in net['component_list']: + comp.adaption_after_derivatives_thermal( + net, branch_pit, node_pit, branch_lookups, options) jacobian, epsilon = build_system_matrix(net, branch_pit, node_pit, True) t_init_old = node_pit[:, TINIT].copy() - t_out_old = branch_pit[:, T_OUT].copy() + t_out_old = branch_pit[:, TOUTINIT].copy() x = spsolve(jacobian, epsilon) node_pit[:, TINIT] += x[:len(node_pit)] * options["alpha"] - branch_pit[:, T_OUT] += x[len(node_pit):] + branch_pit[:, TOUTINIT] += x[len(node_pit):] - return branch_pit[:, T_OUT], t_out_old, node_pit[:, TINIT], t_init_old, epsilon + return branch_pit[:, TOUTINIT], t_out_old, node_pit[:, TINIT], t_init_old, epsilon def set_damping_factor(net, niter, error): @@ -314,8 +317,8 @@ def set_damping_factor(net, niter, error): def finalize_iteration(net, niter, error_1, error_2, residual_norm, nonlinear_method, tol_1, tol_2, - tol_res, vals_1_old, vals_2_old, hyraulic_mode=True): - col1, col2 = (PINIT, VINIT) if hyraulic_mode else (TINIT, T_OUT) + tol_res, vals_1_old, vals_2_old, hydraulic_mode=True): + col1, col2 = (PINIT, VINIT) if hydraulic_mode else (TINIT, TOUTINIT) # Control of damping factor if nonlinear_method == "automatic": @@ -335,7 +338,7 @@ def finalize_iteration(net, niter, error_1, error_2, residual_norm, nonlinear_me elif get_net_option(net, "alpha") == 1: set_net_option(net, "converged", True) - if hyraulic_mode: + if hydraulic_mode: logger.debug("errorv: %s" % error_1[niter]) logger.debug("errorp: %s" % error_2[niter]) logger.debug("alpha: %s" % get_net_option(net, "alpha")) diff --git a/pandapipes/plotting/generic_geodata.py b/pandapipes/plotting/generic_geodata.py index 1a1b1be0..a158651d 100644 --- a/pandapipes/plotting/generic_geodata.py +++ b/pandapipes/plotting/generic_geodata.py @@ -20,7 +20,7 @@ def build_igraph_from_ppipes(net, junctions=None): """ This function uses the igraph library to create an igraph graph for a given pandapipes network. - Pipes and valves are respected. + Any branch component is respected. Performance vs. networkx: https://graph-tool.skewed.de/performance :param net: The pandapipes network @@ -50,19 +50,15 @@ def build_igraph_from_ppipes(net, junctions=None): g.vs["label"] = list(junction_index) pp_junction_mapping = dict(list(zip(junction_index, list(range(nr_junctions))))) - mask = _get_element_mask_from_nodes(net, "pipe", ["from_junction", "to_junction"], junctions) - for pipe in net.pipe[mask].itertuples(): - g.add_edge(pp_junction_mapping[pipe.from_junction], pp_junction_mapping[pipe.to_junction], - weight=pipe.length_km) - for comp in net['component_list']: - if not isinstance(comp, BranchComponent): + if not issubclass(comp, BranchComponent): continue fjc, tjc = comp.from_to_node_cols() mask = _get_element_mask_from_nodes(net, comp.table_name(), [fjc, tjc], junctions) for comp_data in net[comp.table_name()][mask].itertuples(): - g.add_edge(pp_junction_mapping[comp_data[fjc]], pp_junction_mapping[comp_data[tjc]], - weight=0.001) + weight = 0.001 if 'length_km' not in dir(comp_data) else getattr(comp_data, 'length_km') + g.add_edge(pp_junction_mapping[getattr(comp_data, fjc)], pp_junction_mapping[getattr(comp_data, tjc)], + weight=weight) meshed = _igraph_meshed(g) roots = [pp_junction_mapping[s] for s in net.ext_grid.junction.values if s in junction_index] diff --git a/pandapipes/plotting/plotly/__init__.py b/pandapipes/plotting/plotly/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pandapipes/plotting/plotly/simple_plotly.py b/pandapipes/plotting/plotly/simple_plotly.py new file mode 100644 index 00000000..0faee2c8 --- /dev/null +++ b/pandapipes/plotting/plotly/simple_plotly.py @@ -0,0 +1,153 @@ +# Copyright (c) 2022 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel, and University of 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 pandas as pd + +from pandapipes.plotting.plotly.traces import create_junction_trace, create_pipe_trace, \ + create_valve_trace, create_compressor_trace +from pandapower.plotting.plotly.simple_plotly import _simple_plotly_generic, draw_traces + +try: + from pandaplan.core import pplog as logging +except ImportError: + import logging +logger = logging.getLogger(__name__) + + +def get_hoverinfo(net, element, precision=3, sub_index=None): + hover_index = net[element].index + if element == "junction": + sink_str, source_str = [], [] + if hasattr(net, "sink"): + for ln in [net.sink.loc[net.sink.junction == b, "mdot_kg_per_s"].sum() for b in + net.junction.index]: + sink_str.append("Sink: {:.3f} kg/s
".format(ln) if ln != 0. else "") + if hasattr(net, "source"): + for s in [net.source.loc[net.source.junction == b, "mdot_kg_per_s"].sum() for b in + net.junction.index]: + source_str.append("Source: {:.3f} kg/s
".format(s) if s != 0. else "") + hoverinfo = ( + "Index: " + net.junction.index.astype(str) + "
" + + "Name: " + net.junction['name'].astype(str) + "
" + + "Height: " + net.junction['height_m'].astype(str) + " m
" + + "Pressure: " + net.junction['pn_bar'].round(precision).astype(str) + " bar
" + + "Temp.: " + net.junction['tfluid_k'].round(precision).astype(str) + " K
" + # + sink_str + source_str # TODO: fix this (correct indexing) + ).tolist() + elif element == "pipe": + hoverinfo = ( + "Index: " + net.pipe.index.astype(str) + "
" + + "Name: " + net.pipe['name'].astype(str) + "
" + + "Length: " + net.pipe['length_km'].round(precision).astype(str) + " km
" + + "Diameter: " + ((net.pipe['diameter_m']).round(precision)*1e3).astype(str) + " mm" + + "
" + + "k: " + (net.pipe['k_mm']).round(precision).astype(str) + " mm
" + ).tolist() + elif element == "pump": + hoverinfo = ( + "Index: " + net.pump.index.astype(str) + "
" + + "Name: " + net.pump['name'].astype(str) + "
" + + "Std. Type: " + net.pump['std_type'] + "
" + ).tolist() + elif element == "compressor": + hoverinfo = ( + "Index: " + net.compressor.index.astype(str) + "
" + + "Name: " + net.compressor['name'].astype(str) + "
" + + "Pressure ratio: " + net.compressor['pressure_ratio'] + "
" + ).tolist() + elif element == "pressure_control": + hoverinfo = ( + "Index: " + net.pressure_control.index.astype(str) + "
" + + "Name: " + net.pressure_control['name'].astype(str) + "
" + + "controlled junction:" + net.pressure_control['controlled_junction'] + "
" + + "controlled pressure:" + net.pressure_control['controlled_p_bar'] + " bar
" + ).tolist() + elif element == "ext_grid": + hoverinfo = ( + "Index: " + net.ext_grid.index.astype(str) + "
" + + "Name: " + net.ext_grid['name'].astype(str) + "
" + + "Pressure: " + net.ext_grid['p_bar'].round(precision).astype(str) + " bar
" + + "Temp.: " + net.ext_grid['t_k'].round(precision).astype(str) + " K
" + ).tolist() + + hover_index = net.ext_grid.junction.tolist() + elif element == "valve": + hoverinfo = ( + "Index: " + net.valve.index.astype(str) + "
" + + "Name: " + net.valve['name'].astype(str) + "
" + + "Open: " + net.valve['opened'].astype(str) + "
" + + "Diameter: " + ((net.valve['diameter_m']).round(precision)*1e3).astype(str) + + " mm
" + ).tolist() + else: + return None + hoverinfo = pd.Series(index=hover_index, data=hoverinfo) + if sub_index is not None: + hoverinfo = hoverinfo.loc[list(sub_index)] + return hoverinfo + + +def simple_plotly(net, use_pipe_geodata=None, on_map=False, + projection=None, map_style='basic', figsize=1, aspectratio='auto', pipe_width=1, + junction_size=10, ext_grid_size=20.0, valve_size=3.0, compressor_size=3, + junction_color="blue", pipe_color='grey', pressure_reg_color='green', + ext_grid_color="yellow", valve_color="black", compressor_color="cyan", + filename='temp-plot.html', + auto_open=True, showlegend=True, additional_traces=None): + respect_valves = False # TODO, not implemented yet + node_element = "junction" + branch_element = "pipe" + trans_element = "pump" + separator_element = "valve" + + traces, settings = _simple_plotly_generic(net=net, + respect_separators=respect_valves, + use_branch_geodata=use_pipe_geodata, + on_map=on_map, + projection=projection, + map_style=map_style, + figsize=figsize, + aspectratio=aspectratio, + branch_width=pipe_width, + node_size=junction_size, + ext_grid_size=ext_grid_size, + node_color=junction_color, + branch_color=pipe_color, + trafo_color=pressure_reg_color, + trafo3w_color=pressure_reg_color, + ext_grid_color=ext_grid_color, + node_element=node_element, + branch_element=branch_element, + trans_element=trans_element, + trans3w_element=None, + separator_element=separator_element, + branch_trace_func=create_pipe_trace, + node_trace_func=create_junction_trace, + hoverinfo_func=get_hoverinfo, + filename=filename, + auto_open=auto_open, + showlegend=showlegend) + if "valve" in net.keys() and len(net.valve) > 0: + traces.extend(create_valve_trace(net, valves=net.valve.loc[net.valve.opened].index, + size=valve_size, color=valve_color, + trace_name="open valves")) + traces.extend(create_valve_trace(net, valves=net.valve.loc[~net.valve.opened].index, + size=valve_size, color=valve_color, dash="dot", + trace_name="closed valves")) + + if "compressor" in net.keys() and len(net.compressor) > 0: + traces.extend(create_compressor_trace(net, size=compressor_size, color=compressor_color)) + if additional_traces: + if isinstance(additional_traces, dict): + traces.append(additional_traces) + else: + traces.extend(additional_traces) + + return draw_traces(traces, **settings) + + +if __name__ == '__main__': + from pandapipes.networks import gas_versatility + net = gas_versatility() + simple_plotly(net) diff --git a/pandapipes/plotting/plotly/traces.py b/pandapipes/plotting/plotly/traces.py new file mode 100644 index 00000000..aa5e029c --- /dev/null +++ b/pandapipes/plotting/plotly/traces.py @@ -0,0 +1,88 @@ +# Copyright (c) 2022 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel, and University of Kassel. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +from pandapower.plotting.plotly.traces import _create_node_trace, _create_branch_trace, \ + draw_traces + + +def create_junction_trace(net, junctions=None, size=5, patch_type="circle", color="blue", + infofunc=None, trace_name='junctions', legendgroup=None, cmap=None, + cmap_vals=None, cbar_title=None, cmin=None, cmax=None, cpos=1.0, + colormap_column="p_bar"): + + node_element = "junction" + branch_element = "pipe" + + return _create_node_trace(net=net, nodes=junctions, size=size , patch_type=patch_type, + color=color, infofunc=infofunc, trace_name=trace_name, + legendgroup=legendgroup, cmap=cmap, cmap_vals=cmap_vals, + cbar_title=cbar_title, cmin=cmin, cmax=cmax, cpos=cpos, + colormap_column=colormap_column, + node_element=node_element, branch_element=branch_element) + + +def create_pipe_trace(net, pipes=None, use_pipe_geodata=True, respect_valves=False, width=1.0, + color='grey', infofunc=None, trace_name='pipes', legendgroup='pipes', + cmap=None, cbar_title=None, show_colorbar=True, cmap_vals=None, cmin=None, + cmax=None, cpos=1.1): + branch_element = "pipe" + node_element = "junction" + separator_element = "valve" + + return _create_branch_trace(net=net, branches=pipes, use_branch_geodata=use_pipe_geodata, + respect_separators=respect_valves, width=width, color=color, + infofunc=infofunc, trace_name=trace_name, legendgroup=legendgroup, + cmap=cmap, cbar_title=cbar_title, show_colorbar=show_colorbar, + cmap_vals=cmap_vals, cmin=cmin, cmax=cmax, cpos=cpos, + branch_element=branch_element, separator_element=separator_element, + node_element=node_element, cmap_vals_category="vmean_m_s") + + +def create_valve_trace(net, valves=None, use_valve_geodata=False, size=1.0, + color='black', infofunc=None, trace_name='valves', legendgroup='valves', + dash="solid"): + branch_element = "valve" + node_element = "junction" + separator_element = "valve" + respect_valves = False + cmap = None + cbar_title = None + show_colorbar = True + cmap_vals = None + cmin = None + cmax = None + cpos = 1.1 + + return _create_branch_trace(net=net, branches=valves, use_branch_geodata=use_valve_geodata, + respect_separators=respect_valves, width=size, color=color, + infofunc=infofunc, trace_name=trace_name, legendgroup=legendgroup, + cmap=cmap, cbar_title=cbar_title, show_colorbar=show_colorbar, + cmap_vals=cmap_vals, cmin=cmin, cmax=cmax, cpos=cpos, + branch_element=branch_element, separator_element=separator_element, + node_element=node_element, cmap_vals_category="vmean_m_s", + dash=dash) + + +def create_compressor_trace(net, compressors=None, use_pipe_geodata=False, size=3.0, + color='cyan', infofunc=None, trace_name='compressors', + legendgroup='compressors'): + branch_element = "compressor" + node_element = "junction" + separator_element = "valve" + respect_valves = False + cmap = None + cbar_title = None + show_colorbar = True + cmap_vals = None + cmin = None + cmax = None + cpos = 1.1 + + return _create_branch_trace(net=net, branches=compressors, use_branch_geodata=use_pipe_geodata, + respect_separators=respect_valves, width=size, color=color, + infofunc=infofunc, trace_name=trace_name, legendgroup=legendgroup, + cmap=cmap, cbar_title=cbar_title, show_colorbar=show_colorbar, + cmap_vals=cmap_vals, cmin=cmin, cmax=cmax, cpos=cpos, + branch_element=branch_element, separator_element=separator_element, + node_element=node_element, cmap_vals_category="compr_power_mw") \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/compressibility.txt b/pandapipes/properties/biomethane_pure/compressibility.txt new file mode 100644 index 00000000..194a3471 --- /dev/null +++ b/pandapipes/properties/biomethane_pure/compressibility.txt @@ -0,0 +1,3 @@ +# source: linear approximation based on CoolProp (http://www.coolprop.org/) +# slope in 1/bar, offset for linear property +-0.0019303630399938587 0.9958350401842455 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/density.txt b/pandapipes/properties/biomethane_pure/density.txt new file mode 100644 index 00000000..c90eac64 --- /dev/null +++ b/pandapipes/properties/biomethane_pure/density.txt @@ -0,0 +1,58 @@ +# source: CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, nominal density in kg/Nm^3 at 1.01325 bar +263.15 0.7883176797706959 +265.15 0.7823175132720274 +267.15 0.7764086858132603 +269.15 0.7705891091714784 +271.15 0.7648567589356711 +273.15 0.7592096720602627 +275.15 0.7536459445317518 +277.15 0.7481637291423288 +279.15 0.742761233364706 +281.15 0.7374367173227688 +283.15 0.7321884918529732 +285.15 0.7270149166517398 +287.15 0.721914398504361 +289.15 0.7168853895912243 +291.15 0.7119263858673918 +293.15 0.7070359255118169 +295.15 0.7022125874426869 +297.15 0.6974549899243704 +299.15 0.6927617891486424 +301.15 0.6881316779168388 +303.15 0.6835633841304272 +305.15 0.679055670907835 +307.15 0.6746073333963901 +309.15 0.6702171990259529 +311.15 0.6658841261093711 +313.15 0.6616070028247312 +315.15 0.6573847462379292 +317.15 0.6532163013636869 +319.15 0.6491006402632528 +321.15 0.6450367611771036 +323.15 0.6410236876910659 +325.15 0.6370604679343483 +327.15 0.6331461738080604 +329.15 0.62927990024286 +331.15 0.6254607644844474 +333.15 0.6216879054056833 +335.15 0.6179604828441738 +337.15 0.6142776769642169 +339.15 0.6106386876420702 +341.15 0.6070427338735377 +343.15 0.6034890532029326 +345.15 0.5999769011734114 +347.15 0.5965055507927562 +349.15 0.5930742920256563 +351.15 0.589682431296971 +353.15 0.5863292910154656 +355.15 0.5830142091135528 +357.15 0.5797365386029372 +359.15 0.5764956471455245 +361.15 0.5732909166389835 +363.15 0.5701217428163711 +365.15 0.5669875348592726 +367.15 0.5638877150239164 +369.15 0.5608217182797604 +371.15 0.557788991960064 +373.15 0.5547889954239786 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/der_compressibility.txt b/pandapipes/properties/biomethane_pure/der_compressibility.txt new file mode 100644 index 00000000..7561489e --- /dev/null +++ b/pandapipes/properties/biomethane_pure/der_compressibility.txt @@ -0,0 +1,3 @@ +# source: own calculation based on CoolProp (http://www.coolprop.org/) +# derivative (slope) of compressibility +-0.0019303630399938587 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/gas_composition.txt b/pandapipes/properties/biomethane_pure/gas_composition.txt new file mode 100644 index 00000000..a6e56ccc --- /dev/null +++ b/pandapipes/properties/biomethane_pure/gas_composition.txt @@ -0,0 +1,10 @@ +source: Arbeitsblatt DVGW G 260(A) + +Gas composition (molar fraction): +methane: 96.15 % +nitrogen: 0.75 % +carbon dioxide: 2.9 % +oxygen: 0.2 % + +Wobbe-Index (normal conditions) : 13.9 KWh/m³ +HHV (normal conditions) : 10.6 KWh/m³ \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/heat_capacity.txt b/pandapipes/properties/biomethane_pure/heat_capacity.txt new file mode 100644 index 00000000..e02164ac --- /dev/null +++ b/pandapipes/properties/biomethane_pure/heat_capacity.txt @@ -0,0 +1,58 @@ +#source CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, isobaric heat capacity in J/(kg K) +263.15 2043.9248427852253 +265.15 2046.988073303865 +267.15 2050.126087163204 +269.15 2053.3384737054007 +271.15 2056.6247917272185 +273.15 2059.9845702851426 +275.15 2063.417309553297 +277.15 2066.922481727277 +279.15 2070.4995319674445 +281.15 2074.1478793756605 +283.15 2077.8669179998637 +285.15 2081.6560178612567 +287.15 2085.514525999244 +289.15 2089.4417675296154 +291.15 2093.4370467118033 +293.15 2097.499648021338 +295.15 2101.628837223956 +297.15 2105.8238624483056 +299.15 2110.083955253294 +301.15 2114.4083316884407 +303.15 2118.7961933423576 +305.15 2123.246728391504 +307.15 2127.7591126080515 +309.15 2132.332510378635 +311.15 2136.9660756917788 +313.15 2141.6589531087366 +315.15 2146.41027871373 +317.15 2151.219181042416 +319.15 2156.0847819875867 +321.15 2161.0061976812103 +323.15 2165.982539352107 +325.15 2171.0129141586276 +327.15 2176.0964259958378 +329.15 2181.232176276832 +331.15 2186.419264687867 +333.15 2191.6567899171305 +335.15 2196.9438503570186 +337.15 2202.2795447799003 +339.15 2207.662972987398 +341.15 2213.0932364332866 +343.15 2218.569438820182 +345.15 2224.0906866702367 +347.15 2229.656089870096 +349.15 2235.2647621904885 +351.15 2240.9158217807285 +353.15 2246.608391638581 +355.15 2252.3416000559114 +357.15 2258.11458104055 +359.15 2263.9264747148845 +361.15 2269.7764276916773 +363.15 2275.6635934276164 +365.15 2281.587132555168 +367.15 2287.546213193266 +369.15 2293.540011237413 +371.15 2299.567710629771 +373.15 2305.6285036098143 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/higher_heating_value.txt b/pandapipes/properties/biomethane_pure/higher_heating_value.txt new file mode 100644 index 00000000..56c79ba7 --- /dev/null +++ b/pandapipes/properties/biomethane_pure/higher_heating_value.txt @@ -0,0 +1,3 @@ +# source: Arbeitsblatt DVGW G 260(A) +# higher heating value in kWh/kg (at normal conditions) +13.96188746019903 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/lower_heating_value.txt b/pandapipes/properties/biomethane_pure/lower_heating_value.txt new file mode 100644 index 00000000..e7b41c8d --- /dev/null +++ b/pandapipes/properties/biomethane_pure/lower_heating_value.txt @@ -0,0 +1 @@ +# lower heating value in kWh/kg (at normal conditions) diff --git a/pandapipes/properties/biomethane_pure/molar_mass.txt b/pandapipes/properties/biomethane_pure/molar_mass.txt new file mode 100644 index 00000000..8aedf921 --- /dev/null +++ b/pandapipes/properties/biomethane_pure/molar_mass.txt @@ -0,0 +1,2 @@ +# source: CoolProp (http://www.coolprop.org/), kg/kmol +16.9755351 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_pure/viscosity.txt b/pandapipes/properties/biomethane_pure/viscosity.txt new file mode 100644 index 00000000..ae4cb306 --- /dev/null +++ b/pandapipes/properties/biomethane_pure/viscosity.txt @@ -0,0 +1,58 @@ +# source: CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, dynamic viscosity in kg/(m s) +263.15 1.018799917679172e-05 +265.15 1.0255447167391177e-05 +267.15 1.0322713803330764e-05 +269.15 1.0389800774187494e-05 +271.15 1.0456709739161057e-05 +273.15 1.0523442327979087e-05 +275.15 1.0590000141762678e-05 +277.15 1.0656384753854598e-05 +279.15 1.0722597710611642e-05 +281.15 1.0788640532164104e-05 +283.15 1.08545147131434e-05 +285.15 1.0920221723379986e-05 +287.15 1.0985763008573338e-05 +289.15 1.105113999093497e-05 +291.15 1.1116354069806426e-05 +293.15 1.1181406622253389e-05 +295.15 1.124629900363687e-05 +297.15 1.1311032548163775e-05 +299.15 1.1375608569415205e-05 +301.15 1.144002836085609e-05 +303.15 1.1504293196322224e-05 +305.15 1.1568404330518516e-05 +307.15 1.1632362999438391e-05 +309.15 1.1696170420830542e-05 +311.15 1.1759827794618619e-05 +313.15 1.1823336303312924e-05 +315.15 1.1886697112408354e-05 +317.15 1.194991137076894e-05 +319.15 1.201298021099999e-05 +321.15 1.2075904749808127e-05 +323.15 1.2138686088349675e-05 +325.15 1.2201325312568147e-05 +327.15 1.226382349352113e-05 +329.15 1.2326181687697131e-05 +331.15 1.2388400937322568e-05 +333.15 1.2450482270659557e-05 +335.15 1.2512426702294873e-05 +337.15 1.2574235233420227e-05 +339.15 1.2635908852104261e-05 +341.15 1.2697448533556995e-05 +343.15 1.2758855240386152e-05 +345.15 1.282012992284671e-05 +347.15 1.2881273519083055e-05 +349.15 1.2942286955364732e-05 +351.15 1.3003171146315279e-05 +353.15 1.3063926995135216e-05 +355.15 1.3124555393818743e-05 +357.15 1.3185057223364654e-05 +359.15 1.3245433353981949e-05 +361.15 1.3305684645289342e-05 +363.15 1.3365811946510463e-05 +365.15 1.3425816096663347e-05 +367.15 1.3485697924745226e-05 +369.15 1.3545458249912988e-05 +371.15 1.3605097881658395e-05 +373.15 1.3664617619979682e-05 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/compressibility.txt b/pandapipes/properties/biomethane_treated/compressibility.txt new file mode 100644 index 00000000..7ebaa69c --- /dev/null +++ b/pandapipes/properties/biomethane_treated/compressibility.txt @@ -0,0 +1,3 @@ +# source: linear approximation based on CoolProp (http://www.coolprop.org/) +# slope in 1/bar, offset for linear property +-0.0024789428559189412, 0.9962776221466679 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/density.txt b/pandapipes/properties/biomethane_treated/density.txt new file mode 100644 index 00000000..940ab541 --- /dev/null +++ b/pandapipes/properties/biomethane_treated/density.txt @@ -0,0 +1,58 @@ +# source: CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, nominal density in kg/Nm^3 at 1.01325 bar +263.15 0.86048646407235 +265.15 0.8539252359131624 +267.15 0.8474642302098054 +269.15 0.8411011428532086 +271.15 0.8348337407285736 +273.15 0.8286598589619569 +275.15 0.8225773982960998 +277.15 0.816584322588365 +279.15 0.8106786564240795 +281.15 0.8048584828390211 +283.15 0.7991219411451699 +285.15 0.7934672248542186 +287.15 0.7878925796936597 +289.15 0.7823963017106081 +291.15 0.7769767354587873 +293.15 0.7716322722643928 +295.15 0.7663613485668018 +297.15 0.7611624443303306 +299.15 0.7560340815234674 +301.15 0.7509748226622115 +303.15 0.7459832694143467 +305.15 0.7410580612616586 +307.15 0.7361978742172673 +309.15 0.7314014195954124 +311.15 0.7266674428311796 +313.15 0.7219947223477784 +315.15 0.7173820685226427 +317.15 0.712828322486563 +319.15 0.7083323552601722 +321.15 0.7038930663980693 +323.15 0.6995093845290987 +325.15 0.695180264243638 +327.15 0.6909046867186862 +329.15 0.6866816585237271 +331.15 0.6825102108281128 +333.15 0.6783893986382189 +335.15 0.6743183000630635 +337.15 0.670296015607147 +339.15 0.6663216674893319 +341.15 0.6623943989866364 +343.15 0.6585133738018817 +345.15 0.6546777754541744 +347.15 0.6508868066912565 +349.15 0.6471396889228147 +351.15 0.6434356616738607 +353.15 0.6397739820573669 +355.15 0.6361539242660609 +357.15 0.6325747790786994 +359.15 0.6290358533891036 +361.15 0.6255364697463401 +363.15 0.6220759659128245 +365.15 0.6186536944370283 +367.15 0.6152690222406079 +369.15 0.6119213302193801 +371.15 0.60861001285761 +373.15 0.6053344778550793 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/der_compressibility.txt b/pandapipes/properties/biomethane_treated/der_compressibility.txt new file mode 100644 index 00000000..22482b30 --- /dev/null +++ b/pandapipes/properties/biomethane_treated/der_compressibility.txt @@ -0,0 +1,3 @@ +# source: own calculation based on CoolProp (http://www.coolprop.org/) +# derivative (slope) of compressibility +-0.0024789428559189412 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/gas_composition.txt b/pandapipes/properties/biomethane_treated/gas_composition.txt new file mode 100644 index 00000000..c529c1d9 --- /dev/null +++ b/pandapipes/properties/biomethane_treated/gas_composition.txt @@ -0,0 +1,13 @@ +source: Arbeitsblatt DVGW G 260(A) + +Gas composition (molar fraction): +methane: 90.94% +nitrogen: 0.69 % +carbon dioxide: 2.68 % +oxygen: 0.19 % +propane: 5 % +butane: 0.5 % + + +Wobbe-Index (normal conditions) : 14.9 KWh/m³ +HHV (normal conditions) : 11.6 KWh/m³ \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/heat_capacity.txt b/pandapipes/properties/biomethane_treated/heat_capacity.txt new file mode 100644 index 00000000..f9f78e48 --- /dev/null +++ b/pandapipes/properties/biomethane_treated/heat_capacity.txt @@ -0,0 +1,58 @@ +#source CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, isobaric heat capacity in J/(kg K) +263.151976.8823234072695 +265.15 1980.6134982832095 +267.15 1984.416050463013 +269.15 1988.2894370033634 +271.15 1992.2330899146655 +273.15 1996.246416873169 +275.15 2000.328801982753 +277.15 2004.4796065798193 +279.15 2008.6981700751355 +281.15 2012.9838108269525 +283.15 2017.3358270400702 +285.15 2021.753497685928 +287.15 2026.236083439167 +289.15 2030.7828276264033 +291.15 2035.3929571833426 +293.15 2040.0656836165801 +295.15 2044.8002039668168 +297.15 2049.5957017703836 +299.15 2054.4513480163328 +301.15 2059.3663020964914 +303.15 2064.3397127461894 +305.15 2069.370718973518 +307.15 2074.458450975239 +309.15 2079.6020310376007 +311.15 2084.800574420541 +313.15 2090.053190223892 +315.15 2095.358982234797 +317.15 2100.7170497532743 +319.15 2106.126488398847 +321.15 2111.5863908912784 +323.15 2117.0958478222283 +325.15 2122.65394837675 +327.15 2128.2597810582947 +329.15 2133.9124343761173 +331.15 2139.610997511174 +333.15 2145.3545609576 +335.15 2151.142217139677 +337.15 2156.9730610042843 +339.15 2162.8461905888544 +341.15 2168.760707564975 +343.15 2174.7157177577647 +345.15 2180.7103316412727 +347.15 2186.7436648101425 +349.15 2192.814838427854 +351.15 2198.922979651878 +353.15 2205.0672220361303 +355.15 2211.246705911128 +357.15 2217.460578742257 +359.15 2223.707995466675 +361.15 2229.9881188092413 +363.15 2236.300119578022 +365.15 2242.643176939882 +367.15 2249.0164786766545 +369.15 2255.4192214224554 +371.15 2261.8506108826527 +373.15 2268.3098620350665 diff --git a/pandapipes/properties/biomethane_treated/higher_heating_value.txt b/pandapipes/properties/biomethane_treated/higher_heating_value.txt new file mode 100644 index 00000000..dc86eb4a --- /dev/null +++ b/pandapipes/properties/biomethane_treated/higher_heating_value.txt @@ -0,0 +1,3 @@ +# source: Arbeitsblatt DVGW G 260(A) +# higher heating value in kWh/kg (at normal conditions) +13.998505990782578 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/lower_heating_value.txt b/pandapipes/properties/biomethane_treated/lower_heating_value.txt new file mode 100644 index 00000000..e7b41c8d --- /dev/null +++ b/pandapipes/properties/biomethane_treated/lower_heating_value.txt @@ -0,0 +1 @@ +# lower heating value in kWh/kg (at normal conditions) diff --git a/pandapipes/properties/biomethane_treated/molar_mass.txt b/pandapipes/properties/biomethane_treated/molar_mass.txt new file mode 100644 index 00000000..9322e972 --- /dev/null +++ b/pandapipes/properties/biomethane_treated/molar_mass.txt @@ -0,0 +1,2 @@ +# source: CoolProp (http://www.coolprop.org/) kg/kmol +18.518267691999997 \ No newline at end of file diff --git a/pandapipes/properties/biomethane_treated/viscosity.txt b/pandapipes/properties/biomethane_treated/viscosity.txt new file mode 100644 index 00000000..226d005b --- /dev/null +++ b/pandapipes/properties/biomethane_treated/viscosity.txt @@ -0,0 +1,58 @@ +# source: CoolProp (http://www.coolprop.org/) +# temperature in Kelvin, dynamic viscosity in kg/(m s) +263.15 9.987598418270064e-06 +265.15 1.0054271376713275e-05 +267.15 1.01207712445252e-05 +269.15 1.0187099643189088e-05 +271.15 1.0253258161058564e-05 +273.15 1.0319248354546486e-05 +275.15 1.038507174925251e-05 +277.15 1.0450729841031813e-05 +279.15 1.0516224097009172e-05 +281.15 1.0581555956543026e-05 +283.15 1.064672683214113e-05 +285.15 1.0711738110332342e-05 +287.15 1.0776591152496127e-05 +289.15 1.0841287295653449e-05 +291.15 1.0905827853220495e-05 +293.15 1.097021411572817e-05 +295.15 1.1034447351508859e-05 +297.15 1.109852880735291e-05 +299.15 1.116245970913629e-05 +301.15 1.122624126242086e-05 +303.15 1.1289874653029489e-05 +305.15 1.1353361047596854e-05 +307.15 1.141670159409703e-05 +309.15 1.1479897422350304e-05 +311.15 1.1542949644508997e-05 +313.15 1.1605859355524109e-05 +315.15 1.166862763359458e-05 +317.15 1.1731255540595754e-05 +319.15 1.1793744122496459e-05 +321.15 1.185609440975119e-05 +323.15 1.1918307417708628e-05 +325.15 1.1980384146945096e-05 +327.15 1.2042325583645421e-05 +329.15 1.2104132699940739e-05 +331.15 1.2165806454240284e-05 +333.15 1.2227347791551614e-05 +335.15 1.2288757643789442e-05 +337.15 1.2350036930074347e-05 +339.15 1.2411186557021452e-05 +341.15 1.2472207419019115e-05 +343.15 1.2533100398498824e-05 +345.15 1.2593866366196224e-05 +347.15 1.2654506181403717e-05 +349.15 1.271502069221504e-05 +351.15 1.2775410735762185e-05 +353.15 1.2835677138445082e-05 +355.15 1.2895820716153996e-05 +357.15 1.2955842274485576e-05 +359.15 1.3015742608951777e-05 +361.15 1.3075522505183164e-05 +363.15 1.313518273912608e-05 +365.15 1.3194724077234009e-05 +367.15 1.3254147276653159e-05 +369.15 1.331345308540351e-05 +371.15 1.3372642242554015e-05 +373.15 1.3431715478393082e-05 \ No newline at end of file diff --git a/pandapipes/properties/fluids.py b/pandapipes/properties/fluids.py index db0d045e..9721fa3c 100644 --- a/pandapipes/properties/fluids.py +++ b/pandapipes/properties/fluids.py @@ -669,7 +669,7 @@ def linear_property(prop): os.path.join(pp_dir, "properties", fluid_name, prop + ".txt")) liquids = ["water"] - gases = ["air", "lgas", "hgas", "hydrogen", "methane"] + gases = ["air", "lgas", "hgas", "hydrogen", "methane", "biomethane_pure", "biomethane_treated"] if fluid_name == "natural_gas": logger.error("'natural_gas' is ambigious. Please choose 'hgas' or 'lgas' " diff --git a/pandapipes/test/io/test_file_io.py b/pandapipes/test/io/test_file_io.py index 79eb3782..a3c3299c 100644 --- a/pandapipes/test/io/test_file_io.py +++ b/pandapipes/test/io/test_file_io.py @@ -10,7 +10,7 @@ from pandapipes.test.multinet.test_control_multinet import get_gas_example, get_power_example_simple from pandapipes.multinet.create_multinet import create_empty_multinet, add_nets_to_multinet from pandapipes.multinet import MultiNet -from pandapower.toolbox import nets_equal as nets_equal_pandapower +from pandapower import nets_equal as nets_equal_pandapower from pandapipes.toolbox import nets_equal diff --git a/pandapipes/test/pipeflow_internals/test_inservice.py b/pandapipes/test/pipeflow_internals/test_inservice.py index 56beecf2..1db30680 100644 --- a/pandapipes/test/pipeflow_internals/test_inservice.py +++ b/pandapipes/test/pipeflow_internals/test_inservice.py @@ -87,7 +87,7 @@ def complex_heat_connectivity_grid(): pandapipes.create_pipe_from_parameters(net, j3, j5, 0.1, 0.1, alpha_w_per_m2k=5, in_service=False, index=7) pandapipes.create_pipe_from_parameters(net, j6, j7, 0.1, 0.1, alpha_w_per_m2k=5, index=9) - pandapipes.create_pipe_from_parameters(net, j5, 8, 0.1, 0.1, alpha_w_per_m2k=5, + pandapipes.create_pipe_from_parameters(net, j5, j8, 0.1, 0.1, alpha_w_per_m2k=5, in_service=False, index=8) pandapipes.create_pipe_from_parameters(net, j8, j10, 0.1, 0.1, alpha_w_per_m2k=5, index=1) pandapipes.create_pipe_from_parameters(net, j9, j10, 0.1, 0.1, alpha_w_per_m2k=5, index=2) @@ -143,7 +143,7 @@ def test_inservice_gas(create_test_net, use_numba): assert np.all(np.isnan(net.res_pipe.loc[~net.pipe.in_service, :].values)) assert np.all(np.isnan(net.res_valve.loc[~net.valve.opened, :].values)) - assert np.all(np.isnan(net.res_junction.loc[~net.junction.in_service, :].values)) + assert np.all(np.isnan(net.res_junction.p_bar.loc[~net.junction.in_service].values)) oos_sinks = np.isin(net.sink.junction.values, net.junction.index[~net.junction.in_service]) \ | ~net.sink.in_service.values @@ -176,7 +176,7 @@ def test_inservice_water(create_test_net, use_numba): assert np.all(np.isnan(net.res_pipe.loc[~net.pipe.in_service, :].values)) assert np.all(np.isnan(net.res_valve.loc[~net.valve.opened, :].values)) - assert np.all(np.isnan(net.res_junction.loc[~net.junction.in_service, :].values)) + assert np.all(np.isnan(net.res_junction.p_bar.loc[~net.junction.in_service].values)) oos_sinks = np.isin(net.sink.junction.values, net.junction.index[~net.junction.in_service]) \ | ~net.sink.in_service.values @@ -210,7 +210,7 @@ def test_connectivity_hydraulic(create_test_net, use_numba): pandapipes.pipeflow(net, iter=100, tol_p=1e-7, tol_v=1e-7, friction_model="nikuradse", use_numba=use_numba) - assert np.all(np.isnan(net.res_junction.loc[[2, 5, 6], :].values)) + assert np.all(np.isnan(net.res_junction.p_bar.loc[[2, 5, 6]].values)) assert np.all(np.isnan(net.res_pipe.loc[[1, 2, 3], :].values)) assert not np.any(np.isnan(net.res_junction.loc[[0, 1, 3, 4], :].values)) assert not np.any(np.isnan(net.res_pipe.loc[[0, 4], @@ -222,8 +222,8 @@ def test_connectivity_hydraulic(create_test_net, use_numba): assert np.allclose(net.res_ext_grid.mdot_kg_per_s.sum(), -net.res_sink.mdot_kg_per_s.sum(), rtol=1e-10, atol=0) - active_branches = get_lookup(net, "branch", "active") - active_nodes = get_lookup(net, "node", "active") + active_branches = get_lookup(net, "branch", "active_hydraulics") + active_nodes = get_lookup(net, "node", "active_hydraulics") assert np.all(active_nodes == np.array([True, True, False, True, True, False, False, False, False, True])) @@ -257,16 +257,20 @@ def test_connectivity_hydraulic2(create_test_net, use_numba): pandapipes.pipeflow(net, iter=100, tol_p=1e-7, tol_v=1e-7, friction_model="nikuradse", use_numba=use_numba) - active_branches = get_lookup(net, "branch", "active") - active_nodes = get_lookup(net, "node", "active") + active_branches = get_lookup(net, "branch", "active_hydraulics") + active_nodes = get_lookup(net, "node", "active_hydraulics") assert np.all(active_nodes == np.array([True, True, True, True, True, True, True, False, False, True, False, False, True, True, True])) assert np.all(active_branches) - assert not np.all(np.isnan(net.res_junction.loc[[0, 1, 2, 3, 4, 5, 9], :].values)) + assert not np.all(np.isnan(net.res_junction.p_bar.loc[[0, 1, 2, 3, 4, 5, 9]].values)) assert not np.all(np.isnan(net.res_pipe.values)) - assert np.any(np.isnan(net.res_junction.loc[[7, 8, 10, 11], :].values)) + assert np.all(np.isnan(net.res_junction.p_bar.loc[[7, 8, 10, 11]].values)) + + with pytest.raises(PipeflowNotConverged): + pandapipes.pipeflow(net, iter=100, tol_p=1e-7, tol_v=1e-7, friction_model="nikuradse", + use_numba=use_numba, check_connectivity=False) @pytest.mark.parametrize("use_numba", [True, False]) @@ -274,21 +278,30 @@ def test_connectivity_heat1(complex_heat_connectivity_grid, use_numba): net = copy.deepcopy(complex_heat_connectivity_grid) pandapipes.pipeflow(net, mode="all", check_connectivity=True, use_numba=use_numba) - assert set(net.res_junction.loc[net.res_junction.p_bar.notnull()].index) == {8, 9, 10} - assert set(net.res_junction.loc[net.res_junction.p_bar.isnull()].index) \ - == set(net.junction.index) - {8, 9, 10} - assert set(net.res_pipe.loc[net.res_pipe.v_mean_m_per_s.notnull()].index) == {1, 2} - assert set(net.res_pipe.loc[net.res_pipe.v_mean_m_per_s.isnull()].index) \ - == set(net.pipe.index) - {1, 2} + oos_juncs_hyd = {4, 5, 6, 7} + oos_pipe_hyd = {5, 7, 8, 9} + oos_sink_hyd = {4, 5} + oos_source_hyd = {7} + + assert set(net.res_junction.loc[net.res_junction.p_bar.notnull()].index) == \ + set(net.junction.index) - oos_juncs_hyd + assert set(net.res_junction.loc[net.res_junction.p_bar.isnull()].index) == oos_juncs_hyd + + assert set(net.res_pipe.loc[net.res_pipe.v_mean_m_per_s.notnull()].index) == \ + set(net.pipe.index) - oos_pipe_hyd + assert set(net.res_pipe.loc[net.res_pipe.v_mean_m_per_s.isnull()].index) == oos_pipe_hyd + assert set(net.res_valve.loc[net.res_valve.v_mean_m_per_s.notnull()].index) == set() assert set(net.res_valve.loc[net.res_valve.v_mean_m_per_s.isnull()].index) \ == set(net.valve.index) - assert set(net.res_sink.loc[net.res_sink.mdot_kg_per_s.isnull()].index) == {3, 4, 5} + + assert set(net.res_sink.loc[net.res_sink.mdot_kg_per_s.isnull()].index) == oos_sink_hyd assert set(net.res_sink.loc[net.res_sink.mdot_kg_per_s.notnull()].index) == \ - set(net.sink.index) - {3, 4, 5} - assert set(net.res_source.loc[net.res_source.mdot_kg_per_s.isnull()].index) == \ - set(net.source.index) - assert set(net.res_source.loc[net.res_source.mdot_kg_per_s.notnull()].index) == set() + set(net.sink.index) - oos_sink_hyd + + assert set(net.res_source.loc[net.res_source.mdot_kg_per_s.isnull()].index) == oos_source_hyd + assert (set(net.res_source.loc[net.res_source.mdot_kg_per_s.notnull()].index) == + set(net.source.index) - oos_source_hyd) assert np.allclose(net.res_ext_grid.mdot_kg_per_s.sum(), -net.res_sink.mdot_kg_per_s.sum() + net.res_source.mdot_kg_per_s.sum(), @@ -352,6 +365,42 @@ def test_connectivity_heat3(complex_heat_connectivity_grid, use_numba): rtol=1e-10, atol=0) +@pytest.mark.parametrize("use_numba", [True, False]) +def test_connectivity_heat4(complex_heat_connectivity_grid, use_numba): + net = copy.deepcopy(complex_heat_connectivity_grid) + + net.pipe.in_service.loc[[7, 8]] = True + j_new = pandapipes.create_junction(net, 1, 320.15) + pandapipes.create_pipe_from_parameters(net, 8, j_new, 0.1, 0.1, alpha_w_per_m2k=5) + + net2 = copy.deepcopy(net) + + pandapipes.pipeflow(net, mode="all", check_connectivity=True, use_numba=use_numba) + pandapipes.pipeflow(net2, mode="all", check_connectivity=False, use_numba=use_numba) + + assert pandapipes.nets_equal(net, net2, check_only_results=True) + + +@pytest.mark.parametrize("use_numba", [True, False]) +def test_connectivity_heat5(complex_heat_connectivity_grid, use_numba): + net = copy.deepcopy(complex_heat_connectivity_grid) + net.pipe.in_service.loc[[7, 8]] = True + + j_from, j_to = pandapipes.create_junctions(net, 2, 1, 320.15) + + pandapipes.create_pipe_from_parameters(net, j_from, j_to, 0.1, 0.1, alpha_w_per_m2k=5) + pandapipes.create_sink(net, j_to, 0.1) + pandapipes.create_ext_grid(net, j_from, 1, 320.15) + + net.ext_grid.loc[2, 'in_service'] = False + net.ext_grid.loc[1, 'type'] = 'p' + + pandapipes.pipeflow(net, check_connectivity=True, mode='all', use_numba=use_numba) + + with pytest.raises(PipeflowNotConverged): + pandapipes.pipeflow(net, check_connectivity=False, mode='all', use_numba=use_numba) + + @pytest.mark.parametrize("use_numba", [True, False]) def test_exclude_unconnected_junction(use_numba): """ @@ -390,6 +439,12 @@ def get_oos_branch(net, tbl, oosj=()): return get_oos(net, tbl) | net[tbl].from_junction.isin(oosj) | net[tbl].to_junction.isin(oosj) +def get_col_slice_null(tbl): + if tbl == "junction": + return "p_bar" + return slice(None) + + all_tbls_funcs = {"junction": get_oos, "pipe": get_oos_branch, "sink": get_oos_node_elem, "source": get_oos_node_elem, "ext_grid": get_oos_node_elem, "press_control": get_oos_branch} @@ -440,7 +495,8 @@ def test_mixed_indexing_oos2(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) @@ -457,7 +513,8 @@ def test_mixed_indexing_oos3(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) @@ -474,7 +531,8 @@ def test_mixed_indexing_oos4(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) @@ -491,7 +549,8 @@ def test_mixed_indexing_oos5(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) @@ -508,7 +567,8 @@ def test_mixed_indexing_oos6(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) @@ -516,7 +576,8 @@ def test_mixed_indexing_oos6(create_mixed_indexing_grid, use_numba): pandapipes.pipeflow(net, mode="hydraulics", use_numba=use_numba, check_connectivity=True) assert all(np.all(net["res_" + tbl].loc[~oos_func(net, tbl, oos_juncs)].notnull()) for tbl, oos_func in all_tbls_funcs.items()) - assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs)].isnull()) + assert all(np.all(net["res_" + tbl].loc[oos_func(net, tbl, oos_juncs), + get_col_slice_null(tbl)].isnull()) for tbl, oos_func in all_tbls_funcs.items()) assert check_mass_flows(net) diff --git a/pandapipes/test/pipeflow_internals/test_pipeflow_modes.py b/pandapipes/test/pipeflow_internals/test_pipeflow_modes.py index 8daa84d1..d722711a 100644 --- a/pandapipes/test/pipeflow_internals/test_pipeflow_modes.py +++ b/pandapipes/test/pipeflow_internals/test_pipeflow_modes.py @@ -94,8 +94,8 @@ def test_heat_only(use_numba): pandapipes.pipeflow(ntw, stop_condition="tol", iter=50, friction_model="nikuradse", nonlinear_method="automatic", mode="hydraulics", use_numba=use_numba) - p = ntw._pit["node"][:, 5] - v = ntw._pit["branch"][:, 12] + p = ntw._pit["node"][:, PINIT] + v = ntw._pit["branch"][:, VINIT] u = np.concatenate((p, v)) pandapipes.pipeflow(ntw, sol_vec=u, stop_condition="tol", iter=50, friction_model="nikuradse", diff --git a/pandapipes/test/topology/__init__.py b/pandapipes/test/topology/__init__.py new file mode 100644 index 00000000..fe22a75d --- /dev/null +++ b/pandapipes/test/topology/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2020-2023 by Fraunhofer Institute for Energy Economics +# and Energy System Technology (IEE), Kassel, and University of Kassel. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. diff --git a/pandapipes/toolbox.py b/pandapipes/toolbox.py index 1d8de357..7f6c1fb5 100644 --- a/pandapipes/toolbox.py +++ b/pandapipes/toolbox.py @@ -2,39 +2,20 @@ # and Energy System Technology (IEE), Kassel, and University of 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 copy +import os from collections.abc import Iterable import numpy as np import pandas as pd from networkx import has_path -from pandapower.auxiliary import get_indices -from pandapower.toolbox import dataframes_equal, clear_result_tables +from pandapower import get_indices, dataframes_equal, clear_result_tables import pandapipes from pandapipes.component_models.abstract_models.branch_models import BranchComponent from pandapipes.component_models.abstract_models.node_element_models import NodeElementComponent from pandapipes.create import create_empty_network -from pandapipes.idx_branch import TABLE_IDX as TABLE_IDX_BRANCH, ELEMENT_IDX as ELEMENT_IDX_BRANCH, \ - FROM_NODE as FROM_NODE_BRANCH, TO_NODE as TO_NODE_BRANCH, ACTIVE as ACTIVE_BRANCH, \ - LENGTH as LENGTH_BRANCH, D as D_BRANCH, AREA as AREA_BRANCH, RHO as RHO_BRANCH, \ - ETA as ETA_BRANCH, K as K_BRANCH, TINIT as TINIT_BRANCH, VINIT as VINIT_BRANCH, \ - RE as RE_BRANCH, LAMBDA as LAMBDA_BRANCH, JAC_DERIV_DV as JAC_DERIV_DV_BRANCH, \ - JAC_DERIV_DP as JAC_DERIV_DP_BRANCH, JAC_DERIV_DP1 as JAC_DERIV_DP1_BRANCH, \ - LOAD_VEC_BRANCHES as LOAD_VEC_BRANCHES_BRANCH, JAC_DERIV_DV_NODE as JAC_DERIV_DV_NODE_BRANCH, \ - LOAD_VEC_NODES as LOAD_VEC_NODES_BRANCH, LOSS_COEFFICIENT as LOSS_COEFFICIENT_BRANCH, \ - CP as CP_BRANCH, ALPHA as ALPHA_BRANCH, JAC_DERIV_DT as JAC_DERIV_DT_BRANCH, \ - JAC_DERIV_DT1 as JAC_DERIV_DT1_BRANCH, LOAD_VEC_BRANCHES_T as LOAD_VEC_BRANCHES_T_BRANCH, \ - T_OUT as T_OUT_BRANCH, JAC_DERIV_DT_NODE as JAC_DERIV_DT_NODE_BRANCH, \ - LOAD_VEC_NODES_T as LOAD_VEC_NODES_T_BRANCH, VINIT_T as VINIT_T_BRANCH, \ - FROM_NODE_T as FROM_NODE_T_BRANCH, TO_NODE_T as TO_NODE_T_BRANCH, QEXT as QEXT_BRANCH, \ - TEXT as TEXT_BRANCH, STD_TYPE as STD_TYPE_BRANCH, PL as PL_BRANCH, TL as TL_BRANCH, \ - BRANCH_TYPE as BRANCH_TYPE_BRANCH, PRESSURE_RATIO as PRESSURE_RATIO_BRANCH, branch_cols -from pandapipes.idx_node import TABLE_IDX as TABLE_IDX_NODE, ELEMENT_IDX as ELEMENT_IDX_NODE, \ - NODE_TYPE as NODE_TYPE_NODE, ACTIVE as ACTIVE_NODE, RHO as RHO_NODE, PINIT as PINIT_NODE, \ - LOAD as LOAD_NODE, HEIGHT as HEIGHT_NODE, TINIT as TINIT_NODE, PAMB as PAMB_NODE, \ - LOAD_T as LOAD_T_NODE, NODE_TYPE_T as NODE_TYPE_T_NODE, \ - EXT_GRID_OCCURENCE as EXT_GRID_OCCURENCE_NODE, \ - EXT_GRID_OCCURENCE_T as EXT_GRID_OCCURENCE_T_NODE, node_cols, \ +from pandapipes.idx_branch import branch_cols +from pandapipes.idx_node import node_cols, \ T as TYPE_T, P as TYPE_P, PC as TYPE_PC, NONE as TYPE_NONE, L as TYPE_L from pandapipes.pandapipes_net import pandapipesNet from pandapipes.topology import create_nxgraph @@ -543,76 +524,48 @@ def check_pressure_controllability(net, to_junction, controlled_junction): # logger.info("dropped %d %s elements with %d switches" % (len(trafos), table, num_switches)) -node_pit_indices = { - TABLE_IDX_NODE: "TABLE_IDX", - ELEMENT_IDX_NODE: "ELEMENT_IDX", - NODE_TYPE_NODE: "NODE_TYPE", - ACTIVE_NODE: "ACTIVE", - RHO_NODE: "RHO", - PINIT_NODE: "PINIT", - LOAD_NODE: "LOAD", - HEIGHT_NODE: "HEIGHT", - TINIT_NODE: "TINIT", - PAMB_NODE: "PAMB", - LOAD_T_NODE: "LOAD_T", - NODE_TYPE_T_NODE: "NODE_TYPE_T", - EXT_GRID_OCCURENCE_NODE: "EXT_GRID_OCCURENCE", - EXT_GRID_OCCURENCE_T_NODE: "EXT_GRID_OCCURENCE_T", -} - -branch_pit_indices = { - TABLE_IDX_BRANCH: "TABLE_IDX", - ELEMENT_IDX_BRANCH: "ELEMENT_IDX", - FROM_NODE_BRANCH: "FROM_NODE", - TO_NODE_BRANCH: "TO_NODE", - ACTIVE_BRANCH: "ACTIVE", - LENGTH_BRANCH: "LENGTH", - D_BRANCH: "D", - AREA_BRANCH: "AREA", - RHO_BRANCH: "RHO", - ETA_BRANCH: "ETA", - K_BRANCH: "K", - TINIT_BRANCH: "TINIT", - VINIT_BRANCH: "VINIT", - RE_BRANCH: "RE", - LAMBDA_BRANCH: "LAMBDA", - JAC_DERIV_DV_BRANCH: "JAC_DERIV_DV", - JAC_DERIV_DP_BRANCH: "JAC_DERIV_DP", - JAC_DERIV_DP1_BRANCH: "JAC_DERIV_DP1", - LOAD_VEC_BRANCHES_BRANCH: "LOAD_VEC_BRANCHES", - JAC_DERIV_DV_NODE_BRANCH: "JAC_DERIV_DV_NODE", - LOAD_VEC_NODES_BRANCH: "LOAD_VEC_NODES", - LOSS_COEFFICIENT_BRANCH: "LOSS_COEFFICIENT", - CP_BRANCH: "CP", - ALPHA_BRANCH: "ALPHA", - JAC_DERIV_DT_BRANCH: "JAC_DERIV_DT", - JAC_DERIV_DT1_BRANCH: "JAC_DERIV_DT1", - LOAD_VEC_BRANCHES_T_BRANCH: "LOAD_VEC_BRANCHES_T", - T_OUT_BRANCH: "T_OUT", - JAC_DERIV_DT_NODE_BRANCH: "JAC_DERIV_DT_NODE", - LOAD_VEC_NODES_T_BRANCH: "LOAD_VEC_NODES_T", - VINIT_T_BRANCH: "VINIT_T", - FROM_NODE_T_BRANCH: "FROM_NODE_T", - TO_NODE_T_BRANCH: "TO_NODE_T", - QEXT_BRANCH: "QEXT", - TEXT_BRANCH: "TEXT", - STD_TYPE_BRANCH: "STD_TYPE", - PL_BRANCH: "PL", - TL_BRANCH: "TL", - BRANCH_TYPE_BRANCH: "BRANCH_TYPE", - PRESSURE_RATIO_BRANCH: "PRESSURE_RATIO", -} +pit_types = {TYPE_P: "P", TYPE_L: "L", TYPE_NONE: "NONE", TYPE_T: "T", TYPE_PC: "PC", 0: "NONE"} +int_cols = ["FROM_NODE", "TO_NODE", "ELEMENT_IDX", "EXT_GRID_OCCURENCE", "EXT_GRID_OCCURENCE_T"] +bool_cols = ["ACTIVE"] -pit_types = {TYPE_P: "P", TYPE_L: "L", TYPE_NONE: "NONE", TYPE_T: "T", TYPE_PC: "PC", 0: "NONE"} +def get_pit_lookup(pit_type="node"): + """ + Retrieve a lookup for "indices" and "types" from the idx_branch or idx_node files. + :param pit_type: the pit for which the lookup is generated ("branch" or "node") + :type pit_type: str, default "node" + :return: idx_lookup: dictionary of structure {column: name} derived from idx_node.py or \ + idx_branch.py to describe the structure of the pit + :rtype: dict + """ + idx_lookup = {"indices": dict(), "types": dict()} + with open(os.path.join(pandapipes.pp_dir, f"idx_{pit_type}.py")) as f: + lookup_type = None + for ln in f.readlines(): + if ln.strip().startswith("#"): + if "indices" in ln: + lookup_type = "indices" + elif "types" in ln: + lookup_type = "types" + if lookup_type is None: + continue + txt = ln.split("#")[0] + if "=" not in txt: + continue + part1, part2 = txt.split("=") + idx_lookup[lookup_type][int(part2.strip())] = part1.strip() + return idx_lookup -def get_internal_tables_pandas(net): + +def get_internal_tables_pandas(net, convert_types=True): """ Convert the internal structure (pit) for nodes and branches into readable pandas DataFrames. :param net: pandapipes network :type net: pandapipesNet + :param convert_types: if True, convert some columns into a more readable form + :type convert_types: bool, default True :return: node_table, branch_table :rtype: pandas.DataFrame """ @@ -633,17 +586,32 @@ def get_internal_tables_pandas(net): logger.warning("%d branch pit entries are missing. Please verify the correctness of the " "table." % missing_branches) + node_lookup = get_pit_lookup("node") + branch_lookup = get_pit_lookup("branch") + node_table_lookup = pandapipes.get_lookup(net, "node", "table") node_table = pd.DataFrame(node_pit) - node_table.rename(columns=node_pit_indices, inplace=True) - node_table["NODE_TYPE"].replace(pit_types, inplace=True) - node_table["NODE_TYPE_T"].replace(pit_types, inplace=True) - node_table["TABLE_IDX"].replace(node_table_lookup["n2t"], inplace=True) + node_table.rename(columns=node_lookup["indices"], inplace=True) branch_table_lookup = pandapipes.get_lookup(net, "branch", "table") branch_table = pd.DataFrame(branch_pit) - branch_table.rename(columns=branch_pit_indices, inplace=True) - branch_table["BRANCH_TYPE"].replace(pit_types, inplace=True) - branch_table["TABLE_IDX"].replace(branch_table_lookup["n2t"], inplace=True) + branch_table.rename(columns=branch_lookup["indices"], inplace=True) + + if convert_types: + # TODO: replace types with a version retrieved from get_pit_lookup, but before, the types + # need to be defined properly inside the files! + node_table["NODE_TYPE"].replace(pit_types, inplace=True) + node_table["NODE_TYPE_T"].replace(pit_types, inplace=True) + node_table["TABLE_IDX"].replace(node_table_lookup["n2t"], inplace=True) + + branch_table["BRANCH_TYPE"].replace(pit_types, inplace=True) + branch_table["TABLE_IDX"].replace(branch_table_lookup["n2t"], inplace=True) + + for col in int_cols + bool_cols: + for tbl in (node_table, branch_table): + if col in tbl.columns: + tbl[col] = tbl[col].astype(np.int32) + if col in bool_cols: + tbl[col] = tbl[col].astype(np.bool_) return node_table, branch_table diff --git a/tutorials/creating_a_simple_network.ipynb b/tutorials/creating_a_simple_network.ipynb index 8d81ac9d..632540db 100644 --- a/tutorials/creating_a_simple_network.ipynb +++ b/tutorials/creating_a_simple_network.ipynb @@ -36,8 +36,8 @@ "We have to state the name of the fluid for the network. In pandapipes, some fluids are already\n", "pre-defined, e.g. hgas, lgas\n", "(high and low calorific natural gas), hydrogen,\n", - "water, and air, but you can\n", - "also create your own fluid if you prefer (tutorial in preparation).\n", + "water, biomethane_pure, biomethane_treated (see [here](https://pandapipes.readthedocs.io/en/latest/fluid_properties/fluids.html) for the composition) and air. It is\n", + "also possible to create a fluid if needed (tutorial in preparation).\n", "\n", "In this example, we will create a medium pressure gas network and choose lgas from the predefined fluids in pandapipes." ] @@ -372,11 +372,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, + "metadata": {}, "outputs": [], "source": [ "pp.pipeflow(net)" @@ -384,11 +380,7 @@ }, { "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, + "metadata": {}, "source": [ "The results are written to `res_...` tables. They were added to the `net`container. " ] @@ -396,11 +388,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, + "metadata": {}, "outputs": [], "source": [ "net # result tables have been added to the net " @@ -409,11 +397,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, + "metadata": {}, "outputs": [], "source": [ "net.res_junction # calculated pressure and temperature at junctions" @@ -422,11 +406,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, + "metadata": {}, "outputs": [], "source": [ "net.res_pipe # velocities, mass flows through pipes and other results\n", @@ -455,4 +435,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/tutorials/minimal_example.ipynb b/tutorials/minimal_example.ipynb index a8d639ac..8701fc6b 100644 --- a/tutorials/minimal_example.ipynb +++ b/tutorials/minimal_example.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -44,12 +44,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that the fluid used here is lgas. You can find 5 predefined fluids in pandapipes:\n", - " - lgas\n", - " - hgas\n", - " - hydrogen\n", - " - water\n", - " - air\n", + "Note that the fluid used here is ``lgas`` (low-calorific natural gas). You can find 7 predefined fluids in pandapipes:\n", + "- ``lgas`` (low-calorific natural gas)\n", + "- ``hgas`` (high-calorific natural gas)\n", + "- ``hydrogen``\n", + "- ``water``\n", + "- ``biomethane_pure``\n", + "- ``biomethane_treated`` (see [here](https://pandapipes.readthedocs.io/en/latest/fluid_properties/fluids.html) for the composition)\n", + "- ``air``\n", "\n", "And that the predefined valve element is an ideal valve. " ] @@ -65,57 +67,27 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " name pn_bar tfluid_k height_m in_service type\n0 Junction 1 1.05 293.15 0.0 True junction\n1 Junction 2 1.05 293.15 0.0 True junction\n2 Junction 3 1.05 293.15 0.0 True junction", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
namepn_bartfluid_kheight_min_servicetype
0Junction 11.05293.150.0Truejunction
1Junction 21.05293.150.0Truejunction
2Junction 31.05293.150.0Truejunction
\n
" - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.junction" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " name from_junction to_junction std_type length_km diameter_m k_mm \\\n0 Pipe 1 0 1 None 0.1 0.05 1.0 \n\n loss_coefficient alpha_w_per_m2k text_k qext_w sections in_service \\\n0 0.0 0.0 293.0 0.0 1 True \n\n type \n0 pipe ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
namefrom_junctionto_junctionstd_typelength_kmdiameter_mk_mmloss_coefficientalpha_w_per_m2ktext_kqext_wsectionsin_servicetype
0Pipe 101None0.10.051.00.00.0293.00.01Truepipe
\n
" - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.pipe" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " name junction p_bar t_k in_service type\n0 Grid Connection 0 1.1 293.15 True pt", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
namejunctionp_bart_kin_servicetype
0Grid Connection01.1293.15Truept
\n
" - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.ext_grid" ] @@ -131,26 +103,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "hp.pandapipes.pipeflow - INFO: niter 0\n", - "hp.pandapipes.pipeflow - INFO: niter 1\n", - "hp.pandapipes.pipeflow - INFO: niter 2\n", - "hp.pandapipes.pipeflow - INFO: niter 3\n", - "hp.pandapipes.pipeflow - INFO: ---------------------------------------------------------------------------------\n", - "hp.pandapipes.pipeflow - INFO: Calculation completed. Preparing results...\n", - "hp.pandapipes.pipeflow - INFO: Converged after 4 iterations.\n", - "hp.pandapipes.pipeflow - INFO: Norm of residual: 1.6351116571833302e-08\n", - "hp.pandapipes.pipeflow - INFO: tol_p: 0.0001\n", - "hp.pandapipes.pipeflow - INFO: tol_v: 0.0001\n" - ] - } - ], + "outputs": [], "source": [ "pp.pipeflow(net)" ] @@ -164,19 +119,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " p_bar t_k\n0 1.100000 293.15\n1 0.913916 293.15\n2 0.913916 293.15", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
p_bart_k
01.100000293.15
10.913916293.15
20.913916293.15
\n
" - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.res_junction" ] @@ -190,19 +135,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " v_from_m_per_s v_to_m_per_s v_mean_m_per_s p_from_bar p_to_bar \\\n0 15.766074 17.295535 16.483877 1.1 0.913916 \n\n t_from_k t_to_k mdot_from_kg_per_s mdot_to_kg_per_s vdot_norm_m3_per_s \\\n0 293.15 293.15 0.045 -0.045 0.06044 \n\n reynolds lambda normfactor_from normfactor_to \n0 96710.314701 0.049222 0.512189 0.561877 ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
v_from_m_per_sv_to_m_per_sv_mean_m_per_sp_from_barp_to_bart_from_kt_to_kmdot_from_kg_per_smdot_to_kg_per_svdot_norm_m3_per_sreynoldslambdanormfactor_fromnormfactor_to
015.76607417.29553516.4838771.10.913916293.15293.150.045-0.0450.0604496710.3147010.0492220.5121890.561877
\n
" - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.res_pipe" ] @@ -218,7 +153,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -244,37 +179,11 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "hp.pandapipes.pipeflow_setup - INFO: Setting the following nodes out of service for hydraulics calculation in connectivity check:\n", - "In table junction: [2]\n", - "hp.pandapipes.pipeflow - INFO: niter 0\n", - "hp.pandapipes.pipeflow - INFO: niter 1\n", - "hp.pandapipes.pipeflow - INFO: ---------------------------------------------------------------------------------\n", - "hp.pandapipes.pipeflow - INFO: Calculation completed. Preparing results...\n", - "hp.pandapipes.pipeflow - INFO: Converged after 2 iterations.\n", - "hp.pandapipes.pipeflow - INFO: Norm of residual: 6.635561679383765e-07\n", - "hp.pandapipes.pipeflow - INFO: tol_p: 0.0001\n", - "hp.pandapipes.pipeflow - INFO: tol_v: 0.0001\n" - ] - }, - { - "data": { - "text/plain": " p_bar t_k\n0 1.1 293.15\n1 1.1 293.15\n2 NaN NaN", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
p_bart_k
01.1293.15
11.1293.15
2NaNNaN
\n
" - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "pp.pipeflow(net)\n", "net.res_junction" @@ -289,19 +198,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " v_from_m_per_s v_to_m_per_s v_mean_m_per_s p_from_bar p_to_bar \\\n0 0.0 0.0 0.0 1.1 1.1 \n\n t_from_k t_to_k mdot_from_kg_per_s mdot_to_kg_per_s vdot_norm_m3_per_s \\\n0 293.15 293.15 0.0 -0.0 0.0 \n\n reynolds lambda normfactor_from normfactor_to \n0 0.0 0.04856 0.512189 0.512189 ", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
v_from_m_per_sv_to_m_per_sv_mean_m_per_sp_from_barp_to_bart_from_kt_to_kmdot_from_kg_per_smdot_to_kg_per_svdot_norm_m3_per_sreynoldslambdanormfactor_fromnormfactor_to
00.00.00.01.11.1293.15293.150.0-0.00.00.00.048560.5121890.512189
\n
" - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "net.res_pipe" ] @@ -328,4 +227,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +}