From 89707e347123a752d2fafd132f5ef8465e047663 Mon Sep 17 00:00:00 2001 From: Ardt Klapwijk <59741981+ArdtK@users.noreply.github.com> Date: Thu, 5 Dec 2024 20:22:11 +0100 Subject: [PATCH] feat: 622 adaptation calculate full link cost (#623) --- ra2ce/analysis/adaptation/adaptation.py | 20 +++++++++++-------- .../analysis/adaptation/adaptation_option.py | 2 +- .../adaptation_option_collection.py | 4 ++-- tests/analysis/adaptation/conftest.py | 2 +- .../adaptation/test_adaptation_option.py | 4 ++-- .../test_adaptation_option_collection.py | 14 +++++++++++++ .../test_analysis_result_wrapper_exporter.py | 4 ++-- tests/conftest.py | 2 +- 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/ra2ce/analysis/adaptation/adaptation.py b/ra2ce/analysis/adaptation/adaptation.py index 33045cb09..c5219cc4e 100644 --- a/ra2ce/analysis/adaptation/adaptation.py +++ b/ra2ce/analysis/adaptation/adaptation.py @@ -71,20 +71,24 @@ def execute(self) -> GeoDataFrame: def run_cost(self) -> GeoDataFrame: """ - Calculate the unit cost for all adaptation options. - - Returns: - GeoDataFrame: The result of the cost calculation. + <<<<<<< HEAD + Calculate the link cost for all adaptation options. + ======= + Calculate the unit cost for all adaptation options. + >>>>>>> master + + Returns: + GeoDataFrame: The result of the cost calculation. """ _cost_gdf = deepcopy(self.graph_file.get_graph()) for ( _option, _cost, - ) in self.adaptation_collection.calculate_options_cost().items(): - _cost_gdf[f"{_option.id}_cost"] = _cost - - # TODO: calculate link cost instead of unit cost + ) in self.adaptation_collection.calculate_options_unit_cost().items(): + _cost_gdf[f"{_option.id}_cost"] = _cost_gdf.apply( + lambda x, cost=_cost: x["length"] * cost, axis=1 + ) return _cost_gdf diff --git a/ra2ce/analysis/adaptation/adaptation_option.py b/ra2ce/analysis/adaptation/adaptation_option.py index 095b0a905..44901e382 100644 --- a/ra2ce/analysis/adaptation/adaptation_option.py +++ b/ra2ce/analysis/adaptation/adaptation_option.py @@ -96,7 +96,7 @@ def from_config( analysis_config=analysis_config, ) - def calculate_cost(self, time_horizon: float, discount_rate: float) -> float: + def calculate_unit_cost(self, time_horizon: float, discount_rate: float) -> float: """ Calculate the net present value unit cost (per meter) of the adaptation option. diff --git a/ra2ce/analysis/adaptation/adaptation_option_collection.py b/ra2ce/analysis/adaptation/adaptation_option_collection.py index fd6dc4a8b..27b7c09fd 100644 --- a/ra2ce/analysis/adaptation/adaptation_option_collection.py +++ b/ra2ce/analysis/adaptation/adaptation_option_collection.py @@ -91,7 +91,7 @@ def from_config( return _collection - def calculate_options_cost(self) -> dict[AdaptationOption, float]: + def calculate_options_unit_cost(self) -> dict[AdaptationOption, float]: """ Calculate the unit cost for all adaptation options. @@ -99,7 +99,7 @@ def calculate_options_cost(self) -> dict[AdaptationOption, float]: dict[AdaptationOption, float]: The calculated cost for all adaptation options. """ return { - _option: _option.calculate_cost( + _option: _option.calculate_unit_cost( self.time_horizon, self.discount_rate, ) diff --git a/tests/analysis/adaptation/conftest.py b/tests/analysis/adaptation/conftest.py index c64e1d3eb..ff312a607 100644 --- a/tests/analysis/adaptation/conftest.py +++ b/tests/analysis/adaptation/conftest.py @@ -65,7 +65,7 @@ class AdaptationOptionCases: ), ] unit_cost: list[float] = [0.0, 2693.684211, 5231.908660] - total_cost: list[float] = [0.0, 633015.789583, 1229498.535112] + total_cost: list[float] = [0.0, 97411702.122141, 189201512.873560] cases: list[tuple[AnalysisSectionAdaptationOption, float, float]] = list( zip(config_cases, unit_cost, total_cost) ) diff --git a/tests/analysis/adaptation/test_adaptation_option.py b/tests/analysis/adaptation/test_adaptation_option.py index 127e2c535..f79089930 100644 --- a/tests/analysis/adaptation/test_adaptation_option.py +++ b/tests/analysis/adaptation/test_adaptation_option.py @@ -68,7 +68,7 @@ def test_from_config_no_damages_losses_raises(self): "adaptation_option", AdaptationOptionCases.cases, ) - def test_calculate_option_cost_returns_float( + def test_calculate_unit_cost_returns_float( self, valid_adaptation_config: tuple[AnalysisInputWrapper, AnalysisConfigWrapper], adaptation_option: tuple[AnalysisSectionAdaptation, float, float], @@ -82,7 +82,7 @@ def test_calculate_option_cost_returns_float( _discount_rate = valid_adaptation_config[1].config_data.adaptation.discount_rate # 2. Run test. - _cost = _option.calculate_cost(_time_horizon, _discount_rate) + _cost = _option.calculate_unit_cost(_time_horizon, _discount_rate) # 3. Verify expectations. assert isinstance(_cost, float) diff --git a/tests/analysis/adaptation/test_adaptation_option_collection.py b/tests/analysis/adaptation/test_adaptation_option_collection.py index 0dd9a58bf..b6aa381dc 100644 --- a/tests/analysis/adaptation/test_adaptation_option_collection.py +++ b/tests/analysis/adaptation/test_adaptation_option_collection.py @@ -46,3 +46,17 @@ def test_from_config_no_adaptation_raises(self): # 3. Verify expectations. assert _exc.match("No adaptation section found in the analysis config data.") + + def test_calculate_options_unit_cost( + self, + valid_adaptation_config: tuple[AnalysisInputWrapper, AnalysisConfigWrapper], + ): + # 1. Define test data. + _collection = AdaptationOptionCollection.from_config(valid_adaptation_config[1]) + + # 2. Run test. + _result = _collection.calculate_options_unit_cost() + + # 3. Verify expectations. + assert isinstance(_result, dict) + assert all(_option in _result for _option in _collection.adaptation_options) diff --git a/tests/analysis/test_analysis_result_wrapper_exporter.py b/tests/analysis/test_analysis_result_wrapper_exporter.py index 3c3305cc2..ef7d806b3 100644 --- a/tests/analysis/test_analysis_result_wrapper_exporter.py +++ b/tests/analysis/test_analysis_result_wrapper_exporter.py @@ -4,7 +4,7 @@ import pytest from shapely import Point -from ra2ce.analysis.analysis_config_data.analysis_config_data import AnalysisConfigData +from ra2ce.analysis.analysis_config_data.analysis_config_data import AnalysisSectionBase from ra2ce.analysis.analysis_config_data.enums.analysis_losses_enum import ( AnalysisLossesEnum, ) @@ -36,7 +36,7 @@ def valid_result_wrapper( ) -> AnalysisResultWrapper: class MockedAnalysis(AnalysisProtocol): def __init__(self) -> None: - _analysis = AnalysisConfigData.ANALYSIS_SECTION( + _analysis = AnalysisSectionBase( name="Mocked Analysis", save_csv=False, save_gpkg=False ) _analysis.analysis = AnalysisLossesEnum.SINGLE_LINK_LOSSES diff --git a/tests/conftest.py b/tests/conftest.py index 213e10343..e25716920 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,7 +6,7 @@ from tests import acceptance_test_data -@pytest.fixture(name="valid_analysis_ini") +@pytest.fixture(name="valid_analysis_ini", scope="session") def _get_valid_analysis_ini_fixture() -> Iterator[Path]: _ini_file = acceptance_test_data.joinpath("analyses.ini") assert _ini_file.exists()