From 5756352279b2f19afd34f235914b91293b928ec4 Mon Sep 17 00:00:00 2001 From: MartinBelthle Date: Wed, 21 Aug 2024 09:32:17 +0200 Subject: [PATCH] v0.1.5 (#15) --- CHANGELOG.md | 6 +++ pyproject.toml | 2 +- sonar-project.properties | 2 +- src/antares/tsgen/ts_generator.py | 30 +++++++------- tests/test_ts_generator.py | 16 ++++---- tests/test_unit.py | 66 ++++++++++++++++--------------- 6 files changed, 66 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b48820..ad45113 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +v0.1.5 (2023-08-21) +------------------- + +### Refactoring +* transpose all output matrices to fit with Antares expected time-series shape + v0.1.4 (2023-08-09) ------------------- diff --git a/pyproject.toml b/pyproject.toml index b7bffa6..1729f88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "antares-timeseries-generation" -version = "0.1.4" +version = "0.1.5" license = {text="MPL-2.0"} description = 'Timeseries generation library aiming at creating input data for Antares simulator studies.' readme = "README.md" diff --git a/sonar-project.properties b/sonar-project.properties index 51bb48b..4c53aa7 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,4 +1,4 @@ -sonar.projectVersion=0.1.4 +sonar.projectVersion=0.1.5 sonar.organization=antaressimulatorteam sonar.projectKey=AntaresSimulatorTeam_antares-timeseries-generation sonar.sources=src diff --git a/src/antares/tsgen/ts_generator.py b/src/antares/tsgen/ts_generator.py index d963308..14d24b6 100644 --- a/src/antares/tsgen/ts_generator.py +++ b/src/antares/tsgen/ts_generator.py @@ -111,17 +111,17 @@ def _check_cluster(cluster: ThermalCluster) -> None: class OutputTimeseries: def __init__(self, ts_count: int, days: int) -> None: - self.available_units = np.zeros(shape=(ts_count, days), dtype=int) + self.available_units = np.zeros(shape=(days, ts_count), dtype=int) # available power each hours - self.available_power = np.zeros((ts_count, 24 * days), dtype=float) + self.available_power = np.zeros((24 * days, ts_count), dtype=float) # number of pure planed, pure forced and mixed outage each day - self.planned_outages = np.zeros((ts_count, days), dtype=int) - self.forced_outages = np.zeros((ts_count, days), dtype=int) - self.mixed_outages = np.zeros((ts_count, days), dtype=int) + self.planned_outages = np.zeros((days, ts_count), dtype=int) + self.forced_outages = np.zeros((days, ts_count), dtype=int) + self.mixed_outages = np.zeros((days, ts_count), dtype=int) # number of pure planed and pure forced outage duration each day # (mixed outage duration = pod + fod) - self.planned_outage_durations = np.zeros((ts_count, days), dtype=int) - self.forced_outage_durations = np.zeros((ts_count, days), dtype=int) + self.planned_outage_durations = np.zeros((days, ts_count), dtype=int) + self.forced_outage_durations = np.zeros((days, ts_count), dtype=int) def _column_powers(column: FloatArray, width: int) -> npt.NDArray: @@ -140,7 +140,7 @@ def _daily_to_hourly(daily_data: npt.NDArray) -> npt.NDArray: """ if daily_data.ndim != 2: raise ValueError("Daily data must be a 2D-array") - return np.repeat(daily_data, 24, axis=1) + return np.repeat(daily_data, 24, axis=0) def _categorize_outages(available_units: int, po_candidates: int, fo_candidates: int) -> Tuple[int, int, int]: @@ -391,16 +391,16 @@ def generate_time_series( # = storing output in output arrays = if ts_index >= 0: # drop the 2 first generated timeseries - output.planned_outages[ts_index, day] = planned_outages - output.forced_outages[ts_index, day] = forced_outages - output.mixed_outages[ts_index, day] = mixed_outages - output.planned_outage_durations[ts_index, day] = po_duration - output.forced_outage_durations[ts_index, day] = fo_duration - output.available_units[ts_index, day] = current_available_units + output.planned_outages[day, ts_index] = planned_outages + output.forced_outages[day, ts_index] = forced_outages + output.mixed_outages[day, ts_index] = mixed_outages + output.planned_outage_durations[day, ts_index] = po_duration + output.forced_outage_durations[day, ts_index] = fo_duration + output.available_units[day, ts_index] = current_available_units now = (now + 1) % log_size hourly_available_units = _daily_to_hourly(output.available_units) - output.available_power = hourly_available_units * cluster.nominal_power * cluster.modulation + output.available_power = hourly_available_units * cluster.nominal_power * cluster.modulation[:, np.newaxis] np.round(output.available_power) return output diff --git a/tests/test_ts_generator.py b/tests/test_ts_generator.py index 0efc05e..8b9d741 100644 --- a/tests/test_ts_generator.py +++ b/tests/test_ts_generator.py @@ -42,8 +42,8 @@ def test_one_unit_cluster(cluster_1, output_directory): tot_po = 0 tot_fo = 0 for i in range(365 * ts_nb): - tot_po += results.planned_outages[i // 365][i % 365] * 2 - tot_fo += results.forced_outages[i // 365][i % 365] * 8 + tot_po += results.planned_outages[i % 365][i // 365] * 2 + tot_fo += results.forced_outages[i % 365][i // 365] * 8 true_por = tot_po / (365 * ts_nb) true_for = tot_fo / (365 * ts_nb) @@ -66,8 +66,8 @@ def test_hundred_unit_cluster(cluster_100, output_directory): tot_po = 0 tot_fo = 0 for i in range(365 * ts_nb): - tot_po += results.planned_outages[i // 365][i % 365] * 2 - tot_fo += results.forced_outages[i // 365][i % 365] * 8 + tot_po += results.planned_outages[i % 365][i // 365] * 2 + tot_fo += results.forced_outages[i % 365][i // 365] * 8 true_por = tot_po / (365 * ts_nb) true_for = tot_fo / (365 * ts_nb) @@ -76,8 +76,8 @@ def test_hundred_unit_cluster(cluster_100, output_directory): cursor = [0] * 10 tot_simult_po = 0 for i in range(365 * ts_nb): - po = results.planned_outages[i // 365][i % 365] - mo = results.mixed_outages[i // 365][i % 365] + po = results.planned_outages[i % 365][i // 365] + mo = results.mixed_outages[i % 365][i // 365] tot_simult_po += po tot_simult_po += mo @@ -117,8 +117,8 @@ def test_max_po(cluster_high_por, output_directory): cursor = [0] * 10 tot_simult_po = 0 for i in range(365 * ts_nb): - po = results.planned_outages[i // 365][i % 365] - mo = results.mixed_outages[i // 365][i % 365] + po = results.planned_outages[i % 365][i // 365] + mo = results.mixed_outages[i % 365][i // 365] tot_simult_po += po tot_simult_po += mo diff --git a/tests/test_unit.py b/tests/test_unit.py index fe1d4a5..29c723d 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -29,7 +29,7 @@ def test_daily_to_hourly(): daily = np.array([[1, 2]]) hourly = _daily_to_hourly(daily) - expected = [[1] * 24 + [2] * 24] + expected = [[1, 2]] * 24 npt.assert_equal(hourly, expected) @@ -186,17 +186,18 @@ def test_forced_outages(rng): generator = ThermalDataGenerator(rng=rng, days=days) results = generator.generate_time_series(cluster, 1) # 2 forced outages occur on day 5, with duration 10 - npt.assert_equal(results.forced_outages[0][:6], [0, 0, 0, 0, 2, 0]) - npt.assert_equal(results.forced_outage_durations[0][:6], [0, 0, 0, 0, 10, 0]) + npt.assert_equal(results.forced_outages.T[0][:6], [0, 0, 0, 0, 2, 0]) + npt.assert_equal(results.forced_outage_durations.T[0][:6], [0, 0, 0, 0, 10, 0]) # No planned outage - npt.assert_equal(results.planned_outages[0], np.zeros(365)) - npt.assert_equal(results.planned_outage_durations[0], np.zeros(365)) + npt.assert_equal(results.planned_outages.T[0], np.zeros(365)) + npt.assert_equal(results.planned_outage_durations.T[0], np.zeros(365)) - npt.assert_equal(results.available_units[0][:5], [9, 9, 9, 9, 8]) + npt.assert_equal(results.available_units.T[0][:5], [9, 9, 9, 9, 8]) # Check available power consistency with available units and modulation - assert results.available_power[0][0] == 900 - assert results.available_power[0][12] == 450 # Modulation is 0.5 for hour 12 - assert results.available_power[0][4 * 24] == 800 + available_power = results.available_power.T + assert available_power[0][0] == 900 + assert available_power[0][12] == 450 # Modulation is 0.5 for hour 12 + assert available_power[0][4 * 24] == 800 def test_planned_outages(rng): @@ -221,15 +222,16 @@ def test_planned_outages(rng): generator = ThermalDataGenerator(rng=rng, days=days) results = generator.generate_time_series(cluster, 1) # 0 forced outage - npt.assert_equal(results.forced_outages[0], np.zeros(365)) - npt.assert_equal(results.forced_outage_durations[0], np.zeros(365)) + npt.assert_equal(results.forced_outages.T[0], np.zeros(365)) + npt.assert_equal(results.forced_outage_durations.T[0], np.zeros(365)) # No planned outage - npt.assert_equal(results.planned_outages[0][:6], [0, 0, 0, 0, 2, 0]) - npt.assert_equal(results.available_units[0][:5], [9, 9, 9, 9, 8]) + npt.assert_equal(results.planned_outages.T[0][:6], [0, 0, 0, 0, 2, 0]) + npt.assert_equal(results.available_units.T[0][:5], [9, 9, 9, 9, 8]) # Check available power consistency with available units and modulation - assert results.available_power[0][0] == 900 - assert results.available_power[0][12] == 450 # Modulation is 0.5 for hour 12 - assert results.available_power[0][4 * 24] == 800 + available_power = results.available_power.T + assert available_power[0][0] == 900 + assert available_power[0][12] == 450 # Modulation is 0.5 for hour 12 + assert available_power[0][4 * 24] == 800 def test_planned_outages_limitation(rng): @@ -254,15 +256,16 @@ def test_planned_outages_limitation(rng): generator = ThermalDataGenerator(rng=rng, days=days) results = generator.generate_time_series(cluster, 1) # No forced outage - npt.assert_equal(results.forced_outages[0], np.zeros(365)) - npt.assert_equal(results.forced_outage_durations[0], np.zeros(365)) + npt.assert_equal(results.forced_outages.T[0], np.zeros(365)) + npt.assert_equal(results.forced_outage_durations.T[0], np.zeros(365)) # Maxmimum one planned outage at a time - npt.assert_equal(results.planned_outages[0][:6], [1, 0, 1, 0, 1, 0]) - npt.assert_equal(results.planned_outage_durations[0][:6], [2, 0, 2, 0, 2, 0]) - npt.assert_equal(results.available_units[0][:5], [9, 9, 9, 9, 9]) + npt.assert_equal(results.planned_outages.T[0][:6], [1, 0, 1, 0, 1, 0]) + npt.assert_equal(results.planned_outage_durations.T[0][:6], [2, 0, 2, 0, 2, 0]) + npt.assert_equal(results.available_units.T[0][:5], [9, 9, 9, 9, 9]) # Check available power consistency with available units and modulation - assert results.available_power[0][0] == 900 - assert results.available_power[0][4 * 24] == 900 + available_power = results.available_power.T + assert available_power[0][0] == 900 + assert available_power[0][4 * 24] == 900 def test_planned_outages_min_limitation(rng): @@ -287,15 +290,16 @@ def test_planned_outages_min_limitation(rng): generator = ThermalDataGenerator(rng=rng, days=days) results = generator.generate_time_series(cluster, 1) # No forced outage - npt.assert_equal(results.forced_outages[0], np.zeros(365)) - npt.assert_equal(results.forced_outage_durations[0], np.zeros(365)) + npt.assert_equal(results.forced_outages.T[0], np.zeros(365)) + npt.assert_equal(results.forced_outage_durations.T[0], np.zeros(365)) # Maxmimum one planned outage at a time - npt.assert_equal(results.planned_outages[0][:6], [0, 0, 1, 0, 0, 1]) - npt.assert_equal(results.planned_outage_durations[0][:6], [0, 0, 10, 0, 0, 10]) - npt.assert_equal(results.available_units[0][:5], [8, 8, 8, 8, 8]) + npt.assert_equal(results.planned_outages.T[0][:6], [0, 0, 1, 0, 0, 1]) + npt.assert_equal(results.planned_outage_durations.T[0][:6], [0, 0, 10, 0, 0, 10]) + npt.assert_equal(results.available_units.T[0][:5], [8, 8, 8, 8, 8]) # Check available power consistency with available units and modulation - assert results.available_power[0][0] == 800 - assert results.available_power[0][4 * 24] == 800 + available_power = results.available_power.T + assert available_power[0][0] == 800 + assert available_power[0][4 * 24] == 800 def test_with_long_fo_and_po_duration(data_directory): @@ -333,4 +337,4 @@ def test_with_long_fo_and_po_duration(data_directory): expected_matrix = np.loadtxt( data_directory.joinpath(f"expected_result_long_po_and_fo_duration.txt"), delimiter="\t" ) - assert np.array_equal(results.available_power.T, expected_matrix) + assert np.array_equal(results.available_power, expected_matrix)