flood-adapt 0.3.1__py3-none-any.whl → 0.3.3__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 +1 -1
- flood_adapt/adapter/fiat_adapter.py +35 -1
- flood_adapt/config/config.py +58 -36
- flood_adapt/config/fiat.py +7 -7
- flood_adapt/config/gui.py +189 -82
- flood_adapt/config/site.py +2 -2
- flood_adapt/database_builder/database_builder.py +100 -65
- flood_adapt/dbs_classes/database.py +16 -53
- flood_adapt/dbs_classes/dbs_event.py +4 -1
- flood_adapt/dbs_classes/dbs_measure.py +7 -7
- flood_adapt/dbs_classes/dbs_projection.py +4 -1
- flood_adapt/dbs_classes/dbs_scenario.py +17 -8
- flood_adapt/dbs_classes/dbs_static.py +3 -5
- flood_adapt/dbs_classes/dbs_strategy.py +7 -5
- flood_adapt/dbs_classes/dbs_template.py +21 -22
- flood_adapt/dbs_classes/interface/database.py +0 -8
- flood_adapt/dbs_classes/interface/element.py +1 -1
- flood_adapt/flood_adapt.py +135 -273
- flood_adapt/objects/__init__.py +40 -17
- flood_adapt/objects/benefits/benefits.py +6 -6
- flood_adapt/objects/events/event_set.py +4 -4
- flood_adapt/objects/events/events.py +17 -4
- flood_adapt/objects/events/historical.py +11 -8
- flood_adapt/objects/events/hurricane.py +11 -8
- flood_adapt/objects/events/synthetic.py +9 -7
- flood_adapt/objects/forcing/forcing.py +9 -1
- flood_adapt/objects/forcing/plotting.py +1 -0
- flood_adapt/objects/forcing/tide_gauge.py +14 -14
- flood_adapt/objects/forcing/time_frame.py +13 -0
- flood_adapt/objects/measures/measures.py +38 -15
- flood_adapt/objects/object_model.py +2 -2
- flood_adapt/objects/projections/projections.py +18 -18
- flood_adapt/objects/strategies/strategies.py +22 -1
- flood_adapt/workflows/benefit_runner.py +5 -2
- flood_adapt/workflows/scenario_runner.py +8 -7
- flood_adapt-0.3.3.dist-info/LICENSE +674 -0
- flood_adapt-0.3.3.dist-info/METADATA +859 -0
- {flood_adapt-0.3.1.dist-info → flood_adapt-0.3.3.dist-info}/RECORD +41 -41
- flood_adapt-0.3.1.dist-info/LICENSE +0 -21
- flood_adapt-0.3.1.dist-info/METADATA +0 -183
- /flood_adapt/database_builder/templates/{mapbox_layers → output_layers}/bin_colors.toml +0 -0
- {flood_adapt-0.3.1.dist-info → flood_adapt-0.3.3.dist-info}/WHEEL +0 -0
- {flood_adapt-0.3.1.dist-info → flood_adapt-0.3.3.dist-info}/top_level.txt +0 -0
|
@@ -34,12 +34,16 @@ from flood_adapt.config.fiat import (
|
|
|
34
34
|
SVIModel,
|
|
35
35
|
)
|
|
36
36
|
from flood_adapt.config.gui import (
|
|
37
|
+
AggregationDmgLayer,
|
|
38
|
+
BenefitsLayer,
|
|
39
|
+
FloodMapLayer,
|
|
40
|
+
FootprintsDmgLayer,
|
|
37
41
|
GuiModel,
|
|
38
42
|
GuiUnitModel,
|
|
39
|
-
|
|
43
|
+
OutputLayers,
|
|
40
44
|
PlottingModel,
|
|
41
45
|
SyntheticTideModel,
|
|
42
|
-
|
|
46
|
+
VisualizationLayers,
|
|
43
47
|
)
|
|
44
48
|
from flood_adapt.config.sfincs import (
|
|
45
49
|
Cstype,
|
|
@@ -379,6 +383,7 @@ class DatabaseBuilder:
|
|
|
379
383
|
|
|
380
384
|
_has_roads: bool = False
|
|
381
385
|
_aggregation_areas: Optional[list] = None
|
|
386
|
+
_probabilistic_set_name: Optional[str] = None
|
|
382
387
|
|
|
383
388
|
def __init__(self, config: ConfigModel, overwrite: bool = True):
|
|
384
389
|
self.config = config
|
|
@@ -579,19 +584,26 @@ class DatabaseBuilder:
|
|
|
579
584
|
### FIAT ###
|
|
580
585
|
def create_fiat_model(self) -> FiatModel:
|
|
581
586
|
fiat = FiatModel(
|
|
582
|
-
risk=self.create_risk_model(),
|
|
583
587
|
config=self.create_fiat_config(),
|
|
584
588
|
benefits=self.create_benefit_config(),
|
|
589
|
+
risk=self.create_risk_model(),
|
|
585
590
|
)
|
|
586
591
|
return fiat
|
|
587
592
|
|
|
588
|
-
def create_risk_model(self) -> RiskModel:
|
|
593
|
+
def create_risk_model(self) -> Optional[RiskModel]:
|
|
589
594
|
# Check if return periods are provided
|
|
590
595
|
if not self.config.return_periods:
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
596
|
+
if self._probabilistic_set_name:
|
|
597
|
+
risk = RiskModel()
|
|
598
|
+
self.logger.warning(
|
|
599
|
+
f"No return periods provided, but a probabilistic set is available. Using default return periods {risk.return_periods}."
|
|
600
|
+
)
|
|
601
|
+
return risk
|
|
602
|
+
else:
|
|
603
|
+
self.logger.warning(
|
|
604
|
+
"No return periods provided and no probabilistic set available. Risk calculations will not be performed."
|
|
605
|
+
)
|
|
606
|
+
return None
|
|
595
607
|
else:
|
|
596
608
|
risk = RiskModel(return_periods=self.config.return_periods)
|
|
597
609
|
return risk
|
|
@@ -637,6 +649,8 @@ class DatabaseBuilder:
|
|
|
637
649
|
# Update elevations
|
|
638
650
|
self.update_fiat_elevation()
|
|
639
651
|
|
|
652
|
+
self._svi = self.create_svi()
|
|
653
|
+
|
|
640
654
|
config = FiatConfigModel(
|
|
641
655
|
exposure_crs=self.fiat_model.exposure.crs,
|
|
642
656
|
floodmap_type=self.read_floodmap_type(),
|
|
@@ -649,7 +663,7 @@ class DatabaseBuilder:
|
|
|
649
663
|
save_simulation=False, # TODO
|
|
650
664
|
infographics=self.config.infographics,
|
|
651
665
|
aggregation=self._aggregation_areas,
|
|
652
|
-
svi=self.
|
|
666
|
+
svi=self._svi,
|
|
653
667
|
)
|
|
654
668
|
|
|
655
669
|
# Update output geoms names
|
|
@@ -1481,23 +1495,28 @@ class DatabaseBuilder:
|
|
|
1481
1495
|
|
|
1482
1496
|
### SITE ###
|
|
1483
1497
|
def create_site_config(self) -> Site:
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1498
|
+
"""Create the site configuration for the FloodAdapt model.
|
|
1499
|
+
|
|
1500
|
+
The order of these functions is important!
|
|
1501
|
+
1. Create the SFINCS model.
|
|
1502
|
+
needs: water level references
|
|
1503
|
+
provides: updated water level references with optional tide gauge
|
|
1504
|
+
2. Create the FIAT model.
|
|
1505
|
+
needs: water level references and optional probabilistic event set
|
|
1506
|
+
provides: svi and exposure geometries
|
|
1507
|
+
3. Create the GUI model. (requires water level references and FIAT model to be updated)
|
|
1508
|
+
needs: water level references and FIAT model to be updated
|
|
1509
|
+
provides: gui model with output layers, visualization layers and plotting.
|
|
1489
1510
|
|
|
1490
|
-
|
|
1511
|
+
"""
|
|
1512
|
+
sfincs = self.create_sfincs_config()
|
|
1491
1513
|
self.add_probabilistic_set()
|
|
1492
|
-
|
|
1493
|
-
# Create the FIAT configuration
|
|
1494
1514
|
fiat = self.create_fiat_model()
|
|
1495
|
-
|
|
1515
|
+
gui = self.create_gui_config()
|
|
1496
1516
|
|
|
1497
|
-
#
|
|
1517
|
+
# Order doesnt matter from here
|
|
1518
|
+
lon, lat = self.read_location()
|
|
1498
1519
|
std_objs = self.set_standard_objects()
|
|
1499
|
-
|
|
1500
|
-
# Description of site
|
|
1501
1520
|
description = (
|
|
1502
1521
|
self.config.description if self.config.description else self.config.name
|
|
1503
1522
|
)
|
|
@@ -1530,7 +1549,7 @@ class DatabaseBuilder:
|
|
|
1530
1549
|
gui = GuiModel(
|
|
1531
1550
|
units=self.unit_system,
|
|
1532
1551
|
plotting=self.create_hazard_plotting_config(),
|
|
1533
|
-
|
|
1552
|
+
output_layers=self.create_output_layers_config(),
|
|
1534
1553
|
visualization_layers=self.create_visualization_layers(),
|
|
1535
1554
|
)
|
|
1536
1555
|
|
|
@@ -1546,57 +1565,73 @@ class DatabaseBuilder:
|
|
|
1546
1565
|
f"Unit system {self.config.unit_system} not recognized. Please choose 'imperial' or 'metric'."
|
|
1547
1566
|
)
|
|
1548
1567
|
|
|
1549
|
-
def create_visualization_layers(self) ->
|
|
1550
|
-
visualization_layers =
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1568
|
+
def create_visualization_layers(self) -> VisualizationLayers:
|
|
1569
|
+
visualization_layers = VisualizationLayers()
|
|
1570
|
+
if self._svi is not None:
|
|
1571
|
+
visualization_layers.add_layer(
|
|
1572
|
+
name="svi",
|
|
1573
|
+
long_name="Social Vulnerability Index (SVI)",
|
|
1574
|
+
path=str(self.static_path / self._svi.geom),
|
|
1575
|
+
database_path=self.root,
|
|
1576
|
+
field_name="SVI",
|
|
1577
|
+
bins=[0.05, 0.2, 0.4, 0.6, 0.8],
|
|
1578
|
+
)
|
|
1561
1579
|
return visualization_layers
|
|
1562
1580
|
|
|
1563
|
-
def
|
|
1581
|
+
def create_output_layers_config(self) -> OutputLayers:
|
|
1564
1582
|
# Read default colors from template
|
|
1565
1583
|
fd_max = self.config.gui.max_flood_depth
|
|
1566
1584
|
ad_max = self.config.gui.max_aggr_dmg
|
|
1567
1585
|
ftd_max = self.config.gui.max_footprint_dmg
|
|
1568
1586
|
b_max = self.config.gui.max_benefits
|
|
1569
1587
|
|
|
1570
|
-
|
|
1571
|
-
if self.config.
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
0.
|
|
1583
|
-
|
|
1584
|
-
0.5 * ad_max,
|
|
1585
|
-
ad_max,
|
|
1586
|
-
],
|
|
1587
|
-
footprints_dmg_bins=[
|
|
1588
|
-
0.00001,
|
|
1589
|
-
0.06 * ftd_max,
|
|
1590
|
-
0.2 * ftd_max,
|
|
1591
|
-
0.4 * ftd_max,
|
|
1592
|
-
ftd_max,
|
|
1593
|
-
],
|
|
1594
|
-
benefits_bins=[0, 0.01, 0.02 * b_max, 0.2 * b_max, b_max],
|
|
1595
|
-
svi_bins=svi_bins,
|
|
1596
|
-
**self._get_bin_colors(),
|
|
1597
|
-
)
|
|
1588
|
+
benefits_layer = None
|
|
1589
|
+
if self.config.probabilistic_set is not None:
|
|
1590
|
+
benefits_layer = BenefitsLayer(
|
|
1591
|
+
bins=[0, 0.01, 0.02 * b_max, 0.2 * b_max, b_max],
|
|
1592
|
+
colors=[
|
|
1593
|
+
"#FF7D7D",
|
|
1594
|
+
"#FFFFFF",
|
|
1595
|
+
"#DCEDC8",
|
|
1596
|
+
"#AED581",
|
|
1597
|
+
"#7CB342",
|
|
1598
|
+
"#33691E",
|
|
1599
|
+
],
|
|
1600
|
+
threshold=0.0,
|
|
1601
|
+
)
|
|
1598
1602
|
|
|
1599
|
-
|
|
1603
|
+
output_layers = OutputLayers(
|
|
1604
|
+
floodmap=FloodMapLayer(
|
|
1605
|
+
bins=[0.2 * fd_max, 0.6 * fd_max, fd_max],
|
|
1606
|
+
colors=["#D7ECFB", "#8ABDDD", "#1C73A4", "#081D58"],
|
|
1607
|
+
zbmax=-9999,
|
|
1608
|
+
depth_min=0.0,
|
|
1609
|
+
),
|
|
1610
|
+
aggregation_dmg=AggregationDmgLayer(
|
|
1611
|
+
bins=[0.00001, 0.1 * ad_max, 0.25 * ad_max, 0.5 * ad_max, ad_max],
|
|
1612
|
+
colors=[
|
|
1613
|
+
"#FFFFFF",
|
|
1614
|
+
"#FEE9CE",
|
|
1615
|
+
"#FDBB84",
|
|
1616
|
+
"#FC844E",
|
|
1617
|
+
"#E03720",
|
|
1618
|
+
"#860000",
|
|
1619
|
+
],
|
|
1620
|
+
),
|
|
1621
|
+
footprints_dmg=FootprintsDmgLayer(
|
|
1622
|
+
bins=[0.00001, 0.06 * ftd_max, 0.2 * ftd_max, 0.4 * ftd_max, ftd_max],
|
|
1623
|
+
colors=[
|
|
1624
|
+
"#FFFFFF",
|
|
1625
|
+
"#FEE9CE",
|
|
1626
|
+
"#FDBB84",
|
|
1627
|
+
"#FC844E",
|
|
1628
|
+
"#E03720",
|
|
1629
|
+
"#860000",
|
|
1630
|
+
],
|
|
1631
|
+
),
|
|
1632
|
+
benefits=benefits_layer,
|
|
1633
|
+
)
|
|
1634
|
+
return output_layers
|
|
1600
1635
|
|
|
1601
1636
|
def create_hazard_plotting_config(self) -> PlottingModel:
|
|
1602
1637
|
datum_names = [datum.name for datum in self.water_level_references.datums]
|
|
@@ -2151,7 +2186,7 @@ class DatabaseBuilder:
|
|
|
2151
2186
|
"""
|
|
2152
2187
|
templates_path = Path(__file__).parent.resolve().joinpath("templates")
|
|
2153
2188
|
with open(
|
|
2154
|
-
templates_path.joinpath("
|
|
2189
|
+
templates_path.joinpath("output_layers", "bin_colors.toml"), "rb"
|
|
2155
2190
|
) as f:
|
|
2156
2191
|
bin_colors = tomli.load(f)
|
|
2157
2192
|
return bin_colors
|
|
@@ -381,14 +381,6 @@ class Database(IDatabase):
|
|
|
381
381
|
runner = BenefitRunner(self, benefit=benefit)
|
|
382
382
|
runner.run_cost_benefit()
|
|
383
383
|
|
|
384
|
-
def update(self) -> None:
|
|
385
|
-
self.projections_list = self._projections.list_objects()
|
|
386
|
-
self.events_list = self._events.list_objects()
|
|
387
|
-
self.measures_list = self._measures.list_objects()
|
|
388
|
-
self.strategies_list = self._strategies.list_objects()
|
|
389
|
-
self.scenarios_list = self._scenarios.list_objects()
|
|
390
|
-
self.benefits_list = self._benefits.list_objects()
|
|
391
|
-
|
|
392
384
|
def get_outputs(self) -> dict[str, Any]:
|
|
393
385
|
"""Return a dictionary with info on the outputs that currently exist in the database.
|
|
394
386
|
|
|
@@ -397,7 +389,7 @@ class Database(IDatabase):
|
|
|
397
389
|
dict[str, Any]
|
|
398
390
|
Includes 'name', 'path', 'last_modification_date' and "finished" info
|
|
399
391
|
"""
|
|
400
|
-
all_scenarios = pd.DataFrame(self._scenarios.
|
|
392
|
+
all_scenarios = pd.DataFrame(self._scenarios.summarize_objects())
|
|
401
393
|
if len(all_scenarios) > 0:
|
|
402
394
|
df = all_scenarios[all_scenarios["finished"]]
|
|
403
395
|
else:
|
|
@@ -585,17 +577,17 @@ class Database(IDatabase):
|
|
|
585
577
|
"""
|
|
586
578
|
match object_type:
|
|
587
579
|
case "projections":
|
|
588
|
-
return self.projections.
|
|
580
|
+
return self.projections.summarize_objects()
|
|
589
581
|
case "events":
|
|
590
|
-
return self.events.
|
|
582
|
+
return self.events.summarize_objects()
|
|
591
583
|
case "measures":
|
|
592
|
-
return self.measures.
|
|
584
|
+
return self.measures.summarize_objects()
|
|
593
585
|
case "strategies":
|
|
594
|
-
return self.strategies.
|
|
586
|
+
return self.strategies.summarize_objects()
|
|
595
587
|
case "scenarios":
|
|
596
|
-
return self.scenarios.
|
|
588
|
+
return self.scenarios.summarize_objects()
|
|
597
589
|
case "benefits":
|
|
598
|
-
return self.benefits.
|
|
590
|
+
return self.benefits.summarize_objects()
|
|
599
591
|
case _:
|
|
600
592
|
raise ValueError(
|
|
601
593
|
f"Object type '{object_type}' is not valid. Must be one of 'projections', 'events', 'measures', 'strategies' or 'scenarios'."
|
|
@@ -611,16 +603,20 @@ class Database(IDatabase):
|
|
|
611
603
|
scenario_name : str
|
|
612
604
|
name of the scenario to check if needs to be rerun for hazard
|
|
613
605
|
"""
|
|
614
|
-
scenario = self.
|
|
606
|
+
scenario = self.scenarios.get(scenario_name)
|
|
615
607
|
runner = ScenarioRunner(self, scenario=scenario)
|
|
616
608
|
|
|
617
609
|
# Dont do anything if the hazard model has already been run in itself
|
|
618
610
|
if runner.impacts.hazard.has_run:
|
|
619
611
|
return
|
|
620
612
|
|
|
613
|
+
scenarios = [
|
|
614
|
+
self.scenarios.get(scn)
|
|
615
|
+
for scn in self.scenarios.summarize_objects()["name"]
|
|
616
|
+
]
|
|
621
617
|
scns_simulated = [
|
|
622
618
|
sim
|
|
623
|
-
for sim in
|
|
619
|
+
for sim in scenarios
|
|
624
620
|
if self.scenarios.output_path.joinpath(sim.name, "Flooding").is_dir()
|
|
625
621
|
]
|
|
626
622
|
|
|
@@ -630,8 +626,9 @@ class Database(IDatabase):
|
|
|
630
626
|
path_new = self.scenarios.output_path.joinpath(
|
|
631
627
|
scenario.name, "Flooding"
|
|
632
628
|
)
|
|
633
|
-
|
|
634
|
-
|
|
629
|
+
_runner = ScenarioRunner(self, scenario=scn)
|
|
630
|
+
|
|
631
|
+
if _runner.impacts.hazard.has_run: # only copy results if the hazard model has actually finished and skip simulation folders
|
|
635
632
|
shutil.copytree(
|
|
636
633
|
existing,
|
|
637
634
|
path_new,
|
|
@@ -642,40 +639,6 @@ class Database(IDatabase):
|
|
|
642
639
|
f"Hazard simulation is used from the '{scn.name}' scenario"
|
|
643
640
|
)
|
|
644
641
|
|
|
645
|
-
def run_scenario(self, scenario_name: Union[str, list[str]]) -> None:
|
|
646
|
-
"""Run a scenario hazard and impacts.
|
|
647
|
-
|
|
648
|
-
Parameters
|
|
649
|
-
----------
|
|
650
|
-
scenario_name : Union[str, list[str]]
|
|
651
|
-
name(s) of the scenarios to run.
|
|
652
|
-
|
|
653
|
-
Raises
|
|
654
|
-
------
|
|
655
|
-
RuntimeError
|
|
656
|
-
If an error occurs while running one of the scenarios
|
|
657
|
-
"""
|
|
658
|
-
if not isinstance(scenario_name, list):
|
|
659
|
-
scenario_name = [scenario_name]
|
|
660
|
-
|
|
661
|
-
errors = []
|
|
662
|
-
|
|
663
|
-
for scn in scenario_name:
|
|
664
|
-
try:
|
|
665
|
-
self.has_run_hazard(scn)
|
|
666
|
-
scenario = self.scenarios.get(scn)
|
|
667
|
-
runner = ScenarioRunner(self, scenario=scenario)
|
|
668
|
-
runner.run(scenario)
|
|
669
|
-
except RuntimeError as e:
|
|
670
|
-
errors.append(scn)
|
|
671
|
-
self.logger.error(f"Error running scenario {scn}: {e}")
|
|
672
|
-
if errors:
|
|
673
|
-
raise RuntimeError(
|
|
674
|
-
"FloodAdapt failed to run for the following scenarios: "
|
|
675
|
-
+ ", ".join(errors)
|
|
676
|
-
+ ". Check the logs for more information."
|
|
677
|
-
)
|
|
678
|
-
|
|
679
642
|
def cleanup(self) -> None:
|
|
680
643
|
"""
|
|
681
644
|
Remove corrupted scenario output.
|
|
@@ -67,7 +67,10 @@ class DbsEvent(DbsTemplate[Event]):
|
|
|
67
67
|
list of scenarios that use the event
|
|
68
68
|
"""
|
|
69
69
|
# Get all the scenarios
|
|
70
|
-
scenarios =
|
|
70
|
+
scenarios = [
|
|
71
|
+
self._database.scenarios.get(scn)
|
|
72
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
73
|
+
]
|
|
71
74
|
|
|
72
75
|
# Check if event is used in a scenario
|
|
73
76
|
used_in_scenario = [
|
|
@@ -37,20 +37,17 @@ class DbsMeasure(DbsTemplate[Measure]):
|
|
|
37
37
|
# Load and return the object
|
|
38
38
|
return MeasureFactory.get_measure_object(full_path)
|
|
39
39
|
|
|
40
|
-
def
|
|
40
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
41
41
|
"""Return a dictionary with info on the measures that currently exist in the database.
|
|
42
42
|
|
|
43
43
|
Returns
|
|
44
44
|
-------
|
|
45
45
|
dict[str, Any]
|
|
46
|
-
Includes 'name', 'description', 'path' and 'last_modification_date' info
|
|
46
|
+
Includes 'name', 'description', 'path' and 'last_modification_date' and 'geometry' info
|
|
47
47
|
"""
|
|
48
|
-
measures = self.
|
|
48
|
+
measures = self._get_object_summary()
|
|
49
49
|
objects = [self.get(name) for name in measures["name"]]
|
|
50
50
|
|
|
51
|
-
measures["description"] = [obj.description for obj in objects]
|
|
52
|
-
measures["objects"] = objects
|
|
53
|
-
|
|
54
51
|
geometries = []
|
|
55
52
|
for obj in objects:
|
|
56
53
|
# If polygon is used read the polygon file
|
|
@@ -99,7 +96,10 @@ class DbsMeasure(DbsTemplate[Measure]):
|
|
|
99
96
|
list of strategies that use the measure
|
|
100
97
|
"""
|
|
101
98
|
# Get all the strategies
|
|
102
|
-
strategies =
|
|
99
|
+
strategies = [
|
|
100
|
+
self._database.strategies.get(strategy)
|
|
101
|
+
for strategy in self._database.strategies.summarize_objects()["name"]
|
|
102
|
+
]
|
|
103
103
|
|
|
104
104
|
# Check if measure is used in a strategy
|
|
105
105
|
used_in_strategy = [
|
|
@@ -42,7 +42,10 @@ class DbsProjection(DbsTemplate[Projection]):
|
|
|
42
42
|
list of scenarios that use the projection
|
|
43
43
|
"""
|
|
44
44
|
# Get all the scenarios
|
|
45
|
-
scenarios =
|
|
45
|
+
scenarios = [
|
|
46
|
+
self._database.scenarios.get(scn)
|
|
47
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
48
|
+
]
|
|
46
49
|
|
|
47
50
|
# Check if projection is used in a scenario
|
|
48
51
|
used_in_scenario = [
|
|
@@ -12,7 +12,7 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
12
12
|
display_name = "Scenario"
|
|
13
13
|
_object_class = Scenario
|
|
14
14
|
|
|
15
|
-
def
|
|
15
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
16
16
|
"""Return a dictionary with info on the events that currently exist in the database.
|
|
17
17
|
|
|
18
18
|
Returns
|
|
@@ -20,12 +20,18 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
20
20
|
dict[str, Any]
|
|
21
21
|
Includes 'name', 'description', 'path' and 'last_modification_date' info
|
|
22
22
|
"""
|
|
23
|
-
scenarios = super().
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
scenarios["
|
|
23
|
+
scenarios = super().summarize_objects()
|
|
24
|
+
scenarios["Projection"] = [
|
|
25
|
+
self._read_variable_in_toml("projection", path)
|
|
26
|
+
for path in scenarios["path"]
|
|
27
|
+
]
|
|
28
|
+
scenarios["Event"] = [
|
|
29
|
+
self._read_variable_in_toml("event", path) for path in scenarios["path"]
|
|
30
|
+
]
|
|
31
|
+
scenarios["Strategy"] = [
|
|
32
|
+
self._read_variable_in_toml("strategy", path) for path in scenarios["path"]
|
|
33
|
+
]
|
|
34
|
+
scenarios["finished"] = [self.has_run_check(scn) for scn in scenarios["name"]]
|
|
29
35
|
|
|
30
36
|
return scenarios
|
|
31
37
|
|
|
@@ -87,7 +93,10 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
87
93
|
list[str]
|
|
88
94
|
list of benefits that use the scenario
|
|
89
95
|
"""
|
|
90
|
-
benefits =
|
|
96
|
+
benefits = [
|
|
97
|
+
self._database.benefits.get(benefit)
|
|
98
|
+
for benefit in self._database.benefits.summarize_objects()["name"]
|
|
99
|
+
]
|
|
91
100
|
used_in_benefit = []
|
|
92
101
|
for benefit in benefits:
|
|
93
102
|
runner = BenefitRunner(database=self._database, benefit=benefit)
|
|
@@ -128,11 +128,9 @@ class DbsStatic(IDbsStatic):
|
|
|
128
128
|
"""
|
|
129
129
|
# Read the map
|
|
130
130
|
full_path = self._database.static_path / path
|
|
131
|
-
if full_path.is_file():
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# If the file is not found, throw an error
|
|
135
|
-
raise FileNotFoundError(f"File {full_path} not found")
|
|
131
|
+
if not full_path.is_file():
|
|
132
|
+
raise FileNotFoundError(f"File {full_path} not found")
|
|
133
|
+
return gpd.read_file(full_path, engine="pyogrio").to_crs(4326)
|
|
136
134
|
|
|
137
135
|
@cache_method_wrapper
|
|
138
136
|
def get_slr_scn_names(self) -> list:
|
|
@@ -40,7 +40,7 @@ class DbsStrategy(DbsTemplate[Strategy]):
|
|
|
40
40
|
ValueError
|
|
41
41
|
Raise error if name is already in use.
|
|
42
42
|
"""
|
|
43
|
-
object_exists = object_model.name in self.
|
|
43
|
+
object_exists = object_model.name in self.summarize_objects()["name"]
|
|
44
44
|
|
|
45
45
|
# If you want to overwrite the object, and the object already exists, first delete it. If it exists and you
|
|
46
46
|
# don't want to overwrite, raise an error.
|
|
@@ -137,11 +137,13 @@ class DbsStrategy(DbsTemplate[Strategy]):
|
|
|
137
137
|
list[str]
|
|
138
138
|
list of scenarios that use the strategy
|
|
139
139
|
"""
|
|
140
|
-
|
|
140
|
+
scenarios = [
|
|
141
|
+
self._database.scenarios.get(scn)
|
|
142
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
143
|
+
]
|
|
144
|
+
|
|
141
145
|
used_in_scenario = [
|
|
142
|
-
scenario.name
|
|
143
|
-
for scenario in self._database.scenarios.list_objects()["objects"]
|
|
144
|
-
if name == scenario.strategy
|
|
146
|
+
scenario.name for scenario in scenarios if name == scenario.strategy
|
|
145
147
|
]
|
|
146
148
|
|
|
147
149
|
return used_in_scenario
|
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, TypeVar
|
|
5
5
|
|
|
6
|
+
import tomli
|
|
6
7
|
import tomli_w
|
|
7
8
|
|
|
8
9
|
from flood_adapt.dbs_classes.interface.database import IDatabase
|
|
@@ -47,25 +48,16 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
47
48
|
# Load and return the object
|
|
48
49
|
return self._object_class.load_file(full_path)
|
|
49
50
|
|
|
50
|
-
def
|
|
51
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
51
52
|
"""Return a dictionary with info on the objects that currently exist in the database.
|
|
52
53
|
|
|
53
54
|
Returns
|
|
54
55
|
-------
|
|
55
56
|
dict[str, list[Any]]
|
|
56
|
-
A dictionary that contains the keys: `name`,
|
|
57
|
+
A dictionary that contains the keys: `name`, `description`, `path` and `last_modification_date`.
|
|
57
58
|
Each key has a list of the corresponding values, where the index of the values corresponds to the same object.
|
|
58
59
|
"""
|
|
59
|
-
|
|
60
|
-
object_list = self._get_object_list()
|
|
61
|
-
|
|
62
|
-
# Load all objects
|
|
63
|
-
objects = [self.get(name) for name in object_list["name"]]
|
|
64
|
-
|
|
65
|
-
# From the loaded objects, get the description and add them to the object_list
|
|
66
|
-
object_list["description"] = [obj.description for obj in objects]
|
|
67
|
-
object_list["objects"] = objects
|
|
68
|
-
return object_list
|
|
60
|
+
return self._get_object_summary()
|
|
69
61
|
|
|
70
62
|
def copy(self, old_name: str, new_name: str, new_description: str):
|
|
71
63
|
"""Copy (duplicate) an existing object, and give it a new name.
|
|
@@ -155,7 +147,7 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
155
147
|
Raise error if name is already in use.
|
|
156
148
|
"""
|
|
157
149
|
# Check if the object exists
|
|
158
|
-
if object_model.name not in self.
|
|
150
|
+
if object_model.name not in self.summarize_objects()["name"]:
|
|
159
151
|
raise ValueError(
|
|
160
152
|
f"{self.display_name}: '{object_model.name}' does not exist. You cannot edit an {self.display_name.lower()} that does not exist."
|
|
161
153
|
)
|
|
@@ -241,13 +233,13 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
241
233
|
# level object. By default, return an empty list
|
|
242
234
|
return []
|
|
243
235
|
|
|
244
|
-
def
|
|
236
|
+
def _get_object_summary(self) -> dict[str, list[Any]]:
|
|
245
237
|
"""Get a dictionary with all the toml paths and last modification dates that exist in the database of the given object_type.
|
|
246
238
|
|
|
247
239
|
Returns
|
|
248
240
|
-------
|
|
249
241
|
dict[str, Any]
|
|
250
|
-
A dictionary that contains the keys: `name`
|
|
242
|
+
A dictionary that contains the keys: `name`, `description`, `path` and `last_modification_date`.
|
|
251
243
|
Each key has a list of the corresponding values, where the index of the values corresponds to the same object.
|
|
252
244
|
"""
|
|
253
245
|
# If the toml doesnt exist, we might be in the middle of saving a new object or could be a broken object.
|
|
@@ -258,19 +250,30 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
258
250
|
if (dir / f"{dir.name}.toml").is_file()
|
|
259
251
|
]
|
|
260
252
|
paths = [Path(dir / f"{dir.name}.toml") for dir in directories]
|
|
261
|
-
|
|
253
|
+
|
|
254
|
+
names = [self._read_variable_in_toml("name", path) for path in paths]
|
|
255
|
+
descriptions = [
|
|
256
|
+
self._read_variable_in_toml("description", path) for path in paths
|
|
257
|
+
]
|
|
258
|
+
|
|
262
259
|
last_modification_date = [
|
|
263
260
|
datetime.fromtimestamp(file.stat().st_mtime) for file in paths
|
|
264
261
|
]
|
|
265
262
|
|
|
266
263
|
objects = {
|
|
267
264
|
"name": names,
|
|
265
|
+
"description": descriptions,
|
|
268
266
|
"path": paths,
|
|
269
267
|
"last_modification_date": last_modification_date,
|
|
270
268
|
}
|
|
271
|
-
|
|
272
269
|
return objects
|
|
273
270
|
|
|
271
|
+
@staticmethod
|
|
272
|
+
def _read_variable_in_toml(variable_name: str, toml_path: Path) -> str:
|
|
273
|
+
with open(toml_path, "rb") as f:
|
|
274
|
+
data = tomli.load(f)
|
|
275
|
+
return data.get(variable_name, "")
|
|
276
|
+
|
|
274
277
|
def _validate_to_save(self, object_model: T_OBJECTMODEL, overwrite: bool) -> None:
|
|
275
278
|
"""Validate if the object can be saved.
|
|
276
279
|
|
|
@@ -285,11 +288,7 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
285
288
|
Raise error if name is already in use.
|
|
286
289
|
"""
|
|
287
290
|
# Check if the object exists
|
|
288
|
-
|
|
289
|
-
raise ValueError(
|
|
290
|
-
f"{self.display_name}: '{object_model.name}' already exists. Choose a different name."
|
|
291
|
-
)
|
|
292
|
-
object_exists = object_model.name in self.list_objects()["name"]
|
|
291
|
+
object_exists = object_model.name in self.summarize_objects()["name"]
|
|
293
292
|
|
|
294
293
|
# If you want to overwrite the object, and the object already exists, first delete it. If it exists and you
|
|
295
294
|
# don't want to overwrite, raise an error.
|
|
@@ -86,10 +86,6 @@ class IDatabase(ABC):
|
|
|
86
86
|
def run_benefit(self, benefit_name: Union[str, list[str]]) -> None:
|
|
87
87
|
pass
|
|
88
88
|
|
|
89
|
-
@abstractmethod
|
|
90
|
-
def update(self) -> None:
|
|
91
|
-
pass
|
|
92
|
-
|
|
93
89
|
@abstractmethod
|
|
94
90
|
def get_outputs(self) -> dict[str, Any]:
|
|
95
91
|
pass
|
|
@@ -138,10 +134,6 @@ class IDatabase(ABC):
|
|
|
138
134
|
def has_run_hazard(self, scenario_name: str) -> None:
|
|
139
135
|
pass
|
|
140
136
|
|
|
141
|
-
@abstractmethod
|
|
142
|
-
def run_scenario(self, scenario_name: Union[str, list[str]]) -> None:
|
|
143
|
-
pass
|
|
144
|
-
|
|
145
137
|
@abstractmethod
|
|
146
138
|
def cleanup(self) -> None:
|
|
147
139
|
pass
|
|
@@ -33,7 +33,7 @@ class AbstractDatabaseElement(ABC, Generic[T_OBJECT_MODEL]):
|
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
@abstractmethod
|
|
36
|
-
def
|
|
36
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
37
37
|
"""Return a dictionary with info on the objects that currently exist in the database.
|
|
38
38
|
|
|
39
39
|
Returns
|