ras-commander 0.42.0__tar.gz → 0.44.0__tar.gz

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 (34) hide show
  1. {ras_commander-0.42.0/ras_commander.egg-info → ras_commander-0.44.0}/PKG-INFO +9 -2
  2. {ras_commander-0.42.0 → ras_commander-0.44.0}/README.md +8 -1
  3. ras_commander-0.44.0/ras_commander/Decorators.py +111 -0
  4. ras_commander-0.44.0/ras_commander/HdfBase.py +197 -0
  5. ras_commander-0.44.0/ras_commander/HdfBndry.py +505 -0
  6. ras_commander-0.44.0/ras_commander/HdfMesh.py +308 -0
  7. ras_commander-0.44.0/ras_commander/HdfPlan.py +200 -0
  8. ras_commander-0.44.0/ras_commander/HdfResultsMesh.py +662 -0
  9. ras_commander-0.44.0/ras_commander/HdfResultsPlan.py +398 -0
  10. ras_commander-0.44.0/ras_commander/HdfResultsXsec.py +237 -0
  11. ras_commander-0.44.0/ras_commander/HdfStruc.py +147 -0
  12. ras_commander-0.44.0/ras_commander/HdfUtils.py +467 -0
  13. ras_commander-0.44.0/ras_commander/HdfXsec.py +282 -0
  14. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasCmdr.py +2 -1
  15. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasExamples.py +49 -116
  16. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasGeo.py +2 -2
  17. ras_commander-0.44.0/ras_commander/RasGpt.py +19 -0
  18. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasPlan.py +2 -2
  19. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasPrj.py +55 -9
  20. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasUnsteady.py +2 -1
  21. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander/RasUtils.py +198 -73
  22. ras_commander-0.44.0/ras_commander/__init__.py +63 -0
  23. {ras_commander-0.42.0 → ras_commander-0.44.0/ras_commander.egg-info}/PKG-INFO +9 -2
  24. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander.egg-info/SOURCES.txt +12 -2
  25. {ras_commander-0.42.0 → ras_commander-0.44.0}/setup.py +1 -1
  26. ras_commander-0.42.0/ras_commander/RasGpt.py +0 -142
  27. ras_commander-0.42.0/ras_commander/RasHdf.py +0 -1619
  28. ras_commander-0.42.0/ras_commander/__init__.py +0 -41
  29. {ras_commander-0.42.0 → ras_commander-0.44.0}/LICENSE +0 -0
  30. {ras_commander-0.42.0 → ras_commander-0.44.0}/pyproject.toml +0 -0
  31. /ras_commander-0.42.0/ras_commander/logging_config.py → /ras_commander-0.44.0/ras_commander/LoggingConfig.py +0 -0
  32. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander.egg-info/dependency_links.txt +0 -0
  33. {ras_commander-0.42.0 → ras_commander-0.44.0}/ras_commander.egg-info/top_level.txt +0 -0
  34. {ras_commander-0.42.0 → ras_commander-0.44.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ras-commander
3
- Version: 0.42.0
3
+ Version: 0.44.0
4
4
  Summary: A Python library for automating HEC-RAS operations
5
5
  Home-page: https://github.com/billk-FM/ras-commander
6
6
  Author: William M. Katzenmeyer
@@ -62,7 +62,7 @@ Create a virtual environment with conda or venv (ask ChatGPT if you need help)
62
62
  In your virtual environment, install ras-commander using pip:
63
63
  ```
64
64
  pip install h5py numpy pandas requests tqdm scipy
65
- pip install ras-commander
65
+ pip install --upgrade ras-commander
66
66
  ```
67
67
 
68
68
  If you have dependency issues with pip (especially if you have errors with numpy), try clearing your local pip packages 'C:\Users\your_username\AppData\Roaming\Python\' and then creating a new virtual environment.
@@ -299,6 +299,13 @@ These acknowledgments recognize the contributions and inspirations that have hel
299
299
  4. [HEC-Commander Tools](https://github.com/billk-FM/HEC-Commander) - Inspiration and initial code base for the development of RAS Commander.
300
300
 
301
301
 
302
+ ## Official RAS Commander AI-Generated Songs:
303
+
304
+ [No More Wait and See (Bluegrass)](https://suno.com/song/16889f3e-50f1-4afe-b779-a41738d7617a)
305
+
306
+ [No More Wait and See (Cajun Zydeco)](https://suno.com/song/4441c45d-f6cd-47b9-8fbc-1f7b277ee8ed)
307
+
308
+
302
309
  ## Contact
303
310
 
304
311
  For questions, suggestions, or support, please contact:
@@ -51,7 +51,7 @@ Create a virtual environment with conda or venv (ask ChatGPT if you need help)
51
51
  In your virtual environment, install ras-commander using pip:
52
52
  ```
53
53
  pip install h5py numpy pandas requests tqdm scipy
54
- pip install ras-commander
54
+ pip install --upgrade ras-commander
55
55
  ```
56
56
 
57
57
  If you have dependency issues with pip (especially if you have errors with numpy), try clearing your local pip packages 'C:\Users\your_username\AppData\Roaming\Python\' and then creating a new virtual environment.
@@ -288,6 +288,13 @@ These acknowledgments recognize the contributions and inspirations that have hel
288
288
  4. [HEC-Commander Tools](https://github.com/billk-FM/HEC-Commander) - Inspiration and initial code base for the development of RAS Commander.
289
289
 
290
290
 
291
+ ## Official RAS Commander AI-Generated Songs:
292
+
293
+ [No More Wait and See (Bluegrass)](https://suno.com/song/16889f3e-50f1-4afe-b779-a41738d7617a)
294
+
295
+ [No More Wait and See (Cajun Zydeco)](https://suno.com/song/4441c45d-f6cd-47b9-8fbc-1f7b277ee8ed)
296
+
297
+
291
298
  ## Contact
292
299
 
293
300
  For questions, suggestions, or support, please contact:
@@ -0,0 +1,111 @@
1
+ from functools import wraps
2
+ from pathlib import Path
3
+ from typing import Union
4
+ import logging
5
+ import h5py
6
+ import inspect
7
+
8
+
9
+ def log_call(func):
10
+ @wraps(func)
11
+ def wrapper(*args, **kwargs):
12
+ logger = logging.getLogger(func.__module__)
13
+ logger.info(f"Calling {func.__name__}")
14
+ result = func(*args, **kwargs)
15
+ logger.info(f"Finished {func.__name__}")
16
+ return result
17
+ return wrapper
18
+
19
+
20
+ def standardize_input(file_type: str = 'plan_hdf'):
21
+ """
22
+ Decorator to standardize input for HDF file operations.
23
+
24
+ This decorator processes various input types and converts them to a Path object
25
+ pointing to the correct HDF file. It handles the following input types:
26
+ - h5py.File objects
27
+ - pathlib.Path objects
28
+ - Strings (file paths or plan/geom numbers)
29
+ - Integers (interpreted as plan/geom numbers)
30
+
31
+ The decorator also manages RAS object references and logging.
32
+
33
+ Args:
34
+ file_type (str): Specifies whether to look for 'plan_hdf' or 'geom_hdf' files.
35
+
36
+ Returns:
37
+ A decorator that wraps the function to standardize its input to a Path object.
38
+ """
39
+ def decorator(func):
40
+ @wraps(func)
41
+ def wrapper(*args, **kwargs):
42
+ logger = logging.getLogger(func.__module__)
43
+
44
+ # Handle both static method calls and regular function calls
45
+ if args and isinstance(args[0], type):
46
+ # Static method call, remove the class argument
47
+ args = args[1:]
48
+
49
+ hdf_input = kwargs.pop('hdf_path', None) or kwargs.pop('hdf_input', None) or (args[0] if args else None)
50
+ ras_object = kwargs.pop('ras_object', None) or (args[1] if len(args) > 1 else None)
51
+
52
+ hdf_path = None
53
+
54
+ # If hdf_input is already an h5py.File object, use its filename
55
+ if isinstance(hdf_input, h5py.File):
56
+ hdf_path = Path(hdf_input.filename)
57
+ # Handle Path objects
58
+ elif isinstance(hdf_input, Path):
59
+ if hdf_input.is_file():
60
+ hdf_path = hdf_input
61
+ # Handle string inputs
62
+ elif isinstance(hdf_input, str):
63
+ # Check if it's a file path
64
+ if Path(hdf_input).is_file():
65
+ hdf_path = Path(hdf_input)
66
+ # Check if it's a number (with or without 'p' prefix)
67
+ elif hdf_input.isdigit() or (len(hdf_input) == 3 and hdf_input[0] == 'p' and hdf_input[1:].isdigit()):
68
+ if ras_object is None:
69
+ raise ValueError("RAS object is required when using plan or geom numbers.")
70
+ number = hdf_input if hdf_input.isdigit() else hdf_input[1:]
71
+
72
+ if file_type == 'plan_hdf':
73
+ plan_info = ras_object.plan_df[ras_object.plan_df['plan_number'] == number]
74
+ if not plan_info.empty:
75
+ hdf_path = Path(plan_info.iloc[0]['HDF_Results_Path'])
76
+ elif file_type == 'geom_hdf':
77
+ geom_info = ras_object.geom_df[ras_object.geom_df['geom_number'] == number]
78
+ if not geom_info.empty:
79
+ hdf_path = Path(geom_info.iloc[0]['HDF_Path'])
80
+ else:
81
+ raise ValueError(f"Invalid file type: {file_type}")
82
+ # Handle integer inputs (assuming they're plan or geom numbers)
83
+ elif isinstance(hdf_input, int):
84
+ if ras_object is None:
85
+ raise ValueError("RAS object is required when using plan or geom numbers.")
86
+ number = f"{hdf_input:02d}"
87
+
88
+ if file_type == 'plan_hdf':
89
+ plan_info = ras_object.plan_df[ras_object.plan_df['plan_number'] == number]
90
+ if not plan_info.empty:
91
+ hdf_path = Path(plan_info.iloc[0]['HDF_Results_Path'])
92
+ elif file_type == 'geom_hdf':
93
+ geom_info = ras_object.geom_df[ras_object.geom_df['geom_number'] == number]
94
+ if not geom_info.empty:
95
+ hdf_path = Path(geom_info.iloc[0]['HDF_Path'])
96
+ else:
97
+ raise ValueError(f"Invalid file type: {file_type}")
98
+
99
+ if hdf_path is None or not hdf_path.is_file():
100
+ error_msg = f"HDF file not found: {hdf_input}"
101
+ logger.error(error_msg)
102
+ raise FileNotFoundError(error_msg)
103
+
104
+ logger.info(f"Using HDF file: {hdf_path}")
105
+
106
+ # Pass all original arguments and keywords, replacing hdf_input with standardized hdf_path
107
+ new_args = (hdf_path,) + args[1:]
108
+ return func(*new_args, **kwargs)
109
+
110
+ return wrapper
111
+ return decorator
@@ -0,0 +1,197 @@
1
+ """
2
+ Class: HdfBase
3
+
4
+ Attribution: A substantial amount of code in this file is sourced or derived
5
+ from the https://github.com/fema-ffrd/rashdf library,
6
+ released under MIT license and Copyright (c) 2024 fema-ffrd
7
+
8
+ The file has been forked and modified for use in RAS Commander.
9
+ """
10
+ import re
11
+ from datetime import datetime, timedelta
12
+ import h5py
13
+ import numpy as np
14
+ import pandas as pd
15
+ import xarray as xr # Added import for xarray
16
+ from typing import List, Tuple, Union, Optional, Dict
17
+ from pathlib import Path
18
+ import logging
19
+
20
+ from .HdfUtils import HdfUtils
21
+ from .Decorators import standardize_input, log_call
22
+ from .LoggingConfig import setup_logging, get_logger
23
+
24
+ logger = get_logger(__name__)
25
+
26
+ class HdfBase:
27
+ """
28
+ Base class for HEC-RAS HDF file operations.
29
+
30
+ This class provides fundamental methods for interacting with HEC-RAS HDF files,
31
+ including time-related operations and mesh data retrieval. It serves as a foundation
32
+ for more specialized HDF classes.
33
+
34
+ The methods in this class are designed to work with both plan and geometry HDF files,
35
+ providing low-level access to file structure and content.
36
+
37
+ Note:
38
+ - All methods in this class are static, allowing for use without instantiation.
39
+ - This class is not meant to be used directly in most cases, but rather as a base
40
+ for more specialized HDF classes.
41
+ """
42
+
43
+ @staticmethod
44
+ def _get_simulation_start_time(hdf_file: h5py.File) -> datetime:
45
+ """
46
+ Get the simulation start time from the HDF file.
47
+
48
+ Args:
49
+ hdf_file (h5py.File): Open HDF file object.
50
+
51
+ Returns:
52
+ datetime: The simulation start time.
53
+
54
+ Raises:
55
+ ValueError: If Plan Information is not found in the HDF file.
56
+ """
57
+ plan_info = hdf_file.get("Plan Data/Plan Information")
58
+ if plan_info is None:
59
+ raise ValueError("Plan Information not found in HDF file")
60
+ time_str = plan_info.attrs.get('Simulation Start Time')
61
+ return datetime.strptime(time_str.decode('utf-8'), "%d%b%Y %H:%M:%S")
62
+
63
+ @staticmethod
64
+ def _get_unsteady_datetimes(hdf_file: h5py.File) -> List[datetime]:
65
+ """
66
+ Get the list of unsteady datetimes from the HDF file.
67
+
68
+ Args:
69
+ hdf_file (h5py.File): Open HDF file object.
70
+
71
+ Returns:
72
+ List[datetime]: A list of datetime objects representing the unsteady timestamps.
73
+ """
74
+ group_path = "Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Time Date Stamp (ms)"
75
+ raw_datetimes = hdf_file[group_path][:]
76
+ return [HdfBase._parse_ras_datetime_ms(x.decode("utf-8")) for x in raw_datetimes]
77
+
78
+
79
+ @staticmethod
80
+ def _get_2d_flow_area_names_and_counts(hdf_file: h5py.File) -> List[Tuple[str, int]]:
81
+ """
82
+ Get the names and cell counts of 2D flow areas from the HDF file.
83
+
84
+ Args:
85
+ hdf_file (h5py.File): Open HDF file object.
86
+
87
+ Returns:
88
+ List[Tuple[str, int]]: A list of tuples containing the name and cell count of each 2D flow area.
89
+ """
90
+ d2_flow_areas = hdf_file.get("Geometry/2D Flow Areas/Attributes")
91
+ if d2_flow_areas is None:
92
+ return []
93
+ return [(HdfBase._convert_ras_hdf_string(d2_flow_area[0]), d2_flow_area[-1]) for d2_flow_area in d2_flow_areas[:]]
94
+
95
+ @staticmethod
96
+ def _parse_ras_datetime(datetime_str: str) -> datetime:
97
+ """
98
+ Parse a datetime string from a RAS file into a datetime object.
99
+
100
+ Args:
101
+ datetime_str (str): The datetime string to parse.
102
+
103
+ Returns:
104
+ datetime: The parsed datetime object.
105
+ """
106
+ return datetime.strptime(datetime_str, "%d%b%Y %H:%M:%S")
107
+
108
+ @staticmethod
109
+ def _parse_ras_simulation_window_datetime(datetime_str: str) -> datetime:
110
+ """
111
+ Parse a datetime string from a RAS simulation window into a datetime object.
112
+
113
+ Args:
114
+ datetime_str (str): The datetime string to parse.
115
+
116
+ Returns:
117
+ datetime: The parsed datetime object.
118
+ """
119
+ return datetime.strptime(datetime_str, "%d%b%Y %H%M")
120
+
121
+ @staticmethod
122
+ def _parse_duration(duration_str: str) -> timedelta:
123
+ """
124
+ Parse a duration string into a timedelta object.
125
+
126
+ Args:
127
+ duration_str (str): The duration string to parse.
128
+
129
+ Returns:
130
+ timedelta: The parsed duration as a timedelta object.
131
+ """
132
+ hours, minutes, seconds = map(int, duration_str.split(':'))
133
+ return timedelta(hours=hours, minutes=minutes, seconds=seconds)
134
+
135
+ @staticmethod
136
+ def _parse_ras_datetime_ms(datetime_str: str) -> datetime:
137
+ """
138
+ Parse a datetime string with milliseconds from a RAS file.
139
+
140
+ Args:
141
+ datetime_str (str): The datetime string to parse.
142
+
143
+ Returns:
144
+ datetime: The parsed datetime object.
145
+ """
146
+ milliseconds = int(datetime_str[-3:])
147
+ microseconds = milliseconds * 1000
148
+ parsed_dt = HdfBase._parse_ras_datetime(datetime_str[:-4]).replace(microsecond=microseconds)
149
+ return parsed_dt
150
+
151
+ @staticmethod
152
+ def _convert_ras_hdf_string(value: Union[str, bytes]) -> Union[bool, datetime, List[datetime], timedelta, str]:
153
+ """
154
+ Convert a string value from an HEC-RAS HDF file into a Python object.
155
+
156
+ Args:
157
+ value (Union[str, bytes]): The value to convert.
158
+
159
+ Returns:
160
+ Union[bool, datetime, List[datetime], timedelta, str]: The converted value.
161
+ """
162
+ if isinstance(value, bytes):
163
+ s = value.decode("utf-8")
164
+ else:
165
+ s = value
166
+
167
+ if s == "True":
168
+ return True
169
+ elif s == "False":
170
+ return False
171
+
172
+ ras_datetime_format1_re = r"\d{2}\w{3}\d{4} \d{2}:\d{2}:\d{2}"
173
+ ras_datetime_format2_re = r"\d{2}\w{3}\d{4} \d{2}\d{2}"
174
+ ras_duration_format_re = r"\d{2}:\d{2}:\d{2}"
175
+
176
+ if re.match(rf"^{ras_datetime_format1_re}", s):
177
+ if re.match(rf"^{ras_datetime_format1_re} to {ras_datetime_format1_re}$", s):
178
+ split = s.split(" to ")
179
+ return [
180
+ HdfBase._parse_ras_datetime(split[0]),
181
+ HdfBase._parse_ras_datetime(split[1]),
182
+ ]
183
+ return HdfBase._parse_ras_datetime(s)
184
+ elif re.match(rf"^{ras_datetime_format2_re}", s):
185
+ if re.match(rf"^{ras_datetime_format2_re} to {ras_datetime_format2_re}$", s):
186
+ split = s.split(" to ")
187
+ return [
188
+ HdfBase._parse_ras_simulation_window_datetime(split[0]),
189
+ HdfBase._parse_ras_simulation_window_datetime(split[1]),
190
+ ]
191
+ return HdfBase._parse_ras_simulation_window_datetime(s)
192
+ elif re.match(rf"^{ras_duration_format_re}$", s):
193
+ return HdfBase._parse_duration(s)
194
+ return s
195
+
196
+
197
+