From 69fcb7fb79fc887dcc8f00f4839a2feecdfc3fbd Mon Sep 17 00:00:00 2001 From: Jolando Kisse Date: Mon, 22 May 2023 14:02:43 +0200 Subject: [PATCH 1/5] add print_pf_summary; add first draft for diagnostic function --- pandapipes/diagnostic.py | 145 +++++++++++++++++++++++++++++++++++++++ pandapipes/toolbox.py | 37 ++++++++++ 2 files changed, 182 insertions(+) create mode 100644 pandapipes/diagnostic.py diff --git a/pandapipes/diagnostic.py b/pandapipes/diagnostic.py new file mode 100644 index 00000000..294830aa --- /dev/null +++ b/pandapipes/diagnostic.py @@ -0,0 +1,145 @@ +# 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. + +import pandapipes as pp +import numpy as np + +from pandapipes import PipeflowNotConverged + +try: + import pandaplan.core.pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + + +def check_net(net, low_length_limit_km=0.01, check_scaling_factor=1e-5): + """ + Run some diagnostic checks on the net to identify potential flaws. + """ + net = net.deepcopy() # do not modify the direct input + try: + pp.pipeflow(net) + if net.converged: + logger.info("The initial, unmodified pipeflow converges.") + else: + logger.warning("The initial, unmodified pipeflow does NOT converge.") + except Exception as e: + logger.info(f"The initial, unmodified pipeflow does NOT converge.\n" + f"\t\tThis exception is raised:\n\t\t{e}") + + # check ext_grid + if net.fluid.is_gas & (not hasattr(net, "ext_grid") | net.ext_grid.empty): + logger.warning("The net does not have an external grid! " + "An external grid is required for gas networks.") + + # check zero / low length + zl = net.pipe.loc[net.pipe.length_km == 0] + ll = net.pipe.loc[net.pipe.length_km <= low_length_limit_km] + if not zl.empty: + logger.warning(f"{len(zl.index)} pipes have a length of 0.0 km. (IDs: {zl.index})") + if not ll.empty: + logger.warning(f"{len(ll.index)} pipes have a length below" + f" {low_length_limit_km} km. " + f"This could lead to convergence issues. The lowest length in the net is " + f"{ll.length_km.min()} km.\n" + f"(IDs of pipelines with low length: {ll.index})") + + net2 = net.deepcopy() + net2.pipe.loc[net2.pipe.length_km < low_length_limit_km].length_km = low_length_limit_km + try: + pp.pipeflow(net2) + if net2.converged: + logger.info(f"If all short pipelines (< {low_length_limit_km} km) were set to " + f"{low_length_limit_km} km, the pipeflow would converge.") + else: + logger.warning(f"If all short pipelines (< {low_length_limit_km} km) were set to " + f"{low_length_limit_km} km, the pipeflow would still NOT converge.") + except Exception as e: + logger.info(f"Pipeflow does not converge, even if all short pipelines (< 10 m) were set " + f"to {low_length_limit_km} km. \n" + f"\t\tThe error message is: {e}") + + # check iterations + iter = 200 + try: + pp.pipeflow(net, iter=iter) + logger.info(f"The pipeflow converges after {net._internal_results['iterations']:d} " + f"iterations.") + except PipeflowNotConverged: + logger.info(f"After {iter:d} iterations the pipeflow did NOT converge.") + + # check with little sink and source scaling + logger.info("Testing with scaled-down sinks and sources.") + net3 = net.deepcopy() + if hasattr(net, "sink"): + net3.sink.scaling *= check_scaling_factor + if hasattr(net, "source"): + net3.source.scaling *= check_scaling_factor + try: + pp.pipeflow(net3) + if net3.converged: + logger.info(f"If sinks and sources were scaled with a factor of to " + f"{check_scaling_factor}, the pipeflow would converge.") + else: + logger.warning(f"If sinks and sources were scaled with a factor of to " + f"{check_scaling_factor}, the pipeflow would still NOT converge.") + except Exception as e: + logger.info(f"Pipeflow does not converge with sinks/sources scaled by" + f" {check_scaling_factor}.\n" + f"\t\tThe error message is: {e}") + + # check k + if any(net.pipe.k_mm > 0.5): + logger.warning(f"Some pipes have a friction factor k_mm > 0.5 (extremely rough). The " + f"highest value in the net is {net.pipe.k_mm.max()}. Up to " + f"0.2 mm is a common value for old steel pipes." + f"\nRough pipes: {net.pipe.loc[net.pipe.k_mm > 0.5]}.") + net4 = net.deepcopy() + net4.pipe.k_mm = 1e-5 + try: + pp.pipeflow(net4) + if net4.converged: + logger.info(f"If the friction factor would be reduced to 1e-5 for all pipes, " + f"the pipeflow would converge.") + else: + logger.warning(f"If the friction factor would be reduced to 1e-5 for all pipes, " + f"the pipeflow would still NOT converge.") + except Exception as e: + logger.info(f"Pipeflow does not converge with k_mm = 1-e5 for all pipes.\n" + f"\t\tThe error message is: {e}") + + # check sink and source junctions: + node_component = ["sink", "source", "ext_grid"] + for nc in node_component: + if hasattr(net, nc): + missing = np.setdiff1d(net[nc].junction, net.junction.index) + if len(missing): + logger.warning(f"Some {nc}s are connected to non-existing junctions!" + f"\n{nc}s:{net[nc].loc[net[nc].junction.isin(missing)]}" + f"\nmissing junctions:{missing}") + + # check from and to junctions + branch_component = ["pipe", "valve", "compressor", "pump", "heat_exchanger", "circulation_pump"] + for bc in branch_component: + if hasattr(net, bc): + missing_f = np.setdiff1d(net[bc].from_junction, net.junction.index) + missing_t = np.setdiff1d(net[bc].to_junction, net.junction.index) + if len(missing_t) | len(missing_t): + logger.warning(f"Some {bc}s are connected to non-existing junctions!") + logger.warning(f"missing 'from' junctions:{missing_f}") + logger.warning(f"missing 'to' junctions:{missing_t}") + + +if __name__ == '__main__': + import pandapipes.networks + net = pandapipes.networks.schutterwald() + net.ext_grid.p_bar = 0.08 + net.sink.loc[1505, "mdot_kg_per_s"] = 1000 + try: + pandapipes.pipeflow(net) + except Exception as e: + print(f"pipeflow raised: \n {e}") + check_net(net) \ No newline at end of file diff --git a/pandapipes/toolbox.py b/pandapipes/toolbox.py index afb78a44..42d7f46f 100644 --- a/pandapipes/toolbox.py +++ b/pandapipes/toolbox.py @@ -646,3 +646,40 @@ def get_internal_tables_pandas(net): branch_table["TABLE_IDX"].replace(branch_table_lookup["n2t"], inplace=True) return node_table, branch_table + + +def print_pf_summary(net): + """Print some basic results of the pipeflow. + + Min./max. pressure, junctions with NaN results, max. v, sum of sinks / sources. + """ + if not net.converged: + try: + pandapipes.pipeflow(net) + except Exception as e: + return logger.Error(f"Could not print pipeflow summary because the pipeflow " + f"calculation was not successful (Exception: {e})") + if not net.converged: + return logger.Error(f"Could not print pipeflow summary because the pipeflow " + f"calculation did not converge.") + else: + if any(net.res_junction.loc[net.junction.in_service].p_bar.isna()): + print(f"For {sum(net.res_junction.loc[net.junction.in_service].p_bar.isna())} " + f"junctions, no pressure could be calculated (NaN)!") + print(f"The minimum pressure is {net.res_junction.p_bar.min():.2f} bar.") + print(f"The maximum pressure is {net.res_junction.p_bar.max():.2f} bar.") + print(f"The highest velocity is {net.res_pipe.v_mean_m_per_s.abs().max():.2f} m/s.") + if hasattr(net, "source") & (~net.source.empty): + total_source_mdot = net.res_source.mdot_kg_per_s.sum() + print(f"The total gas infeed from sources is {total_source_mdot:.2f} kg/s " + f"(i.e. {total_source_mdot * float(net.fluid.get_property('hhv'))*3.6:.2f} " + f"MW_th (HHV).)") + else: + print("There are no sources connected to the net.") + if hasattr(net, "sink") & (~net.sink.empty): + total_sink_mdot = net.res_sink.mdot_kg_per_s.sum() + print(f"The total gas demand from sinks is {total_sink_mdot:.2f} kg/s " + f"(i.e. {total_sink_mdot * float(net.fluid.get_property('hhv'))*3.6:.2f} MW_th " + f"(HHV).)") + else: + print("There are no sinks connected to the net.") \ No newline at end of file From 1a459af462b03ac7776a51cc475a4a9a2efba56a Mon Sep 17 00:00:00 2001 From: eprade Date: Tue, 2 Jul 2024 11:19:11 +0200 Subject: [PATCH 2/5] -doc --- src/pandapipes/diagnostic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pandapipes/diagnostic.py b/src/pandapipes/diagnostic.py index 294830aa..daaa40ea 100644 --- a/src/pandapipes/diagnostic.py +++ b/src/pandapipes/diagnostic.py @@ -63,13 +63,13 @@ def check_net(net, low_length_limit_km=0.01, check_scaling_factor=1e-5): f"\t\tThe error message is: {e}") # check iterations - iter = 200 + iterations = 200 try: - pp.pipeflow(net, iter=iter) + pp.pipeflow(net, iter=iterations) logger.info(f"The pipeflow converges after {net._internal_results['iterations']:d} " f"iterations.") except PipeflowNotConverged: - logger.info(f"After {iter:d} iterations the pipeflow did NOT converge.") + logger.info(f"After {iterations:d} iterations the pipeflow did NOT converge.") # check with little sink and source scaling logger.info("Testing with scaled-down sinks and sources.") From 7ca6c5fdfd63a2fa0b7ada44713ab8802e7d065b Mon Sep 17 00:00:00 2001 From: Jolando Kisse Date: Thu, 16 Jan 2025 18:58:48 +0100 Subject: [PATCH 3/5] Update diagnostic.py (alpha sweep pipeflow) --- src/pandapipes/diagnostic.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/pandapipes/diagnostic.py b/src/pandapipes/diagnostic.py index daaa40ea..86fb7ac7 100644 --- a/src/pandapipes/diagnostic.py +++ b/src/pandapipes/diagnostic.py @@ -133,6 +133,30 @@ def check_net(net, low_length_limit_km=0.01, check_scaling_factor=1e-5): logger.warning(f"missing 'to' junctions:{missing_t}") +def pipeflow_alpha_sweep(net, **kwargs): + """Run the pipeflow many times with different alpha (NR damping factor) settings between 0.1 and 1 in steps of 0.1""" + net.converged = False + alphas = [1] + for i in range(1, 10): + if i % 2 == 1: + alphas.append(round(1 - (i // 2) * 0.1, 1)) + else: + alphas.append(round((i // 2) * 0.1, 1)) + if kwargs is None: + kwargs = {} + + for alpha in alphas: + kwargs.update({"alpha": alpha}) + try: + pps.pipeflow(net, **kwargs) + except Exception as e: + logger.debug(f"Pipeflow did not converge with alpha = {alpha}.\nError: {e}") + if net.converged: + logger.info(f"Pipeflow did converge with alpha = {alpha}.") + return + logger.warn(f"Pipeflow did not converge with any alpha in {alphas}.") + + if __name__ == '__main__': import pandapipes.networks net = pandapipes.networks.schutterwald() @@ -142,4 +166,4 @@ def check_net(net, low_length_limit_km=0.01, check_scaling_factor=1e-5): pandapipes.pipeflow(net) except Exception as e: print(f"pipeflow raised: \n {e}") - check_net(net) \ No newline at end of file + check_net(net) From 1ac38cc3fd218bd52d8a7f4b5a7afeabf1dc8afa Mon Sep 17 00:00:00 2001 From: Jolando Kisse Date: Thu, 16 Jan 2025 19:07:52 +0100 Subject: [PATCH 4/5] Update diagnostic.py - remove main call --- src/pandapipes/diagnostic.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/pandapipes/diagnostic.py b/src/pandapipes/diagnostic.py index 86fb7ac7..cdea6234 100644 --- a/src/pandapipes/diagnostic.py +++ b/src/pandapipes/diagnostic.py @@ -155,15 +155,3 @@ def pipeflow_alpha_sweep(net, **kwargs): logger.info(f"Pipeflow did converge with alpha = {alpha}.") return logger.warn(f"Pipeflow did not converge with any alpha in {alphas}.") - - -if __name__ == '__main__': - import pandapipes.networks - net = pandapipes.networks.schutterwald() - net.ext_grid.p_bar = 0.08 - net.sink.loc[1505, "mdot_kg_per_s"] = 1000 - try: - pandapipes.pipeflow(net) - except Exception as e: - print(f"pipeflow raised: \n {e}") - check_net(net) From d5a35a69863e10ac72026e9bd77cf7b3f32b9a4c Mon Sep 17 00:00:00 2001 From: Jolando Kisse Date: Fri, 17 Jan 2025 14:29:37 +0100 Subject: [PATCH 5/5] Update diagnostic.py - fix typo --- src/pandapipes/diagnostic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pandapipes/diagnostic.py b/src/pandapipes/diagnostic.py index cdea6234..0fec0ac5 100644 --- a/src/pandapipes/diagnostic.py +++ b/src/pandapipes/diagnostic.py @@ -148,10 +148,11 @@ def pipeflow_alpha_sweep(net, **kwargs): for alpha in alphas: kwargs.update({"alpha": alpha}) try: - pps.pipeflow(net, **kwargs) + pp.pipeflow(net, **kwargs) except Exception as e: logger.debug(f"Pipeflow did not converge with alpha = {alpha}.\nError: {e}") if net.converged: logger.info(f"Pipeflow did converge with alpha = {alpha}.") return - logger.warn(f"Pipeflow did not converge with any alpha in {alphas}.") + logger.warning(f"Pipeflow did not converge with any alpha in {alphas}.") +