flood-adapt 0.3.15__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flood_adapt/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # has to be here at the start to avoid circular imports
2
- __version__ = "0.3.15"
2
+ __version__ = "1.0.0"
3
3
 
4
4
  from flood_adapt import adapter, database_builder, dbs_classes, objects
5
5
  from flood_adapt.config.config import Settings
@@ -23,6 +23,7 @@ from hydromt_fiat.fiat import FiatModel
23
23
 
24
24
  from flood_adapt.adapter.interface.impact_adapter import IImpactAdapter
25
25
  from flood_adapt.config.fiat import FiatConfigModel
26
+ from flood_adapt.config.impacts import FloodmapType
26
27
  from flood_adapt.misc.log import FloodAdaptLogging
27
28
  from flood_adapt.misc.path_builder import (
28
29
  ObjectDir,
@@ -39,7 +40,7 @@ from flood_adapt.objects.measures.measures import (
39
40
  )
40
41
  from flood_adapt.objects.projections.projections import Projection
41
42
  from flood_adapt.objects.scenarios.scenarios import Scenario
42
- from flood_adapt.workflows.floodmap import FloodMap, FloodmapType
43
+ from flood_adapt.workflows.floodmap import FloodMap
43
44
  from flood_adapt.workflows.impacts_integrator import Impacts
44
45
 
45
46
  # Define naming structure for saved files
@@ -934,7 +934,11 @@ class SfincsAdapter(IHazardAdapter):
934
934
  timeseries=tmp_path, magnitude=None, direction=None
935
935
  )
936
936
  elif isinstance(wind, WindMeteo):
937
- ds = MeteoHandler().read(time_frame)
937
+ ds = MeteoHandler(
938
+ dir=self.database.static_path / "meteo",
939
+ lat=self.database.site.lat,
940
+ lon=self.database.site.lon,
941
+ ).read(time_frame)
938
942
  # data already in metric units so no conversion needed
939
943
 
940
944
  # HydroMT function: set wind forcing from grid
@@ -1018,7 +1022,11 @@ class SfincsAdapter(IHazardAdapter):
1018
1022
 
1019
1023
  self._model.setup_precip_forcing(timeseries=tmp_path)
1020
1024
  elif isinstance(rainfall, RainfallMeteo):
1021
- ds = MeteoHandler().read(time_frame)
1025
+ ds = MeteoHandler(
1026
+ dir=self.database.static_path / "meteo",
1027
+ lat=self.database.site.lat,
1028
+ lon=self.database.site.lon,
1029
+ ).read(time_frame)
1022
1030
  # MeteoHandler always return metric so no conversion needed
1023
1031
  self._model.setup_precip_forcing_from_grid(precip=ds, aggregate=False)
1024
1032
  elif isinstance(rainfall, RainfallTrack):
@@ -138,7 +138,11 @@ class OffshoreSfincsHandler(IOffshoreSfincsHandler, DatabaseUser):
138
138
 
139
139
  # Add pressure forcing for the offshore model (this doesnt happen normally in _add_forcing_wind() for overland models)
140
140
  if isinstance(wind_forcing, WindMeteo):
141
- ds = MeteoHandler().read(_offshore_model._event.time)
141
+ ds = MeteoHandler(
142
+ dir=self.database.static_path / "meteo",
143
+ lat=self.database.site.lat,
144
+ lon=self.database.site.lon,
145
+ ).read(_offshore_model._event.time)
142
146
  _offshore_model._add_pressure_forcing_from_grid(ds=ds)
143
147
 
144
148
  # write sfincs model in output destination
