flood-adapt 0.3.0__py3-none-any.whl → 0.3.1__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.0"
2
+ __version__ = "0.3.1"
3
3
 
4
4
  from flood_adapt import adapter, dbs_classes, objects
5
5
  from flood_adapt.config.config import Settings
@@ -2,9 +2,8 @@ import gc
2
2
  import os
3
3
  import shutil
4
4
  import time
5
- from datetime import datetime
6
5
  from pathlib import Path
7
- from typing import Any, Optional, Union
6
+ from typing import Any, Literal, Optional, Union
8
7
 
9
8
  import geopandas as gpd
10
9
  import numpy as np
@@ -566,7 +565,12 @@ class Database(IDatabase):
566
565
  gdfs[label] = gdfs[label].to_crs(4326)
567
566
  return gdfs
568
567
 
569
- def get_object_list(self, object_type: str) -> dict[str, Any]:
568
+ def get_object_list(
569
+ self,
570
+ object_type: Literal[
571
+ "projections", "events", "measures", "strategies", "scenarios", "benefits"
572
+ ],
573
+ ) -> dict[str, Any]:
570
574
  """Get a dictionary with all the toml paths and last modification dates that exist in the database that correspond to object_type.
571
575
 
572
576
  Parameters
@@ -579,20 +583,23 @@ class Database(IDatabase):
579
583
  dict[str, Any]
580
584
  Includes 'path' and 'last_modification_date' info
581
585
  """
582
- paths = [
583
- path / f"{path.name}.toml"
584
- for path in list((self.input_path / object_type).iterdir())
585
- ]
586
- last_modification_date = [
587
- datetime.fromtimestamp(file.stat().st_mtime) for file in paths
588
- ]
589
-
590
- objects = {
591
- "path": paths,
592
- "last_modification_date": last_modification_date,
593
- }
594
-
595
- return objects
586
+ match object_type:
587
+ case "projections":
588
+ return self.projections.list_objects()
589
+ case "events":
590
+ return self.events.list_objects()
591
+ case "measures":
592
+ return self.measures.list_objects()
593
+ case "strategies":
594
+ return self.strategies.list_objects()
595
+ case "scenarios":
596
+ return self.scenarios.list_objects()
597
+ case "benefits":
598
+ return self.benefits.list_objects()
599
+ case _:
600
+ raise ValueError(
601
+ f"Object type '{object_type}' is not valid. Must be one of 'projections', 'events', 'measures', 'strategies' or 'scenarios'."
602
+ )
596
603
 
597
604
  def has_run_hazard(self, scenario_name: str) -> None:
598
605
  """Check if there is already a simulation that has the exact same hazard component.
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Any
3
2
 
4
3
  from flood_adapt.dbs_classes.dbs_template import DbsTemplate
5
4
  from flood_adapt.objects.events.event_factory import EventFactory
@@ -34,20 +33,6 @@ class DbsEvent(DbsTemplate[Event]):
34
33
  # Load event
35
34
  return EventFactory.load_file(event_path)
36
35
 
37
- def list_objects(self) -> dict[str, list[Any]]:
38
- """Return a dictionary with info on the events that currently exist in the database.
39
-
40
- Returns
41
- -------
42
- dict[str, Any]
43
- Includes 'name', 'description', 'path' and 'last_modification_date' info
44
- """
45
- events = self._get_object_list()
46
- objects = [self.get(name) for name in events["name"]]
47
- events["description"] = [obj.description for obj in objects]
48
- events["objects"] = objects
49
- return events
50
-
51
36
  def _check_standard_objects(self, name: str) -> bool:
52
37
  """Check if an event is a standard event.
53
38
 
@@ -1,3 +1,4 @@
1
+ from pathlib import Path
1
2
  from typing import Any
2
3
 
3
4
  import geopandas as gpd
@@ -26,9 +27,15 @@ class DbsMeasure(DbsTemplate[Measure]):
26
27
  Measure
27
28
  measure object
28
29
  """
29
- measure_path = self.input_path / name / f"{name}.toml"
30
- measure = MeasureFactory.get_measure_object(measure_path)
31
- return measure
30
+ # Make the full path to the object
31
+ full_path = self.input_path / name / f"{name}.toml"
32
+
33
+ # Check if the object exists
34
+ if not Path(full_path).is_file():
35
+ raise ValueError(f"{self.display_name}: '{name}' does not exist.")
36
+
37
+ # Load and return the object
38
+ return MeasureFactory.get_measure_object(full_path)
32
39
 
33
40
  def list_objects(self) -> dict[str, list[Any]]:
34
41
  """Return a dictionary with info on the measures that currently exist in the database.
