ras-commander 0.48.0__py3-none-any.whl → 0.49.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.
- ras_commander/Decorators.py +18 -1
- ras_commander/HdfBase.py +307 -197
- ras_commander/HdfBndry.py +94 -287
- ras_commander/HdfFluvialPluvial.py +155 -247
- ras_commander/HdfInfiltration.py +410 -0
- ras_commander/HdfMesh.py +117 -36
- ras_commander/HdfPipe.py +127 -175
- ras_commander/HdfPlan.py +144 -58
- ras_commander/HdfPlot.py +104 -0
- ras_commander/HdfPump.py +76 -28
- ras_commander/HdfResultsMesh.py +186 -167
- ras_commander/HdfResultsPlan.py +76 -220
- ras_commander/HdfResultsPlot.py +182 -0
- ras_commander/HdfResultsXsec.py +185 -145
- ras_commander/HdfStruc.py +65 -35
- ras_commander/HdfUtils.py +435 -518
- ras_commander/HdfXsec.py +137 -127
- ras_commander/RasCmdr.py +13 -0
- ras_commander/RasExamples.py +14 -0
- ras_commander/RasGeo.py +11 -0
- ras_commander/RasGpt.py +8 -0
- ras_commander/RasMapper.py +105 -0
- ras_commander/RasPlan.py +30 -0
- ras_commander/RasPrj.py +34 -0
- ras_commander/RasToGo.py +16 -0
- ras_commander/RasUnsteady.py +15 -0
- ras_commander/RasUtils.py +31 -0
- ras_commander/__init__.py +10 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/METADATA +73 -8
- ras_commander-0.49.0.dist-info/RECORD +34 -0
- ras_commander-0.48.0.dist-info/RECORD +0 -30
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/WHEEL +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/top_level.txt +0 -0
ras_commander/HdfResultsPlan.py
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
HdfResultsPlan: A module for extracting and analyzing HEC-RAS plan HDF file results.
|
3
3
|
|
4
|
-
Attribution:
|
5
|
-
from
|
6
|
-
|
4
|
+
Attribution:
|
5
|
+
Substantial code sourced/derived from https://github.com/fema-ffrd/rashdf
|
6
|
+
Copyright (c) 2024 fema-ffrd, MIT license
|
7
7
|
|
8
|
-
|
8
|
+
Description:
|
9
|
+
Provides static methods for extracting unsteady flow results, volume accounting,
|
10
|
+
and reference data from HEC-RAS plan HDF files.
|
11
|
+
|
12
|
+
Available Functions:
|
13
|
+
- get_unsteady_info: Extract unsteady attributes
|
14
|
+
- get_unsteady_summary: Extract unsteady summary data
|
15
|
+
- get_volume_accounting: Extract volume accounting data
|
16
|
+
- get_runtime_data: Extract runtime and compute time data
|
17
|
+
|
18
|
+
Note:
|
19
|
+
All methods are static and designed to be used without class instantiation.
|
9
20
|
"""
|
10
21
|
|
11
22
|
from typing import Dict, List, Union, Optional
|
@@ -14,7 +25,7 @@ import h5py
|
|
14
25
|
import pandas as pd
|
15
26
|
import xarray as xr
|
16
27
|
from .Decorators import standardize_input, log_call
|
17
|
-
from .
|
28
|
+
from .HdfUtils import HdfUtils
|
18
29
|
from .HdfResultsXsec import HdfResultsXsec
|
19
30
|
from .LoggingConfig import get_logger
|
20
31
|
import numpy as np
|
@@ -25,26 +36,27 @@ logger = get_logger(__name__)
|
|
25
36
|
|
26
37
|
class HdfResultsPlan:
|
27
38
|
"""
|
28
|
-
|
29
|
-
|
30
|
-
This class provides methods for extracting and analyzing data from HEC-RAS plan HDF files,
|
31
|
-
focusing on unsteady flow results, volume accounting, and reference line/point time series outputs.
|
39
|
+
Handles extraction of results data from HEC-RAS plan HDF files.
|
32
40
|
|
33
|
-
|
34
|
-
|
41
|
+
This class provides static methods for accessing and analyzing:
|
42
|
+
- Unsteady flow results
|
43
|
+
- Volume accounting data
|
44
|
+
- Runtime statistics
|
45
|
+
- Reference line/point time series outputs
|
35
46
|
|
36
|
-
|
37
|
-
|
47
|
+
All methods use:
|
48
|
+
- @standardize_input decorator for consistent file path handling
|
49
|
+
- @log_call decorator for operation logging
|
50
|
+
- HdfUtils class for common HDF operations
|
38
51
|
|
39
52
|
Note:
|
40
|
-
|
41
|
-
for some of its operations.
|
53
|
+
No instantiation required - all methods are static.
|
42
54
|
"""
|
43
55
|
|
44
56
|
@staticmethod
|
45
57
|
@log_call
|
46
58
|
@standardize_input(file_type='plan_hdf')
|
47
|
-
def
|
59
|
+
def get_unsteady_info(hdf_path: Path) -> pd.DataFrame:
|
48
60
|
"""
|
49
61
|
Get unsteady attributes from a HEC-RAS HDF plan file.
|
50
62
|
|
@@ -52,7 +64,7 @@ class HdfResultsPlan:
|
|
52
64
|
hdf_path (Path): Path to the HEC-RAS plan HDF file.
|
53
65
|
|
54
66
|
Returns:
|
55
|
-
|
67
|
+
pd.DataFrame: A DataFrame containing the unsteady attributes.
|
56
68
|
|
57
69
|
Raises:
|
58
70
|
FileNotFoundError: If the specified HDF file is not found.
|
@@ -62,16 +74,22 @@ class HdfResultsPlan:
|
|
62
74
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
63
75
|
if "Results/Unsteady" not in hdf_file:
|
64
76
|
raise KeyError("Results/Unsteady group not found in the HDF file.")
|
65
|
-
|
77
|
+
|
78
|
+
# Create dictionary from attributes
|
79
|
+
attrs_dict = dict(hdf_file["Results/Unsteady"].attrs)
|
80
|
+
|
81
|
+
# Create DataFrame with a single row index
|
82
|
+
return pd.DataFrame(attrs_dict, index=[0])
|
83
|
+
|
66
84
|
except FileNotFoundError:
|
67
85
|
raise FileNotFoundError(f"HDF file not found: {hdf_path}")
|
68
86
|
except Exception as e:
|
69
87
|
raise RuntimeError(f"Error reading unsteady attributes: {str(e)}")
|
70
|
-
|
88
|
+
|
71
89
|
@staticmethod
|
72
90
|
@log_call
|
73
91
|
@standardize_input(file_type='plan_hdf')
|
74
|
-
def
|
92
|
+
def get_unsteady_summary(hdf_path: Path) -> pd.DataFrame:
|
75
93
|
"""
|
76
94
|
Get results unsteady summary attributes from a HEC-RAS HDF plan file.
|
77
95
|
|
@@ -79,7 +97,7 @@ class HdfResultsPlan:
|
|
79
97
|
hdf_path (Path): Path to the HEC-RAS plan HDF file.
|
80
98
|
|
81
99
|
Returns:
|
82
|
-
|
100
|
+
pd.DataFrame: A DataFrame containing the results unsteady summary attributes.
|
83
101
|
|
84
102
|
Raises:
|
85
103
|
FileNotFoundError: If the specified HDF file is not found.
|
@@ -89,16 +107,22 @@ class HdfResultsPlan:
|
|
89
107
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
90
108
|
if "Results/Unsteady/Summary" not in hdf_file:
|
91
109
|
raise KeyError("Results/Unsteady/Summary group not found in the HDF file.")
|
92
|
-
|
110
|
+
|
111
|
+
# Create dictionary from attributes
|
112
|
+
attrs_dict = dict(hdf_file["Results/Unsteady/Summary"].attrs)
|
113
|
+
|
114
|
+
# Create DataFrame with a single row index
|
115
|
+
return pd.DataFrame(attrs_dict, index=[0])
|
116
|
+
|
93
117
|
except FileNotFoundError:
|
94
118
|
raise FileNotFoundError(f"HDF file not found: {hdf_path}")
|
95
119
|
except Exception as e:
|
96
120
|
raise RuntimeError(f"Error reading unsteady summary attributes: {str(e)}")
|
97
|
-
|
121
|
+
|
98
122
|
@staticmethod
|
99
123
|
@log_call
|
100
124
|
@standardize_input(file_type='plan_hdf')
|
101
|
-
def
|
125
|
+
def get_volume_accounting(hdf_path: Path) -> pd.DataFrame:
|
102
126
|
"""
|
103
127
|
Get volume accounting attributes from a HEC-RAS HDF plan file.
|
104
128
|
|
@@ -106,7 +130,7 @@ class HdfResultsPlan:
|
|
106
130
|
hdf_path (Path): Path to the HEC-RAS plan HDF file.
|
107
131
|
|
108
132
|
Returns:
|
109
|
-
|
133
|
+
pd.DataFrame: A DataFrame containing the volume accounting attributes.
|
110
134
|
|
111
135
|
Raises:
|
112
136
|
FileNotFoundError: If the specified HDF file is not found.
|
@@ -116,7 +140,13 @@ class HdfResultsPlan:
|
|
116
140
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
117
141
|
if "Results/Unsteady/Summary/Volume Accounting" not in hdf_file:
|
118
142
|
raise KeyError("Results/Unsteady/Summary/Volume Accounting group not found in the HDF file.")
|
119
|
-
|
143
|
+
|
144
|
+
# Get attributes and create dictionary
|
145
|
+
attrs_dict = dict(hdf_file["Results/Unsteady/Summary/Volume Accounting"].attrs)
|
146
|
+
|
147
|
+
# Create DataFrame with a single row index
|
148
|
+
return pd.DataFrame(attrs_dict, index=[0])
|
149
|
+
|
120
150
|
except FileNotFoundError:
|
121
151
|
raise FileNotFoundError(f"HDF file not found: {hdf_path}")
|
122
152
|
except Exception as e:
|
@@ -126,13 +156,29 @@ class HdfResultsPlan:
|
|
126
156
|
@standardize_input(file_type='plan_hdf')
|
127
157
|
def get_runtime_data(hdf_path: Path) -> Optional[pd.DataFrame]:
|
128
158
|
"""
|
129
|
-
Extract runtime and
|
159
|
+
Extract detailed runtime and computational performance metrics from HDF file.
|
130
160
|
|
131
161
|
Args:
|
132
|
-
hdf_path (Path):
|
162
|
+
hdf_path (Path): Path to HEC-RAS plan HDF file
|
133
163
|
|
134
164
|
Returns:
|
135
|
-
Optional[pd.DataFrame]: DataFrame containing
|
165
|
+
Optional[pd.DataFrame]: DataFrame containing:
|
166
|
+
- Plan identification (name, file)
|
167
|
+
- Simulation timing (start, end, duration)
|
168
|
+
- Process-specific compute times
|
169
|
+
- Performance metrics (simulation speeds)
|
170
|
+
Returns None if required data cannot be extracted
|
171
|
+
|
172
|
+
Notes:
|
173
|
+
- Times are reported in multiple units (ms, s, hours)
|
174
|
+
- Compute speeds are calculated as simulation-time/compute-time ratios
|
175
|
+
- Process times include: geometry, preprocessing, event conditions,
|
176
|
+
and unsteady flow computations
|
177
|
+
|
178
|
+
Example:
|
179
|
+
>>> runtime_stats = HdfResultsPlan.get_runtime_data('path/to/plan.hdf')
|
180
|
+
>>> if runtime_stats is not None:
|
181
|
+
>>> print(f"Total compute time: {runtime_stats['Complete Process (hr)'][0]:.2f} hours")
|
136
182
|
"""
|
137
183
|
if hdf_path is None:
|
138
184
|
logger.error(f"Could not find HDF file for input")
|
@@ -206,196 +252,6 @@ class HdfResultsPlan:
|
|
206
252
|
|
207
253
|
return compute_summary_df
|
208
254
|
|
209
|
-
|
210
|
-
|
211
|
-
@staticmethod
|
212
|
-
@log_call
|
213
|
-
@standardize_input(file_type='plan_hdf')
|
214
|
-
def reference_timeseries_output(hdf_path: Path, reftype: str = "lines") -> xr.Dataset:
|
215
|
-
"""
|
216
|
-
Get timeseries output for reference lines or points.
|
217
|
-
|
218
|
-
Args:
|
219
|
-
hdf_path (Path): Path to the HDF file.
|
220
|
-
reftype (str): Type of reference, either "lines" or "points" (default "lines").
|
221
|
-
|
222
|
-
Returns:
|
223
|
-
xr.Dataset: Dataset containing the timeseries output for reference lines or points.
|
224
|
-
|
225
|
-
Raises:
|
226
|
-
FileNotFoundError: If the specified HDF file is not found.
|
227
|
-
ValueError: If an invalid reftype is provided.
|
228
|
-
"""
|
229
|
-
try:
|
230
|
-
with h5py.File(hdf_path, 'r') as hdf_file:
|
231
|
-
return HdfResultsPlan._reference_timeseries_output(hdf_file, reftype)
|
232
|
-
except FileNotFoundError:
|
233
|
-
raise FileNotFoundError(f"HDF file not found: {hdf_path}")
|
234
|
-
except ValueError as ve:
|
235
|
-
raise ValueError(f"Invalid reftype: {str(ve)}")
|
236
|
-
except Exception as e:
|
237
|
-
raise RuntimeError(f"Error getting reference timeseries output: {str(e)}")
|
238
|
-
|
239
|
-
|
240
|
-
@staticmethod
|
241
|
-
def _reference_timeseries_output(hdf_file: h5py.File, reftype: str = "lines") -> xr.Dataset:
|
242
|
-
"""
|
243
|
-
Private method to return timeseries output data for reference lines or points from a HEC-RAS HDF plan file.
|
244
|
-
|
245
|
-
Parameters
|
246
|
-
----------
|
247
|
-
hdf_file : h5py.File
|
248
|
-
Open HDF file object.
|
249
|
-
reftype : str, optional
|
250
|
-
The type of reference data to retrieve. Must be either "lines" or "points".
|
251
|
-
(default: "lines")
|
252
|
-
|
253
|
-
Returns
|
254
|
-
-------
|
255
|
-
xr.Dataset
|
256
|
-
An xarray Dataset with reference line or point timeseries data.
|
257
|
-
Returns an empty Dataset if the reference output data is not found.
|
258
|
-
|
259
|
-
Raises
|
260
|
-
------
|
261
|
-
ValueError
|
262
|
-
If reftype is not "lines" or "points".
|
263
|
-
"""
|
264
|
-
if reftype == "lines":
|
265
|
-
output_path = "Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Reference Lines"
|
266
|
-
abbrev = "refln"
|
267
|
-
elif reftype == "points":
|
268
|
-
output_path = "Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Reference Points"
|
269
|
-
abbrev = "refpt"
|
270
|
-
else:
|
271
|
-
raise ValueError('reftype must be either "lines" or "points".')
|
272
|
-
|
273
|
-
try:
|
274
|
-
reference_group = hdf_file[output_path]
|
275
|
-
except KeyError:
|
276
|
-
logger.error(f"Could not find HDF group at path '{output_path}'. "
|
277
|
-
f"The Plan HDF file may not contain reference {reftype[:-1]} output data.")
|
278
|
-
return xr.Dataset()
|
279
|
-
|
280
|
-
reference_names = reference_group["Name"][:]
|
281
|
-
names = []
|
282
|
-
mesh_areas = []
|
283
|
-
for s in reference_names:
|
284
|
-
name, mesh_area = s.decode("utf-8").split("|")
|
285
|
-
names.append(name)
|
286
|
-
mesh_areas.append(mesh_area)
|
287
|
-
|
288
|
-
times = HdfBase._get_unsteady_datetimes(hdf_file)
|
289
|
-
|
290
|
-
das = {}
|
291
|
-
for var in ["Flow", "Velocity", "Water Surface"]:
|
292
|
-
group = reference_group.get(var)
|
293
|
-
if group is None:
|
294
|
-
continue
|
295
|
-
values = group[:]
|
296
|
-
units = group.attrs["Units"].decode("utf-8")
|
297
|
-
da = xr.DataArray(
|
298
|
-
values,
|
299
|
-
name=var,
|
300
|
-
dims=["time", f"{abbrev}_id"],
|
301
|
-
coords={
|
302
|
-
"time": times,
|
303
|
-
f"{abbrev}_id": range(values.shape[1]),
|
304
|
-
f"{abbrev}_name": (f"{abbrev}_id", names),
|
305
|
-
"mesh_name": (f"{abbrev}_id", mesh_areas),
|
306
|
-
},
|
307
|
-
attrs={"units": units, "hdf_path": f"{output_path}/{var}"},
|
308
|
-
)
|
309
|
-
das[var] = da
|
310
|
-
return xr.Dataset(das)
|
311
|
-
|
312
255
|
|
313
256
|
|
314
257
|
|
315
|
-
@staticmethod
|
316
|
-
@log_call
|
317
|
-
@standardize_input(file_type='plan_hdf')
|
318
|
-
def reference_lines_timeseries_output(hdf_path: Path) -> xr.Dataset:
|
319
|
-
"""
|
320
|
-
Get timeseries output for reference lines.
|
321
|
-
|
322
|
-
Args:
|
323
|
-
hdf_path (Path): Path to the HDF file.
|
324
|
-
|
325
|
-
Returns:
|
326
|
-
xr.Dataset: Dataset containing the timeseries output for reference lines.
|
327
|
-
|
328
|
-
Raises:
|
329
|
-
FileNotFoundError: If the specified HDF file is not found.
|
330
|
-
"""
|
331
|
-
return HdfResultsPlan.reference_timeseries_output(hdf_path, reftype="lines")
|
332
|
-
|
333
|
-
@staticmethod
|
334
|
-
@log_call
|
335
|
-
@standardize_input(file_type='plan_hdf')
|
336
|
-
def reference_points_timeseries_output(hdf_path: Path) -> xr.Dataset:
|
337
|
-
"""
|
338
|
-
Get timeseries output for reference points.
|
339
|
-
|
340
|
-
Args:
|
341
|
-
hdf_path (Path): Path to the HDF file.
|
342
|
-
|
343
|
-
Returns:
|
344
|
-
xr.Dataset: Dataset containing the timeseries output for reference points.
|
345
|
-
|
346
|
-
Raises:
|
347
|
-
FileNotFoundError: If the specified HDF file is not found.
|
348
|
-
"""
|
349
|
-
return HdfResultsPlan.reference_timeseries_output(hdf_path, reftype="points")
|
350
|
-
|
351
|
-
@staticmethod
|
352
|
-
@log_call
|
353
|
-
@standardize_input(file_type='plan_hdf')
|
354
|
-
def reference_summary_output(hdf_path: Path, reftype: str = "lines") -> pd.DataFrame:
|
355
|
-
"""
|
356
|
-
Get summary output for reference lines or points.
|
357
|
-
|
358
|
-
Args:
|
359
|
-
hdf_path (Path): Path to the HDF file.
|
360
|
-
reftype (str): Type of reference, either "lines" or "points" (default "lines").
|
361
|
-
|
362
|
-
Returns:
|
363
|
-
pd.DataFrame: DataFrame containing the summary output for reference lines or points.
|
364
|
-
|
365
|
-
Raises:
|
366
|
-
ValueError: If an invalid reftype is provided.
|
367
|
-
"""
|
368
|
-
if not hdf_path.exists():
|
369
|
-
logger.error(f"HDF file not found: {hdf_path}")
|
370
|
-
return pd.DataFrame() # Return an empty DataFrame if the path doesn't exist
|
371
|
-
|
372
|
-
try:
|
373
|
-
# Get the timeseries output
|
374
|
-
ds = HdfResultsPlan.reference_timeseries_output(hdf_path, reftype)
|
375
|
-
|
376
|
-
if 'station' not in ds.dims:
|
377
|
-
logger.error("No 'station' dimension found in the dataset.")
|
378
|
-
return pd.DataFrame() # Return an empty DataFrame if 'station' dimension is missing
|
379
|
-
|
380
|
-
# Calculate summary statistics
|
381
|
-
summary = ds.groupby('station').agg({
|
382
|
-
'WSE': ['min', 'max', 'mean'],
|
383
|
-
'Q': ['min', 'max', 'mean']
|
384
|
-
})
|
385
|
-
|
386
|
-
# Flatten column names
|
387
|
-
summary.columns = ['_'.join(col).strip() for col in summary.columns.values]
|
388
|
-
|
389
|
-
# Reset index to make 'station' a column
|
390
|
-
summary = summary.reset_index()
|
391
|
-
|
392
|
-
return summary
|
393
|
-
except ValueError as ve:
|
394
|
-
logger.error(f"Invalid reftype: {str(ve)}")
|
395
|
-
return pd.DataFrame() # Return an empty DataFrame on ValueError
|
396
|
-
except Exception as e:
|
397
|
-
logger.error(f"Error in reference_summary_output: {str(e)}")
|
398
|
-
return pd.DataFrame() # Return an empty DataFrame on general error
|
399
|
-
|
400
|
-
|
401
|
-
|
@@ -0,0 +1,182 @@
|
|
1
|
+
"""
|
2
|
+
Class: HdfResultsPlot
|
3
|
+
|
4
|
+
A collection of static methods for visualizing HEC-RAS results data from HDF files using matplotlib.
|
5
|
+
|
6
|
+
Public Functions:
|
7
|
+
plot_results_mesh_variable(variable_df, variable_name, colormap='viridis', point_size=10):
|
8
|
+
Generic plotting function for any mesh variable with customizable styling.
|
9
|
+
|
10
|
+
plot_results_max_wsel(max_ws_df):
|
11
|
+
Visualizes the maximum water surface elevation distribution across mesh cells.
|
12
|
+
|
13
|
+
plot_results_max_wsel_time(max_ws_df):
|
14
|
+
Displays the timing of maximum water surface elevation for each cell,
|
15
|
+
including statistics about the temporal distribution.
|
16
|
+
|
17
|
+
Requirements:
|
18
|
+
- matplotlib
|
19
|
+
- pandas
|
20
|
+
- geopandas (for geometry handling)
|
21
|
+
|
22
|
+
Input DataFrames must contain:
|
23
|
+
- 'geometry' column with Point objects containing x,y coordinates
|
24
|
+
- Variable data columns as specified in individual function docstrings
|
25
|
+
"""
|
26
|
+
|
27
|
+
import matplotlib.pyplot as plt
|
28
|
+
import pandas as pd
|
29
|
+
from typing import Dict
|
30
|
+
from .Decorators import log_call
|
31
|
+
from .HdfMesh import HdfMesh
|
32
|
+
|
33
|
+
class HdfResultsPlot:
|
34
|
+
"""
|
35
|
+
A class containing static methods for plotting HEC-RAS results data.
|
36
|
+
|
37
|
+
This class provides visualization methods for various types of HEC-RAS results,
|
38
|
+
including maximum water surface elevations and timing information.
|
39
|
+
"""
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
@log_call
|
43
|
+
def plot_results_max_wsel(max_ws_df: pd.DataFrame) -> None:
|
44
|
+
"""
|
45
|
+
Plots the maximum water surface elevation per cell.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
max_ws_df (pd.DataFrame): DataFrame containing merged data with coordinates
|
49
|
+
and max water surface elevations.
|
50
|
+
"""
|
51
|
+
# Extract x and y coordinates from the geometry column
|
52
|
+
max_ws_df['x'] = max_ws_df['geometry'].apply(lambda geom: geom.x if geom is not None else None)
|
53
|
+
max_ws_df['y'] = max_ws_df['geometry'].apply(lambda geom: geom.y if geom is not None else None)
|
54
|
+
|
55
|
+
if 'x' not in max_ws_df.columns or 'y' not in max_ws_df.columns:
|
56
|
+
print("Error: 'x' or 'y' columns not found in the merged dataframe.")
|
57
|
+
print("Available columns:", max_ws_df.columns.tolist())
|
58
|
+
return
|
59
|
+
|
60
|
+
fig, ax = plt.subplots(figsize=(12, 8))
|
61
|
+
scatter = ax.scatter(max_ws_df['x'], max_ws_df['y'],
|
62
|
+
c=max_ws_df['maximum_water_surface'],
|
63
|
+
cmap='viridis', s=10)
|
64
|
+
|
65
|
+
ax.set_title('Max Water Surface per Cell')
|
66
|
+
ax.set_xlabel('X Coordinate')
|
67
|
+
ax.set_ylabel('Y Coordinate')
|
68
|
+
plt.colorbar(scatter, label='Max Water Surface (ft)')
|
69
|
+
|
70
|
+
ax.grid(True, linestyle='--', alpha=0.7)
|
71
|
+
plt.rcParams.update({'font.size': 12})
|
72
|
+
plt.tight_layout()
|
73
|
+
plt.show()
|
74
|
+
|
75
|
+
@staticmethod
|
76
|
+
@log_call
|
77
|
+
def plot_results_max_wsel_time(max_ws_df: pd.DataFrame) -> None:
|
78
|
+
"""
|
79
|
+
Plots the time of the maximum water surface elevation (WSEL) per cell.
|
80
|
+
|
81
|
+
Args:
|
82
|
+
max_ws_df (pd.DataFrame): DataFrame containing merged data with coordinates
|
83
|
+
and max water surface timing information.
|
84
|
+
"""
|
85
|
+
# Convert datetime strings using the renamed utility function
|
86
|
+
max_ws_df['max_wsel_time'] = pd.to_datetime(max_ws_df['maximum_water_surface_time'])
|
87
|
+
|
88
|
+
# Extract coordinates
|
89
|
+
max_ws_df['x'] = max_ws_df['geometry'].apply(lambda geom: geom.x if geom is not None else None)
|
90
|
+
max_ws_df['y'] = max_ws_df['geometry'].apply(lambda geom: geom.y if geom is not None else None)
|
91
|
+
|
92
|
+
if 'x' not in max_ws_df.columns or 'y' not in max_ws_df.columns:
|
93
|
+
raise ValueError("x and y coordinates are missing from the DataFrame. Make sure the 'geometry' column exists and contains valid coordinate data.")
|
94
|
+
|
95
|
+
fig, ax = plt.subplots(figsize=(12, 8))
|
96
|
+
|
97
|
+
min_time = max_ws_df['max_wsel_time'].min()
|
98
|
+
color_values = (max_ws_df['max_wsel_time'] - min_time).dt.total_seconds() / 3600
|
99
|
+
|
100
|
+
scatter = ax.scatter(max_ws_df['x'], max_ws_df['y'],
|
101
|
+
c=color_values, cmap='viridis', s=10)
|
102
|
+
|
103
|
+
ax.set_title('Time of Maximum Water Surface Elevation per Cell')
|
104
|
+
ax.set_xlabel('X Coordinate')
|
105
|
+
ax.set_ylabel('Y Coordinate')
|
106
|
+
|
107
|
+
cbar = plt.colorbar(scatter)
|
108
|
+
cbar.set_label('Hours since simulation start')
|
109
|
+
cbar.set_ticks(range(0, int(color_values.max()) + 1, 6))
|
110
|
+
cbar.set_ticklabels([f'{h}h' for h in range(0, int(color_values.max()) + 1, 6)])
|
111
|
+
|
112
|
+
ax.grid(True, linestyle='--', alpha=0.7)
|
113
|
+
plt.rcParams.update({'font.size': 12})
|
114
|
+
plt.tight_layout()
|
115
|
+
plt.show()
|
116
|
+
|
117
|
+
# Print timing information
|
118
|
+
print(f"\nSimulation Start Time: {min_time}")
|
119
|
+
print(f"Time Range: {color_values.max():.1f} hours")
|
120
|
+
print("\nTiming Statistics (hours since start):")
|
121
|
+
print(color_values.describe())
|
122
|
+
|
123
|
+
@staticmethod
|
124
|
+
@log_call
|
125
|
+
def plot_results_mesh_variable(variable_df: pd.DataFrame, variable_name: str, colormap: str = 'viridis', point_size: int = 10) -> None:
|
126
|
+
"""
|
127
|
+
Plot any mesh variable with consistent styling.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
variable_df (pd.DataFrame): DataFrame containing the variable data
|
131
|
+
variable_name (str): Name of the variable (for labels)
|
132
|
+
colormap (str): Matplotlib colormap to use. Default: 'viridis'
|
133
|
+
point_size (int): Size of the scatter points. Default: 10
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
None
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
ImportError: If matplotlib is not installed
|
140
|
+
ValueError: If required columns are missing from variable_df
|
141
|
+
"""
|
142
|
+
try:
|
143
|
+
import matplotlib.pyplot as plt
|
144
|
+
except ImportError:
|
145
|
+
logger.error("matplotlib is required for plotting. Please install it with 'pip install matplotlib'")
|
146
|
+
raise ImportError("matplotlib is required for plotting")
|
147
|
+
|
148
|
+
# Get cell coordinates if not in variable_df
|
149
|
+
if 'geometry' not in variable_df.columns:
|
150
|
+
cell_coords = HdfMesh.mesh_cell_points(plan_hdf_path)
|
151
|
+
merged_df = pd.merge(variable_df, cell_coords, on=['mesh_name', 'cell_id'])
|
152
|
+
else:
|
153
|
+
merged_df = variable_df
|
154
|
+
|
155
|
+
# Extract coordinates, handling None values
|
156
|
+
merged_df = merged_df.dropna(subset=['geometry'])
|
157
|
+
merged_df['x'] = merged_df['geometry'].apply(lambda geom: geom.x if geom is not None else None)
|
158
|
+
merged_df['y'] = merged_df['geometry'].apply(lambda geom: geom.y if geom is not None else None)
|
159
|
+
|
160
|
+
# Drop any rows with None coordinates
|
161
|
+
merged_df = merged_df.dropna(subset=['x', 'y'])
|
162
|
+
|
163
|
+
if len(merged_df) == 0:
|
164
|
+
logger.error("No valid coordinates found for plotting")
|
165
|
+
raise ValueError("No valid coordinates found for plotting")
|
166
|
+
|
167
|
+
# Create plot
|
168
|
+
fig, ax = plt.subplots(figsize=(12, 8))
|
169
|
+
scatter = ax.scatter(merged_df['x'], merged_df['y'],
|
170
|
+
c=merged_df[variable_name],
|
171
|
+
cmap=colormap,
|
172
|
+
s=point_size)
|
173
|
+
|
174
|
+
# Customize plot
|
175
|
+
ax.set_title(f'{variable_name} per Cell')
|
176
|
+
ax.set_xlabel('X Coordinate')
|
177
|
+
ax.set_ylabel('Y Coordinate')
|
178
|
+
plt.colorbar(scatter, label=variable_name)
|
179
|
+
ax.grid(True, linestyle='--', alpha=0.7)
|
180
|
+
plt.rcParams.update({'font.size': 12})
|
181
|
+
plt.tight_layout()
|
182
|
+
plt.show()
|