flood-adapt 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. flood_adapt/__init__.py +22 -0
  2. flood_adapt/adapter/__init__.py +9 -0
  3. flood_adapt/adapter/fiat_adapter.py +1502 -0
  4. flood_adapt/adapter/interface/__init__.py +0 -0
  5. flood_adapt/adapter/interface/hazard_adapter.py +70 -0
  6. flood_adapt/adapter/interface/impact_adapter.py +36 -0
  7. flood_adapt/adapter/interface/model_adapter.py +89 -0
  8. flood_adapt/adapter/interface/offshore.py +19 -0
  9. flood_adapt/adapter/sfincs_adapter.py +1857 -0
  10. flood_adapt/adapter/sfincs_offshore.py +193 -0
  11. flood_adapt/config/__init__.py +0 -0
  12. flood_adapt/config/config.py +245 -0
  13. flood_adapt/config/fiat.py +219 -0
  14. flood_adapt/config/gui.py +224 -0
  15. flood_adapt/config/sfincs.py +336 -0
  16. flood_adapt/config/site.py +124 -0
  17. flood_adapt/database_builder/__init__.py +0 -0
  18. flood_adapt/database_builder/database_builder.py +2175 -0
  19. flood_adapt/database_builder/templates/default_units/imperial.toml +9 -0
  20. flood_adapt/database_builder/templates/default_units/metric.toml +9 -0
  21. flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +10 -0
  22. flood_adapt/database_builder/templates/icons/black_down_48x48.png +0 -0
  23. flood_adapt/database_builder/templates/icons/black_left_48x48.png +0 -0
  24. flood_adapt/database_builder/templates/icons/black_right_48x48.png +0 -0
  25. flood_adapt/database_builder/templates/icons/black_up_48x48.png +0 -0
  26. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_down.png +0 -0
  27. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_left.png +0 -0
  28. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_right.png +0 -0
  29. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_up.png +0 -0
  30. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_down.png +0 -0
  31. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_left.png +0 -0
  32. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_right.png +0 -0
  33. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_up.png +0 -0
  34. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_left.png +0 -0
  35. flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_right.png +0 -0
  36. flood_adapt/database_builder/templates/icons/white_down_48x48.png +0 -0
  37. flood_adapt/database_builder/templates/icons/white_left_48x48.png +0 -0
  38. flood_adapt/database_builder/templates/icons/white_right_48x48.png +0 -0
  39. flood_adapt/database_builder/templates/icons/white_up_48x48.png +0 -0
  40. flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +90 -0
  41. flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +57 -0
  42. flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +121 -0
  43. flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +65 -0
  44. flood_adapt/database_builder/templates/infographics/OSM/styles.css +45 -0
  45. flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +126 -0
  46. flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +60 -0
  47. flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +121 -0
  48. flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +65 -0
  49. flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +45 -0
  50. flood_adapt/database_builder/templates/infographics/images/ambulance.png +0 -0
  51. flood_adapt/database_builder/templates/infographics/images/car.png +0 -0
  52. flood_adapt/database_builder/templates/infographics/images/cart.png +0 -0
  53. flood_adapt/database_builder/templates/infographics/images/firetruck.png +0 -0
  54. flood_adapt/database_builder/templates/infographics/images/hospital.png +0 -0
  55. flood_adapt/database_builder/templates/infographics/images/house.png +0 -0
  56. flood_adapt/database_builder/templates/infographics/images/info.png +0 -0
  57. flood_adapt/database_builder/templates/infographics/images/money.png +0 -0
  58. flood_adapt/database_builder/templates/infographics/images/person.png +0 -0
  59. flood_adapt/database_builder/templates/infographics/images/school.png +0 -0
  60. flood_adapt/database_builder/templates/infographics/images/truck.png +0 -0
  61. flood_adapt/database_builder/templates/infographics/images/walking_person.png +0 -0
  62. flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +4 -0
  63. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +143 -0
  64. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +153 -0
  65. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +127 -0
  66. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +57 -0
  67. flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +4 -0
  68. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +191 -0
  69. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +153 -0
  70. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +178 -0
  71. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +57 -0
  72. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +9 -0
  73. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +65 -0
  74. flood_adapt/database_builder/templates/mapbox_layers/bin_colors.toml +5 -0
  75. flood_adapt/database_builder.py +16 -0
  76. flood_adapt/dbs_classes/__init__.py +21 -0
  77. flood_adapt/dbs_classes/database.py +716 -0
  78. flood_adapt/dbs_classes/dbs_benefit.py +97 -0
  79. flood_adapt/dbs_classes/dbs_event.py +91 -0
  80. flood_adapt/dbs_classes/dbs_measure.py +103 -0
  81. flood_adapt/dbs_classes/dbs_projection.py +52 -0
  82. flood_adapt/dbs_classes/dbs_scenario.py +150 -0
  83. flood_adapt/dbs_classes/dbs_static.py +261 -0
  84. flood_adapt/dbs_classes/dbs_strategy.py +147 -0
  85. flood_adapt/dbs_classes/dbs_template.py +302 -0
  86. flood_adapt/dbs_classes/interface/database.py +147 -0
  87. flood_adapt/dbs_classes/interface/element.py +137 -0
  88. flood_adapt/dbs_classes/interface/static.py +47 -0
  89. flood_adapt/flood_adapt.py +1371 -0
  90. flood_adapt/misc/__init__.py +0 -0
  91. flood_adapt/misc/database_user.py +16 -0
  92. flood_adapt/misc/log.py +183 -0
  93. flood_adapt/misc/path_builder.py +54 -0
  94. flood_adapt/misc/utils.py +185 -0
  95. flood_adapt/objects/__init__.py +59 -0
  96. flood_adapt/objects/benefits/__init__.py +0 -0
  97. flood_adapt/objects/benefits/benefits.py +61 -0
  98. flood_adapt/objects/events/__init__.py +0 -0
  99. flood_adapt/objects/events/event_factory.py +135 -0
  100. flood_adapt/objects/events/event_set.py +84 -0
  101. flood_adapt/objects/events/events.py +221 -0
  102. flood_adapt/objects/events/historical.py +55 -0
  103. flood_adapt/objects/events/hurricane.py +64 -0
  104. flood_adapt/objects/events/synthetic.py +48 -0
  105. flood_adapt/objects/forcing/__init__.py +0 -0
  106. flood_adapt/objects/forcing/csv.py +68 -0
  107. flood_adapt/objects/forcing/discharge.py +66 -0
  108. flood_adapt/objects/forcing/forcing.py +142 -0
  109. flood_adapt/objects/forcing/forcing_factory.py +182 -0
  110. flood_adapt/objects/forcing/meteo_handler.py +93 -0
  111. flood_adapt/objects/forcing/netcdf.py +40 -0
  112. flood_adapt/objects/forcing/plotting.py +428 -0
  113. flood_adapt/objects/forcing/rainfall.py +98 -0
  114. flood_adapt/objects/forcing/tide_gauge.py +191 -0
  115. flood_adapt/objects/forcing/time_frame.py +77 -0
  116. flood_adapt/objects/forcing/timeseries.py +552 -0
  117. flood_adapt/objects/forcing/unit_system.py +580 -0
  118. flood_adapt/objects/forcing/waterlevels.py +108 -0
  119. flood_adapt/objects/forcing/wind.py +124 -0
  120. flood_adapt/objects/measures/__init__.py +0 -0
  121. flood_adapt/objects/measures/measure_factory.py +92 -0
  122. flood_adapt/objects/measures/measures.py +506 -0
  123. flood_adapt/objects/object_model.py +68 -0
  124. flood_adapt/objects/projections/__init__.py +0 -0
  125. flood_adapt/objects/projections/projections.py +89 -0
  126. flood_adapt/objects/scenarios/__init__.py +0 -0
  127. flood_adapt/objects/scenarios/scenarios.py +22 -0
  128. flood_adapt/objects/strategies/__init__.py +0 -0
  129. flood_adapt/objects/strategies/strategies.py +68 -0
  130. flood_adapt/workflows/__init__.py +0 -0
  131. flood_adapt/workflows/benefit_runner.py +541 -0
  132. flood_adapt/workflows/floodmap.py +85 -0
  133. flood_adapt/workflows/impacts_integrator.py +82 -0
  134. flood_adapt/workflows/scenario_runner.py +69 -0
  135. flood_adapt-0.3.0.dist-info/LICENSE +21 -0
  136. flood_adapt-0.3.0.dist-info/METADATA +183 -0
  137. flood_adapt-0.3.0.dist-info/RECORD +139 -0
  138. flood_adapt-0.3.0.dist-info/WHEEL +5 -0
  139. flood_adapt-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,97 @@
