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.
- flood_adapt/__init__.py +22 -0
- flood_adapt/adapter/__init__.py +9 -0
- flood_adapt/adapter/fiat_adapter.py +1502 -0
- flood_adapt/adapter/interface/__init__.py +0 -0
- flood_adapt/adapter/interface/hazard_adapter.py +70 -0
- flood_adapt/adapter/interface/impact_adapter.py +36 -0
- flood_adapt/adapter/interface/model_adapter.py +89 -0
- flood_adapt/adapter/interface/offshore.py +19 -0
- flood_adapt/adapter/sfincs_adapter.py +1857 -0
- flood_adapt/adapter/sfincs_offshore.py +193 -0
- flood_adapt/config/__init__.py +0 -0
- flood_adapt/config/config.py +245 -0
- flood_adapt/config/fiat.py +219 -0
- flood_adapt/config/gui.py +224 -0
- flood_adapt/config/sfincs.py +336 -0
- flood_adapt/config/site.py +124 -0
- flood_adapt/database_builder/__init__.py +0 -0
- flood_adapt/database_builder/database_builder.py +2175 -0
- flood_adapt/database_builder/templates/default_units/imperial.toml +9 -0
- flood_adapt/database_builder/templates/default_units/metric.toml +9 -0
- flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +10 -0
- flood_adapt/database_builder/templates/icons/black_down_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/black_left_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/black_right_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/black_up_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_down.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_left.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_right.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_up.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_down.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_left.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_right.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_up.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_left.png +0 -0
- flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_right.png +0 -0
- flood_adapt/database_builder/templates/icons/white_down_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/white_left_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/white_right_48x48.png +0 -0
- flood_adapt/database_builder/templates/icons/white_up_48x48.png +0 -0
- flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +90 -0
- flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +57 -0
- flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +121 -0
- flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +65 -0
- flood_adapt/database_builder/templates/infographics/OSM/styles.css +45 -0
- flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +126 -0
- flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +60 -0
- flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +121 -0
- flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +65 -0
- flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +45 -0
- flood_adapt/database_builder/templates/infographics/images/ambulance.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/car.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/cart.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/firetruck.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/hospital.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/house.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/info.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/money.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/person.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/school.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/truck.png +0 -0
- flood_adapt/database_builder/templates/infographics/images/walking_person.png +0 -0
- flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +4 -0
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +143 -0
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +153 -0
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +127 -0
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +57 -0
- flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +4 -0
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +191 -0
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +153 -0
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +178 -0
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +57 -0
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +9 -0
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +65 -0
- flood_adapt/database_builder/templates/mapbox_layers/bin_colors.toml +5 -0
- flood_adapt/database_builder.py +16 -0
- flood_adapt/dbs_classes/__init__.py +21 -0
- flood_adapt/dbs_classes/database.py +716 -0
- flood_adapt/dbs_classes/dbs_benefit.py +97 -0
- flood_adapt/dbs_classes/dbs_event.py +91 -0
- flood_adapt/dbs_classes/dbs_measure.py +103 -0
- flood_adapt/dbs_classes/dbs_projection.py +52 -0
- flood_adapt/dbs_classes/dbs_scenario.py +150 -0
- flood_adapt/dbs_classes/dbs_static.py +261 -0
- flood_adapt/dbs_classes/dbs_strategy.py +147 -0
- flood_adapt/dbs_classes/dbs_template.py +302 -0
- flood_adapt/dbs_classes/interface/database.py +147 -0
- flood_adapt/dbs_classes/interface/element.py +137 -0
- flood_adapt/dbs_classes/interface/static.py +47 -0
- flood_adapt/flood_adapt.py +1371 -0
- flood_adapt/misc/__init__.py +0 -0
- flood_adapt/misc/database_user.py +16 -0
- flood_adapt/misc/log.py +183 -0
- flood_adapt/misc/path_builder.py +54 -0
- flood_adapt/misc/utils.py +185 -0
- flood_adapt/objects/__init__.py +59 -0
- flood_adapt/objects/benefits/__init__.py +0 -0
- flood_adapt/objects/benefits/benefits.py +61 -0
- flood_adapt/objects/events/__init__.py +0 -0
- flood_adapt/objects/events/event_factory.py +135 -0
- flood_adapt/objects/events/event_set.py +84 -0
- flood_adapt/objects/events/events.py +221 -0
- flood_adapt/objects/events/historical.py +55 -0
- flood_adapt/objects/events/hurricane.py +64 -0
- flood_adapt/objects/events/synthetic.py +48 -0
- flood_adapt/objects/forcing/__init__.py +0 -0
- flood_adapt/objects/forcing/csv.py +68 -0
- flood_adapt/objects/forcing/discharge.py +66 -0
- flood_adapt/objects/forcing/forcing.py +142 -0
- flood_adapt/objects/forcing/forcing_factory.py +182 -0
- flood_adapt/objects/forcing/meteo_handler.py +93 -0
- flood_adapt/objects/forcing/netcdf.py +40 -0
- flood_adapt/objects/forcing/plotting.py +428 -0
- flood_adapt/objects/forcing/rainfall.py +98 -0
- flood_adapt/objects/forcing/tide_gauge.py +191 -0
- flood_adapt/objects/forcing/time_frame.py +77 -0
- flood_adapt/objects/forcing/timeseries.py +552 -0
- flood_adapt/objects/forcing/unit_system.py +580 -0
- flood_adapt/objects/forcing/waterlevels.py +108 -0
- flood_adapt/objects/forcing/wind.py +124 -0
- flood_adapt/objects/measures/__init__.py +0 -0
- flood_adapt/objects/measures/measure_factory.py +92 -0
- flood_adapt/objects/measures/measures.py +506 -0
- flood_adapt/objects/object_model.py +68 -0
- flood_adapt/objects/projections/__init__.py +0 -0
- flood_adapt/objects/projections/projections.py +89 -0
- flood_adapt/objects/scenarios/__init__.py +0 -0
- flood_adapt/objects/scenarios/scenarios.py +22 -0
- flood_adapt/objects/strategies/__init__.py +0 -0
- flood_adapt/objects/strategies/strategies.py +68 -0
- flood_adapt/workflows/__init__.py +0 -0
- flood_adapt/workflows/benefit_runner.py +541 -0
- flood_adapt/workflows/floodmap.py +85 -0
- flood_adapt/workflows/impacts_integrator.py +82 -0
- flood_adapt/workflows/scenario_runner.py +69 -0
- flood_adapt-0.3.0.dist-info/LICENSE +21 -0
- flood_adapt-0.3.0.dist-info/METADATA +183 -0
- flood_adapt-0.3.0.dist-info/RECORD +139 -0
- flood_adapt-0.3.0.dist-info/WHEEL +5 -0
- 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)
|