flood-adapt 0.3.9__py3-none-any.whl → 0.3.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. flood_adapt/__init__.py +26 -22
  2. flood_adapt/adapter/__init__.py +9 -9
  3. flood_adapt/adapter/fiat_adapter.py +1541 -1541
  4. flood_adapt/adapter/interface/hazard_adapter.py +70 -70
  5. flood_adapt/adapter/interface/impact_adapter.py +36 -36
  6. flood_adapt/adapter/interface/model_adapter.py +89 -89
  7. flood_adapt/adapter/interface/offshore.py +19 -19
  8. flood_adapt/adapter/sfincs_adapter.py +1848 -1848
  9. flood_adapt/adapter/sfincs_offshore.py +193 -193
  10. flood_adapt/config/config.py +248 -248
  11. flood_adapt/config/fiat.py +219 -219
  12. flood_adapt/config/gui.py +331 -331
  13. flood_adapt/config/sfincs.py +481 -336
  14. flood_adapt/config/site.py +129 -129
  15. flood_adapt/database_builder/database_builder.py +2210 -2210
  16. flood_adapt/database_builder/templates/default_units/imperial.toml +9 -9
  17. flood_adapt/database_builder/templates/default_units/metric.toml +9 -9
  18. flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +10 -10
  19. flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +90 -90
  20. flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +57 -57
  21. flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +121 -121
  22. flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +65 -65
  23. flood_adapt/database_builder/templates/infographics/OSM/styles.css +45 -45
  24. flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +126 -126
  25. flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +60 -60
  26. flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +121 -121
  27. flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +65 -65
  28. flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +45 -45
  29. flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +4 -4
  30. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +143 -143
  31. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +153 -153
  32. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +127 -127
  33. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +57 -57
  34. flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +4 -4
  35. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +191 -191
  36. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +153 -153
  37. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +178 -178
  38. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +57 -57
  39. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +9 -9
  40. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +65 -65
  41. flood_adapt/database_builder/templates/output_layers/bin_colors.toml +5 -5
  42. flood_adapt/database_builder.py +16 -16
  43. flood_adapt/dbs_classes/__init__.py +21 -21
  44. flood_adapt/dbs_classes/database.py +495 -684
  45. flood_adapt/dbs_classes/dbs_benefit.py +77 -76
  46. flood_adapt/dbs_classes/dbs_event.py +61 -59
  47. flood_adapt/dbs_classes/dbs_measure.py +112 -111
  48. flood_adapt/dbs_classes/dbs_projection.py +34 -34
  49. flood_adapt/dbs_classes/dbs_scenario.py +137 -137
  50. flood_adapt/dbs_classes/dbs_static.py +274 -273
  51. flood_adapt/dbs_classes/dbs_strategy.py +130 -129
  52. flood_adapt/dbs_classes/dbs_template.py +279 -278
  53. flood_adapt/dbs_classes/interface/database.py +107 -139
  54. flood_adapt/dbs_classes/interface/element.py +121 -121
  55. flood_adapt/dbs_classes/interface/static.py +47 -47
  56. flood_adapt/flood_adapt.py +1207 -1178
  57. flood_adapt/misc/database_user.py +16 -16
  58. flood_adapt/misc/exceptions.py +22 -0
  59. flood_adapt/misc/log.py +183 -183
  60. flood_adapt/misc/path_builder.py +54 -54
  61. flood_adapt/misc/utils.py +185 -185
  62. flood_adapt/objects/__init__.py +82 -82
  63. flood_adapt/objects/benefits/benefits.py +61 -61
  64. flood_adapt/objects/events/event_factory.py +135 -135
  65. flood_adapt/objects/events/event_set.py +88 -84
  66. flood_adapt/objects/events/events.py +234 -234
  67. flood_adapt/objects/events/historical.py +58 -58
  68. flood_adapt/objects/events/hurricane.py +68 -67
  69. flood_adapt/objects/events/synthetic.py +46 -50
  70. flood_adapt/objects/forcing/__init__.py +92 -92
  71. flood_adapt/objects/forcing/csv.py +68 -68
  72. flood_adapt/objects/forcing/discharge.py +66 -66
  73. flood_adapt/objects/forcing/forcing.py +150 -150
  74. flood_adapt/objects/forcing/forcing_factory.py +182 -182
  75. flood_adapt/objects/forcing/meteo_handler.py +93 -93
  76. flood_adapt/objects/forcing/netcdf.py +40 -40
  77. flood_adapt/objects/forcing/plotting.py +453 -429
  78. flood_adapt/objects/forcing/rainfall.py +98 -98
  79. flood_adapt/objects/forcing/tide_gauge.py +191 -191
  80. flood_adapt/objects/forcing/time_frame.py +90 -90
  81. flood_adapt/objects/forcing/timeseries.py +564 -564
  82. flood_adapt/objects/forcing/unit_system.py +580 -580
  83. flood_adapt/objects/forcing/waterlevels.py +108 -108
  84. flood_adapt/objects/forcing/wind.py +124 -124
  85. flood_adapt/objects/measures/measure_factory.py +92 -92
  86. flood_adapt/objects/measures/measures.py +529 -529
  87. flood_adapt/objects/object_model.py +74 -68
  88. flood_adapt/objects/projections/projections.py +103 -103
  89. flood_adapt/objects/scenarios/scenarios.py +22 -22
  90. flood_adapt/objects/strategies/strategies.py +89 -89
  91. flood_adapt/workflows/benefit_runner.py +579 -554
  92. flood_adapt/workflows/floodmap.py +85 -85
  93. flood_adapt/workflows/impacts_integrator.py +85 -85
  94. flood_adapt/workflows/scenario_runner.py +70 -70
  95. {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/LICENSE +674 -674
  96. {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/METADATA +866 -865
  97. flood_adapt-0.3.10.dist-info/RECORD +140 -0
  98. flood_adapt-0.3.9.dist-info/RECORD +0 -139
  99. {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/WHEEL +0 -0
  100. {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/top_level.txt +0 -0
@@ -1,248 +1,248 @@
1
- from os import environ, listdir
2
- from pathlib import Path
3
- from typing import Optional
4
-
5
- import tomli
6
- import tomli_w
7
- from pydantic import (
8
- Field,
9
- computed_field,
10
- field_serializer,
11
- model_validator,
12
- )
13
- from pydantic_settings import BaseSettings, SettingsConfigDict
14
-
15
-
16
- class Settings(BaseSettings):
17
- """
18
- The configuration settings for the FloodAdapt database and integrator.
19
-
20
- Precedence is as follows: user arguments > environment variables > defaults in this class.
21
- When loading is done, the settings are validated and the environment variables are updated with the loaded values.
22
-
23
- If any required settings are missing or invalid, a ValidationError is raised.
24
-
25
- Usage
26
- -----
27
- from flood_adapt import Settings
28
-
29
- One of the following:
30
-
31
- 1) Load settings from environment variables, if no environment variables are set, use defaults defined in the class:
32
- `settings = Settings()`
33
-
34
- 2) Load settings from a .toml file, overwriting any environment variables set:
35
- `settings = Settings.read(toml_path: Path)`
36
-
37
- 3) Load settings from keyword arguments, overwriting any environment variables:
38
- `settings = Settings(DATABASE_ROOT="path/to/database", DATABASE_NAME="database_name")`
39
-
40
- Attributes
41
- ----------
42
- database_name : str
43
- The name of the database. Alias: `DATABASE_NAME` (environment variable).
44
- database_root : Path
45
- The root directory of the database. Alias: `DATABASE_ROOT` (environment variable).
46
- delete_crashed_runs : bool
47
- Whether to delete crashed/corrupted runs immediately after they are detected. Alias: `DELETE_CRASHED_RUNS` (environment variable).
48
- validate_allowed_forcings : bool
49
- Whether to validate the forcing types and sources against the allowed forcings in the event model. Alias: `VALIDATE_ALLOWED_FORCINGS` (environment variable).
50
- validate_binaries : bool
51
- Whether to validate the existence of the paths to the SFINCS and FIAT binaries. Alias: `VALIDATE_BINARIES` (environment variable).
52
- sfincs_bin_path : Path
53
- The path to the SFINCS binary. Alias: `SFINCS_BIN_PATH` (environment variable).
54
- fiat_bin_path : Path
55
- The path to the FIAT binary. Alias: `FIAT_BIN_PATH` (environment variable).
56
-
57
- Properties
58
- ----------
59
- database_path : Path
60
- The full path to the database.
61
-
62
- Raises
63
- ------
64
- ValidationError
65
- If required settings are missing or invalid.
66
- """
67
-
68
- model_config = SettingsConfigDict(env_ignore_empty=True, validate_default=True)
69
-
70
- database_root: Path = Field(
71
- alias="DATABASE_ROOT", # environment variable DATABASE_ROOT
72
- default=Path(__file__).parents[3]
73
- / "Database", # If you clone FloodAdapt, default is to look for the Database next to the FloodAdapt folder
74
- description="The root directory of the database that contains site(s). Usually the directory name is 'Database'. Default is to look for the Database in the same dir as the FloodAdapt cloned repo.",
75
- )
76
- database_name: str = Field(
77
- alias="DATABASE_NAME", # environment variable DATABASE_NAME
78
- default="",
79
- description="The name of the database site, should be a folder inside the database root. The site must contain an 'input' and 'static' folder.",
80
- )
81
-
82
- delete_crashed_runs: bool = Field(
83
- alias="DELETE_CRASHED_RUNS", # environment variable: DELETE_CRASHED_RUNS
84
- default=False,
85
- description="Whether to delete the output of crashed/corrupted runs. Be careful when setting this to False, as it may lead to a broken database that cannot be read in anymore.",
86
- exclude=True,
87
- )
88
- validate_allowed_forcings: bool = Field(
89
- alias="VALIDATE_ALLOWED_FORCINGS", # environment variable: VALIDATE_ALLOWED_FORCINGS
90
- default=False,
91
- description="Whether to validate the forcing types and sources against the allowed forcings in the event model.",
92
- exclude=True,
93
- )
94
- validate_binaries: bool = Field(
95
- alias="VALIDATE_BINARIES", # environment variable: VALIDATE_BINARIES
96
- default=False,
97
- description="Whether to validate the existence of the paths to the SFINCS and FIAT binaries.",
98
- exclude=True,
99
- )
100
-
101
- sfincs_bin_path: Optional[Path] = Field(
102
- default=None,
103
- alias="SFINCS_BIN_PATH", # environment variable: SFINCS_BIN_PATH
104
- description="The path of the sfincs binary.",
105
- exclude=True,
106
- )
107
-
108
- fiat_bin_path: Optional[Path] = Field(
109
- default=None,
110
- alias="FIAT_BIN_PATH", # environment variable: FIAT_BIN_PATH
111
- description="The path of the fiat binary.",
112
- exclude=True,
113
- )
114
-
115
- @computed_field
116
- @property
117
- def database_path(self) -> Path:
118
- return self.database_root / self.database_name
119
-
120
- @model_validator(mode="after")
121
- def validate_settings(self):
122
- self._validate_database_path()
123
- if self.validate_binaries:
124
- self._validate_fiat_path()
125
- self._validate_sfincs_path()
126
- self._update_environment_variables()
127
- return self
128
-
129
- def _update_environment_variables(self):
130
- environ["DATABASE_ROOT"] = str(self.database_root)
131
- environ["DATABASE_NAME"] = self.database_name
132
-
133
- if self.delete_crashed_runs:
134
- environ["DELETE_CRASHED_RUNS"] = str(self.delete_crashed_runs)
135
- else:
136
- environ.pop("DELETE_CRASHED_RUNS", None)
137
-
138
- if self.validate_allowed_forcings:
139
- environ["VALIDATE_ALLOWED_FORCINGS"] = str(self.validate_allowed_forcings)
140
- else:
141
- environ.pop("VALIDATE_ALLOWED_FORCINGS", None)
142
-
143
- if self.validate_binaries:
144
- environ["VALIDATE_BINARIES"] = str(self.validate_binaries)
145
- environ["SFINCS_BIN_PATH"] = str(self.sfincs_bin_path)
146
- environ["FIAT_BIN_PATH"] = str(self.fiat_bin_path)
147
- else:
148
- environ.pop("VALIDATE_BINARIES", None)
149
-
150
- return self
151
-
152
- def _validate_database_path(self):
153
- if not self.database_root.is_dir():
154
- raise ValueError(f"Database root {self.database_root} does not exist.")
155
-
156
- if self.database_name == "":
157
- # If database_name is not given as arg or set in env, compute default as the first dir in database_root excluding 'system'
158
- sites = [
159
- d
160
- for d in listdir(self.database_root)
161
- if d != "system" and not d.startswith(".")
162
- ]
163
- if not sites:
164
- raise ValueError(f"No databases found in {self.database_root}.")
165
- self.database_name = sites[0]
166
-
167
- if not self.database_path.is_dir():
168
- raise ValueError(
169
- f"Database {self.database_name} at {self.database_root} does not exist. Full path: {self.database_path}"
170
- )
171
-
172
- if not (self.database_path / "input").is_dir():
173
- raise ValueError(
174
- f"Database {self.database_name} at {self.database_path} does not contain an input folder."
175
- )
176
-
177
- if not (self.database_path / "static").is_dir():
178
- raise ValueError(
179
- f"Database {self.database_name} at {self.database_path} does not contain a static folder."
180
- )
181
-
182
- return self
183
-
184
- def _validate_sfincs_path(self):
185
- if not self.sfincs_bin_path.exists():
186
- raise ValueError(f"SFINCS binary {self.sfincs_bin_path} does not exist.")
187
- return self
188
-
189
- def _validate_fiat_path(self):
190
- if not self.fiat_bin_path.exists():
191
- raise ValueError(f"FIAT binary {self.fiat_bin_path} does not exist.")
192
- return self
193
-
194
- @field_serializer("database_root", "database_path")
195
- def serialize_path(self, path: Path) -> str:
196
- return str(path)
197
-
198
- @staticmethod
199
- def read(toml_path: Path) -> "Settings":
200
- """
201
- Parse the configuration file and return the parsed settings.
202
-
203
- Parameters
204
- ----------
205
- toml_path : Path
206
- The path to the configuration file.
207
-
208
- Returns
209
- -------
210
- Settings
211
- The parsed configuration settings.
212
-
213
- Raises
214
- ------
215
- ValidationError
216
- If required configuration values are missing or if there is an error parsing the configuration file.
217
- """
218
- with open(toml_path, "rb") as f:
219
- settings = tomli.load(f)
220
-
221
- return Settings(**settings)
222
-
223
- def write(self, toml_path: Path) -> None:
224
- """
225
- Write the configuration settings to a .toml file.
226
-
227
- Parameters
228
- ----------
229
- toml_path : Path
230
- The path to the configuration file.
231
-
232
- Returns
233
- -------
234
- None
235
-
236
- """
237
- toml_path = Path(toml_path).resolve()
238
- if not toml_path.parent.exists():
239
- toml_path.parent.mkdir(parents=True, exist_ok=True)
240
-
241
- with open(toml_path, "wb") as f:
242
- tomli_w.dump(
243
- self.model_dump(
244
- by_alias=True,
245
- exclude={"sfincs_bin_path", "fiat_bin_path", "database_path"},
246
- ),
247
- f,
248
- )
1
+ from os import environ, listdir
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ import tomli
6
+ import tomli_w
7
+ from pydantic import (
8
+ Field,
9
+ computed_field,
10
+ field_serializer,
11
+ model_validator,
12
+ )
13
+ from pydantic_settings import BaseSettings, SettingsConfigDict
14
+
15
+
16
+ class Settings(BaseSettings):
17
+ """
18
+ The configuration settings for the FloodAdapt database and integrator.
19
+
20
+ Precedence is as follows: user arguments > environment variables > defaults in this class.
21
+ When loading is done, the settings are validated and the environment variables are updated with the loaded values.
22
+
23
+ If any required settings are missing or invalid, a ValidationError is raised.
24
+
25
+ Usage
26
+ -----
27
+ from flood_adapt import Settings
28
+
29
+ One of the following:
30
+
31
+ 1) Load settings from environment variables, if no environment variables are set, use defaults defined in the class:
32
+ `settings = Settings()`
33
+
34
+ 2) Load settings from a .toml file, overwriting any environment variables set:
35
+ `settings = Settings.read(toml_path: Path)`
36
+
37
+ 3) Load settings from keyword arguments, overwriting any environment variables:
38
+ `settings = Settings(DATABASE_ROOT="path/to/database", DATABASE_NAME="database_name")`
39
+
40
+ Attributes
41
+ ----------
42
+ database_name : str
43
+ The name of the database. Alias: `DATABASE_NAME` (environment variable).
44
+ database_root : Path
45
+ The root directory of the database. Alias: `DATABASE_ROOT` (environment variable).
46
+ delete_crashed_runs : bool
47
+ Whether to delete crashed/corrupted runs immediately after they are detected. Alias: `DELETE_CRASHED_RUNS` (environment variable).
48
+ validate_allowed_forcings : bool
49
+ Whether to validate the forcing types and sources against the allowed forcings in the event model. Alias: `VALIDATE_ALLOWED_FORCINGS` (environment variable).
50
+ validate_binaries : bool
51
+ Whether to validate the existence of the paths to the SFINCS and FIAT binaries. Alias: `VALIDATE_BINARIES` (environment variable).
52
+ sfincs_bin_path : Path
53
+ The path to the SFINCS binary. Alias: `SFINCS_BIN_PATH` (environment variable).
54
+ fiat_bin_path : Path
55
+ The path to the FIAT binary. Alias: `FIAT_BIN_PATH` (environment variable).
56
+
57
+ Properties
58
+ ----------
59
+ database_path : Path
60
+ The full path to the database.
61
+
62
+ Raises
63
+ ------
64
+ ValidationError
65
+ If required settings are missing or invalid.
66
+ """
67
+
68
+ model_config = SettingsConfigDict(env_ignore_empty=True, validate_default=True)
69
+
70
+ database_root: Path = Field(
71
+ alias="DATABASE_ROOT", # environment variable DATABASE_ROOT
72
+ default=Path(__file__).parents[3]
73
+ / "Database", # If you clone FloodAdapt, default is to look for the Database next to the FloodAdapt folder
74
+ description="The root directory of the database that contains site(s). Usually the directory name is 'Database'. Default is to look for the Database in the same dir as the FloodAdapt cloned repo.",
75
+ )
76
+ database_name: str = Field(
77
+ alias="DATABASE_NAME", # environment variable DATABASE_NAME
78
+ default="",
79
+ description="The name of the database site, should be a folder inside the database root. The site must contain an 'input' and 'static' folder.",
80
+ )
81
+
82
+ delete_crashed_runs: bool = Field(
83
+ alias="DELETE_CRASHED_RUNS", # environment variable: DELETE_CRASHED_RUNS
84
+ default=False,
85
+ description="Whether to delete the output of crashed/corrupted runs. Be careful when setting this to False, as it may lead to a broken database that cannot be read in anymore.",
86
+ exclude=True,
87
+ )
88
+ validate_allowed_forcings: bool = Field(
89
+ alias="VALIDATE_ALLOWED_FORCINGS", # environment variable: VALIDATE_ALLOWED_FORCINGS
90
+ default=False,
91
+ description="Whether to validate the forcing types and sources against the allowed forcings in the event model.",
92
+ exclude=True,
93
+ )
94
+ validate_binaries: bool = Field(
95
+ alias="VALIDATE_BINARIES", # environment variable: VALIDATE_BINARIES
96
+ default=False,
97
+ description="Whether to validate the existence of the paths to the SFINCS and FIAT binaries.",
98
+ exclude=True,
99
+ )
100
+
101
+ sfincs_bin_path: Optional[Path] = Field(
102
+ default=None,
103
+ alias="SFINCS_BIN_PATH", # environment variable: SFINCS_BIN_PATH
104
+ description="The path of the sfincs binary.",
105
+ exclude=True,
106
+ )
107
+
108
+ fiat_bin_path: Optional[Path] = Field(
109
+ default=None,
110
+ alias="FIAT_BIN_PATH", # environment variable: FIAT_BIN_PATH
111
+ description="The path of the fiat binary.",
112
+ exclude=True,
113
+ )
114
+
115
+ @computed_field
116
+ @property
117
+ def database_path(self) -> Path:
118
+ return self.database_root / self.database_name
119
+
120
+ @model_validator(mode="after")
121
+ def validate_settings(self):
122
+ self._validate_database_path()
123
+ if self.validate_binaries:
124
+ self._validate_fiat_path()
125
+ self._validate_sfincs_path()
126
+ self._update_environment_variables()
127
+ return self
128
+
129
+ def _update_environment_variables(self):
130
+ environ["DATABASE_ROOT"] = str(self.database_root)
131
+ environ["DATABASE_NAME"] = self.database_name
132
+
133
+ if self.delete_crashed_runs:
134
+ environ["DELETE_CRASHED_RUNS"] = str(self.delete_crashed_runs)
135
+ else:
136
+ environ.pop("DELETE_CRASHED_RUNS", None)
137
+
138
+ if self.validate_allowed_forcings:
139
+ environ["VALIDATE_ALLOWED_FORCINGS"] = str(self.validate_allowed_forcings)
140
+ else:
141
+ environ.pop("VALIDATE_ALLOWED_FORCINGS", None)
142
+
143
+ if self.validate_binaries:
144
+ environ["VALIDATE_BINARIES"] = str(self.validate_binaries)
145
+ environ["SFINCS_BIN_PATH"] = str(self.sfincs_bin_path)
146
+ environ["FIAT_BIN_PATH"] = str(self.fiat_bin_path)
147
+ else:
148
+ environ.pop("VALIDATE_BINARIES", None)
149
+
150
+ return self
151
+
152
+ def _validate_database_path(self):
153
+ if not self.database_root.is_dir():
154
+ raise ValueError(f"Database root {self.database_root} does not exist.")
155
+
156
+ if self.database_name == "":
157
+ # If database_name is not given as arg or set in env, compute default as the first dir in database_root excluding 'system'
158
+ sites = [
159
+ d
160
+ for d in listdir(self.database_root)
161
+ if d != "system" and not d.startswith(".")
162
+ ]
163
+ if not sites:
164
+ raise ValueError(f"No databases found in {self.database_root}.")
165
+ self.database_name = sites[0]
166
+
167
+ if not self.database_path.is_dir():
168
+ raise ValueError(
169
+ f"Database {self.database_name} at {self.database_root} does not exist. Full path: {self.database_path}"
170
+ )
171
+
172
+ if not (self.database_path / "input").is_dir():
173
+ raise ValueError(
174
+ f"Database {self.database_name} at {self.database_path} does not contain an input folder."
175
+ )
176
+
177
+ if not (self.database_path / "static").is_dir():
178
+ raise ValueError(
179
+ f"Database {self.database_name} at {self.database_path} does not contain a static folder."
180
+ )
181
+
182
+ return self
183
+
184
+ def _validate_sfincs_path(self):
185
+ if not self.sfincs_bin_path.exists():
186
+ raise ValueError(f"SFINCS binary {self.sfincs_bin_path} does not exist.")
187
+ return self
188
+
189
+ def _validate_fiat_path(self):
190
+ if not self.fiat_bin_path.exists():
191
+ raise ValueError(f"FIAT binary {self.fiat_bin_path} does not exist.")
192
+ return self
193
+
194
+ @field_serializer("database_root", "database_path")
195
+ def serialize_path(self, path: Path) -> str:
196
+ return str(path)
197
+
198
+ @staticmethod
199
+ def read(toml_path: Path) -> "Settings":
200
+ """
201
+ Parse the configuration file and return the parsed settings.
202
+
203
+ Parameters
204
+ ----------
205
+ toml_path : Path
206
+ The path to the configuration file.
207
+
208
+ Returns
209
+ -------
210
+ Settings
211
+ The parsed configuration settings.
212
+
213
+ Raises
214
+ ------
215
+ ValidationError
216
+ If required configuration values are missing or if there is an error parsing the configuration file.
217
+ """
218
+ with open(toml_path, "rb") as f:
219
+ settings = tomli.load(f)
220
+
221
+ return Settings(**settings)
222
+
223
+ def write(self, toml_path: Path) -> None:
224
+ """
225
+ Write the configuration settings to a .toml file.
226
+
227
+ Parameters
228
+ ----------
229
+ toml_path : Path
230
+ The path to the configuration file.
231
+
232
+ Returns
233
+ -------
234
+ None
235
+
236
+ """
237
+ toml_path = Path(toml_path).resolve()
238
+ if not toml_path.parent.exists():
239
+ toml_path.parent.mkdir(parents=True, exist_ok=True)
240
+
241
+ with open(toml_path, "wb") as f:
242
+ tomli_w.dump(
243
+ self.model_dump(
244
+ by_alias=True,
245
+ exclude={"sfincs_bin_path", "fiat_bin_path", "database_path"},
246
+ ),
247
+ f,
248
+ )