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,193 @@
1
+ import shutil
2
+ from pathlib import Path
3
+
4
+ import pandas as pd
5
+
6
+ from flood_adapt.adapter.interface.offshore import IOffshoreSfincsHandler
7
+ from flood_adapt.adapter.sfincs_adapter import SfincsAdapter
8
+ from flood_adapt.misc.database_user import DatabaseUser
9
+ from flood_adapt.misc.log import FloodAdaptLogging
10
+ from flood_adapt.misc.path_builder import (
11
+ ObjectDir,
12
+ TopLevelDir,
13
+ db_path,
14
+ )
15
+ from flood_adapt.objects.events.event_set import EventSet
16
+ from flood_adapt.objects.events.events import Event, Mode
17
+ from flood_adapt.objects.events.historical import HistoricalEvent
18
+ from flood_adapt.objects.forcing.forcing import (
19
+ ForcingSource,
20
+ IWind,
21
+ )
22
+ from flood_adapt.objects.forcing.meteo_handler import MeteoHandler
23
+ from flood_adapt.objects.forcing.wind import WindMeteo
24
+ from flood_adapt.objects.scenarios.scenarios import Scenario
25
+
26
+
27
+ class OffshoreSfincsHandler(IOffshoreSfincsHandler, DatabaseUser):
28
+ logger = FloodAdaptLogging.getLogger("OffshoreSfincsAdapter")
29
+ template_path: Path
30
+
31
+ def __init__(self, scenario: Scenario, event: Event) -> None:
32
+ self.template_path = (
33
+ self.database.static.get_offshore_sfincs_model().get_model_root()
34
+ )
35
+ self.scenario = scenario
36
+ if isinstance(event, EventSet):
37
+ raise ValueError(
38
+ "OffshoreSfincsHandler does not support EventSets. Provide the sub events directly "
39
+ )
40
+ self.event = event
41
+
42
+ def get_resulting_waterlevels(self) -> pd.DataFrame:
43
+ """Get the water levels from the offshore model.
44
+
45
+ Note that the returned water levels are relative to the reference datum of the offshore model.
46
+ To convert to a different datum, add the offshore reference datum height and subtract the desired reference datum height.
47
+
48
+ Returns
49
+ -------
50
+ pd.DataFrame
51
+ A DataFrame with the water levels for each boundary condition point. Relative to the reference datum of the offshore model.
52
+
53
+ """
54
+ path = self._get_simulation_path()
55
+ if not self.requires_offshore_run(self.event):
56
+ raise ValueError("Offshore model is not required for this event")
57
+
58
+ self.run_offshore()
59
+
60
+ with SfincsAdapter(model_root=path) as offshore_model:
61
+ waterlevels = offshore_model.get_wl_df_from_offshore_his_results()
62
+
63
+ return waterlevels
64
+
65
+ @staticmethod
66
+ def requires_offshore_run(event: Event) -> bool:
67
+ return any(
68
+ forcing.source in [ForcingSource.MODEL, ForcingSource.TRACK]
69
+ for forcing in event.get_forcings()
70
+ )
71
+
72
+ def run_offshore(self):
73
+ """Prepare the forcings of the historical event.
74
+
75
+ If the forcings require it, this function will:
76
+ - preprocess and run offshore model: prepare and run the offshore model to obtain water levels for the boundary condition of the nearshore model.
77
+
78
+ """
79
+ sim_path = self._get_simulation_path()
80
+
81
+ sim_path.mkdir(parents=True, exist_ok=True)
82
+ self._preprocess_sfincs_offshore()
83
+ self._execute_sfincs_offshore(sim_path)
84
+
85
+ def _preprocess_sfincs_offshore(self):
86
+ """Preprocess offshore model to obtain water levels for boundary condition of the nearshore model.
87
+
88
+ This function is reused for ForcingSources: MODEL & TRACK.
89
+
90
+ Args:
91
+ sim_path path to the root of the offshore model
92
+ """
93
+ self.logger.info(
94
+ f"Preparing offshore model to generate waterlevels for `{self.scenario.name}`"
95
+ )
96
+ sim_path = self._get_simulation_path()
97
+ # SfincsAdapter.write() doesnt write the bca file apparently so we need to copy the template
98
+ if sim_path.exists():
99
+ shutil.rmtree(sim_path)
100
+ shutil.copytree(self.template_path, sim_path)
101
+
102
+ with SfincsAdapter(model_root=sim_path) as _offshore_model:
103
+ if _offshore_model.sfincs_completed(sim_path):
104
+ _offshore_model.logger.info(
105
+ f"Skip preprocessing offshore model as it has already been run for `{self.scenario.name}`."
106
+ )
107
+ return
108
+ # Load objects, set root & write template model
109
+ _offshore_model._load_scenario_objects(self.scenario, self.event)
110
+ _offshore_model.write(path_out=sim_path)
111
+ _offshore_model.set_timing(self.event.time)
112
+
113
+ # Add water levels
114
+ _offshore_model._add_bzs_from_bca(
115
+ _offshore_model._event, _offshore_model._projection.physical_projection
116
+ )
117
+
118
+ # Add spw if applicable
119
+ track_forcings = [
120
+ f
121
+ for f in _offshore_model._event.get_forcings()
122
+ if f.source == ForcingSource.TRACK
123
+ ]
124
+ if track_forcings:
125
+ for forcing in track_forcings:
126
+ _offshore_model.add_forcing(forcing)
127
+
128
+ # Add wind and if applicable pressure forcing from meteo data
129
+ elif isinstance(_offshore_model._event, HistoricalEvent):
130
+ wind_forcings = [
131
+ f
132
+ for f in _offshore_model._event.get_forcings()
133
+ if isinstance(f, IWind)
134
+ ]
135
+
136
+ if wind_forcings:
137
+ if len(wind_forcings) > 1:
138
+ raise ValueError("Only one wind forcing is allowed")
139
+ wind_forcing = wind_forcings[0]
140
+
141
+ # Add wind forcing
142
+ if wind_forcing not in track_forcings:
143
+ _offshore_model.add_forcing(wind_forcing)
144
+
145
+ # Add pressure forcing for the offshore model (this doesnt happen normally in _add_forcing_wind() for overland models)
146
+ if isinstance(wind_forcing, WindMeteo):
147
+ ds = MeteoHandler().read(_offshore_model._event.time)
148
+ _offshore_model._add_pressure_forcing_from_grid(ds=ds)
149
+
150
+ # write sfincs model in output destination
151
+ _offshore_model.write(path_out=sim_path)
152
+
153
+ def _execute_sfincs_offshore(self, sim_path: Path):
154
+ self.logger.info(f"Running offshore model in {sim_path}")
155
+ sim_path = self._get_simulation_path()
156
+ with SfincsAdapter(model_root=sim_path) as _offshore_model:
157
+ if _offshore_model.sfincs_completed(sim_path):
158
+ self.logger.info(
159
+ "Skip running offshore model as it has already been run."
160
+ )
161
+ return
162
+ try:
163
+ _offshore_model.execute(path=sim_path)
164
+ except RuntimeError as e:
165
+ raise RuntimeError(
166
+ f"Failed to run offshore model for {self.scenario.name}"
167
+ ) from e
168
+
169
+ def _get_simulation_path(self) -> Path:
170
+ main_event = self.database.events.get(self.scenario.event)
171
+ if main_event.mode == Mode.risk:
172
+ return (
173
+ db_path(
174
+ TopLevelDir.output,
175
+ object_dir=ObjectDir.scenario,
176
+ obj_name=self.scenario.name,
177
+ )
178
+ / "Flooding"
179
+ / "simulations"
180
+ / self.event.name
181
+ / self.template_path.name
182
+ )
183
+ else:
184
+ return (
185
+ db_path(
186
+ TopLevelDir.output,
187
+ object_dir=ObjectDir.scenario,
188
+ obj_name=self.scenario.name,
189
+ )
190
+ / "Flooding"
191
+ / "simulations"
192
+ / self.template_path.name
193
+ )
File without changes
@@ -0,0 +1,245 @@
1
+ from os import environ, listdir
2
+ from pathlib import Path
3
+ from platform import system
4
+ from typing import ClassVar
5
+
6
+ import tomli
7
+ import tomli_w
8
+ from pydantic import (
9
+ Field,
10
+ computed_field,
11
+ field_serializer,
12
+ model_validator,
13
+ )
14
+ from pydantic_settings import BaseSettings, SettingsConfigDict
15
+
16
+
17
+ class Settings(BaseSettings):
18
+ """
19
+ The configuration settings for the FloodAdapt database and integrator.
20
+
21
+ Precedence is as follows: user arguments > environment variables > defaults in this class.
22
+ When loading is done, the settings are validated and the environment variables are updated with the loaded values.
23
+
24
+ If any required settings are missing or invalid, a ValidationError is raised.
25
+
26
+ Usage
27
+ -----
28
+ from flood_adapt.config import Settings
29
+
30
+ One of the following:
31
+
32
+ 1) Load settings from environment variables, if no environment variables are set, use defaults defined in the class:
33
+ `settings = Settings()`
34
+
35
+ 2) Load settings from a .toml file, overwriting any environment variables set:
36
+ `settings = Settings.read(toml_path: Path)`
37
+
38
+ 3) Load settings from keyword arguments, overwriting any environment variables:
39
+ `settings = Settings(DATABASE_ROOT="path/to/database", DATABASE_NAME="database_name", SYSTEM_FOLDER="path/to/system_folder")`
40
+
41
+ Attributes
42
+ ----------
43
+ database_name : str
44
+ The name of the database.
45
+ database_root : Path
46
+ The root directory of the database.
47
+ system_folder : Path
48
+ The root directory of the system folder containing the kernels.
49
+ delete_crashed_runs : bool
50
+ Whether to delete crashed/corrupted runs immediately after they are detected.
51
+
52
+ Properties
53
+ ----------
54
+ database_path : Path
55
+ The full path to the database.
56
+ sfincs_path : Path
57
+ The path to the SFINCS binary.
58
+ fiat_path : Path
59
+ The path to the FIAT binary.
60
+
61
+ Raises
62
+ ------
63
+ ValidationError
64
+ If required settings are missing or invalid.
65
+ """
66
+
67
+ SYSTEM_SUFFIXES: ClassVar[dict[str, str]] = {
68
+ "Windows": ".exe",
69
+ "Linux": "",
70
+ "Darwin": "",
71
+ }
72
+
73
+ model_config = SettingsConfigDict(env_ignore_empty=True, validate_default=True)
74
+
75
+ database_root: Path = Field(
76
+ alias="DATABASE_ROOT", # environment variable DATABASE_ROOT
77
+ default=Path(__file__).parents[3]
78
+ / "Database", # If you clone FloodAdapt, default is to look for the Database next to the FloodAdapt folder
79
+ description="The root directory of the database that contains site(s). Usually the directory name is 'Database'. Default is to look for the Database in the same dir as the FloodAdapt cloned repo.",
80
+ )
81
+ database_name: str = Field(
82
+ alias="DATABASE_NAME", # environment variable DATABASE_NAME
83
+ default="",
84
+ description="The name of the database site, should be a folder inside the database root. The site must contain an 'input' and 'static' folder.",
85
+ )
86
+ system_folder: Path = Field(
87
+ alias="SYSTEM_FOLDER", # environment variable: SYSTEM_FOLDER
88
+ default=Path(__file__).parents[1] / "system",
89
+ description="The path of the system folder containing the kernels that run the calculations. Default is to look for the system folder in `FloodAdapt/flood_adapt/system`",
90
+ )
91
+ delete_crashed_runs: bool = Field(
92
+ alias="DELETE_CRASHED_RUNS", # environment variable: DELETE_CRASHED_RUNS
93
+ default=True,
94
+ description="Whether to delete the output of crashed/corrupted runs. Be careful when setting this to False, as it may lead to a broken database that cannot be read in anymore.",
95
+ exclude=True,
96
+ )
97
+ validate_allowed_forcings: bool = Field(
98
+ alias="VALIDATE_ALLOWED_FORCINGS", # environment variable: VALIDATE_ALLOWED_FORCINGS
99
+ default=True,
100
+ description="Whether to validate the forcing types and sources against the allowed forcings in the event model.",
101
+ exclude=True,
102
+ )
103
+
104
+ @computed_field
105
+ @property
106
+ def sfincs_path(self) -> Path:
107
+ return self.system_folder / "sfincs" / f"sfincs{Settings._system_extension()}"
108
+
109
+ @computed_field
110
+ @property
111
+ def fiat_path(self) -> Path:
112
+ return self.system_folder / "fiat" / f"fiat{Settings._system_extension()}"
113
+
114
+ @computed_field
115
+ @property
116
+ def database_path(self) -> Path:
117
+ return self.database_root / self.database_name
118
+
119
+ @model_validator(mode="after")
120
+ def validate_settings(self):
121
+ self._validate_database_path()
122
+ self._validate_system_folder()
123
+ self._validate_fiat_path()
124
+ self._validate_sfincs_path()
125
+ self._update_environment_variables()
126
+ return self
127
+
128
+ def _update_environment_variables(self):
129
+ environ["DATABASE_ROOT"] = str(self.database_root)
130
+ environ["DATABASE_NAME"] = self.database_name
131
+ environ["SYSTEM_FOLDER"] = str(self.system_folder)
132
+ environ["DELETE_CRASHED_RUNS"] = str(self.delete_crashed_runs)
133
+ environ["VALIDATE_ALLOWED_FORCINGS"] = str(self.validate_allowed_forcings)
134
+ return self
135
+
136
+ def _validate_database_path(self):
137
+ if not self.database_root.is_dir():
138
+ raise ValueError(f"Database root {self.database_root} does not exist.")
139
+
140
+ if self.database_name == "":
141
+ # If database_name is not given as arg or set in env, compute default as the first dir in database_root excluding 'system'
142
+ sites = [
143
+ d
144
+ for d in listdir(self.database_root)
145
+ if d != "system" and not d.startswith(".")
146
+ ]
147
+ if not sites:
148
+ raise ValueError(f"No databases found in {self.database_root}.")
149
+ self.database_name = sites[0]
150
+
151
+ if not self.database_path.is_dir():
152
+ raise ValueError(
153
+ f"Database {self.database_name} at {self.database_root} does not exist. Full path: {self.database_path}"
154
+ )
155
+
156
+ if not (self.database_path / "input").is_dir():
157
+ raise ValueError(
158
+ f"Database {self.database_name} at {self.database_path} does not contain an input folder."
159
+ )
160
+
161
+ if not (self.database_path / "static").is_dir():
162
+ raise ValueError(
163
+ f"Database {self.database_name} at {self.database_path} does not contain a static folder."
164
+ )
165
+
166
+ return self
167
+
168
+ def _validate_system_folder(self):
169
+ if not self.system_folder.is_dir():
170
+ raise ValueError(f"System folder {self.system_folder} does not exist.")
171
+ return self
172
+
173
+ def _validate_sfincs_path(self):
174
+ if not self.sfincs_path.exists():
175
+ raise ValueError(f"SFINCS binary {self.sfincs_path} does not exist.")
176
+ return self
177
+
178
+ def _validate_fiat_path(self):
179
+ if not self.fiat_path.exists():
180
+ raise ValueError(f"FIAT binary {self.fiat_path} does not exist.")
181
+ return self
182
+
183
+ @field_serializer(
184
+ "database_root", "system_folder", "sfincs_path", "fiat_path", "database_path"
185
+ )
186
+ def serialize_path(self, path: Path) -> str:
187
+ return str(path)
188
+
189
+ @staticmethod
190
+ def _system_extension() -> str:
191
+ if system() not in Settings.SYSTEM_SUFFIXES:
192
+ raise ValueError(f"Unsupported system {system()}")
193
+ return Settings.SYSTEM_SUFFIXES[system()]
194
+
195
+ @staticmethod
196
+ def read(toml_path: Path) -> "Settings":
197
+ """
198
+ Parse the configuration file and return the parsed settings.
199
+
200
+ Parameters
201
+ ----------
202
+ toml_path : Path
203
+ The path to the configuration file.
204
+
205
+ Returns
206
+ -------
207
+ Settings
208
+ The parsed configuration settings.
209
+
210
+ Raises
211
+ ------
212
+ ValidationError
213
+ If required configuration values are missing or if there is an error parsing the configuration file.
214
+ """
215
+ with open(toml_path, "rb") as f:
216
+ settings = tomli.load(f)
217
+
218
+ return Settings(**settings)
219
+
220
+ def write(self, toml_path: Path) -> None:
221
+ """
222
+ Write the configuration settings to a .toml file.
223
+
224
+ Parameters
225
+ ----------
226
+ toml_path : Path
227
+ The path to the configuration file.
228
+
229
+ Returns
230
+ -------
231
+ None
232
+
233
+ """
234
+ toml_path = Path(toml_path).resolve()
235
+ if not toml_path.parent.exists():
236
+ toml_path.parent.mkdir(parents=True, exist_ok=True)
237
+
238
+ with open(toml_path, "wb") as f:
239
+ tomli_w.dump(
240
+ self.model_dump(
241
+ by_alias=True,
242
+ exclude={"sfincs_path", "fiat_path", "database_path"},
243
+ ),
244
+ f,
245
+ )
@@ -0,0 +1,219 @@
1
+ from enum import Enum
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from pydantic import BaseModel
6
+ from tomli import load as load_toml
7
+
8
+ from flood_adapt.config.sfincs import FloodmapType
9
+
10
+
11
+ class BenefitsModel(BaseModel):
12
+ """The accepted input for the variable benefits in Site.
13
+
14
+ Attributes
15
+ ----------
16
+ current_year : int
17
+ The current year used in benefits calculations.
18
+ current_projection : str
19
+ The current projection used in benefits calculations.
20
+ baseline_strategy : str
21
+ The baseline strategy used in benefits calculations.
22
+ event_set : str
23
+ The event set used in benefits calculations.
24
+ """
25
+
26
+ current_year: int
27
+ current_projection: str
28
+ baseline_strategy: str
29
+ event_set: str
30
+
31
+
32
+ class EquityModel(BaseModel):
33
+ """
34
+ The accepted input for the variable equity in Site.
35
+
36
+ Attributes
37
+ ----------
38
+ census_data : str
39
+ TODO
40
+ percapitaincome_label : Optional[str], default="PerCapitaIncome"
41
+ TODO
42
+ totalpopulation_label : Optional[str], default="TotalPopulation"
43
+ TODO
44
+ """
45
+
46
+ census_data: str
47
+ percapitaincome_label: Optional[str] = "PerCapitaIncome"
48
+ totalpopulation_label: Optional[str] = "TotalPopulation"
49
+
50
+
51
+ class AggregationModel(BaseModel):
52
+ """The accepted input for the variable aggregation in Site.
53
+
54
+ Attributes
55
+ ----------
56
+ name : str
57
+ TODO
58
+ file : str
59
+ TODO
60
+ field_name : str
61
+ TODO
62
+ equity : Optional[EquityModel], default=None
63
+ TODO
64
+ """
65
+
66
+ name: str
67
+ file: str
68
+ field_name: str
69
+ equity: Optional[EquityModel] = None
70
+
71
+
72
+ class BFEModel(BaseModel):
73
+ """The accepted input for the variable bfe in Site.
74
+
75
+ Attributes
76
+ ----------
77
+ geom : str
78
+ TODO
79
+ table : Optional[str], default=None
80
+ TODO
81
+ field_name : str
82
+ TODO
83
+ """
84
+
85
+ geom: str
86
+ table: Optional[str] = None
87
+ field_name: str
88
+
89
+
90
+ class SVIModel(BaseModel):
91
+ """The accepted input for the variable svi in Site.
92
+
93
+ Attributes
94
+ ----------
95
+ geom : str
96
+ TODO
97
+ field_name : str
98
+ TODO
99
+ """
100
+
101
+ geom: str
102
+ field_name: str
103
+
104
+
105
+ class NoFootprintsModel(BaseModel):
106
+ """
107
+ The configuration on the how to show objects with no footprints.
108
+
109
+ Attributes
110
+ ----------
111
+ shape : Optional[str], default="triangle"
112
+ The shape of the object with no footprints.
113
+ diameter_meters : Optional[float], default=10
114
+ The diameter of the object with no footprints in meters.
115
+ """
116
+
117
+ shape: Optional[str] = "triangle"
118
+ diameter_meters: Optional[float] = 10
119
+
120
+
121
+ class RiskModel(BaseModel):
122
+ """The accepted input for the variable risk in Site.
123
+
124
+ Attributes
125
+ ----------
126
+ return_periods : list[int]
127
+ The return periods for the risk model.
128
+ """
129
+
130
+ return_periods: list = [1, 2, 5, 10, 25, 50, 100]
131
+
132
+
133
+ class DamageType(str, Enum):
134
+ """The accepted input for the variable footprints_dmg_type."""
135
+
136
+ absolute = "absolute"
137
+ relative = "relative"
138
+
139
+
140
+ class FiatConfigModel(BaseModel):
141
+ """Configuration settings for the FIAT model.
142
+
143
+ Attributes
144
+ ----------
145
+ exposure_crs : str
146
+ The coordinate reference system of the exposure data.
147
+ bfe : Optional[BFEModel], default=None
148
+ The base flood elevation model.
149
+ aggregation : list[AggregationModel]
150
+ Configuration for the aggregation model.
151
+ floodmap_type : FloodmapType
152
+ The type of flood map to be used.
153
+ non_building_names : Optional[list[str]], default=None
154
+ List of non-building names to be used in the model.
155
+ damage_unit : str, default="$"
156
+ The unit of damage used in the model.
157
+ building_footprints : Optional[str], default=None
158
+ Path to the building footprints data.
159
+ roads_file_name : Optional[str], default=None
160
+ Path to the roads data.
161
+ new_development_file_name : Optional[str], default="new_development_area.gpkg"
162
+ Path to the new development area data.
163
+ save_simulation : Optional[bool], default=False
164
+ Whether to keep or delete the simulation files after the simulation is finished and all output files are created.
165
+ If True, the simulation files are kept. If False, the simulation files are deleted.
166
+ svi : Optional[SVIModel], default=None
167
+ The social vulnerability index model.
168
+ infographics : Optional[bool], default=False
169
+ Whether to create infographics or not.
170
+ no_footprints : Optional[NoFootprintsModel], default=NoFootprintsModel()
171
+ Configuration for objects with no footprints.
172
+ """
173
+
174
+ exposure_crs: str
175
+ bfe: Optional[BFEModel] = None
176
+ aggregation: list[AggregationModel]
177
+ floodmap_type: FloodmapType
178
+ non_building_names: Optional[list[str]]
179
+ damage_unit: str = "$"
180
+ building_footprints: Optional[str] = None
181
+ roads_file_name: Optional[str] = None
182
+ new_development_file_name: Optional[str] = "new_development_area.gpkg"
183
+ save_simulation: Optional[bool] = False
184
+ svi: Optional[SVIModel] = None
185
+ infographics: Optional[bool] = False
186
+ no_footprints: Optional[NoFootprintsModel] = NoFootprintsModel()
187
+
188
+ @staticmethod
189
+ def read_toml(path: Path) -> "FiatConfigModel":
190
+ with open(path, mode="rb") as fp:
191
+ toml_contents = load_toml(fp)
192
+
193
+ return FiatConfigModel(**toml_contents)
194
+
195
+
196
+ class FiatModel(BaseModel):
197
+ """The expected variables and data types of attributes of the Fiat class.
198
+
199
+ Attributes
200
+ ----------
201
+ risk : RiskModel
202
+ Configuration of probabilistic risk runs.
203
+ config : FiatConfigModel
204
+ Configuration for the FIAT model.
205
+ benefits : BenefitsModel
206
+ Configuration for running benefit calculations.
207
+ """
208
+
209
+ risk: RiskModel
210
+
211
+ config: FiatConfigModel
212
+ benefits: BenefitsModel
213
+
214
+ @staticmethod
215
+ def read_toml(path: Path) -> "FiatModel":
216
+ with open(path, mode="rb") as fp:
217
+ toml_contents = load_toml(fp)
218
+
219
+ return FiatModel(**toml_contents)