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,506 @@
1
+ import os
2
+ from enum import Enum
3
+ from pathlib import Path
4
+ from typing import Any, Optional
5
+
6
+ import geopandas as gpd
7
+ import pyproj
8
+ from pydantic import Field, field_validator, model_validator
9
+
10
+ from flood_adapt.config.site import Site
11
+ from flood_adapt.misc.utils import resolve_filepath, save_file_to_database
12
+ from flood_adapt.objects.forcing import unit_system as us
13
+ from flood_adapt.objects.object_model import Object
14
+
15
+
16
+ class MeasureCategory(str, Enum):
17
+ """Class describing the accepted input for the variable 'type' in Measure."""
18
+
19
+ impact = "impact"
20
+ hazard = "hazard"
21
+
22
+
23
+ class MeasureType(str, Enum):
24
+ # Hazard measures
25
+ floodwall = "floodwall"
26
+ thin_dam = "thin_dam" # For now, same functionality as floodwall TODO: Add thin dam functionality
27
+ levee = "levee" # For now, same functionality as floodwall TODO: Add levee functionality
28
+ pump = "pump"
29
+ culvert = (
30
+ "culvert" # For now, same functionality as pump TODO: Add culvert functionality
31
+ )
32
+ water_square = "water_square"
33
+ greening = "greening"
34
+ total_storage = "total_storage"
35
+
36
+ # Impact measures
37
+ elevate_properties = "elevate_properties"
38
+ buyout_properties = "buyout_properties"
39
+ floodproof_properties = "floodproof_properties"
40
+
41
+ @classmethod
42
+ def is_hazard(cls, measure_type: str) -> bool:
43
+ return measure_type in [
44
+ cls.floodwall,
45
+ cls.thin_dam,
46
+ cls.levee,
47
+ cls.pump,
48
+ cls.culvert,
49
+ cls.water_square,
50
+ cls.greening,
51
+ cls.total_storage,
52
+ ]
53
+
54
+ @classmethod
55
+ def is_impact(cls, measure_type: str) -> bool:
56
+ return measure_type in [
57
+ cls.elevate_properties,
58
+ cls.buyout_properties,
59
+ cls.floodproof_properties,
60
+ ]
61
+
62
+ @classmethod
63
+ def get_measure_category(cls, measure_type: str) -> MeasureCategory:
64
+ if cls.is_hazard(measure_type):
65
+ return MeasureCategory.hazard
66
+ elif cls.is_impact(measure_type):
67
+ return MeasureCategory.impact
68
+ else:
69
+ raise ValueError(f"Invalid measure type: {measure_type}")
70
+
71
+
72
+ class SelectionType(str, Enum):
73
+ """Class describing the accepted input for the variable 'selection_type' in ImpactMeasure."""
74
+
75
+ aggregation_area = "aggregation_area"
76
+ polygon = "polygon"
77
+ polyline = "polyline"
78
+ all = "all"
79
+
80
+
81
+ class Measure(Object):
82
+ """The expected variables and data types of attributes common to all measures.
83
+
84
+ A measure is a collection of attributes that can be applied to a model.
85
+
86
+ Attributes
87
+ ----------
88
+ name: str
89
+ Name of the measure.
90
+ description: str
91
+ Description of the measure.
92
+ type: MeasureType
93
+ Type of measure. Should be one of the MeasureType enum values.
94
+ """
95
+
96
+ type: MeasureType
97
+
98
+
99
+ class HazardMeasure(Measure):
100
+ """The expected variables and data types of attributes common to all hazard measures.
101
+
102
+ Attributes
103
+ ----------
104
+ name: str
105
+ Name of the measure.
106
+ description: str
107
+ Description of the measure.
108
+ type: MeasureType
109
+ Type of measure. Should be one of the MeasureType enum values and is_hazard.
110
+ selection_type: SelectionType
111
+ Type of selection. Should be one of the SelectionType enum values.
112
+ polygon_file: str, Optional, default = None
113
+ Path to a polygon file, either absolute or relative to the measure path in the database.
114
+
115
+ """
116
+
117
+ selection_type: SelectionType
118
+ polygon_file: Optional[str] = Field(
119
+ default=None,
120
+ min_length=1,
121
+ description="Path to a polygon file, either absolute or relative to the measure path.",
122
+ )
123
+
124
+ @field_validator("type")
125
+ def validate_type(cls, value):
126
+ if not MeasureType.is_hazard(value):
127
+ raise ValueError(f"Invalid hazard type: {value}")
128
+ return value
129
+
130
+ @model_validator(mode="after")
131
+ def validate_selection_type(self) -> "HazardMeasure":
132
+ if (
133
+ self.selection_type
134
+ not in [SelectionType.aggregation_area, SelectionType.all]
135
+ and self.polygon_file is None
136
+ ):
137
+ raise ValueError(
138
+ "If `selection_type` is not 'aggregation_area' or 'all', then `polygon_file` needs to be set."
139
+ )
140
+ return self
141
+
142
+ def save_additional(self, output_dir: Path | str | os.PathLike) -> None:
143
+ if self.polygon_file:
144
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
145
+ src_path = resolve_filepath("measures", self.name, self.polygon_file)
146
+ path = save_file_to_database(src_path, Path(output_dir))
147
+ # Update the shapefile path in the object so it is saved in the toml file as well
148
+ self.polygon_file = path.name
149
+
150
+
151
+ class ImpactMeasure(Measure):
152
+ """The expected variables and data types of attributes common to all impact measures.
153
+
154
+ Attributes
155
+ ----------
156
+ name: str
157
+ Name of the measure.
158
+ description: str
159
+ Description of the measure.
160
+ type: MeasureType
161
+ Type of measure. Should be one of the MeasureType enum values and is_hazard.
162
+ selection_type: SelectionType
163
+ Type of selection. Should be one of the SelectionType enum values.
164
+ polygon_file: str, Optional, default = None
165
+ Path to a polygon file, either absolute or relative to the measure path in the database.
166
+ property_type: str
167
+ Type of property. Should be one of the PropertyType enum values.
168
+ aggregation_area_type: str, Optional, default = None
169
+ Type of aggregation area. Should be one of the SelectionType enum values.
170
+ aggregation_area_name: str, Optional, default = None
171
+ Name of the aggregation area.
172
+ """
173
+
174
+ type: MeasureType
175
+ selection_type: SelectionType
176
+ aggregation_area_type: Optional[str] = None
177
+ aggregation_area_name: Optional[str] = None
178
+ polygon_file: Optional[str] = Field(
179
+ default=None,
180
+ min_length=1,
181
+ description="Path to a polygon file, relative to the database path.",
182
+ )
183
+ property_type: str # TODO make enum
184
+
185
+ @field_validator("type")
186
+ def validate_type(cls, value):
187
+ if not MeasureType.is_impact(value):
188
+ raise ValueError(f"Invalid impact type: {value}")
189
+ return value
190
+
191
+ @model_validator(mode="after")
192
+ def validate_aggregation_area_name(self):
193
+ if (
194
+ self.selection_type == SelectionType.aggregation_area
195
+ and self.aggregation_area_name is None
196
+ ):
197
+ raise ValueError(
198
+ "If `selection_type` is 'aggregation_area', then `aggregation_area_name` needs to be set."
199
+ )
200
+ return self
201
+
202
+ @model_validator(mode="after")
203
+ def validate_polygon_file(self):
204
+ if self.selection_type == SelectionType.polygon and self.polygon_file is None:
205
+ raise ValueError(
206
+ "If `selection_type` is 'polygon', then `polygon_file` needs to be set."
207
+ )
208
+
209
+ return self
210
+
211
+ def save_additional(self, output_dir: Path | str | os.PathLike) -> None:
212
+ """Save the additional files to the database."""
213
+ if self.polygon_file:
214
+ src_path = resolve_filepath("measures", self.name, self.polygon_file)
215
+ path = save_file_to_database(src_path, Path(output_dir))
216
+ # Update the shapefile path in the object so it is saved in the toml file as well
217
+ self.polygon_file = path.name
218
+
219
+
220
+ class Elevate(ImpactMeasure):
221
+ """The expected variables and data types of the "elevate" impact measure.
222
+
223
+ Attributes
224
+ ----------
225
+ name: str
226
+ Name of the measure.
227
+ description: str
228
+ Description of the measure.
229
+ type : MeasureType
230
+ Type of measure. Should be "elevate_properties".
231
+ selection_type : SelectionType
232
+ Type of selection. Should be "polygon" or "aggregation_area".
233
+ polygon_file : str, Optional
234
+ Path to a polygon file, either absolute or relative to the measure path.
235
+ aggregation_area_type : str, Optional
236
+ Type of aggregation area. Should be "aggregation_area" or "all".
237
+ aggregation_area_name : str, Optional
238
+ Name of the aggregation area.
239
+ property_type : str
240
+ Type of property. Should be "residential" or "commercial".
241
+ elevation : us.UnitfulLengthRefValue
242
+ Elevation of the properties.
243
+ """
244
+
245
+ type: MeasureType = MeasureType.elevate_properties
246
+ elevation: us.UnitfulLengthRefValue
247
+
248
+
249
+ class Buyout(ImpactMeasure):
250
+ """The expected variables and data types of the "buyout" impact measure.
251
+
252
+ Attributes
253
+ ----------
254
+ name: str
255
+ Name of the measure.
256
+ description: str, default ""
257
+ Description of the measure.
258
+ type : MeasureType, default MeasureType.buyout_properties
259
+ Type of measure.
260
+ selection_type : SelectionType
261
+ Type of selection. Should be "polygon" or "aggregation_area".
262
+ polygon_file : str, Optional
263
+ Path to a polygon file, either absolute or relative to the measure path.
264
+ aggregation_area_type : str, Optional
265
+ Type of aggregation area. Should be "aggregation_area" or "all".
266
+ aggregation_area_name : str, Optional
267
+ Name of the aggregation area.
268
+ property_type : str
269
+ Type of property. Should be "residential" or "commercial".
270
+ elevation : UnitfulLengthRefValue
271
+ Elevation of the properties.
272
+
273
+ """
274
+
275
+ # Buyout has only the basic impact measure attributes
276
+ type: MeasureType = MeasureType.buyout_properties
277
+
278
+
279
+ class FloodProof(ImpactMeasure):
280
+ """The expected variables and data types of the "floodproof" impact measure.
281
+
282
+ Attributes
283
+ ----------
284
+ name: str
285
+ Name of the measure.
286
+ description: str
287
+ Description of the measure.
288
+ type : MeasureType
289
+ Type of measure. Should be "floodproof_properties".
290
+ selection_type : SelectionType
291
+ Type of selection. Should be "polygon" or "aggregation_area".
292
+ polygon_file : str, Optional
293
+ Path to a polygon file, either absolute or relative to the measure path.
294
+ aggregation_area_type : str, Optional
295
+ Type of aggregation area. Should be "aggregation_area" or "all".
296
+ aggregation_area_name : str, Optional
297
+ Name of the aggregation area.
298
+ property_type : str
299
+ Type of property. Should be "residential" or "commercial".
300
+ elevation : UnitfulLengthRefValue
301
+ Elevation of the properties.
302
+
303
+ """
304
+
305
+ type: MeasureType = MeasureType.floodproof_properties
306
+ elevation: us.UnitfulLength
307
+
308
+
309
+ class FloodWall(HazardMeasure):
310
+ """
311
+ The expected variables and data types of the "floodwall" hazard measure.
312
+
313
+ Attributes
314
+ ----------
315
+ name: str
316
+ Name of the measure.
317
+ description: str
318
+ Description of the measure.
319
+ type : MeasureType, default MeasureType.floodwall
320
+ Type of measure. Should be "floodwall"
321
+ selection_type : SelectionType
322
+ Type of selection. Should be "polygon" or "aggregation_area".
323
+ polygon_file : str, Optional
324
+ Path to a polygon file, either absolute or relative to the measure path.
325
+ elevation : us.UnitfulLength
326
+ Height of the floodwall.
327
+ absolute_elevation : bool
328
+ TODO remove?
329
+ """
330
+
331
+ type: MeasureType = MeasureType.floodwall
332
+ elevation: us.UnitfulLength
333
+ absolute_elevation: Optional[bool] = False
334
+
335
+
336
+ class Pump(HazardMeasure):
337
+ """
338
+ The expected variables and data types of the "pump" hazard measure.
339
+
340
+ Attributes
341
+ ----------
342
+ name: str
343
+ Name of the measure.
344
+ description: str
345
+ Description of the measure.
346
+ type : MeasureType
347
+ Type of measure. Should be "pump"
348
+ selection_type : SelectionType
349
+ Type of selection. Should be "polyline".
350
+ polygon_file : str, Optional
351
+ Path to a polygon file, either absolute or relative to the measure path.
352
+ elevation : us.UnitfulLength
353
+ Height of the floodwall.
354
+ absolute_elevation : bool
355
+ TODO remove?
356
+ """
357
+
358
+ type: MeasureType = MeasureType.pump
359
+ discharge: us.UnitfulDischarge
360
+
361
+
362
+ class GreenInfrastructure(HazardMeasure):
363
+ """The expected variables and data types of the "green infrastructure" hazard measure.
364
+
365
+ Attributes
366
+ ----------
367
+ name: str
368
+ Name of the measure.
369
+ description: str
370
+ Description of the measure.
371
+ type : MeasureType
372
+ Type of measure. Should be "greening"
373
+ selection_type : SelectionType
374
+ Type of selection. Should be "polygon" or "aggregation_area".
375
+ height : us.UnitfulHeight, Optional
376
+ Height of the green infrastructure.
377
+ volume : us.UnitfulVolume, Optional
378
+ Volume of the green infrastructure.
379
+ polygon_file : str, Optional
380
+ Path to a polygon file, either absolute or relative to the measure path.
381
+ aggregation_area_type : str, Optional
382
+ Type of aggregation area. Should be "aggregation_area".
383
+ aggregation_area_name : str, Optional
384
+ Name of the aggregation area.
385
+ percent_area : float, Optional
386
+ Percentage of the area that is green infrastructure.
387
+ """
388
+
389
+ type: MeasureType = MeasureType.greening
390
+ volume: us.UnitfulVolume
391
+ height: Optional[us.UnitfulHeight] = None
392
+ aggregation_area_type: Optional[str] = None
393
+ aggregation_area_name: Optional[str] = None
394
+ percent_area: Optional[float] = Field(default=None, ge=0, le=100)
395
+
396
+ @field_validator("height", mode="before", check_fields=False)
397
+ def height_from_length(value: Any) -> Any:
398
+ if isinstance(value, us.UnitfulLength):
399
+ return us.UnitfulHeight(value=value.value, units=value.units)
400
+ return value
401
+
402
+ @model_validator(mode="after")
403
+ def validate_hazard_type_values(self) -> "GreenInfrastructure":
404
+ e_msg = f"Error parsing GreenInfrastructure: {self.name}"
405
+
406
+ if self.type == MeasureType.total_storage:
407
+ if self.height is not None or self.percent_area is not None:
408
+ raise ValueError(
409
+ f"{e_msg}\nHeight and percent_area cannot be set for total storage type measures"
410
+ )
411
+ return self
412
+ elif self.type == MeasureType.water_square:
413
+ if self.percent_area is not None:
414
+ raise ValueError(
415
+ f"{e_msg}\nPercentage_area cannot be set for water square type measures"
416
+ )
417
+ elif not isinstance(self.height, us.UnitfulHeight):
418
+ raise ValueError(
419
+ f"{e_msg}\nHeight needs to be set for water square type measures"
420
+ )
421
+ return self
422
+ elif self.type == MeasureType.greening:
423
+ if not isinstance(self.height, us.UnitfulHeight) or not isinstance(
424
+ self.percent_area, float
425
+ ):
426
+ raise ValueError(
427
+ f"{e_msg}\nHeight and percent_area needs to be set for greening type measures"
428
+ )
429
+ else:
430
+ raise ValueError(
431
+ f"{e_msg}\nType must be one of 'water_square', 'greening', or 'total_storage'"
432
+ )
433
+ return self
434
+
435
+ @model_validator(mode="after")
436
+ def validate_selection_type_values(self) -> "GreenInfrastructure":
437
+ if self.selection_type == SelectionType.aggregation_area:
438
+ if self.aggregation_area_name is None:
439
+ raise ValueError(
440
+ "If `selection_type` is 'aggregation_area', then `aggregation_area_name` needs to be set."
441
+ )
442
+ if self.aggregation_area_type is None:
443
+ raise ValueError(
444
+ "If `selection_type` is 'aggregation_area', then `aggregation_area_type` needs to be set."
445
+ )
446
+ return self
447
+
448
+ @staticmethod
449
+ def calculate_volume(
450
+ area: us.UnitfulArea,
451
+ height: us.UnitfulHeight,
452
+ percent_area: float = 100.0,
453
+ ) -> float:
454
+ """Determine volume from area of the polygon and infiltration height.
455
+
456
+ Parameters
457
+ ----------
458
+ area : us.UnitfulArea
459
+ Area of polygon with units (calculated using calculate_polygon_area)
460
+ height : us.UnitfulHeight
461
+ Water height with units
462
+ percent_area : float, optional
463
+ Percentage area covered by green infrastructure [%], by default 100.0
464
+
465
+ Returns
466
+ -------
467
+ float
468
+
469
+
470
+ Returns
471
+ -------
472
+ float
473
+ Volume [m3]
474
+ """
475
+ volume = (
476
+ area.convert(us.UnitTypesArea.m2)
477
+ * height.convert(us.UnitTypesLength.meters)
478
+ * (percent_area / 100.0)
479
+ )
480
+ return volume
481
+
482
+ @staticmethod
483
+ def calculate_polygon_area(gdf: gpd.GeoDataFrame, site: Site) -> float:
484
+ """Calculate area of a GeoDataFrame Polygon.
485
+
486
+ Parameters
487
+ ----------
488
+ gdf : gpd.GeoDataFrame
489
+ Polygon object
490
+ site : Site
491
+ site config (used for CRS)
492
+
493
+ Returns
494
+ -------
495
+ floatd
496
+ Area [m2]
497
+ """
498
+ # Determine local CRS
499
+ crs = pyproj.CRS.from_string(site.sfincs.config.csname)
500
+ gdf = gdf.to_crs(crs)
501
+
502
+ # The GeoJSON file can contain multiple polygons
503
+ polygon = gdf.geometry
504
+ # Calculate the area of all polygons
505
+ area = polygon.area.sum()
506
+ return area
@@ -0,0 +1,68 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ import tomli
5
+ import tomli_w
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class Object(BaseModel):
10
+ """Base class for FloodAdapt objects.
11
+
12
+ Attributes
13
+ ----------
14
+ name : str
15
+ Name of the object.
16
+ description : str (optional)
17
+ Description of the object.
18
+ """
19
+
20
+ name: str = Field(
21
+ ...,
22
+ description="Name of the object.",
23
+ min_length=1,
24
+ pattern='^[^<>:"/\\\\|?* ]*$',
25
+ )
26
+ description: str = Field(default="", description="Description of the object.")
27
+
28
+ @classmethod
29
+ def load_file(cls, file_path: Path | str | os.PathLike) -> "Object":
30
+ """Load object from file.
31
+
32
+ Parameters
33
+ ----------
34
+ file_path : Path | str | os.PathLike
35
+ Path to the file to load.
36
+
37
+ """
38
+ with open(file_path, mode="rb") as fp:
39
+ toml = tomli.load(fp)
40
+ return cls.model_validate(toml)
41
+
42
+ def save(self, toml_path: Path | str | os.PathLike) -> None:
43
+ """Save object to disk.
44
+
45
+ Parameters
46
+ ----------
47
+ toml_path : Path | str | os.PathLike
48
+ Path to the file to save.
49
+
50
+ """
51
+ self.save_additional(output_dir=Path(toml_path).parent)
52
+ with open(toml_path, "wb") as f:
53
+ tomli_w.dump(self.model_dump(exclude_none=True), f)
54
+
55
+ def save_additional(self, output_dir: Path | str | os.PathLike) -> None:
56
+ """Save additional files to database if the object has any and update attrs to reflect the change in file location.
57
+
58
+ This method should be overridden if the object has additional files.
59
+ """
60
+ pass
61
+
62
+ def __eq__(self, value):
63
+ if not isinstance(value, self.__class__):
64
+ # don't attempt to compare against unrelated types
65
+ return False
66
+ _self = self.model_dump(exclude={"name", "description"}, exclude_none=True)
67
+ _other = value.model_dump(exclude={"name", "description"}, exclude_none=True)
68
+ return _self == _other
File without changes
@@ -0,0 +1,89 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+ from flood_adapt.misc.utils import resolve_filepath, save_file_to_database
8
+ from flood_adapt.objects.forcing import unit_system as us
9
+ from flood_adapt.objects.object_model import Object
10
+
11
+
12
+ class PhysicalProjection(BaseModel):
13
+ """The accepted input for a physical projection in FloodAdapt.
14
+
15
+ Attributes
16
+ ----------
17
+ sea_level_rise : us.UnitfulLength, default = us.UnitfulLength(0.0, us.UnitTypesLength.meters).
18
+ The sea level rise in meters.
19
+ subsidence : us.UnitfulLength, default = us.UnitfulLength(0.0, us.UnitTypesLength.meters).
20
+ The subsidence in meters.
21
+ rainfall_multiplier : float, default = 1.0.
22
+ The rainfall multiplier.
23
+ storm_frequency_increase : float, default = 0.0.
24
+ The storm frequency increase as a percentage.
25
+ """
26
+
27
+ sea_level_rise: us.UnitfulLength = us.UnitfulLength(
28
+ value=0.0, units=us.UnitTypesLength.meters
29
+ )
30
+ subsidence: us.UnitfulLength = us.UnitfulLength(
31
+ value=0.0, units=us.UnitTypesLength.meters
32
+ )
33
+ rainfall_multiplier: float = Field(default=1.0, ge=0.0)
34
+ storm_frequency_increase: float = 0.0
35
+
36
+
37
+ class SocioEconomicChange(BaseModel):
38
+ """The accepted input for socio-economic change in FloodAdapt.
39
+
40
+ Attributes
41
+ ----------
42
+ population_growth_existing : float, default = 0.0.
43
+ The existing population growth rate.
44
+ economic_growth : float, default = 0.0.
45
+ The economic growth rate.
46
+ population_growth_new : float, default = 0.0.
47
+ The population growth rate for new developments.
48
+ new_development_elevation : us.UnitfulLengthRefValue, default = None.
49
+ The elevation of new developments.
50
+ new_development_shapefile : str, default = None.
51
+ The path to the shapefile of new developments.
52
+ """
53
+
54
+ population_growth_existing: Optional[float] = 0.0
55
+ economic_growth: Optional[float] = 0.0
56
+
57
+ population_growth_new: Optional[float] = 0.0
58
+ new_development_elevation: Optional[us.UnitfulLengthRefValue] = None
59
+ new_development_shapefile: Optional[str] = None
60
+
61
+
62
+ class Projection(Object):
63
+ """The accepted input for a projection in FloodAdapt.
64
+
65
+ A projection is a combination of a physical projection and a socio-economic change.
66
+
67
+ Attributes
68
+ ----------
69
+ physical_projection : PhysicalProjection
70
+ The physical projection model. Contains information about hazard drivers.
71
+ socio_economic_change : SocioEconomicChange
72
+ The socio-economic change model. Contains information about impact drivers.
73
+
74
+ """
75
+
76
+ physical_projection: PhysicalProjection
77
+ socio_economic_change: SocioEconomicChange
78
+
79
+ def save_additional(self, output_dir: Path | str | os.PathLike) -> None:
80
+ if self.socio_economic_change.new_development_shapefile:
81
+ src_path = resolve_filepath(
82
+ "projections",
83
+ self.name,
84
+ self.socio_economic_change.new_development_shapefile,
85
+ )
86
+ path = save_file_to_database(src_path, Path(output_dir))
87
+
88
+ # Update the shapefile path in the object so it is saved in the toml file as well
89
+ self.socio_economic_change.new_development_shapefile = path.name
File without changes
@@ -0,0 +1,22 @@
1
+ from flood_adapt.objects.object_model import Object
2
+
3
+
4
+ class Scenario(Object):
5
+ """BaseModel describing the expected variables and data types of a scenario.
6
+
7
+ A scenario is a combination of an event, a projection, and a strategy, that all should be saved in the database.
8
+
9
+ Attributes
10
+ ----------
11
+ event : str
12
+ The name of the event.
13
+ projection : str
14
+ The name of the projection.
15
+ strategy : str
16
+ The name of the strategy.
17
+
18
+ """
19
+
20
+ event: str
21
+ projection: str
22
+ strategy: str
File without changes