From d83c6f964267dd88f0f5825e5d4cafb6e0c72645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=2E=20Magalh=C3=A3es?= <pmlpm@posteo.de> Date: Thu, 25 Apr 2024 17:49:33 +0200 Subject: [PATCH] Started revising analysis methods. --- src/topupopt/problems/esipp/network.py | 25 ++++++++++++++++ src/topupopt/problems/esipp/time.py | 25 +++++++++++++++- tests/test_esipp_network.py | 41 ++++++++++++++++++++++++++ tests/test_esipp_time.py | 37 +++++++++++++++++++++++ tests/test_esipp_utils.py | 3 ++ 5 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/topupopt/problems/esipp/network.py b/src/topupopt/problems/esipp/network.py index abb30f5..1b129d7 100644 --- a/src/topupopt/problems/esipp/network.py +++ b/src/topupopt/problems/esipp/network.py @@ -1256,6 +1256,31 @@ class Network(nx.MultiDiGraph): return nx.is_tree(network_view) + # ************************************************************************* + # ************************************************************************* + + def has_selected_antiparallel_arcs(self) -> bool: + "Returns True if any two nodes have selected arcs in both directions." + return len(self.find_selected_antiparallel_arcs()) != 0 + + # ************************************************************************* + # ************************************************************************* + + def find_selected_antiparallel_arcs(self) -> list: + """Returns True if any two nodes have (selected) forward and reverse arcs.""" + + # check the existence of forward and reverse arcs in the same segment + arcs = [ # get the arcs selected + arc_key[0:2] + for arc_key in self.edges(keys=True) + if True in self.edges[arc_key][Network.KEY_ARC_TECH].options_selected + ] + arcs = [ # get the selected arcs that exist both ways + arc_key + for arc_key in arcs + if (arc_key[1], arc_key[0]) in arcs + ] + return arcs # ***************************************************************************** # ***************************************************************************** diff --git a/src/topupopt/problems/esipp/time.py b/src/topupopt/problems/esipp/time.py index 7db047c..7fc02bb 100644 --- a/src/topupopt/problems/esipp/time.py +++ b/src/topupopt/problems/esipp/time.py @@ -240,6 +240,29 @@ class TimeFrame: # ************************************************************************* # ************************************************************************* + + def assessments_overlap(self) -> bool: + "Returns True if any period is covered by more than one assessment." + # if there is only one assessment, return False + if self.number_assessments() == 1: + return False + else: + # if there is more than one assessment, check whether two or more + # cover the same period + qs = tuple(self.assessments) + # for each assessment + for q1, q2 in zip(qs, qs[1:]): + # for each period in one assessment (q1) + for p in self.reporting_periods[q1]: + # check if it is covered by the other assessment (q2) + if p in self.reporting_periods[q2]: + # p is covered by at least two assessments (q1 and q2) + return True + # if no period is covered by more than one assessment, return False + return False + + # ************************************************************************* + # ************************************************************************* # ***************************************************************************** # ***************************************************************************** @@ -279,7 +302,7 @@ class EconomicTimeFrame(TimeFrame): # dict: 1 value per p and q self._discount_rates = dict(discount_rates_q) else: - raise ValueError('Unrecognised inputs.') + raise TypeError('Unrecognised inputs.') # TODO: validate the discount rate object diff --git a/tests/test_esipp_network.py b/tests/test_esipp_network.py index 0136b0f..743f246 100644 --- a/tests/test_esipp_network.py +++ b/tests/test_esipp_network.py @@ -2325,6 +2325,47 @@ class TestNetwork: except ValueError: error_raised = True assert error_raised + + # ************************************************************************* + # ************************************************************************* + + def test_antiparallel_arcs(self): + + # create network + net = Network() + + # add nodes + node_a = 'A' + net.add_waypoint_node(node_a) + node_b = 'B' + net.add_waypoint_node(node_b) + node_c = 'C' + net.add_waypoint_node(node_c) + + # add arcs + node_pairs = ((node_a, node_b), (node_b, node_a),) + + # test network + for node_pair in node_pairs: + net.add_preexisting_directed_arc( + *node_pair, + efficiency=None, + static_loss=None, + capacity=1, + capacity_is_instantaneous=False + ) + # identify the node types + net.identify_node_types() + + # assert that it can detected the selected antiparallel arcs + assert net.has_selected_antiparallel_arcs() + # check that it finds the right node pairs + identified_node_pairs = net.find_selected_antiparallel_arcs() + assert (node_a, node_b) in identified_node_pairs + assert (node_b, node_a) in identified_node_pairs + + # ************************************************************************* + # ************************************************************************* # ***************************************************************************** # ***************************************************************************** diff --git a/tests/test_esipp_time.py b/tests/test_esipp_time.py index 46bfdb2..349f270 100644 --- a/tests/test_esipp_time.py +++ b/tests/test_esipp_time.py @@ -85,6 +85,8 @@ class TestTimeFrame: assert tf.number_reporting_periods(0) == 3 # number of time intervals assert tf.number_time_intervals(0) == 2 + # no overlapping assessments + assert not tf.assessments_overlap() # q: valid assert tf.valid_q(reporting_periods) @@ -243,6 +245,8 @@ class TestTimeFrame: # number of time intervals assert tf.number_time_intervals(0) == 2 assert tf.number_time_intervals(1) == 2 + # no overlapping assessments + assert not tf.assessments_overlap() # q: valid assert tf.valid_q(reporting_periods) @@ -390,6 +394,8 @@ class TestTimeFrame: assert not tf.valid_q({2: 1}) assert tf.complete_q(reporting_periods) assert not tf.complete_q({1: [365 * 24 * 3600]}) + # no overlapping assessments + assert not tf.assessments_overlap() # qk: valid assert tf.valid_qk( @@ -541,6 +547,8 @@ class TestTimeFrame: # number of time intervals assert tf.number_time_intervals(0) == 2 assert tf.number_time_intervals(1) == 2 + # assessments overlap + assert tf.assessments_overlap() # q: valid assert tf.valid_q(reporting_periods) @@ -737,6 +745,8 @@ class TestTimeFrame: assert tf.number_time_intervals(3) == 2 assert tf.number_time_intervals(4) == 2 assert tf.number_time_intervals(5) == 2 + # assessments overlap + assert tf.assessments_overlap() # q: valid assert tf.valid_q(reporting_periods) @@ -1113,6 +1123,33 @@ class TestTimeFrame: for df, true_df in zip(factors, true_factors): assert isclose(df, true_df, abs_tol=0.001) + + # ************************************************************************* + # ************************************************************************* + + def test_etf_unrecognised_input(self): + + # define the discount rate using a set + error_raised = False + try: + EconomicTimeFrame( + discount_rate={0.035}, + reporting_periods={ + 0: [0,1,2,3] + }, + reporting_period_durations={ + 0: [1,1,1,1] + }, + time_intervals={ + 0: [0] + }, + time_interval_durations={ + 0: [1] + }, + ) + except TypeError: + error_raised = True + assert error_raised # ***************************************************************************** # ***************************************************************************** diff --git a/tests/test_esipp_utils.py b/tests/test_esipp_utils.py index 49c7c0e..f211c52 100644 --- a/tests/test_esipp_utils.py +++ b/tests/test_esipp_utils.py @@ -50,6 +50,9 @@ class TestProblemUtils: except ValueError: error_raised = True assert error_raised + + # ************************************************************************* + # ************************************************************************* # ***************************************************************************** # ***************************************************************************** -- GitLab