@@ -0,0 +1,92 @@
1
+ from flood_adapt.config.config import Settings
2
+ from flood_adapt.config.fiat import (
3
+ FiatConfigModel,
4
+ FiatModel,
5
+ )
6
+ from flood_adapt.config.gui import (
7
+ AggregationDmgLayer,
8
+ BenefitsLayer,
9
+ FloodMapLayer,
10
+ FootprintsDmgLayer,
11
+ GuiModel,
12
+ GuiUnitModel,
13
+ Layer,
14
+ OutputLayers,
15
+ PlottingModel,
16
+ VisualizationLayer,
17
+ VisualizationLayers,
18
+ )
19
+ from flood_adapt.config.hazard import (
20
+ AsciiStr,
21
+ Cstype,
22
+ CycloneTrackDatabaseModel,
23
+ DatumModel,
24
+ DemModel,
25
+ FloodFrequencyModel,
26
+ FloodModel,
27
+ ObsPointModel,
28
+ RiverModel,
29
+ SCSModel,
30
+ Scstype,
31
+ SlrScenariosModel,
32
+ WaterlevelReferenceModel,
33
+ )
34
+ from flood_adapt.config.impacts import (
35
+ AggregationModel,
36
+ BenefitsModel,
37
+ EquityModel,
38
+ FloodmapType,
39
+ NoFootprintsModel,
40
+ RiskModel,
41
+ )
42
+ from flood_adapt.config.sfincs import (
43
+ SfincsConfigModel,
44
+ SfincsModel,
45
+ )
46
+ from flood_adapt.config.site import Site, SiteBuilder, StandardObjectModel
47
+
48
+ __all__ = [
49
+ # GUI
50
+ "GuiModel",
51
+ "GuiUnitModel",
52
+ "PlottingModel",
53
+ "Layer",
54
+ "OutputLayers",
55
+ "BenefitsLayer",
56
+ "FloodMapLayer",
57
+ "FootprintsDmgLayer",
58
+ "VisualizationLayer",
59
+ "VisualizationLayers",
60
+ "AggregationDmgLayer",
61
+ # FIAT
62
+ "FiatModel",
63
+ "FiatConfigModel",
64
+ "FloodmapType",
65
+ "NoFootprintsModel",
66
+ "RiskModel",
67
+ "EquityModel",
68
+ "BenefitsModel",
69
+ "AggregationModel",
70
+ # Config
71
+ "Settings",
72
+ # Sfincs
73
+ "SfincsModel",
74
+ "SfincsConfigModel",
75
+ "SCSModel",
76
+ "Scstype",
77
+ "Cstype",
78
+ "DemModel",
79
+ "DatumModel",
80
+ "FloodModel",
81
+ "RiverModel",
82
+ "ObsPointModel",
83
+ "SlrScenariosModel",
84
+ "FloodFrequencyModel",
85
+ "WaterlevelReferenceModel",
86
+ "CycloneTrackDatabaseModel",
87
+ "AsciiStr",
88
+ # Site
89
+ "Site",
90
+ "SiteBuilder",
91
+ "StandardObjectModel",
92
+ ]
@@ -1,140 +1,18 @@
1
- from enum import Enum
2
1
  from pathlib import Path
3
2
  from typing import Optional
4
3
 
5
4
  from pydantic import BaseModel
6
5
  from tomli import load as load_toml
7
6
 
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"
7
+ from flood_adapt.config.impacts import (
8
+ AggregationModel,
9
+ BenefitsModel,
10
+ BFEModel,
11
+ FloodmapType,
12
+ NoFootprintsModel,
13
+ RiskModel,
14
+ SVIModel,
15
+ )
138
16
 
139
17
 
140
18
  class FiatConfigModel(BaseModel):
flood_adapt/config/gui.py CHANGED
@@ -7,7 +7,7 @@ import pandas as pd
7
7
  import tomli
8
8
  from pydantic import BaseModel, Field, model_validator
9
9
 
10
- from flood_adapt.config.fiat import DamageType
10
+ from flood_adapt.config.impacts import DamageType
11
11
  from flood_adapt.objects.forcing import unit_system as us
12
12
 
13
13
 
