flood-adapt 0.3.8__py3-none-any.whl → 0.3.10__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 (100) hide show
  1. flood_adapt/__init__.py +26 -22
  2. flood_adapt/adapter/__init__.py +9 -9
  3. flood_adapt/adapter/fiat_adapter.py +1541 -1536
  4. flood_adapt/adapter/interface/hazard_adapter.py +70 -70
  5. flood_adapt/adapter/interface/impact_adapter.py +36 -36
  6. flood_adapt/adapter/interface/model_adapter.py +89 -89
  7. flood_adapt/adapter/interface/offshore.py +19 -19
  8. flood_adapt/adapter/sfincs_adapter.py +1848 -1846
  9. flood_adapt/adapter/sfincs_offshore.py +193 -193
  10. flood_adapt/config/config.py +248 -290
  11. flood_adapt/config/fiat.py +219 -219
  12. flood_adapt/config/gui.py +331 -331
  13. flood_adapt/config/sfincs.py +481 -336
  14. flood_adapt/config/site.py +129 -129
  15. flood_adapt/database_builder/database_builder.py +2210 -2210
  16. flood_adapt/database_builder/templates/default_units/imperial.toml +9 -9
  17. flood_adapt/database_builder/templates/default_units/metric.toml +9 -9
  18. flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +10 -10
  19. flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +90 -90
  20. flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +57 -57
  21. flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +121 -121
  22. flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +65 -65
  23. flood_adapt/database_builder/templates/infographics/OSM/styles.css +45 -45
  24. flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +126 -126
  25. flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +60 -60
  26. flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +121 -121
  27. flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +65 -65
  28. flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +45 -45
  29. flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +4 -4
  30. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +143 -143
  31. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +153 -153
  32. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +127 -127
  33. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +57 -57
  34. flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +4 -4
  35. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +191 -191
  36. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +153 -153
  37. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +178 -178
  38. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +57 -57
  39. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +9 -9
  40. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +65 -65
  41. flood_adapt/database_builder/templates/output_layers/bin_colors.toml +5 -5
  42. flood_adapt/database_builder.py +16 -16
  43. flood_adapt/dbs_classes/__init__.py +21 -21
  44. flood_adapt/dbs_classes/database.py +495 -688
  45. flood_adapt/dbs_classes/dbs_benefit.py +77 -76
  46. flood_adapt/dbs_classes/dbs_event.py +61 -59
  47. flood_adapt/dbs_classes/dbs_measure.py +112 -111
  48. flood_adapt/dbs_classes/dbs_projection.py +34 -34
  49. flood_adapt/dbs_classes/dbs_scenario.py +137 -137
  50. flood_adapt/dbs_classes/dbs_static.py +274 -273
  51. flood_adapt/dbs_classes/dbs_strategy.py +130 -129
  52. flood_adapt/dbs_classes/dbs_template.py +279 -278
  53. flood_adapt/dbs_classes/interface/database.py +107 -139
  54. flood_adapt/dbs_classes/interface/element.py +121 -121
  55. flood_adapt/dbs_classes/interface/static.py +47 -47
  56. flood_adapt/flood_adapt.py +1207 -1178
  57. flood_adapt/misc/database_user.py +16 -16
  58. flood_adapt/misc/exceptions.py +22 -0
  59. flood_adapt/misc/log.py +183 -183
  60. flood_adapt/misc/path_builder.py +54 -54
  61. flood_adapt/misc/utils.py +185 -185
  62. flood_adapt/objects/__init__.py +82 -82
  63. flood_adapt/objects/benefits/benefits.py +61 -61
  64. flood_adapt/objects/events/event_factory.py +135 -135
  65. flood_adapt/objects/events/event_set.py +88 -84
  66. flood_adapt/objects/events/events.py +234 -234
  67. flood_adapt/objects/events/historical.py +58 -58
  68. flood_adapt/objects/events/hurricane.py +68 -67
  69. flood_adapt/objects/events/synthetic.py +46 -50
  70. flood_adapt/objects/forcing/__init__.py +92 -92
  71. flood_adapt/objects/forcing/csv.py +68 -68
  72. flood_adapt/objects/forcing/discharge.py +66 -66
  73. flood_adapt/objects/forcing/forcing.py +150 -150
  74. flood_adapt/objects/forcing/forcing_factory.py +182 -182
  75. flood_adapt/objects/forcing/meteo_handler.py +93 -93
  76. flood_adapt/objects/forcing/netcdf.py +40 -40
  77. flood_adapt/objects/forcing/plotting.py +453 -429
  78. flood_adapt/objects/forcing/rainfall.py +98 -98
  79. flood_adapt/objects/forcing/tide_gauge.py +191 -191
  80. flood_adapt/objects/forcing/time_frame.py +90 -90
  81. flood_adapt/objects/forcing/timeseries.py +564 -564
  82. flood_adapt/objects/forcing/unit_system.py +580 -580
  83. flood_adapt/objects/forcing/waterlevels.py +108 -108
  84. flood_adapt/objects/forcing/wind.py +124 -124
  85. flood_adapt/objects/measures/measure_factory.py +92 -92
  86. flood_adapt/objects/measures/measures.py +529 -529
  87. flood_adapt/objects/object_model.py +74 -68
  88. flood_adapt/objects/projections/projections.py +103 -89
  89. flood_adapt/objects/scenarios/scenarios.py +22 -22
  90. flood_adapt/objects/strategies/strategies.py +89 -89
  91. flood_adapt/workflows/benefit_runner.py +579 -544
  92. flood_adapt/workflows/floodmap.py +85 -85
  93. flood_adapt/workflows/impacts_integrator.py +85 -82
  94. flood_adapt/workflows/scenario_runner.py +70 -70
  95. {flood_adapt-0.3.8.dist-info → flood_adapt-0.3.10.dist-info}/LICENSE +674 -674
  96. {flood_adapt-0.3.8.dist-info → flood_adapt-0.3.10.dist-info}/METADATA +866 -860
  97. flood_adapt-0.3.10.dist-info/RECORD +140 -0
  98. flood_adapt-0.3.8.dist-info/RECORD +0 -139
  99. {flood_adapt-0.3.8.dist-info → flood_adapt-0.3.10.dist-info}/WHEEL +0 -0
  100. {flood_adapt-0.3.8.dist-info → flood_adapt-0.3.10.dist-info}/top_level.txt +0 -0