1
+ import shutil
2
+
3
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
4
+ from flood_adapt.workflows.benefit_runner import Benefit, BenefitRunner
5
+
6
+
7
+ class DbsBenefit(DbsTemplate[Benefit]):
8
+ display_name = "Benefit"
9
+ dir_name = "benefits"
10
+ _object_class = Benefit
11
+
12
+ def save(self, object_model: Benefit, overwrite: bool = False):
13
+ """Save a benefit object in the database.
14
+
15
+ Parameters
16
+ ----------
17
+ object_model : Benefit
18
+ object of Benefit type
19
+ overwrite : bool, optional
20
+ whether to overwrite existing benefit with same name, by default False
21
+
22
+ Raises
23
+ ------
24
+ ValueError
25
+ Raise error if name is already in use. Names of benefits assessments should be unique.
26
+ """
27
+ runner = BenefitRunner(self._database, benefit=object_model)
28
+
29
+ # Check if all scenarios are created
30
+ if not all(runner.scenarios["scenario created"] != "No"):
31
+ raise ValueError(
32
+ f"'{object_model.name}' name cannot be created before all necessary scenarios are created."
33
+ )
34
+
35
+ # Save the benefit
36
+ super().save(object_model, overwrite=overwrite)
37
+
38
+ def delete(self, name: str, toml_only: bool = False):
39
+ """Delete an already existing benefit in the database.
40
+
41
+ Parameters
42
+ ----------
43
+ name : str
44
+ name of the benefit
45
+ toml_only : bool, optional
46
+ whether to only delete the toml file or the entire folder. If the folder is empty after deleting the toml,
47
+ it will always be deleted. By default False
48
+
49
+ Raises
50
+ ------
51
+ ValueError
52
+ Raise error if benefit has already model output
53
+ """
54
+ # First delete the benefit
55
+ super().delete(name, toml_only=toml_only)
56
+
57
+ # Delete output if edited
58
+ output_path = self.output_path / name
59
+ if output_path.exists():
60
+ shutil.rmtree(output_path, ignore_errors=True)
61
+
62
+ def edit(self, benefit: Benefit):
63
+ """Edits an already existing benefit in the database.
64
+
65
+ Parameters
66
+ ----------
67
+ benefit : Benefit
68
+ benefit to be edited in the database
69
+
70
+ Raises
71
+ ------
72
+ ValueError
73
+ Raise error if name is already in use.
74
+ """
75
+ # Check if it is possible to edit the benefit.
76
+ super().edit(benefit)
77
+
78
+ # Delete output if edited
79
+ output_path = self.output_path / benefit.name
80
+ if output_path.exists():
81
+ shutil.rmtree(output_path, ignore_errors=True)
82
+
83
+ def get_runner(self, name: str) -> BenefitRunner:
84
+ return BenefitRunner(self._database, self.get(name))
85
+
86
+ def has_run_check(self, name: str) -> bool:
87
+ return self.get_runner(name).has_run_check()
88
+
89
+ def ready_to_run(self, name: str) -> bool:
90
+ """Check if all the required scenarios have already been run.
91
+
92
+ Returns
93
+ -------
94
+ bool
95
+ True if required scenarios have been already run
96
+ """
97
+ return self.get_runner(name).ready_to_run()
@@ -0,0 +1,91 @@
1
+ from pathlib import Path
2
+ from typing import Any
3
+
4
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
5
+ from flood_adapt.objects.events.event_factory import EventFactory
6
+ from flood_adapt.objects.events.events import Event
7
+
8
+
9
+ class DbsEvent(DbsTemplate[Event]):
10
+ dir_name = "events"
11
+ display_name = "Event"
12
+ _object_class = Event
13
+
14
+ def get(self, name: str) -> Event:
15
+ """Return an event object.
16
+
17
+ Parameters
18
+ ----------
19
+ name : str
20
+ name of the event to be returned
21
+
22
+ Returns
23
+ -------
24
+ Event
25
+ event object
26
+ """
27
+ # Get event path
28
+ event_path = self.input_path / f"{name}" / f"{name}.toml"
29
+
30
+ # Check if the object exists
31
+ if not Path(event_path).is_file():
32
+ raise ValueError(f"{self.display_name} '{name}' does not exist.")
33
+
34
+ # Load event
35
+ return EventFactory.load_file(event_path)
36
+
37
+ def list_objects(self) -> dict[str, list[Any]]:
38
+ """Return a dictionary with info on the events that currently exist in the database.
39
+
40
+ Returns
41
+ -------
42
+ dict[str, Any]
43
+ Includes 'name', 'description', 'path' and 'last_modification_date' info
44
+ """
45
+ events = self._get_object_list()
46
+ objects = [self.get(name) for name in events["name"]]
47
+ events["description"] = [obj.description for obj in objects]
48
+ events["objects"] = objects
49
+ return events
50
+
51
+ def _check_standard_objects(self, name: str) -> bool:
52
+ """Check if an event is a standard event.
53
+
54
+ Parameters
55
+ ----------
56
+ name : str
57
+ name of the event to be checked
58
+
59
+ Returns
60
+ -------
61
+ bool
62
+ True if the event is a standard event, False otherwise
63
+ """
64
+ # Check if event is a standard event
65
+ if self._database.site.standard_objects:
66
+ if self._database.site.standard_objects.events:
67
+ if name in self._database.site.standard_objects.events:
68
+ return True
69
+ return False
70
+
71
+ def check_higher_level_usage(self, name: str) -> list[str]:
72
+ """Check if an event is used in a scenario.
73
+
74
+ Parameters
75
+ ----------
76
+ name : str
77
+ name of the event to be checked
78
+
79
+ Returns
80
+ -------
81
+ list[str]
82
+ list of scenarios that use the event
83
+ """
84
+ # Get all the scenarios
85
+ scenarios = self._database.scenarios.list_objects()["objects"]
86
+
87
+ # Check if event is used in a scenario
88
+ used_in_scenario = [
89
+ scenario.name for scenario in scenarios if name == scenario.event
90
+ ]
91
+ return used_in_scenario
@@ -0,0 +1,103 @@
1
+ from typing import Any
2
+
3
+ import geopandas as gpd
4
+
5
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
6
+ from flood_adapt.misc.utils import resolve_filepath
7
+ from flood_adapt.objects.measures.measure_factory import MeasureFactory
8
+ from flood_adapt.objects.measures.measures import Measure
9
+
10
+
11
+ class DbsMeasure(DbsTemplate[Measure]):
12
+ dir_name = "measures"
13
+ display_name = "Measure"
14
+ _object_class = Measure
15
+
16
+ def get(self, name: str) -> Measure:
17
+ """Return a measure object.
18
+
19
+ Parameters
20
+ ----------
21
+ name : str
22
+ name of the measure to be returned
23
+
24
+ Returns
25
+ -------
26
+ Measure
27
+ measure object
28
+ """
29
+ measure_path = self.input_path / name / f"{name}.toml"
30
+ measure = MeasureFactory.get_measure_object(measure_path)
31
+ return measure
32
+
33
+ def list_objects(self) -> dict[str, list[Any]]:
34
+ """Return a dictionary with info on the measures that currently exist in the database.
35
+
36
+ Returns
37
+ -------
38
+ dict[str, Any]
39
+ Includes 'name', 'description', 'path' and 'last_modification_date' info
40
+ """
41
+ measures = self._get_object_list()
42
+ objects = [MeasureFactory.get_measure_object(path) for path in measures["path"]]
43
+ measures["description"] = [obj.description for obj in objects]
44
+ measures["objects"] = objects
45
+
46
+ geometries = []
47
+ for path, obj in zip(measures["path"], objects):
48
+ # If polygon is used read the polygon file
49
+ if obj.polygon_file:
50
+ src_path = resolve_filepath(
51
+ object_dir=self.dir_name,
52
+ obj_name=obj.name,
53
+ path=obj.polygon_file,
54
+ )
55
+ geometries.append(gpd.read_file(src_path))
56
+ # If aggregation area is used read the polygon from the aggregation area name
57
+ elif obj.aggregation_area_name:
58
+ if (
59
+ obj.aggregation_area_type
60
+ not in self._database.static.get_aggregation_areas()
61
+ ):
62
+ raise ValueError(
63
+ f"Aggregation area type {obj.aggregation_area_type} for measure {obj.name} does not exist."
64
+ )
65
+ gdf = self._database.static.get_aggregation_areas()[
66
+ obj.aggregation_area_type
67
+ ]
68
+ if obj.aggregation_area_name not in gdf["name"].to_numpy():
69
+ raise ValueError(
70
+ f"Aggregation area name {obj.aggregation_area_name} for measure {obj.name} does not exist."
71
+ )
72
+ geometries.append(gdf.loc[gdf["name"] == obj.aggregation_area_name, :])
73
+ # Else assign a None value
74
+ else:
75
+ geometries.append(None)
76
+
77
+ measures["geometry"] = geometries
78
+ return measures
79
+
80
+ def check_higher_level_usage(self, name: str) -> list[str]:
81
+ """Check if a measure is used in a strategy.
82
+
83
+ Parameters
84
+ ----------
85
+ name : str
86
+ name of the measure to be checked
87
+
88
+ Returns
89
+ -------
90
+ list[str]
91
+ list of strategies that use the measure
92
+ """
93
+ # Get all the strategies
94
+ strategies = self._database.strategies.list_objects()["objects"]
95
+
96
+ # Check if measure is used in a strategy
97
+ used_in_strategy = [
98
+ strategy.name
99
+ for strategy in strategies
100
+ for measure in strategy.measures
101
+ if name == measure
102
+ ]
103
+ return used_in_strategy
@@ -0,0 +1,52 @@
1
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
2
+ from flood_adapt.objects.projections.projections import Projection
3
+
4
+
5
+ class DbsProjection(DbsTemplate[Projection]):
6
+ dir_name = "projections"
7
+ display_name = "Projection"
8
+ _object_class = Projection
9
+
10
+ def _check_standard_objects(self, name: str) -> bool:
11
+ """Check if a projection is a standard projection.
12
+
13
+ Parameters
14
+ ----------
15
+ name : str
16
+ name of the projection to be checked
17
+
18
+ Raises
19
+ ------
20
+ ValueError
21
+ Raise error if projection is a standard projection.
22
+ """
23
+ # Check if projection is a standard projection
24
+ if self._database.site.standard_objects:
25
+ if self._database.site.standard_objects.projections:
26
+ if name in self._database.site.standard_objects.projections:
27
+ return True
28
+
29
+ return False
30
+
31
+ def check_higher_level_usage(self, name: str) -> list[str]:
32
+ """Check if a projection is used in a scenario.
33
+
34
+ Parameters
35
+ ----------
36
+ name : str
37
+ name of the projection to be checked
38
+
39
+ Returns
40
+ -------
41
+ list[str]
42
+ list of scenarios that use the projection
43
+ """
44
+ # Get all the scenarios
45
+ scenarios = self._database.scenarios.list_objects()["objects"]
46
+
47
+ # Check if projection is used in a scenario
48
+ used_in_scenario = [
49
+ scenario.name for scenario in scenarios if name == scenario.projection
50
+ ]
51
+
52
+ return used_in_scenario
@@ -0,0 +1,150 @@
1
+ import shutil
2
+ from typing import Any
3
+
4
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
5
+ from flood_adapt.misc.utils import finished_file_exists
6
+ from flood_adapt.objects.scenarios.scenarios import Scenario
7
+ from flood_adapt.workflows.benefit_runner import BenefitRunner
8
+
9
+
10
+ class DbsScenario(DbsTemplate[Scenario]):
11
+ dir_name = "scenarios"
12
+ display_name = "Scenario"
13
+ _object_class = Scenario
14
+
15
+ def list_objects(self) -> dict[str, list[Any]]:
16
+ """Return a dictionary with info on the events that currently exist in the database.
17
+
18
+ Returns
19
+ -------
20
+ dict[str, Any]
21
+ Includes 'name', 'description', 'path' and 'last_modification_date' info
22
+ """
23
+ scenarios = super().list_objects()
24
+ objects = scenarios["objects"]
25
+ scenarios["Projection"] = [obj.projection for obj in objects]
26
+ scenarios["Event"] = [obj.event for obj in objects]
27
+ scenarios["Strategy"] = [obj.strategy for obj in objects]
28
+ scenarios["finished"] = [self.has_run_check(obj.name) for obj in objects]
29
+
30
+ return scenarios
31
+
32
+ def delete(self, name: str, toml_only: bool = False):
33
+ """Delete an already existing scenario in the database.
34
+
35
+ Parameters
36
+ ----------
37
+ name : str
38
+ name of the scenario to be deleted
39
+ toml_only : bool, optional
40
+ whether to only delete the toml file or the entire folder. If the folder is empty after deleting the toml,
41
+ it will always be deleted. By default False
42
+
43
+ Raises
44
+ ------
45
+ ValueError
46
+ Raise error if scenario to be deleted is already in use.
47
+ """
48
+ # First delete the scenario
49
+ super().delete(name, toml_only)
50
+
51
+ # Then delete the results
52
+ if (self.output_path / name).exists():
53
+ shutil.rmtree(self.output_path / name, ignore_errors=False)
54
+
55
+ def edit(self, scenario: Scenario):
56
+ """Edits an already existing scenario in the database.
57
+
58
+ Parameters
59
+ ----------
60
+ scenario : Scenario
61
+ scenario to be edited in the database
62
+
63
+ Raises
64
+ ------
65
+ ValueError
66
+ Raise error if name is already in use.
67
+ """
68
+ # Check if it is possible to edit the scenario. This then also covers checking whether the
69
+ # scenario is already used in a higher level object. If this is the case, it cannot be edited.
70
+ super().edit(scenario)
71
+
72
+ # Delete output if edited
73
+ output_path = self.output_path / scenario.name
74
+ if output_path.exists():
75
+ shutil.rmtree(output_path, ignore_errors=True)
76
+
77
+ def check_higher_level_usage(self, name: str) -> list[str]:
78
+ """Check if a scenario is used in a benefit.
79
+
80
+ Parameters
81
+ ----------
82
+ name : str
83
+ name of the scenario to be checked
84
+
85
+ Returns
86
+ -------
87
+ list[str]
88
+ list of benefits that use the scenario
89
+ """
90
+ benefits = self._database.benefits.list_objects()["objects"]
91
+ used_in_benefit = []
92
+ for benefit in benefits:
93
+ runner = BenefitRunner(database=self._database, benefit=benefit)
94
+ scenarios = runner.check_scenarios()["scenario created"].to_list()
95
+ for scenario in scenarios:
96
+ if name == scenario:
97
+ used_in_benefit.append(benefit.name)
98
+
99
+ return used_in_benefit
100
+
101
+ def equal_hazard_components(self, left: Scenario, right: Scenario) -> bool:
102
+ """Check if two scenarios have the same hazard components.
103
+
104
+ Parameters
105
+ ----------
106
+ left : Scenario
107
+ first scenario to be compared
108
+ right : Scenario
109
+ second scenario to be compared
110
+
111
+ Returns
112
+ -------
113
+ bool
114
+ True if the scenarios have the same hazard components, False otherwise
115
+ """
116
+ event_left = self._database.events.get(left.event)
117
+ event_right = self._database.events.get(right.event)
118
+ equal_events = event_left == event_right
119
+
120
+ left_projection = self._database.projections.get(left.projection)
121
+ right_projection = self._database.projections.get(right.projection)
122
+ equal_projection = (
123
+ left_projection.physical_projection == right_projection.physical_projection
124
+ )
125
+
126
+ left_strategy = self._database.strategies.get(
127
+ left.strategy
128
+ ).get_hazard_strategy()
129
+ right_strategy = self._database.strategies.get(
130
+ right.strategy
131
+ ).get_hazard_strategy()
132
+ equal_strategy = left_strategy == right_strategy
133
+
134
+ return equal_events and equal_projection and equal_strategy
135
+
136
+ def has_run_check(self, name: str) -> bool:
137
+ """Check if the scenario has been run.
138
+
139
+ Parameters
140
+ ----------
141
+ name : str
142
+ name of the scenario to be checked
143
+
144
+ Returns
145
+ -------
146
+ bool
147
+ True if the scenario has been run, False otherwise
148
+ """
149
+ results_path = self.output_path / name
150
+ return finished_file_exists(results_path)