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,261 @@
1
+ from pathlib import Path
2
+ from typing import Any, Callable, Tuple, Union
3
+
4
+ import geopandas as gpd
5
+ import pandas as pd
6
+ from cht_cyclones.cyclone_track_database import CycloneTrackDatabase
7
+
8
+ from flood_adapt.adapter.fiat_adapter import FiatAdapter
9
+ from flood_adapt.adapter.sfincs_adapter import SfincsAdapter
10
+ from flood_adapt.config.config import Settings
11
+ from flood_adapt.dbs_classes.interface.database import IDatabase
12
+ from flood_adapt.dbs_classes.interface.static import IDbsStatic
13
+
14
+
15
+ def cache_method_wrapper(func: Callable) -> Callable:
16
+ def wrapper(self, *args: Tuple[Any], **kwargs: dict[str, Any]) -> Any:
17
+ if func.__name__ not in self._cached_data:
18
+ self._cached_data[func.__name__] = {}
19
+
20
+ args_key = (
21
+ str(args) + str(sorted(kwargs.items())) if args or kwargs else "no_args"
22
+ )
23
+ if args_key in self._cached_data[func.__name__]:
24
+ return self._cached_data[func.__name__][args_key]
25
+
26
+ result = func(self, *args, **kwargs)
27
+ self._cached_data[func.__name__][args_key] = result
28
+
29
+ return result
30
+
31
+ return wrapper
32
+
33
+
34
+ class DbsStatic(IDbsStatic):
35
+ _cached_data: dict[str, Any] = {}
36
+ _database: IDatabase
37
+
38
+ def __init__(self, database: IDatabase):
39
+ """Initialize any necessary attributes."""
40
+ self._database = database
41
+
42
+ @cache_method_wrapper
43
+ def get_aggregation_areas(self) -> dict[str, gpd.GeoDataFrame]:
44
+ """Get a list of the aggregation areas that are provided in the site configuration.
45
+
46
+ These are expected to much the ones in the FIAT model.
47
+
48
+ Returns
49
+ -------
50
+ list[gpd.GeoDataFrame]
51
+ list of gpd.GeoDataFrames with the polygons defining the aggregation areas
52
+ """
53
+ aggregation_areas = {}
54
+ for aggr_dict in self._database.site.fiat.config.aggregation:
55
+ aggregation_areas[aggr_dict.name] = gpd.read_file(
56
+ self._database.static_path / aggr_dict.file,
57
+ engine="pyogrio",
58
+ ).to_crs(4326)
59
+ # Use always the same column name for name labels
60
+ aggregation_areas[aggr_dict.name] = aggregation_areas[
61
+ aggr_dict.name
62
+ ].rename(columns={aggr_dict.field_name: "name"})
63
+ # Make sure they are ordered alphabetically
64
+ aggregation_areas[aggr_dict.name].sort_values(by="name").reset_index(
65
+ drop=True
66
+ )
67
+ return aggregation_areas
68
+
69
+ @cache_method_wrapper
70
+ def get_model_boundary(self) -> gpd.GeoDataFrame:
71
+ """Get the model boundary from the SFINCS model."""
72
+ bnd = self.get_overland_sfincs_model().get_model_boundary()
73
+ return bnd
74
+
75
+ @cache_method_wrapper
76
+ def get_model_grid(self):
77
+ """Get the model grid from the SFINCS model.
78
+
79
+ Returns
80
+ -------
81
+ QuadtreeGrid
82
+ The model grid
83
+ """
84
+ grid = self.get_overland_sfincs_model().get_model_grid()
85
+ return grid
86
+
87
+ @cache_method_wrapper
88
+ def get_obs_points(self) -> gpd.GeoDataFrame:
89
+ """Get the observation points from the flood hazard model."""
90
+ names = []
91
+ descriptions = []
92
+ lat = []
93
+ lon = []
94
+ if self._database.site.sfincs.obs_point is not None:
95
+ obs_points = self._database.site.sfincs.obs_point
96
+ for pt in obs_points:
97
+ names.append(pt.name)
98
+ descriptions.append(pt.description)
99
+ lat.append(pt.lat)
100
+ lon.append(pt.lon)
101
+
102
+ # create gpd.GeoDataFrame from obs_points in site file
103
+ df = pd.DataFrame({"name": names, "description": descriptions})
104
+ # TODO: make crs flexible and add this as a parameter to site.toml?
105
+ gdf = gpd.GeoDataFrame(
106
+ df, geometry=gpd.points_from_xy(lon, lat), crs="EPSG:4326"
107
+ )
108
+ return gdf
109
+
110
+ @cache_method_wrapper
111
+ def get_static_map(self, path: Union[str, Path]) -> gpd.GeoDataFrame:
112
+ """Get a map from the static folder.
113
+
114
+ Parameters
115
+ ----------
116
+ path : Union[str, Path]
117
+ Path to the map relative to the static folder
118
+
119
+ Returns
120
+ -------
121
+ gpd.GeoDataFrame
122
+ gpd.GeoDataFrame with the map in crs 4326
123
+
124
+ Raises
125
+ ------
126
+ FileNotFoundError
127
+ If the file is not found
128
+ """
129
+ # Read the map
130
+ full_path = self._database.static_path / path
131
+ if full_path.is_file():
132
+ return gpd.read_file(full_path, engine="pyogrio").to_crs(4326)
133
+
134
+ # If the file is not found, throw an error
135
+ raise FileNotFoundError(f"File {full_path} not found")
136
+
137
+ @cache_method_wrapper
138
+ def get_slr_scn_names(self) -> list:
139
+ """Get the names of the sea level rise scenarios from the file provided.
140
+
141
+ Returns
142
+ -------
143
+ list
144
+ List of scenario names
145
+ """
146
+ input_file = self._database.static_path.joinpath(
147
+ self._database.site.sfincs.slr_scenarios.file
148
+ )
149
+ df = pd.read_csv(input_file)
150
+ names = df.columns[2:].to_list()
151
+ return names
152
+
153
+ @cache_method_wrapper
154
+ def get_green_infra_table(self, measure_type: str) -> pd.DataFrame:
155
+ """Return a table with different types of green infrastructure measures and their infiltration depths.
156
+
157
+ This is read by a csv file in the database.
158
+
159
+ Returns
160
+ -------
161
+ pd.DataFrame
162
+ Table with values
163
+ """
164
+ # Read file from database
165
+ df = pd.read_csv(
166
+ self._database.static_path.joinpath(
167
+ "green_infra_table", "green_infra_lookup_table.csv"
168
+ )
169
+ )
170
+
171
+ # Get column with values
172
+ val_name = "Infiltration depth"
173
+ col_name = [name for name in df.columns if val_name in name][0]
174
+ if not col_name:
175
+ raise KeyError(f"A column with a name containing {val_name} was not found!")
176
+
177
+ # Get list of types per measure
178
+ df["types"] = [
179
+ [x.strip() for x in row["types"].split(",")] for i, row in df.iterrows()
180
+ ]
181
+
182
+ # Show specific values based on measure type
183
+ inds = [i for i, row in df.iterrows() if measure_type in row["types"]]
184
+ df = df.drop(columns="types").iloc[inds, :]
185
+
186
+ return df
187
+
188
+ @cache_method_wrapper
189
+ def get_buildings(self) -> gpd.GeoDataFrame:
190
+ """Get the building footprints from the FIAT model.
191
+
192
+ This should only be the buildings excluding any other types (e.g., roads)
193
+ The parameters non_building_names in the site config is used for that.
194
+
195
+ Returns
196
+ -------
197
+ gpd.GeoDataFrame
198
+ building footprints with all the FIAT columns
199
+ """
200
+ return self.get_fiat_model().get_buildings()
201
+
202
+ @cache_method_wrapper
203
+ def get_property_types(self) -> list:
204
+ """_summary_.
205
+
206
+ Returns
207
+ -------
208
+ list
209
+ _description_
210
+ """
211
+ return self.get_fiat_model().get_property_types()
212
+
213
+ def get_overland_sfincs_model(self) -> SfincsAdapter:
214
+ """Get the template offshore SFINCS model."""
215
+ overland_path = (
216
+ self._database.static_path
217
+ / "templates"
218
+ / self._database.site.sfincs.config.overland_model.name
219
+ )
220
+ with SfincsAdapter(model_root=overland_path) as overland_model:
221
+ return overland_model
222
+
223
+ def get_offshore_sfincs_model(self) -> SfincsAdapter:
224
+ """Get the template overland Sfincs model."""
225
+ if self._database.site.sfincs.config.offshore_model is None:
226
+ raise ValueError("No offshore model defined in the site configuration.")
227
+
228
+ offshore_path = (
229
+ self._database.static_path
230
+ / "templates"
231
+ / self._database.site.sfincs.config.offshore_model.name
232
+ )
233
+ with SfincsAdapter(model_root=offshore_path) as offshore_model:
234
+ return offshore_model
235
+
236
+ def get_fiat_model(self) -> FiatAdapter:
237
+ """Get the path to the FIAT model."""
238
+ if self._database.site.fiat is None:
239
+ raise ValueError("No FIAT model defined in the site configuration.")
240
+ template_path = self._database.static_path / "templates" / "fiat"
241
+ with FiatAdapter(
242
+ model_root=template_path,
243
+ config=self._database.site.fiat.config,
244
+ exe_path=Settings().fiat_path,
245
+ delete_crashed_runs=Settings().delete_crashed_runs,
246
+ config_base_path=self._database.static_path,
247
+ ) as fm:
248
+ return fm
249
+
250
+ @cache_method_wrapper
251
+ def get_cyclone_track_database(self) -> CycloneTrackDatabase:
252
+ if self._database.site.sfincs.cyclone_track_database is None:
253
+ raise ValueError(
254
+ "No cyclone track database defined in the site configuration."
255
+ )
256
+ database_file = str(
257
+ self._database.static_path
258
+ / "cyclone_track_database"
259
+ / self._database.site.sfincs.cyclone_track_database.file
260
+ )
261
+ return CycloneTrackDatabase("ibtracs", file_name=database_file)
@@ -0,0 +1,147 @@
1
+ from itertools import combinations
2
+
3
+ from flood_adapt.dbs_classes.dbs_template import DbsTemplate
4
+ from flood_adapt.objects.measures.measures import MeasureType
5
+ from flood_adapt.objects.strategies.strategies import Strategy
6
+
7
+
8
+ class DbsStrategy(DbsTemplate[Strategy]):
9
+ dir_name = "strategies"
10
+ display_name = "Strategy"
11
+ _object_class = Strategy
12
+
13
+ def get(self, name: str) -> Strategy:
14
+ strategy = super().get(name)
15
+ measures = [
16
+ self._database.measures.get(measure) for measure in strategy.measures
17
+ ]
18
+ strategy.initialize_measure_objects(measures)
19
+ return strategy
20
+
21
+ def save(
22
+ self,
23
+ object_model: Strategy,
24
+ overwrite: bool = False,
25
+ ):
26
+ """Save an object in the database and all associated files.
27
+
28
+ This saves the toml file and any additional files attached to the object.
29
+
30
+ Parameters
31
+ ----------
32
+ object_model : Object
33
+ object to be saved in the database
34
+ overwrite : bool, optional
35
+ whether to overwrite the object if it already exists in the
36
+ database, by default False
37
+
38
+ Raises
39
+ ------
40
+ ValueError
41
+ Raise error if name is already in use.
42
+ """
43
+ object_exists = object_model.name in self.list_objects()["name"]
44
+
45
+ # If you want to overwrite the object, and the object already exists, first delete it. If it exists and you
46
+ # don't want to overwrite, raise an error.
47
+ if overwrite and object_exists:
48
+ self.delete(object_model.name, toml_only=True)
49
+ elif not overwrite and object_exists:
50
+ raise ValueError(
51
+ f"'{object_model.name}' name is already used by another {self.display_name}. Choose a different name"
52
+ )
53
+
54
+ # Check if any measures overlap
55
+ self._check_overlapping_measures(object_model.measures)
56
+
57
+ # If the folder doesnt exist yet, make the folder and save the object
58
+ if not (self.input_path / object_model.name).exists():
59
+ (self.input_path / object_model.name).mkdir()
60
+
61
+ # Save the object and any additional files
62
+ object_model.save(
63
+ self.input_path / object_model.name / f"{object_model.name}.toml",
64
+ )
65
+
66
+ def _check_overlapping_measures(self, measures: list[str]):
67
+ """Validate if the combination of impact measures can happen, since impact measures cannot affect the same properties.
68
+
69
+ Raises
70
+ ------
71
+ ValueError
72
+ information on which combinations of measures have overlapping properties
73
+ """
74
+ measure_objects = [self._database.measures.get(measure) for measure in measures]
75
+ impact_measures = [
76
+ measure
77
+ for measure in measure_objects
78
+ if MeasureType.is_impact(measure.type)
79
+ ]
80
+
81
+ adapter = self._database.static.get_fiat_model()
82
+
83
+ ids = [adapter.get_object_ids(measure) for measure in impact_measures]
84
+
85
+ # Get all possible pairs of measures and check overlapping buildings for each measure
86
+ combs = list(combinations(enumerate(ids), 2))
87
+ common_elements = []
88
+ for comb in combs:
89
+ common_elements.append(list(set(comb[0][1]).intersection(comb[1][1])))
90
+ # If there is any combination with overlapping buildings raise Error and do not allow for Strategy object creation
91
+ overlapping = [len(k) > 0 for k in common_elements]
92
+
93
+ if any(overlapping):
94
+ msg = "Cannot create strategy! There are overlapping buildings for which measures are proposed"
95
+ counter = 0
96
+ for i, comb in enumerate(combs):
97
+ if overlapping[i]:
98
+ if counter > 0:
99
+ msg += " and"
100
+ msg += " between '{}' and '{}'".format(
101
+ impact_measures[comb[0][0]].name,
102
+ impact_measures[comb[1][0]].name,
103
+ )
104
+ counter += 1
105
+ raise ValueError(msg)
106
+
107
+ def _check_standard_objects(self, name: str) -> bool:
108
+ """Check if a strategy is a standard strategy.
109
+
110
+ Parameters
111
+ ----------
112
+ name : str
113
+ name of the strategy to be checked
114
+
115
+ Raises
116
+ ------
117
+ ValueError
118
+ Raise error if strategy is a standard strategy.
119
+ """
120
+ # Check if strategy is a standard strategy
121
+ if self._database.site.standard_objects:
122
+ if self._database.site.standard_objects.strategies:
123
+ if name in self._database.site.standard_objects.strategies:
124
+ return True
125
+ return False
126
+
127
+ def check_higher_level_usage(self, name: str) -> list[str]:
128
+ """Check if a strategy is used in a scenario.
129
+
130
+ Parameters
131
+ ----------
132
+ name : str
133
+ name of the strategy to be checked
134
+
135
+ Returns
136
+ -------
137
+ list[str]
138
+ list of scenarios that use the strategy
139
+ """
140
+ # Check if strategy is used in a scenario
141
+ used_in_scenario = [
142
+ scenario.name
143
+ for scenario in self._database.scenarios.list_objects()["objects"]
144
+ if name == scenario.strategy
145
+ ]
146
+
147
+ return used_in_scenario