@@ -1,336 +1,481 @@
1
- import math
2
- from enum import Enum
3
- from pathlib import Path
4
- from typing import Optional, Union
5
-
6
- from pydantic import AfterValidator, BaseModel, Field, model_validator
7
- from tomli import load as load_toml
8
- from typing_extensions import Annotated
9
-
10
- from flood_adapt.objects.forcing import unit_system as us
11
- from flood_adapt.objects.forcing.tide_gauge import TideGauge
12
- from flood_adapt.objects.forcing.timeseries import Scstype
13
-
14
-
15
- def ensure_ascii(s: str):
16
- assert s.isascii()
17
- return s
18
-
19
-
20
- AsciiStr = Annotated[str, AfterValidator(ensure_ascii)]
21
-
22
-
23
- class Cstype(str, Enum):
24
- """The accepted input for the variable cstype in Site."""
25
-
26
- projected = "projected"
27
- spherical = "spherical"
28
-
29
-
30
- class SCSModel(BaseModel):
31
- """Class describing the accepted input for the variable scs.
32
-
33
- Includes the file with the non-dimensional SCS rainfall curves in the site folder and the SCS rainfall curve type.
34
-
35
- Attributes
36
- ----------
37
- file : str
38
- The path to the SCS rainfall curves file.
39
- type : Scstype
40
- The type of the SCS rainfall curve.
41
- """
42
-
43
- file: str
44
- type: Scstype
45
-
46
-
47
- class RiverModel(BaseModel):
48
- """Model that describes the accepted input for the variable river in Site.
49
-
50
- Attributes
51
- ----------
52
- name : str
53
- The name of the river.
54
- description : Optional[str], default=None
55
- description of the river.
56
- mean_discharge : us.UnitfulDischarge
57
- The mean discharge of the river.
58
- x_coordinate : float
59
- The x coordinate of the river.
60
- y_coordinate : float
61
- The y coordinate of the river.
62
- """
63
-
64
- name: str
65
- description: Optional[str] = None
66
- mean_discharge: us.UnitfulDischarge
67
- x_coordinate: float
68
- y_coordinate: float
69
-
70
-
71
- class ObsPointModel(BaseModel):
72
- """The accepted input for the variable obs_point in Site.
73
-
74
- obs_points is used to define output locations in the hazard model, which will be plotted in the user interface.
75
-
76
- Attributes
77
- ----------
78
- name : Union[int, AsciiStr]
79
- The name of the observation point.
80
- description : Optional[str], default=""
81
- The description of the observation point.
82
- ID : Optional[int], default=None
83
- The ID of the observation point.
84
- file : Optional[str], default=None
85
- The path to the observation point data file.
86
- """
87
-
88
- name: Union[int, AsciiStr]
89
- description: Optional[str] = ""
90
- ID: Optional[int] = (
91
- None # if the observation station is also a tide gauge, this ID should be the same as for obs_station
92
- )
93
- file: Optional[str] = None # for locally stored data
94
- lat: float
95
- lon: float
96
-
97
-
98
- class FloodFrequencyModel(BaseModel):
99
- """The accepted input for the variable flood_frequency in Site."""
100
-
101
- flooding_threshold: us.UnitfulLength
102
-
103
-
104
- class DemModel(BaseModel):
105
- """The accepted input for the variable dem in Site.
106
-
107
- Attributes
108
- ----------
109
- filename : str
110
- The path to the digital elevation model file.
111
- units : us.UnitTypesLength
112
- The units of the digital elevation model file.
113
- """
114
-
115
- filename: str
116
- units: us.UnitTypesLength
117
-
118
-
119
- class FloodmapType(str, Enum):
120
- """The accepted input for the variable floodmap in Site."""
121
-
122
- water_level = "water_level"
123
- water_depth = "water_depth"
124
-
125
-
126
- class DatumModel(BaseModel):
127
- """
128
- The accepted input for the variable datums in WaterlevelReferenceModel.
129
-
130
- Attributes
131
- ----------
132
- name : str
133
- The name of the vertical reference model.
134
- height : us.UnitfulLength
135
- The height of the vertical reference model relative to the main reference.
136
- """
137
-
138
- name: str
139
- height: us.UnitfulLength
140
-
141
-
142
- class WaterlevelReferenceModel(BaseModel):
143
- """The accepted input for the variable water_level in Site.
144
-
145
- Waterlevels timeseries are calculated from user input, assumed to be relative to the `reference` vertical reference model.
146
-
147
- For plotting in the GUI, the `reference` vertical reference model is used as the main zero-reference, all values are relative to this.
148
- All other vertical reference models are plotted as dashed lines.
149
-
150
- Attributes
151
- ----------
152
- reference : str
153
- The name of the vertical reference model that is used as the main zero-reference.
154
- datums : list[DatumModel]
155
- The vertical reference models that are used to calculate the waterlevels timeseries.
156
- The datums are used to calculate the waterlevels timeseries, which are relative to the `reference` vertical reference model.
157
- """
158
-
159
- reference: str
160
- datums: list[DatumModel] = Field(default_factory=list)
161
-
162
- def get_datum(self, name: str) -> DatumModel:
163
- for datum in self.datums:
164
- if datum.name == name:
165
- return datum
166
- raise ValueError(f"Could not find datum with name {name}")
167
-
168
- @model_validator(mode="after")
169
- def main_reference_should_be_in_datums_and_eq_zero(self):
170
- if self.reference not in [datum.name for datum in self.datums]:
171
- raise ValueError(f"Reference {self.reference} not in {self.datums}")
172
- if not math.isclose(
173
- self.get_datum(self.reference).height.value, 0, abs_tol=1e-6
174
- ):
175
- raise ValueError(f"Reference {self.reference} height is not zero")
176
- return self
177
-
178
- @model_validator(mode="after")
179
- def all_datums_should_have_unique_names(self):
180
- datum_names = [datum.name for datum in self.datums]
181
- if len(set(datum_names)) != len(datum_names):
182
- raise ValueError(f"Duplicate datum names found: {datum_names}")
183
- return self
184
-
185
-
186
- class CycloneTrackDatabaseModel(BaseModel):
187
- """The accepted input for the variable cyclone_track_database in Site.
188
-
189
- Attributes
190
- ----------
191
- file : str
192
- The path to the cyclone track database file.
193
- """
194
-
195
- file: str
196
-
197
-
198
- class SlrScenariosModel(BaseModel):
199
- """The accepted input for the variable slr_scenarios.
200
-
201
- Attributes
202
- ----------
203
- file : str
204
- The path to the sea level rise scenarios file.
205
- relative_to_year : int
206
- The year to which the sea level rise scenarios are relative.
207
- """
208
-
209
- file: str
210
- relative_to_year: int
211
-
212
-
213
- class FloodModel(BaseModel):
214
- """The accepted input for the variable overland_model and offshore_model in Site.
215
-
216
- Attributes
217
- ----------
218
- name : str
219
- The name of the directory in `static/templates/<directory>` that contains the template model files.
220
- reference : str
221
- The name of the vertical reference model that is used as the reference datum. Should be defined in water_level.datums.
222
- vertical_offset : Optional[us.UnitfulLength], default = None
223
- The vertical offset of the vertical reference model relative to the main reference.
224
- Given that the height of the vertical reference model is often determined by external sources,
225
- this vertical offset can be used to correct systematic over-/underestimation of a vertical reference model.
226
- """
227
-
228
- name: str
229
- reference: str
230
-
231
- # this used to be water_level_offset from events
232
- vertical_offset: Optional[us.UnitfulLength] = None
233
-
234
-
235
- class SfincsConfigModel(BaseModel):
236
- """The expected variables and data types of attributes of the SfincsConfig class.
237
-
238
- Attributes
239
- ----------
240
- csname : str
241
- The name of the CS model.
242
- cstype : Cstype
243
- Cstype of the CS model. must be either "projected" or "spherical".
244
- version : Optional[str], default = None
245
- The version of the CS model. If None, the version is not specified.
246
- offshore_model : Optional[FloodModel], default = None
247
- The offshore model. If None, the offshore model is not specified.
248
- overland_model : FloodModel
249
- The overland model. This is the main model used for the simulation.
250
- floodmap_units : us.UnitTypesLength
251
- The units used for the output floodmap. Sfincs always produces in metric units, this is used to convert the floodmap to the correct units.
252
- save_simulation : Optional[bool], default = False
253
- Whether to keep or delete the simulation files after the simulation is finished and all output files are created.
254
- If True, the simulation files are kept. If False, the simulation files are deleted.
255
- """
256
-
257
- csname: str
258
- cstype: Cstype
259
- version: Optional[str] = None
260
- offshore_model: Optional[FloodModel] = None
261
- overland_model: FloodModel
262
- floodmap_units: us.UnitTypesLength
263
- save_simulation: Optional[bool] = False
264
-
265
-
266
- class SfincsModel(BaseModel):
267
- """The expected variables and data types of attributes of the Sfincs class.
268
-
269
- Attributes
270
- ----------
271
- config : SfincsConfigModel
272
- The configuration of the Sfincs model.
273
- water_level : WaterlevelReferenceModel
274
- The collection of all datums and the main reference datum.
275
- dem : DemModel
276
- The digital elevation model.
277
- flood_frequency : FloodFrequencyModel, default = FloodFrequencyModel()
278
- The flood frequency model.
279
- slr : SlrScenariosModel
280
- Specification of the sea level rise scenarios.
281
- cyclone_track_database : CycloneTrackDatabaseModel, optional, default = None
282
- The cyclone track database model.
283
- scs : SCSModel, optional, default = None
284
- The SCS model.
285
- tide_gauge : TideGauge, optional, default = None
286
- The tide gauge model.
287
- river : list[RiverModel], optional, default = None
288
- The river model.
289
- obs_point : list[ObsPointModel], optional, default = None
290
- The observation point model.
291
- """
292
-
293
- config: SfincsConfigModel
294
- water_level: WaterlevelReferenceModel
295
- cyclone_track_database: Optional[CycloneTrackDatabaseModel] = None
296
- slr_scenarios: Optional[SlrScenariosModel] = None
297
- scs: Optional[SCSModel] = None # optional for the US to use SCS rainfall curves
298
- dem: DemModel
299
-
300
- flood_frequency: FloodFrequencyModel = FloodFrequencyModel(
301
- flooding_threshold=us.UnitfulLength(value=0.0, units=us.UnitTypesLength.meters)
302
- ) # TODO we dont actually use this anywhere?
303
-
304
- tide_gauge: Optional[TideGauge] = None
305
- river: Optional[list[RiverModel]] = None
306
- obs_point: Optional[list[ObsPointModel]] = None
307
-
308
- @staticmethod
309
- def read_toml(path: Path) -> "SfincsModel":
310
- with open(path, mode="rb") as fp:
311
- toml_contents = load_toml(fp)
312
-
313
- return SfincsModel(**toml_contents)
314
-
315
- @model_validator(mode="after")
316
- def ensure_references_exist(self):
317
- datum_names = [d.name for d in self.water_level.datums]
318
-
319
- if self.config.overland_model.reference not in datum_names:
320
- raise ValueError(
321
- f"Could not find reference `{self.config.overland_model.reference}` in available datums: {datum_names}."
322
- )
323
-
324
- if self.config.offshore_model is not None:
325
- if self.config.offshore_model.reference not in datum_names:
326
- raise ValueError(
327
- f"Could not find reference `{self.config.offshore_model.reference}` in available datums: {datum_names}."
328
- )
329
-
330
- if self.tide_gauge is not None:
331
- if self.tide_gauge.reference not in datum_names:
332
- raise ValueError(
333
- f"Could not find reference `{self.tide_gauge.reference}` in available datums: {datum_names}."
334
- )
335
-
336
- return self
1
+ import math
2
+ from enum import Enum
3
+ from pathlib import Path
4
+ from typing import Optional, Union
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ from plotly.express import line
9
+ from plotly.express.colors import sample_colorscale
10
+ from pydantic import AfterValidator, BaseModel, Field, model_validator
11
+ from tomli import load as load_toml
12
+ from typing_extensions import Annotated
13
+
14
+ from flood_adapt.objects.forcing import unit_system as us
15
+ from flood_adapt.objects.forcing.tide_gauge import TideGauge
16
+ from flood_adapt.objects.forcing.timeseries import Scstype
17
+
18
+
19
+ def ensure_ascii(s: str):
20
+ assert s.isascii()
21
+ return s
22
+
23
+
24
+ AsciiStr = Annotated[str, AfterValidator(ensure_ascii)]
25
+
26
+
27
+ class Cstype(str, Enum):
28
+ """The accepted input for the variable cstype in Site."""
29
+
30
+ projected = "projected"
31
+ spherical = "spherical"
32
+
33
+
34
+ class SCSModel(BaseModel):
35
+ """Class describing the accepted input for the variable scs.
36
+
37
+ Includes the file with the non-dimensional SCS rainfall curves in the site folder and the SCS rainfall curve type.
38
+
39
+ Attributes
40
+ ----------
41
+ file : str
42
+ The path to the SCS rainfall curves file.
43
+ type : Scstype
44
+ The type of the SCS rainfall curve.
45
+ """
46
+
47
+ file: str
48
+ type: Scstype
49
+
50
+
51
+ class RiverModel(BaseModel):
52
+ """Model that describes the accepted input for the variable river in Site.
53
+
54
+ Attributes
55
+ ----------
56
+ name : str
57
+ The name of the river.
58
+ description : Optional[str], default=None
59
+ description of the river.
60
+ mean_discharge : us.UnitfulDischarge
61
+ The mean discharge of the river.
62
+ x_coordinate : float
63
+ The x coordinate of the river.
64
+ y_coordinate : float
65
+ The y coordinate of the river.
66
+ """
67
+
68
+ name: str
69
+ description: Optional[str] = None
70
+ mean_discharge: us.UnitfulDischarge
71
+ x_coordinate: float
72
+ y_coordinate: float
73
+
74
+
75
+ class ObsPointModel(BaseModel):
76
+ """The accepted input for the variable obs_point in Site.
77
+
78
+ obs_points is used to define output locations in the hazard model, which will be plotted in the user interface.
79
+
80
+ Attributes
81
+ ----------
82
+ name : Union[int, AsciiStr]
83
+ The name of the observation point.
84
+ description : Optional[str], default=""
85
+ The description of the observation point.
86
+ ID : Optional[int], default=None
87
+ The ID of the observation point.
88
+ file : Optional[str], default=None
89
+ The path to the observation point data file.
90
+ """
91
+
92
+ name: Union[int, AsciiStr]
93
+ description: Optional[str] = ""
94
+ ID: Optional[int] = (
95
+ None # if the observation station is also a tide gauge, this ID should be the same as for obs_station
96
+ )
97
+ file: Optional[str] = None # for locally stored data
98
+ lat: float
99
+ lon: float
100
+
101
+
102
+ class FloodFrequencyModel(BaseModel):
103
+ """The accepted input for the variable flood_frequency in Site."""
104
+
105
+ flooding_threshold: us.UnitfulLength
106
+
107
+
108
+ class DemModel(BaseModel):
109
+ """The accepted input for the variable dem in Site.
110
+
111
+ Attributes
112
+ ----------
113
+ filename : str
114
+ The path to the digital elevation model file.
115
+ units : us.UnitTypesLength
116
+ The units of the digital elevation model file.
117
+ """
118
+
119
+ filename: str
120
+ units: us.UnitTypesLength
121
+
122
+
123
+ class FloodmapType(str, Enum):
124
+ """The accepted input for the variable floodmap in Site."""
125
+
126
+ water_level = "water_level"
127
+ water_depth = "water_depth"
128
+
129
+
130
+ class DatumModel(BaseModel):
131
+ """
132
+ The accepted input for the variable datums in WaterlevelReferenceModel.
133
+
134
+ Attributes
135
+ ----------
136
+ name : str
137
+ The name of the vertical reference model.
138
+ height : us.UnitfulLength
139
+ The height of the vertical reference model relative to the main reference.
140
+ """
141
+
142
+ name: str
143
+ height: us.UnitfulLength
144
+
145
+
146
+ class WaterlevelReferenceModel(BaseModel):
147
+ """The accepted input for the variable water_level in Site.
148
+
149
+ Waterlevels timeseries are calculated from user input, assumed to be relative to the `reference` vertical reference model.
150
+
151
+ For plotting in the GUI, the `reference` vertical reference model is used as the main zero-reference, all values are relative to this.
152
+ All other vertical reference models are plotted as dashed lines.
153
+
154
+ Attributes
155
+ ----------
156
+ reference : str
157
+ The name of the vertical reference model that is used as the main zero-reference.
158
+ datums : list[DatumModel]
159
+ The vertical reference models that are used to calculate the waterlevels timeseries.
160
+ The datums are used to calculate the waterlevels timeseries, which are relative to the `reference` vertical reference model.
161
+ """
162
+
163
+ reference: str
164
+ datums: list[DatumModel] = Field(default_factory=list)
165
+
166
+ def get_datum(self, name: str) -> DatumModel:
167
+ for datum in self.datums:
168
+ if datum.name == name:
169
+ return datum
170
+ raise ValueError(f"Could not find datum with name {name}")
171
+
172
+ @model_validator(mode="after")
173
+ def main_reference_should_be_in_datums_and_eq_zero(self):
174
+ if self.reference not in [datum.name for datum in self.datums]:
175
+ raise ValueError(f"Reference {self.reference} not in {self.datums}")
176
+ if not math.isclose(
177
+ self.get_datum(self.reference).height.value, 0, abs_tol=1e-6
178
+ ):
179
+ raise ValueError(f"Reference {self.reference} height is not zero")
180
+ return self
181
+
182
+ @model_validator(mode="after")
183
+ def all_datums_should_have_unique_names(self):
184
+ datum_names = [datum.name for datum in self.datums]
185
+ if len(set(datum_names)) != len(datum_names):
186
+ raise ValueError(f"Duplicate datum names found: {datum_names}")
187
+ return self
188
+
189
+
190
+ class CycloneTrackDatabaseModel(BaseModel):
191
+ """The accepted input for the variable cyclone_track_database in Site.
192
+
193
+ Attributes
194
+ ----------
195
+ file : str
196
+ The path to the cyclone track database file.
197
+ """
198
+
199
+ file: str
200
+
201
+
202
+ class SlrScenariosModel(BaseModel):
203
+ """The accepted input for the variable slr_scenarios.
204
+
205
+ Attributes
206
+ ----------
207
+ file : str
208
+ The path to the sea level rise scenarios file.
209
+ relative_to_year : int
210
+ The year to which the sea level rise scenarios are relative.
211
+ """
212
+
213
+ file: str
214
+ relative_to_year: int
215
+
216
+ def interp_slr(
217
+ self,
218
+ scenario: str,
219
+ year: float,
220
+ units: us.UnitTypesLength = us.UnitTypesLength.meters,
221
+ ) -> float:
222
+ """Interpolate SLR value and reference it to the SLR reference year from the site toml.
223
+
224
+ Parameters
225
+ ----------
226
+ csv_path : Path
227
+ Path to the slr.csv file containing the SLR scenarios.
228
+ scenario : str
229
+ SLR scenario name to use from the column names in self.file
230
+ year : float
231
+ year to evaluate
232
+ units : us.UnitTypesLength, default = us.UnitTypesLength.meters
233
+ The units to convert the SLR value to. Default is meters.
234
+
235
+ Returns
236
+ -------
237
+ float
238
+ The interpolated sea level rise value in the specified units, relative to the reference year.
239
+
240
+ Raises
241
+ ------
242
+ ValueError
243
+ if the reference year is outside of the time range in the slr.csv file
244
+ ValueError
245
+ if the year to evaluate is outside of the time range in the slr.csv file
246
+ """
247
+ df = pd.read_csv(self.file)
248
+ if year > df["year"].max() or year < df["year"].min():
249
+ raise ValueError(
250
+ "The selected year is outside the range of the available SLR scenarios"
251
+ )
252
+
253
+ if (
254
+ self.relative_to_year > df["year"].max()
255
+ or self.relative_to_year < df["year"].min()
256
+ ):
257
+ raise ValueError(
258
+ f"The reference year {self.relative_to_year} is outside the range of the available SLR scenarios"
259
+ )
260
+
261
+ slr = np.interp(year, df["year"], df[scenario])
262
+ ref_slr = np.interp(self.relative_to_year, df["year"], df[scenario])
263
+
264
+ new_slr = us.UnitfulLength(
265
+ value=slr - ref_slr,
266
+ units=df["units"][0],
267
+ )
268
+ return np.round(new_slr.convert(units), decimals=2)
269
+
270
+ def plot_slr_scenarios(
271
+ self,
272
+ scenario_names: list[str],
273
+ output_loc: Path,
274
+ units: us.UnitTypesLength = us.UnitTypesLength.meters,
275
+ ) -> str:
276
+ """
277
+ Plot sea level rise scenarios.
278
+
279
+ Returns
280
+ -------
281
+ html_path : str
282
+ The path to the html plot of the sea level rise scenarios.
283
+ """
284
+ df = pd.read_csv(self.file)
285
+
286
+ ncolors = len(df.columns) - 2
287
+ if "units" not in df.columns:
288
+ raise ValueError(f"Expected column `units` in {self.file}.")
289
+
290
+ _units = df["units"].iloc[0]
291
+ _units = us.UnitTypesLength(_units)
292
+
293
+ if "year" not in df.columns:
294
+ raise ValueError(f"Expected column `year` in {self.file}.")
295
+
296
+ if (
297
+ self.relative_to_year > df["year"].max()
298
+ or self.relative_to_year < df["year"].min()
299
+ ):
300
+ raise ValueError(
301
+ f"The reference year {self.relative_to_year} is outside the range of the available SLR scenarios"
302
+ )
303
+
304
+ for scn in scenario_names:
305
+ ref_slr = np.interp(self.relative_to_year, df["year"], df[scn])
306
+ df[scn] -= ref_slr
307
+
308
+ df = df.drop(columns="units").melt(id_vars=["year"]).reset_index(drop=True)
309
+ # convert to units used in GUI
310
+ conversion_factor = us.UnitfulLength(value=1.0, units=_units).convert(units)
311
+ df.iloc[:, -1] = (conversion_factor * df.iloc[:, -1]).round(decimals=2)
312
+
313
+ # rename column names that will be shown in html
314
+ df = df.rename(
315
+ columns={
316
+ "variable": "Scenario",
317
+ "value": f"Sea level rise [{units.value}]",
318
+ }
319
+ )
320
+
321
+ colors = sample_colorscale(
322
+ "rainbow", [n / (ncolors - 1) for n in range(ncolors)]
323
+ )
324
+ fig = line(
325
+ df,
326
+ x="year",
327
+ y=f"Sea level rise [{units.value}]",
328
+ color="Scenario",
329
+ color_discrete_sequence=colors,
330
+ )
331
+
332
+ # fig.update_traces(marker={"line": {"color": "#000000", "width": 2}})
333
+
334
+ fig.update_layout(
335
+ autosize=False,
336
+ height=100 * 1.2,
337
+ width=280 * 1.3,
338
+ margin={"r": 0, "l": 0, "b": 0, "t": 0},
339
+ font={"size": 10, "color": "black", "family": "Arial"},
340
+ title_font={"size": 10, "color": "black", "family": "Arial"},
341
+ legend_font={"size": 10, "color": "black", "family": "Arial"},
342
+ legend_grouptitlefont={"size": 10, "color": "black", "family": "Arial"},
343
+ legend={"entrywidthmode": "fraction", "entrywidth": 0.2},
344
+ yaxis_title_font={"size": 10, "color": "black", "family": "Arial"},
345
+ xaxis_title=None,
346
+ xaxis_range=[self.relative_to_year, df["year"].max()],
347
+ legend_title=None,
348
+ # paper_bgcolor="#3A3A3A",
349
+ # plot_bgcolor="#131313",
350
+ )
351
+
352
+ # write html to results folder
353
+ output_loc.parent.mkdir(parents=True, exist_ok=True)
354
+ fig.write_html(output_loc)
355
+ return str(output_loc)
356
+
357
+
358
+ class FloodModel(BaseModel):
359
+ """The accepted input for the variable overland_model and offshore_model in Site.
360
+
361
+ Attributes
362
+ ----------
363
+ name : str
364
+ The name of the directory in `static/templates/<directory>` that contains the template model files.
365
+ reference : str
366
+ The name of the vertical reference model that is used as the reference datum. Should be defined in water_level.datums.
367
+ vertical_offset : Optional[us.UnitfulLength], default = None
368
+ The vertical offset of the vertical reference model relative to the main reference.
369
+ Given that the height of the vertical reference model is often determined by external sources,
370
+ this vertical offset can be used to correct systematic over-/underestimation of a vertical reference model.
371
+ """
372
+
373
+ name: str
374
+ reference: str
375
+
376
+ # this used to be water_level_offset from events
377
+ vertical_offset: Optional[us.UnitfulLength] = None
378
+
379
+
380
+ class SfincsConfigModel(BaseModel):
381
+ """The expected variables and data types of attributes of the SfincsConfig class.
382
+
383
+ Attributes
384
+ ----------
385
+ csname : str
386
+ The name of the CS model.
387
+ cstype : Cstype
388
+ Cstype of the CS model. must be either "projected" or "spherical".
389
+ version : Optional[str], default = None
390
+ The version of the CS model. If None, the version is not specified.
391
+ offshore_model : Optional[FloodModel], default = None
392
+ The offshore model. If None, the offshore model is not specified.
393
+ overland_model : FloodModel
394
+ The overland model. This is the main model used for the simulation.
395
+ floodmap_units : us.UnitTypesLength
396
+ The units used for the output floodmap. Sfincs always produces in metric units, this is used to convert the floodmap to the correct units.
397
+ save_simulation : Optional[bool], default = False
398
+ Whether to keep or delete the simulation files after the simulation is finished and all output files are created.
399
+ If True, the simulation files are kept. If False, the simulation files are deleted.
400
+ """
401
+
402
+ csname: str
403
+ cstype: Cstype
404
+ version: Optional[str] = None
405
+ offshore_model: Optional[FloodModel] = None
406
+ overland_model: FloodModel
407
+ floodmap_units: us.UnitTypesLength
408
+ save_simulation: Optional[bool] = False
409
+
410
+
411
+ class SfincsModel(BaseModel):
412
+ """The expected variables and data types of attributes of the Sfincs class.
413
+
414
+ Attributes
415
+ ----------
416
+ config : SfincsConfigModel
417
+ The configuration of the Sfincs model.
418
+ water_level : WaterlevelReferenceModel
419
+ The collection of all datums and the main reference datum.
420
+ dem : DemModel
421
+ The digital elevation model.
422
+ flood_frequency : FloodFrequencyModel, default = FloodFrequencyModel()
423
+ The flood frequency model.
424
+ slr : SlrScenariosModel
425
+ Specification of the sea level rise scenarios.
426
+ cyclone_track_database : CycloneTrackDatabaseModel, optional, default = None
427
+ The cyclone track database model.
428
+ scs : SCSModel, optional, default = None
429
+ The SCS model.
430
+ tide_gauge : TideGauge, optional, default = None
431
+ The tide gauge model.
432
+ river : list[RiverModel], optional, default = None
433
+ The river model.
434
+ obs_point : list[ObsPointModel], optional, default = None
435
+ The observation point model.
436
+ """
437
+
438
+ config: SfincsConfigModel
439
+ water_level: WaterlevelReferenceModel
440
+ cyclone_track_database: Optional[CycloneTrackDatabaseModel] = None
441
+ slr_scenarios: Optional[SlrScenariosModel] = None
442
+ scs: Optional[SCSModel] = None # optional for the US to use SCS rainfall curves
443
+ dem: DemModel
444
+
445
+ flood_frequency: FloodFrequencyModel = FloodFrequencyModel(
446
+ flooding_threshold=us.UnitfulLength(value=0.0, units=us.UnitTypesLength.meters)
447
+ ) # TODO we dont actually use this anywhere?
448
+
449
+ tide_gauge: Optional[TideGauge] = None
450
+ river: Optional[list[RiverModel]] = None
451
+ obs_point: Optional[list[ObsPointModel]] = None
452
+
453
+ @staticmethod
454
+ def read_toml(path: Path) -> "SfincsModel":
455
+ with open(path, mode="rb") as fp:
456
+ toml_contents = load_toml(fp)
457
+
458
+ return SfincsModel(**toml_contents)
459
+
460
+ @model_validator(mode="after")
461
+ def ensure_references_exist(self):
462
+ datum_names = [d.name for d in self.water_level.datums]
463
+
464
+ if self.config.overland_model.reference not in datum_names:
465
+ raise ValueError(
466
+ f"Could not find reference `{self.config.overland_model.reference}` in available datums: {datum_names}."
467
+ )
468
+
469
+ if self.config.offshore_model is not None:
470
+ if self.config.offshore_model.reference not in datum_names:
471
+ raise ValueError(
472
+ f"Could not find reference `{self.config.offshore_model.reference}` in available datums: {datum_names}."
473
+ )
474
+
475
+ if self.tide_gauge is not None:
476
+ if self.tide_gauge.reference not in datum_names:
477
+ raise ValueError(
478
+ f"Could not find reference `{self.tide_gauge.reference}` in available datums: {datum_names}."
479
+ )
480
+
481
+ return self