@@ -0,0 +1,366 @@
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 typing_extensions import Annotated
12
+
13
+ from flood_adapt.objects.forcing import unit_system as us
14
+ from flood_adapt.objects.forcing.timeseries import Scstype
15
+
16
+
17
+ def ensure_ascii(s: str):
18
+ assert s.isascii()
19
+ return s
20
+
21
+
22
+ AsciiStr = Annotated[str, AfterValidator(ensure_ascii)]
23
+
24
+
25
+ class Cstype(str, Enum):
26
+ """The accepted input for the variable cstype in Site."""
27
+
28
+ projected = "projected"
29
+ spherical = "spherical"
30
+
31
+
32
+ class SCSModel(BaseModel):
33
+ """Class describing the accepted input for the variable scs.
34
+
35
+ Includes the file with the non-dimensional SCS rainfall curves in the site folder and the SCS rainfall curve type.
36
+
37
+ Attributes
38
+ ----------
39
+ file : str
40
+ The path to the SCS rainfall curves file.
41
+ type : Scstype
42
+ The type of the SCS rainfall curve.
43
+ """
44
+
45
+ file: str
46
+ type: Scstype
47
+
48
+
49
+ class RiverModel(BaseModel):
50
+ """Model that describes the accepted input for the variable river in Site.
51
+
52
+ Attributes
53
+ ----------
54
+ name : str
55
+ The name of the river.
56
+ description : Optional[str], default=None
57
+ description of the river.
58
+ mean_discharge : us.UnitfulDischarge
59
+ The mean discharge of the river.
60
+ x_coordinate : float
61
+ The x coordinate of the river.
62
+ y_coordinate : float
63
+ The y coordinate of the river.
64
+ """
65
+
66
+ name: str
67
+ description: Optional[str] = None
68
+ mean_discharge: us.UnitfulDischarge
69
+ x_coordinate: float
70
+ y_coordinate: float
71
+
72
+
73
+ class ObsPointModel(BaseModel):
74
+ """The accepted input for the variable obs_point in Site.
75
+
76
+ obs_points is used to define output locations in the hazard model, which will be plotted in the user interface.
77
+
78
+ Attributes
79
+ ----------
80
+ name : Union[int, AsciiStr]
81
+ The name of the observation point.
82
+ description : Optional[str], default=""
83
+ The description of the observation point.
84
+ ID : Optional[int], default=None
85
+ The ID of the observation point.
86
+ file : Optional[str], default=None
87
+ The path to the observation point data file.
88
+ """
89
+
90
+ name: Union[int, AsciiStr]
91
+ description: Optional[str] = ""
92
+ ID: Optional[int] = (
93
+ None # if the observation station is also a tide gauge, this ID should be the same as for obs_station
94
+ )
95
+ file: Optional[str] = None # for locally stored data
96
+ lat: float
97
+ lon: float
98
+
99
+
100
+ class FloodFrequencyModel(BaseModel):
101
+ """The accepted input for the variable flood_frequency in Site."""
102
+
103
+ flooding_threshold: us.UnitfulLength
104
+
105
+
106
+ class DemModel(BaseModel):
107
+ """The accepted input for the variable dem in Site.
108
+
109
+ Attributes
110
+ ----------
111
+ filename : str
112
+ The path to the digital elevation model file.
113
+ units : us.UnitTypesLength
114
+ The units of the digital elevation model file.
115
+ """
116
+
117
+ filename: str
118
+ units: us.UnitTypesLength
119
+
120
+
121
+ class DatumModel(BaseModel):
122
+ """
123
+ The accepted input for the variable datums in WaterlevelReferenceModel.
124
+
125
+ Attributes
126
+ ----------
127
+ name : str
128
+ The name of the vertical reference model.
129
+ height : us.UnitfulLength
130
+ The height of the vertical reference model relative to the main reference.
131
+ """
132
+
133
+ name: str
134
+ height: us.UnitfulLength
135
+
136
+
137
+ class WaterlevelReferenceModel(BaseModel):
138
+ """The accepted input for the variable water_level in Site.
139
+
140
+ Waterlevels timeseries are calculated from user input, assumed to be relative to the `reference` vertical reference model.
141
+
142
+ For plotting in the GUI, the `reference` vertical reference model is used as the main zero-reference, all values are relative to this.
143
+ All other vertical reference models are plotted as dashed lines.
144
+
145
+ Attributes
146
+ ----------
147
+ reference : str
148
+ The name of the vertical reference model that is used as the main zero-reference.
149
+ datums : list[DatumModel]
150
+ The vertical reference models that are used to calculate the waterlevels timeseries.
151
+ The datums are used to calculate the waterlevels timeseries, which are relative to the `reference` vertical reference model.
152
+ """
153
+
154
+ reference: str
155
+ datums: list[DatumModel] = Field(default_factory=list)
156
+
157
+ def get_datum(self, name: str) -> DatumModel:
158
+ for datum in self.datums:
159
+ if datum.name == name:
160
+ return datum
161
+ raise ValueError(f"Could not find datum with name {name}")
162
+
163
+ @model_validator(mode="after")
164
+ def main_reference_should_be_in_datums_and_eq_zero(self):
165
+ if self.reference not in [datum.name for datum in self.datums]:
166
+ raise ValueError(f"Reference {self.reference} not in {self.datums}")
167
+ if not math.isclose(
168
+ self.get_datum(self.reference).height.value, 0, abs_tol=1e-6
169
+ ):
170
+ raise ValueError(f"Reference {self.reference} height is not zero")
171
+ return self
172
+
173
+ @model_validator(mode="after")
174
+ def all_datums_should_have_unique_names(self):
175
+ datum_names = [datum.name for datum in self.datums]
176
+ if len(set(datum_names)) != len(datum_names):
177
+ raise ValueError(f"Duplicate datum names found: {datum_names}")
178
+ return self
179
+
180
+
181
+ class CycloneTrackDatabaseModel(BaseModel):
182
+ """The accepted input for the variable cyclone_track_database in Site.
183
+
184
+ Attributes
185
+ ----------
186
+ file : str
187
+ The path to the cyclone track database file.
188
+ """
189
+
190
+ file: str
191
+
192
+
193
+ class SlrScenariosModel(BaseModel):
194
+ """The accepted input for the variable slr_scenarios.
195
+
196
+ Attributes
197
+ ----------
198
+ file : str
199
+ The path to the sea level rise scenarios file.
200
+ relative_to_year : int
201
+ The year to which the sea level rise scenarios are relative.
202
+ """
203
+
204
+ file: str
205
+ relative_to_year: int
206
+
207
+ def interp_slr(
208
+ self,
209
+ scenario: str,
210
+ year: float,
211
+ units: us.UnitTypesLength = us.UnitTypesLength.meters,
212
+ ) -> float:
213
+ """Interpolate SLR value and reference it to the SLR reference year from the site toml.
214
+
215
+ Parameters
216
+ ----------
217
+ scenario : str
218
+ SLR scenario name to use from the column names in self.file
219
+ year : float
220
+ year to evaluate
221
+ units : us.UnitTypesLength, default = us.UnitTypesLength.meters
222
+ The units to convert the SLR value to. Default is meters.
223
+
224
+ Returns
225
+ -------
226
+ float
227
+ The interpolated sea level rise value in the specified units, relative to the reference year.
228
+
229
+ Raises
230
+ ------
231
+ ValueError
232
+ if the reference year is outside of the time range in the slr.csv file
233
+ ValueError
234
+ if the year to evaluate is outside of the time range in the slr.csv file
235
+ """
236
+ df = pd.read_csv(self.file)
237
+ if year > df["year"].max() or year < df["year"].min():
238
+ raise ValueError(
239
+ "The selected year is outside the range of the available SLR scenarios"
240
+ )
241
+
242
+ if (
243
+ self.relative_to_year > df["year"].max()
244
+ or self.relative_to_year < df["year"].min()
245
+ ):
246
+ raise ValueError(
247
+ f"The reference year {self.relative_to_year} is outside the range of the available SLR scenarios"
248
+ )
249
+
250
+ slr = np.interp(year, df["year"], df[scenario])
251
+ ref_slr = np.interp(self.relative_to_year, df["year"], df[scenario])
252
+
253
+ new_slr = us.UnitfulLength(
254
+ value=slr - ref_slr,
255
+ units=df["units"][0],
256
+ )
257
+ return np.round(new_slr.convert(units), decimals=2)
258
+
259
+ def plot_slr_scenarios(
260
+ self,
261
+ scenario_names: list[str],
262
+ output_loc: Path,
263
+ units: us.UnitTypesLength = us.UnitTypesLength.meters,
264
+ ) -> str:
265
+ """
266
+ Plot sea level rise scenarios.
267
+
268
+ Returns
269
+ -------
270
+ html_path : str
271
+ The path to the html plot of the sea level rise scenarios.
272
+ """
273
+ df = pd.read_csv(self.file)
274
+
275
+ ncolors = len(df.columns) - 2
276
+ if "units" not in df.columns:
277
+ raise ValueError(f"Expected column `units` in {self.file}.")
278
+
279
+ _units = df["units"].iloc[0]
280
+ _units = us.UnitTypesLength(_units)
281
+
282
+ if "year" not in df.columns:
283
+ raise ValueError(f"Expected column `year` in {self.file}.")
284
+
285
+ if (
286
+ self.relative_to_year > df["year"].max()
287
+ or self.relative_to_year < df["year"].min()
288
+ ):
289
+ raise ValueError(
290
+ f"The reference year {self.relative_to_year} is outside the range of the available SLR scenarios"
291
+ )
292
+
293
+ for scn in scenario_names:
294
+ ref_slr = np.interp(self.relative_to_year, df["year"], df[scn])
295
+ df[scn] -= ref_slr
296
+
297
+ df = df.drop(columns="units").melt(id_vars=["year"]).reset_index(drop=True)
298
+ # convert to units used in GUI
299
+ conversion_factor = us.UnitfulLength(value=1.0, units=_units).convert(units)
300
+ df.iloc[:, -1] = (conversion_factor * df.iloc[:, -1]).round(decimals=2)
301
+
302
+ # rename column names that will be shown in html
303
+ df = df.rename(
304
+ columns={
305
+ "variable": "Scenario",
306
+ "value": f"Sea level rise [{units.value}]",
307
+ }
308
+ )
309
+
310
+ colors = sample_colorscale(
311
+ "rainbow", [n / (ncolors - 1) for n in range(ncolors)]
312
+ )
313
+ fig = line(
314
+ df,
315
+ x="year",
316
+ y=f"Sea level rise [{units.value}]",
317
+ color="Scenario",
318
+ color_discrete_sequence=colors,
319
+ )
320
+
321
+ # fig.update_traces(marker={"line": {"color": "#000000", "width": 2}})
322
+
323
+ fig.update_layout(
324
+ autosize=False,
325
+ height=100 * 1.2,
326
+ width=280 * 1.3,
327
+ margin={"r": 0, "l": 0, "b": 0, "t": 0},
328
+ font={"size": 10, "color": "black", "family": "Arial"},
329
+ title_font={"size": 10, "color": "black", "family": "Arial"},
330
+ legend_font={"size": 10, "color": "black", "family": "Arial"},
331
+ legend_grouptitlefont={"size": 10, "color": "black", "family": "Arial"},
332
+ legend={"entrywidthmode": "fraction", "entrywidth": 0.2},
333
+ yaxis_title_font={"size": 10, "color": "black", "family": "Arial"},
334
+ xaxis_title=None,
335
+ xaxis_range=[self.relative_to_year, df["year"].max()],
336
+ legend_title=None,
337
+ # paper_bgcolor="#3A3A3A",
338
+ # plot_bgcolor="#131313",
339
+ )
340
+
341
+ # write html to results folder
342
+ output_loc.parent.mkdir(parents=True, exist_ok=True)
343
+ fig.write_html(output_loc)
344
+ return str(output_loc)
345
+
346
+
347
+ class FloodModel(BaseModel):
348
+ """The accepted input for the variable overland_model and offshore_model in Site.
349
+
350
+ Attributes
351
+ ----------
352
+ name : str
353
+ The name of the directory in `static/templates/<directory>` that contains the template model files.
354
+ reference : str
355
+ The name of the vertical reference model that is used as the reference datum. Should be defined in water_level.datums.
356
+ vertical_offset : Optional[us.UnitfulLength], default = None
357
+ The vertical offset of the vertical reference model relative to the main reference.
358
+ Given that the height of the vertical reference model is often determined by external sources,
359
+ this vertical offset can be used to correct systematic over-/underestimation of a vertical reference model.
360
+ """
361
+
362
+ name: str
363
+ reference: str
364
+
365
+ # this used to be water_level_offset from events
366
+ vertical_offset: Optional[us.UnitfulLength] = None