@@ -39,14 +46,15 @@ class DbsMeasure(DbsTemplate[Measure]):
39
46
  Includes 'name', 'description', 'path' and 'last_modification_date' info
40
47
  """
41
48
  measures = self._get_object_list()
42
- objects = [MeasureFactory.get_measure_object(path) for path in measures["path"]]
49
+ objects = [self.get(name) for name in measures["name"]]
50
+
43
51
  measures["description"] = [obj.description for obj in objects]
44
52
  measures["objects"] = objects
45
53
 
46
54
  geometries = []
47
- for path, obj in zip(measures["path"], objects):
55
+ for obj in objects:
48
56
  # If polygon is used read the polygon file
49
- if obj.polygon_file:
57
+ if hasattr(obj, "polygon_file") and obj.polygon_file:
50
58
  src_path = resolve_filepath(
51
59
  object_dir=self.dir_name,
52
60
  obj_name=obj.name,
@@ -54,7 +62,7 @@ class DbsMeasure(DbsTemplate[Measure]):
54
62
  )
55
63
  geometries.append(gpd.read_file(src_path))
56
64
  # If aggregation area is used read the polygon from the aggregation area name
57
- elif obj.aggregation_area_name:
65
+ elif hasattr(obj, "aggregation_area_name") and obj.aggregation_area_name:
58
66
  if (
59
67
  obj.aggregation_area_type
60
68
  not in self._database.static.get_aggregation_areas()
@@ -60,10 +60,9 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
60
60
  object_list = self._get_object_list()
61
61
 
62
62
  # Load all objects
63
- objects = [self._object_class.load_file(path) for path in object_list["path"]]
63
+ objects = [self.get(name) for name in object_list["name"]]
64
64
 
65
- # From the loaded objects, get the name and description and add them to the object_list
66
- object_list["name"] = [obj.name for obj in objects]
65
+ # From the loaded objects, get the description and add them to the object_list
67
66
  object_list["description"] = [obj.description for obj in objects]
68
67
  object_list["objects"] = objects
69
68
  return object_list
@@ -248,7 +247,7 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
248
247
  Returns
249
248
  -------
250
249
  dict[str, Any]
251
- A dictionary that contains the keys: `name` to 'path' and 'last_modification_date'
250
+ A dictionary that contains the keys: `name` to `path` and `last_modification_date`
252
251
  Each key has a list of the corresponding values, where the index of the values corresponds to the same object.
253
252
  """
254
253
  # If the toml doesnt exist, we might be in the middle of saving a new object or could be a broken object.
@@ -200,7 +200,7 @@ class Event(Object):
200
200
  f"Allowed sources are: {allowed_sources}"
201
201
  )
202
202
 
203
- if Settings().validate_allowed_forcings:
203
+ if Settings().validate_allowed_forcings and hasattr(self, "ALLOWED_FORCINGS"):
204
204
  # Validate forcings
205
205
  for _, concrete_forcings in self.forcings.items():
206
206
  for concrete_forcing in concrete_forcings:
@@ -331,15 +331,21 @@ class BlockTimeseries(SyntheticTimeseries):
331
331
  end=(REFERENCE_TIME + self.end_time.to_timedelta()),
332
332
  freq=time_step,
333
333
  )
334
- ts = np.zeros((len(tt),)) + self.peak_value.value
334
+ if self.peak_value:
335
+ height_value = self.peak_value.value
336
+ elif self.cumulative:
337
+ area = self.cumulative.value
338
+ base = self.duration.convert(
339
+ us.UnitTypesTime.hours
340
+ ) # always expect duration in hours
341
+ height_value = area / base
342
+
343
+ ts = np.zeros((len(tt),)) + height_value
335
344
  return ts
336
345
 
337
346
  @model_validator(mode="after")
338
347
  def validate_attrs(self):
339
- if not self.peak_value:
340
- raise ValueError(
341
- f"{self.__class__.__name__} must have `peak_value` specified. {self.peak_value}"
342
- )
348
+ # either peak_value or cumulative must be set, which is already checked in the parent class: `either_value_or_cumulative`
343
349
  return self
344
350
 
345
351
 
@@ -355,17 +361,26 @@ class TriangleTimeseries(SyntheticTimeseries):
355
361
  freq=time_step,
356
362
  )
357
363
  tt_seconds = (tt - REFERENCE_TIME).total_seconds()
364
+ peak_time = self.peak_time.to_timedelta().total_seconds()
365
+ start_time = self.start_time.to_timedelta().total_seconds()
366
+
367
+ if self.peak_value:
368
+ height_value = self.peak_value.value
369
+ elif self.cumulative:
370
+ area = self.cumulative.value
371
+ base = self.duration.convert(
372
+ us.UnitTypesTime.hours
373
+ ) # always expect duration in hours
374
+ height_value = (2 * area) / base
358
375
 
359
376
  ascending_slope = (
360
- self.peak_value.value
377
+ height_value
361
378
  / (self.peak_time - self.start_time).to_timedelta().total_seconds()
362
379
  )
363
380
  descending_slope = (
364
- -self.peak_value.value
381
+ -height_value
365
382
  / (self.end_time - self.peak_time).to_timedelta().total_seconds()
366
383
  )
367
- peak_time = self.peak_time.to_timedelta().total_seconds()
368
- start_time = self.start_time.to_timedelta().total_seconds()
369
384
 
370
385
  ts = np.piecewise(
371
386
  tt_seconds,
@@ -373,7 +388,7 @@ class TriangleTimeseries(SyntheticTimeseries):
373
388
  [
374
389
  lambda x: np.maximum(ascending_slope * (x - start_time), 0),
375
390
  lambda x: np.maximum(
376
- descending_slope * (x - peak_time) + self.peak_value.value, 0
391
+ descending_slope * (x - peak_time) + height_value, 0
377
392
  ),
378
393
  0,
379
394
  ],
@@ -382,10 +397,7 @@ class TriangleTimeseries(SyntheticTimeseries):
382
397
 
383
398
  @model_validator(mode="after")
384
399
  def validate_attrs(self):
385
- if not self.peak_value:
386
- raise ValueError(
387
- f"{self.__class__.__name__} must have `peak_value` specified. {self.peak_value}"
388
- )
400
+ # either peak_value or cumulative must be set, which is already checked in the parent class: `either_value_or_cumulative`
389
401
  return self
390
402
 
391
403
 
@@ -549,4 +561,4 @@ class TimeseriesFactory:
549
561
 
550
562
  @staticmethod
551
563
  def from_object(obj: SyntheticTimeseries) -> SyntheticTimeseries:
552
- return TimeseriesFactory.from_args(**obj.model_dump())
564
+ return TimeseriesFactory.from_args(**obj.model_dump(exclude_none=True))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: flood-adapt
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A software package support system which can be used to assess the benefits and costs of flood resilience measures
5
5
  Author-email: Gundula Winter <Gundula.Winter@deltares.nl>, Panos Athanasiou <Panos.Athanasiou@deltares.nl>, Frederique de Groen <Frederique.deGroen@deltares.nl>, Tim de Wilde <Tim.deWilde@deltares.nl>, Julian Hofer <Julian.Hofer@deltares.nl>, Daley Adrichem <Daley.Adrichem@deltares.nl>, Luuk Blom <Luuk.Blom@deltares.nl>
6
6
  Project-URL: Source, https://github.com/Deltares-research/FloodAdapt
@@ -1,4 +1,4 @@
1
- flood_adapt/__init__.py,sha256=qr2n76U7FJKoIQRAha8u1esAPHPpP3yEWXkbE1GWAKI,596
1
+ flood_adapt/__init__.py,sha256=YmSOHgogAZ9iXZ-KEQR6w8Yk-H-T8_aY8UZ2jcjCIl0,596
2
2
  flood_adapt/database_builder.py,sha256=eq_LAqaGpV-KJkLNBVvXjyjFRDQaBFA5ubANiuVsZWU,420
3
3
  flood_adapt/flood_adapt.py,sha256=9_JJtB96QWF9GEYTzs4WEAScYPYwt8Ye3VnCD5UHMAU,39229
4
4
  flood_adapt/adapter/__init__.py,sha256=C6XBZK8x4MHe3beeKa9rPbXn0-gBHPEPnTKn14C-5gI,249
@@ -75,15 +75,15 @@ flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographi
75
75
  flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml,sha256=ayNiJHEMdRk82nhfV1srRNavUtxpQEWX71fJf9nsxu4,1988
76
76
  flood_adapt/database_builder/templates/mapbox_layers/bin_colors.toml,sha256=OXlAdFfu3S99qrxFqn_Xu6ZIZMvk-0_LQhdsj5q_rZk,412
77
77
  flood_adapt/dbs_classes/__init__.py,sha256=_8DX6dK62U1RjRKz0zOgM0yR-kiBlBxz2dLkCWylmXc,710
78
- flood_adapt/dbs_classes/database.py,sha256=RBwC_fEFAp48fxiyNay6am1rGGb55LL48UBWrT3YWlw,25332
78
+ flood_adapt/dbs_classes/database.py,sha256=FVpwnx4gEWnY6IEALsfWgZNk3DrZXW5blEzRPdm__ew,25772
79
79
  flood_adapt/dbs_classes/dbs_benefit.py,sha256=Fzm0eCWOPFLorKkk86Qzt4Wq0ZGJTh1vwJ5I6vm-6zQ,3096
80
- flood_adapt/dbs_classes/dbs_event.py,sha256=5khfCMn05TPe4mA1o6ApAvtIPMmlLrT_uLe_40M-r_k,2702
81
- flood_adapt/dbs_classes/dbs_measure.py,sha256=oOOkSPuaq2seG3l2c2nZ_eMi9SrH-c7EZnC2ijTXI1Q,3613
80
+ flood_adapt/dbs_classes/dbs_event.py,sha256=EfupVyP5XwiNM9dTuJBKCqQBruAO_Ma_IgoxlFlG-L8,2150
81
+ flood_adapt/dbs_classes/dbs_measure.py,sha256=EwIBphrR48jIoyRvRYq8dF8tBchq3xftD4kzgFQxbD4,3868
82
82
  flood_adapt/dbs_classes/dbs_projection.py,sha256=wVtcP8grBbQ-Y2_Rvjp9Rw8ae2uZoLeGRutiuf0aJrg,1575
83
83
  flood_adapt/dbs_classes/dbs_scenario.py,sha256=P7yyLSYt-W1TgNbsNyF9XvB6UNv-9qVR5DXfq6G4UaQ,5138
84
84
  flood_adapt/dbs_classes/dbs_static.py,sha256=LKBDrqoRcLUZmkT4683kB61kbgHp5atVE5qq6U9dpig,9099
85
85
  flood_adapt/dbs_classes/dbs_strategy.py,sha256=kgqbnUyG9_IMS1oIsqgZkiMgrDuhV_Hw0pfFQcJDMjc,5284
86
- flood_adapt/dbs_classes/dbs_template.py,sha256=mii9PpngbEtFUO4Y0Xj_AtFcXn_HO2H7kW1eGmf0AbI,11118
86
+ flood_adapt/dbs_classes/dbs_template.py,sha256=cSSoXu8_kvReOLQxTeIoBms3cz9lqG6OxcmcZ93hrVw,11029
87
87
  flood_adapt/dbs_classes/interface/database.py,sha256=HcJWAQP3mx5rKWvdHeqG8id-AqzYgOXGIrIAR7WhkmA,3510
88
88
  flood_adapt/dbs_classes/interface/element.py,sha256=gKFtE-a0cnkMpXrAwrYFWSvzhePSbDqFxwz6ChS0O6E,3772
89
89
  flood_adapt/dbs_classes/interface/static.py,sha256=3kLaWabSWKe7h8-SiBTJlr09sO8Ykbk4NIE5mrMoCp0,1225
@@ -99,7 +99,7 @@ flood_adapt/objects/benefits/benefits.py,sha256=Jk-PsHM3sxp6pUoT28tMuxF-IsONVNH5
99
99
  flood_adapt/objects/events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
100
  flood_adapt/objects/events/event_factory.py,sha256=sGBNyFijKXT-Ltx7vEyiJpnSnBOoSLnYyuI6NC7ZtuM,4068
101
101
  flood_adapt/objects/events/event_set.py,sha256=NqaP1TA4pXWtjDG0bfIflT2E6aPPqULClenWLqNsG4A,2584
102
- flood_adapt/objects/events/events.py,sha256=g1PrdBJostlVN6DFiJoyCf8LJ8pjcCNwOnvnK78z8PM,8118
102
+ flood_adapt/objects/events/events.py,sha256=GKVRDNCLtGUhABIdpzk1v_J0g2fB2OFNSCO6-ERTuBM,8156
103
103
  flood_adapt/objects/events/historical.py,sha256=AvafuI8Pc8SWie7e7p0yxLM7IXJ7WJnpSF90iJacJGI,1679
104
104
  flood_adapt/objects/events/hurricane.py,sha256=QwnXQ2jXAqDHP82ANVAJZlZQ2umoJLA401Bx6X6KT4I,2093
105
105
  flood_adapt/objects/events/synthetic.py,sha256=kJy2DN9JqXsBqhW7nzgBwTltscHTnoKpbE0jH7u4BmA,1380
@@ -114,7 +114,7 @@ flood_adapt/objects/forcing/plotting.py,sha256=dfhSY6_IfJzG1N0ivGTWjCMtiBj-4UAyG
114
114
  flood_adapt/objects/forcing/rainfall.py,sha256=ndR1T-Zi-m7eek-7w1VGJC3xPlDq97yGvgqKctz647c,3184
115
115
  flood_adapt/objects/forcing/tide_gauge.py,sha256=T6XLOAuFvUjThbg0PcSbMs1_m-SpZ9W2pQ-4q48HdNk,6996
116
116
  flood_adapt/objects/forcing/time_frame.py,sha256=u1slwo2aJHG3lK7QWNMY1yL84adpW7yfrlMlZO1sgbs,2415
117
- flood_adapt/objects/forcing/timeseries.py,sha256=NR0jR9j5HQEtUZlCc0eFhsREVZArl9Mg2TSD9RrQR5Q,18641
117
+ flood_adapt/objects/forcing/timeseries.py,sha256=uSWIRP7KNsH_iy0DcE2S1gaP0mFtft2pEPA74redUGE,19178
118
118
  flood_adapt/objects/forcing/unit_system.py,sha256=oYk09TC0ksyeqHmT2CkA2fPkKYFR2YeT-ymvnXAAbJM,15922
119
119
  flood_adapt/objects/forcing/waterlevels.py,sha256=oKVKXA0SMbOPujCFn53agpJpqXuC7cawF8wfHbxUSgA,3329
120
120
  flood_adapt/objects/forcing/wind.py,sha256=VtbopiBGyi2T6Id0fnRKYLTzfRt9f_QxupyxIyFZQKI,3801
@@ -132,8 +132,8 @@ flood_adapt/workflows/benefit_runner.py,sha256=nLmWqbzBsXJOikCnm7S6tSbJ9Ex1zjkVL
132
132
  flood_adapt/workflows/floodmap.py,sha256=6XatakhiZDkIi82VpRDb5dWtWAs-aSho8_iaCGph1_c,2932
133
133
  flood_adapt/workflows/impacts_integrator.py,sha256=Iq8namwu90SP_BTazUsDqROqOrJk7rGEyR6nA1Q7Vho,2330
134
134
  flood_adapt/workflows/scenario_runner.py,sha256=6d1ypkHNdFgL7WMEXiM3scBs43IfLFqueGqL4LI9emQ,2833
135
- flood_adapt-0.3.0.dist-info/LICENSE,sha256=K5036N4YhOu7BzQ4o2tQbe1NBYOqI-WF3ahlmdm5PlU,1065
136
- flood_adapt-0.3.0.dist-info/METADATA,sha256=dGVGJfUuA-I3wTikKwVeyFJfsQcvM8q6UJ7Sriv173I,10801
137
- flood_adapt-0.3.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
138
- flood_adapt-0.3.0.dist-info/top_level.txt,sha256=JvzMi6cTcQPEThCfpgMEeVny3ghI1urSH0CCgVIqSzw,12
139
- flood_adapt-0.3.0.dist-info/RECORD,,
135
+ flood_adapt-0.3.1.dist-info/LICENSE,sha256=K5036N4YhOu7BzQ4o2tQbe1NBYOqI-WF3ahlmdm5PlU,1065
136
+ flood_adapt-0.3.1.dist-info/METADATA,sha256=PZENK0K3uWXuKMLj3Fzi-a6aniKcM9utSXy3hHixA4s,10801
137
+ flood_adapt-0.3.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
138
+ flood_adapt-0.3.1.dist-info/top_level.txt,sha256=JvzMi6cTcQPEThCfpgMEeVny3ghI1urSH0CCgVIqSzw,12
139
+ flood_adapt-0.3.1.dist-info/RECORD,,