ras-commander 0.33.0__py3-none-any.whl → 0.35.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/RasCmdr.py +171 -138
- ras_commander/RasExamples.py +334 -120
- ras_commander/RasGeo.py +27 -6
- ras_commander/RasHdf.py +1702 -0
- ras_commander/RasPlan.py +398 -437
- ras_commander/RasPrj.py +403 -65
- ras_commander/RasUnsteady.py +24 -4
- ras_commander/RasUtils.py +352 -51
- ras_commander/__init__.py +4 -1
- ras_commander-0.35.0.dist-info/METADATA +319 -0
- ras_commander-0.35.0.dist-info/RECORD +15 -0
- ras_commander-0.33.0.dist-info/METADATA +0 -5
- ras_commander-0.33.0.dist-info/RECORD +0 -14
- {ras_commander-0.33.0.dist-info → ras_commander-0.35.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.33.0.dist-info → ras_commander-0.35.0.dist-info}/WHEEL +0 -0
- {ras_commander-0.33.0.dist-info → ras_commander-0.35.0.dist-info}/top_level.txt +0 -0
ras_commander/RasPlan.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
"""
|
2
|
-
Operations for modifying and updating HEC-RAS plan files.
|
3
|
-
|
4
|
-
"""
|
5
1
|
import re
|
2
|
+
import logging
|
6
3
|
from pathlib import Path
|
7
4
|
import shutil
|
8
5
|
from typing import Union, Optional
|
@@ -10,11 +7,27 @@ import pandas as pd
|
|
10
7
|
from .RasPrj import RasPrj, ras
|
11
8
|
from .RasUtils import RasUtils
|
12
9
|
|
10
|
+
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import Union, Any
|
13
|
+
import logging
|
14
|
+
import re
|
15
|
+
|
16
|
+
|
17
|
+
# Configure logging
|
18
|
+
logging.basicConfig(
|
19
|
+
level=logging.INFO,
|
20
|
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
21
|
+
handlers=[
|
22
|
+
logging.StreamHandler()
|
23
|
+
]
|
24
|
+
)
|
25
|
+
|
13
26
|
class RasPlan:
|
14
27
|
"""
|
15
28
|
A class for operations on HEC-RAS plan files.
|
16
29
|
"""
|
17
|
-
|
30
|
+
|
18
31
|
@staticmethod
|
19
32
|
def set_geom(plan_number: Union[str, int], new_geom: Union[str, int], ras_object=None) -> pd.DataFrame:
|
20
33
|
"""
|
@@ -47,24 +60,29 @@ class RasPlan:
|
|
47
60
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
48
61
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
49
62
|
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
# Log the current geometry DataFrame for debugging
|
64
|
+
logging.debug("Current geometry DataFrame within the function:")
|
65
|
+
logging.debug(ras_obj.geom_df)
|
66
|
+
|
54
67
|
if new_geom not in ras_obj.geom_df['geom_number'].values:
|
68
|
+
logging.error(f"Geometry {new_geom} not found in project.")
|
55
69
|
raise ValueError(f"Geometry {new_geom} not found in project.")
|
56
70
|
|
57
71
|
# Update the geometry for the specified plan
|
58
72
|
ras_obj.plan_df.loc[ras_obj.plan_df['plan_number'] == plan_number, 'geom_number'] = new_geom
|
59
73
|
|
60
|
-
|
61
|
-
|
62
|
-
|
74
|
+
logging.info(f"Geometry for plan {plan_number} set to {new_geom}")
|
75
|
+
logging.debug("Updated plan DataFrame:")
|
76
|
+
logging.debug(ras_obj.plan_df)
|
63
77
|
|
64
78
|
# Update the project file
|
65
79
|
prj_file_path = ras_obj.prj_file
|
66
|
-
|
67
|
-
|
80
|
+
try:
|
81
|
+
with open(prj_file_path, 'r') as f:
|
82
|
+
lines = f.readlines()
|
83
|
+
except FileNotFoundError:
|
84
|
+
logging.error(f"Project file not found: {prj_file_path}")
|
85
|
+
raise
|
68
86
|
|
69
87
|
plan_pattern = re.compile(rf"^Plan File=p{plan_number}", re.IGNORECASE)
|
70
88
|
geom_pattern = re.compile(r"^Geom File=g\d+", re.IGNORECASE)
|
@@ -74,13 +92,17 @@ class RasPlan:
|
|
74
92
|
for j in range(i+1, len(lines)):
|
75
93
|
if geom_pattern.match(lines[j]):
|
76
94
|
lines[j] = f"Geom File=g{new_geom}\n"
|
95
|
+
logging.info(f"Updated Geom File in project file to g{new_geom} for plan {plan_number}")
|
77
96
|
break
|
78
97
|
break
|
79
98
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
99
|
+
try:
|
100
|
+
with open(prj_file_path, 'w') as f:
|
101
|
+
f.writelines(lines)
|
102
|
+
logging.info(f"Updated project file with new geometry for plan {plan_number}")
|
103
|
+
except IOError as e:
|
104
|
+
logging.error(f"Failed to write to project file: {e}")
|
105
|
+
raise
|
84
106
|
|
85
107
|
# Re-initialize the ras object to reflect changes
|
86
108
|
ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
|
@@ -118,15 +140,22 @@ class RasPlan:
|
|
118
140
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
119
141
|
|
120
142
|
if new_steady_flow_number not in ras_obj.flow_df['flow_number'].values:
|
143
|
+
logging.error(f"Steady flow number {new_steady_flow_number} not found in project file.")
|
121
144
|
raise ValueError(f"Steady flow number {new_steady_flow_number} not found in project file.")
|
122
145
|
|
123
146
|
# Resolve the full path of the plan file
|
124
147
|
plan_file_path = RasPlan.get_plan_path(plan_number, ras_obj)
|
125
148
|
if not plan_file_path:
|
149
|
+
logging.error(f"Plan file not found: {plan_number}")
|
126
150
|
raise FileNotFoundError(f"Plan file not found: {plan_number}")
|
127
151
|
|
128
|
-
|
129
|
-
|
152
|
+
try:
|
153
|
+
with open(plan_file_path, 'r') as f:
|
154
|
+
lines = f.readlines()
|
155
|
+
except FileNotFoundError:
|
156
|
+
logging.error(f"Plan file not found: {plan_file_path}")
|
157
|
+
raise
|
158
|
+
|
130
159
|
with open(plan_file_path, 'w') as f:
|
131
160
|
for line in lines:
|
132
161
|
if line.startswith("Flow File=f"):
|
@@ -135,6 +164,7 @@ class RasPlan:
|
|
135
164
|
else:
|
136
165
|
f.write(line)
|
137
166
|
|
167
|
+
# Update the ras object's dataframes
|
138
168
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
139
169
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
140
170
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
@@ -163,7 +193,7 @@ class RasPlan:
|
|
163
193
|
Note:
|
164
194
|
This function updates the ras object's dataframes after modifying the project structure.
|
165
195
|
"""
|
166
|
-
|
196
|
+
logging.info(f"Setting unsteady flow file to {new_unsteady_flow_number} in Plan {plan_number}")
|
167
197
|
|
168
198
|
ras_obj = ras_object or ras
|
169
199
|
ras_obj.check_initialized()
|
@@ -172,20 +202,23 @@ class RasPlan:
|
|
172
202
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
173
203
|
|
174
204
|
if new_unsteady_flow_number not in ras_obj.unsteady_df['unsteady_number'].values:
|
205
|
+
logging.error(f"Unsteady number {new_unsteady_flow_number} not found in project file.")
|
175
206
|
raise ValueError(f"Unsteady number {new_unsteady_flow_number} not found in project file.")
|
176
207
|
|
177
208
|
# Get the full path of the plan file
|
178
209
|
plan_file_path = RasPlan.get_plan_path(plan_number, ras_obj)
|
179
210
|
if not plan_file_path:
|
211
|
+
logging.error(f"Plan file not found: {plan_number}")
|
180
212
|
raise FileNotFoundError(f"Plan file not found: {plan_number}")
|
181
213
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
214
|
+
try:
|
215
|
+
RasUtils.update_plan_file(plan_file_path, 'Unsteady', new_unsteady_flow_number)
|
216
|
+
logging.info(f"Updated unsteady flow file in {plan_file_path} to u{new_unsteady_flow_number}")
|
217
|
+
except Exception as e:
|
218
|
+
logging.error(f"Failed to update unsteady flow file: {e}")
|
219
|
+
raise
|
188
220
|
|
221
|
+
# Update the ras object's dataframes
|
189
222
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
190
223
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
191
224
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
@@ -220,7 +253,7 @@ class RasPlan:
|
|
220
253
|
Note:
|
221
254
|
This function updates the ras object's dataframes after modifying the project structure.
|
222
255
|
"""
|
223
|
-
|
256
|
+
logging.info(f"Setting num_cores to {num_cores} in Plan {plan_number}")
|
224
257
|
|
225
258
|
ras_obj = ras_object or ras
|
226
259
|
ras_obj.check_initialized()
|
@@ -229,6 +262,7 @@ class RasPlan:
|
|
229
262
|
if Path(plan_number).is_file():
|
230
263
|
plan_file_path = Path(plan_number)
|
231
264
|
if not plan_file_path.exists():
|
265
|
+
logging.error(f"Plan file not found: {plan_file_path}. Please provide a valid plan number or path.")
|
232
266
|
raise FileNotFoundError(f"Plan file not found: {plan_file_path}. Please provide a valid plan number or path.")
|
233
267
|
else:
|
234
268
|
# Update the plan dataframe in the ras instance to ensure it is current
|
@@ -237,22 +271,32 @@ class RasPlan:
|
|
237
271
|
# Get the full path of the plan file
|
238
272
|
plan_file_path = RasPlan.get_plan_path(plan_number, ras_obj)
|
239
273
|
if not plan_file_path:
|
274
|
+
logging.error(f"Plan file not found: {plan_number}. Please provide a valid plan number or path.")
|
240
275
|
raise FileNotFoundError(f"Plan file not found: {plan_number}. Please provide a valid plan number or path.")
|
241
276
|
|
242
277
|
cores_pattern = re.compile(r"(UNET D1 Cores= )\d+")
|
243
|
-
|
244
|
-
|
278
|
+
try:
|
279
|
+
with open(plan_file_path, 'r') as file:
|
280
|
+
content = file.read()
|
281
|
+
except FileNotFoundError:
|
282
|
+
logging.error(f"Plan file not found: {plan_file_path}")
|
283
|
+
raise
|
284
|
+
|
245
285
|
new_content = cores_pattern.sub(rf"\g<1>{num_cores}", content)
|
246
|
-
|
247
|
-
|
248
|
-
|
286
|
+
try:
|
287
|
+
with open(plan_file_path, 'w') as file:
|
288
|
+
file.write(new_content)
|
289
|
+
logging.info(f"Updated {plan_file_path} with {num_cores} cores.")
|
290
|
+
except IOError as e:
|
291
|
+
logging.error(f"Failed to write to plan file: {e}")
|
292
|
+
raise
|
249
293
|
|
294
|
+
# Update the ras object's dataframes
|
250
295
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
251
296
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
252
297
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
253
298
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
254
|
-
|
255
|
-
|
299
|
+
|
256
300
|
@staticmethod
|
257
301
|
def set_geom_preprocessor(file_path, run_htab, use_ib_tables, ras_object=None):
|
258
302
|
"""
|
@@ -286,41 +330,46 @@ class RasPlan:
|
|
286
330
|
ras_obj.check_initialized()
|
287
331
|
|
288
332
|
if run_htab not in [-1, 0]:
|
333
|
+
logging.error("Invalid value for `Run HTab`. Expected `0` or `-1`.")
|
289
334
|
raise ValueError("Invalid value for `Run HTab`. Expected `0` or `-1`.")
|
290
335
|
if use_ib_tables not in [-1, 0]:
|
336
|
+
logging.error("Invalid value for `UNET Use Existing IB Tables`. Expected `0` or `-1`.")
|
291
337
|
raise ValueError("Invalid value for `UNET Use Existing IB Tables`. Expected `0` or `-1`.")
|
292
338
|
try:
|
293
|
-
|
339
|
+
logging.info(f"Reading the file: {file_path}")
|
294
340
|
with open(file_path, 'r') as file:
|
295
341
|
lines = file.readlines()
|
296
|
-
|
342
|
+
logging.info("Updating the file with new settings...")
|
297
343
|
updated_lines = []
|
298
344
|
for line in lines:
|
299
345
|
if line.lstrip().startswith("Run HTab="):
|
300
346
|
updated_line = f"Run HTab= {run_htab} \n"
|
301
347
|
updated_lines.append(updated_line)
|
302
|
-
|
348
|
+
logging.info(f"Updated 'Run HTab' to {run_htab}")
|
303
349
|
elif line.lstrip().startswith("UNET Use Existing IB Tables="):
|
304
350
|
updated_line = f"UNET Use Existing IB Tables= {use_ib_tables} \n"
|
305
351
|
updated_lines.append(updated_line)
|
306
|
-
|
352
|
+
logging.info(f"Updated 'UNET Use Existing IB Tables' to {use_ib_tables}")
|
307
353
|
else:
|
308
354
|
updated_lines.append(line)
|
309
|
-
|
355
|
+
logging.info(f"Writing the updated settings back to the file: {file_path}")
|
310
356
|
with open(file_path, 'w') as file:
|
311
357
|
file.writelines(updated_lines)
|
312
|
-
|
358
|
+
logging.info("File update completed successfully.")
|
313
359
|
except FileNotFoundError:
|
360
|
+
logging.error(f"The file '{file_path}' does not exist.")
|
314
361
|
raise FileNotFoundError(f"The file '{file_path}' does not exist.")
|
315
362
|
except IOError as e:
|
363
|
+
logging.error(f"An error occurred while reading or writing the file: {e}")
|
316
364
|
raise IOError(f"An error occurred while reading or writing the file: {e}")
|
317
365
|
|
366
|
+
# Update the ras object's dataframes
|
318
367
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
319
368
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
320
369
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
321
370
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
322
371
|
|
323
|
-
# Get Functions to retrieve file paths for plan, flow, unsteady, geometry and results files
|
372
|
+
# Get Functions to retrieve file paths for plan, flow, unsteady, geometry and results files
|
324
373
|
|
325
374
|
@staticmethod
|
326
375
|
def get_results_path(plan_number: str, ras_object=None) -> Optional[str]:
|
@@ -352,28 +401,25 @@ class RasPlan:
|
|
352
401
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
353
402
|
|
354
403
|
# Ensure plan_number is a string
|
355
|
-
plan_number = str(plan_number)
|
356
|
-
|
357
|
-
# Ensure plan_number is formatted as '01', '02', etc.
|
358
|
-
plan_number = plan_number.zfill(2)
|
404
|
+
plan_number = str(plan_number).zfill(2)
|
359
405
|
|
360
|
-
#
|
361
|
-
|
362
|
-
|
406
|
+
# Log the plan dataframe for debugging
|
407
|
+
logging.debug("Plan DataFrame:")
|
408
|
+
logging.debug(ras_obj.plan_df)
|
363
409
|
|
364
410
|
plan_entry = ras_obj.plan_df[ras_obj.plan_df['plan_number'] == plan_number]
|
365
411
|
if not plan_entry.empty:
|
366
412
|
results_path = plan_entry['HDF_Results_Path'].iloc[0]
|
367
|
-
if results_path:
|
368
|
-
|
413
|
+
if results_path and Path(results_path).exists():
|
414
|
+
logging.info(f"Results file for Plan number {plan_number} exists at: {results_path}")
|
369
415
|
return results_path
|
370
416
|
else:
|
371
|
-
|
417
|
+
logging.warning(f"Results file for Plan number {plan_number} does not exist.")
|
372
418
|
return None
|
373
419
|
else:
|
374
|
-
|
420
|
+
logging.warning(f"Plan number {plan_number} not found in the entries.")
|
375
421
|
return None
|
376
|
-
|
422
|
+
|
377
423
|
@staticmethod
|
378
424
|
def get_plan_path(plan_number: str, ras_object=None) -> Optional[str]:
|
379
425
|
"""
|
@@ -403,8 +449,6 @@ class RasPlan:
|
|
403
449
|
ras_obj = ras_object or ras
|
404
450
|
ras_obj.check_initialized()
|
405
451
|
|
406
|
-
project_name = ras_obj.project_name
|
407
|
-
|
408
452
|
# Use updated plan dataframe
|
409
453
|
plan_df = ras_obj.get_plan_entries()
|
410
454
|
|
@@ -412,9 +456,10 @@ class RasPlan:
|
|
412
456
|
|
413
457
|
if not plan_path.empty:
|
414
458
|
full_path = plan_path['full_path'].iloc[0]
|
459
|
+
logging.info(f"Plan file for Plan number {plan_number} found at: {full_path}")
|
415
460
|
return full_path
|
416
461
|
else:
|
417
|
-
|
462
|
+
logging.warning(f"Plan number {plan_number} not found in the updated plan entries.")
|
418
463
|
return None
|
419
464
|
|
420
465
|
@staticmethod
|
@@ -449,9 +494,10 @@ class RasPlan:
|
|
449
494
|
flow_path = ras_obj.flow_df[ras_obj.flow_df['flow_number'] == flow_number]
|
450
495
|
if not flow_path.empty:
|
451
496
|
full_path = flow_path['full_path'].iloc[0]
|
497
|
+
logging.info(f"Flow file for Flow number {flow_number} found at: {full_path}")
|
452
498
|
return full_path
|
453
499
|
else:
|
454
|
-
|
500
|
+
logging.warning(f"Flow number {flow_number} not found in the updated flow entries.")
|
455
501
|
return None
|
456
502
|
|
457
503
|
@staticmethod
|
@@ -486,9 +532,10 @@ class RasPlan:
|
|
486
532
|
unsteady_path = ras_obj.unsteady_df[ras_obj.unsteady_df['unsteady_number'] == unsteady_number]
|
487
533
|
if not unsteady_path.empty:
|
488
534
|
full_path = unsteady_path['full_path'].iloc[0]
|
535
|
+
logging.info(f"Unsteady file for Unsteady number {unsteady_number} found at: {full_path}")
|
489
536
|
return full_path
|
490
537
|
else:
|
491
|
-
|
538
|
+
logging.warning(f"Unsteady number {unsteady_number} not found in the updated unsteady entries.")
|
492
539
|
return None
|
493
540
|
|
494
541
|
@staticmethod
|
@@ -523,12 +570,14 @@ class RasPlan:
|
|
523
570
|
geom_path = ras_obj.geom_df[ras_obj.geom_df['geom_number'] == geom_number]
|
524
571
|
if not geom_path.empty:
|
525
572
|
full_path = geom_path['full_path'].iloc[0]
|
573
|
+
logging.info(f"Geometry file for Geom number {geom_number} found at: {full_path}")
|
526
574
|
return full_path
|
527
575
|
else:
|
528
|
-
|
576
|
+
logging.warning(f"Geometry number {geom_number} not found in the updated geometry entries.")
|
529
577
|
return None
|
530
|
-
|
531
|
-
|
578
|
+
|
579
|
+
# Clone Functions to copy unsteady, flow, and geometry files from templates
|
580
|
+
|
532
581
|
@staticmethod
|
533
582
|
def clone_plan(template_plan, new_plan_shortid=None, ras_object=None):
|
534
583
|
"""
|
@@ -561,13 +610,18 @@ class RasPlan:
|
|
561
610
|
new_plan_path = ras_obj.project_folder / f"{ras_obj.project_name}.p{new_plan_num}"
|
562
611
|
|
563
612
|
if not template_plan_path.exists():
|
613
|
+
logging.error(f"Template plan file '{template_plan_path}' does not exist.")
|
564
614
|
raise FileNotFoundError(f"Template plan file '{template_plan_path}' does not exist.")
|
565
615
|
|
566
616
|
shutil.copy(template_plan_path, new_plan_path)
|
567
|
-
|
617
|
+
logging.info(f"Copied {template_plan_path} to {new_plan_path}")
|
568
618
|
|
569
|
-
|
570
|
-
|
619
|
+
try:
|
620
|
+
with open(new_plan_path, 'r') as f:
|
621
|
+
plan_lines = f.readlines()
|
622
|
+
except FileNotFoundError:
|
623
|
+
logging.error(f"New plan file not found after copying: {new_plan_path}")
|
624
|
+
raise
|
571
625
|
|
572
626
|
shortid_pattern = re.compile(r'^Short Identifier=(.*)$', re.IGNORECASE)
|
573
627
|
for i, line in enumerate(plan_lines):
|
@@ -579,15 +633,23 @@ class RasPlan:
|
|
579
633
|
else:
|
580
634
|
new_shortid = new_plan_shortid[:24]
|
581
635
|
plan_lines[i] = f"Short Identifier={new_shortid}\n"
|
636
|
+
logging.info(f"Updated 'Short Identifier' to '{new_shortid}' in {new_plan_path}")
|
582
637
|
break
|
583
638
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
639
|
+
try:
|
640
|
+
with open(new_plan_path, 'w') as f:
|
641
|
+
f.writelines(plan_lines)
|
642
|
+
logging.info(f"Updated short identifier in {new_plan_path}")
|
643
|
+
except IOError as e:
|
644
|
+
logging.error(f"Failed to write updated short identifier to {new_plan_path}: {e}")
|
645
|
+
raise
|
588
646
|
|
589
|
-
|
590
|
-
|
647
|
+
try:
|
648
|
+
with open(ras_obj.prj_file, 'r') as f:
|
649
|
+
lines = f.readlines()
|
650
|
+
except FileNotFoundError:
|
651
|
+
logging.error(f"Project file not found: {ras_obj.prj_file}")
|
652
|
+
raise
|
591
653
|
|
592
654
|
# Prepare the new Plan File entry line
|
593
655
|
new_plan_line = f"Plan File=p{new_plan_num}\n"
|
@@ -607,28 +669,32 @@ class RasPlan:
|
|
607
669
|
|
608
670
|
if insertion_index is not None:
|
609
671
|
lines.insert(insertion_index, new_plan_line)
|
672
|
+
logging.info(f"Inserted new plan line at index {insertion_index}")
|
610
673
|
else:
|
611
674
|
# Try to insert after the last Plan File entry
|
612
675
|
plan_indices = [i for i, line in enumerate(lines) if plan_file_pattern.match(line.strip())]
|
613
676
|
if plan_indices:
|
614
677
|
last_plan_index = plan_indices[-1]
|
615
678
|
lines.insert(last_plan_index + 1, new_plan_line)
|
679
|
+
logging.info(f"Inserted new plan line after index {last_plan_index}")
|
616
680
|
else:
|
617
681
|
# Append at the end if no Plan File entries exist
|
618
682
|
lines.append(new_plan_line)
|
683
|
+
logging.info(f"Appended new plan line at the end of the project file")
|
619
684
|
|
620
|
-
|
621
|
-
|
622
|
-
|
685
|
+
try:
|
686
|
+
# Write the updated lines back to the project file
|
687
|
+
with open(ras_obj.prj_file, 'w') as f:
|
688
|
+
f.writelines(lines)
|
689
|
+
logging.info(f"Updated {ras_obj.prj_file} with new plan p{new_plan_num}")
|
690
|
+
except IOError as e:
|
691
|
+
logging.error(f"Failed to write updated project file: {e}")
|
692
|
+
raise
|
623
693
|
|
624
|
-
print(f"Updated {ras_obj.prj_file} with new plan p{new_plan_num}")
|
625
694
|
new_plan = new_plan_num
|
626
695
|
|
627
|
-
# Store the project folder path
|
628
|
-
project_folder = ras_obj.project_folder
|
629
|
-
|
630
696
|
# Re-initialize the ras global object
|
631
|
-
ras_obj.initialize(project_folder, ras_obj.ras_exe_path)
|
697
|
+
ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
|
632
698
|
|
633
699
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
634
700
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
@@ -637,7 +703,6 @@ class RasPlan:
|
|
637
703
|
|
638
704
|
return new_plan
|
639
705
|
|
640
|
-
|
641
706
|
@staticmethod
|
642
707
|
def clone_unsteady(template_unsteady, ras_object=None):
|
643
708
|
"""
|
@@ -670,22 +735,27 @@ class RasPlan:
|
|
670
735
|
new_unsteady_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{new_unsteady_num}"
|
671
736
|
|
672
737
|
if not template_unsteady_path.exists():
|
738
|
+
logging.error(f"Template unsteady file '{template_unsteady_path}' does not exist.")
|
673
739
|
raise FileNotFoundError(f"Template unsteady file '{template_unsteady_path}' does not exist.")
|
674
740
|
|
675
741
|
shutil.copy(template_unsteady_path, new_unsteady_path)
|
676
|
-
|
742
|
+
logging.info(f"Copied {template_unsteady_path} to {new_unsteady_path}")
|
677
743
|
|
678
744
|
# Copy the corresponding .hdf file if it exists
|
679
745
|
template_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{template_unsteady}.hdf"
|
680
746
|
new_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{new_unsteady_num}.hdf"
|
681
747
|
if template_hdf_path.exists():
|
682
748
|
shutil.copy(template_hdf_path, new_hdf_path)
|
683
|
-
|
749
|
+
logging.info(f"Copied {template_hdf_path} to {new_hdf_path}")
|
684
750
|
else:
|
685
|
-
|
751
|
+
logging.warning(f"No corresponding .hdf file found for '{template_unsteady_path}'. Skipping '.hdf' copy.")
|
686
752
|
|
687
|
-
|
688
|
-
|
753
|
+
try:
|
754
|
+
with open(ras_obj.prj_file, 'r') as f:
|
755
|
+
lines = f.readlines()
|
756
|
+
except FileNotFoundError:
|
757
|
+
logging.error(f"Project file not found: {ras_obj.prj_file}")
|
758
|
+
raise
|
689
759
|
|
690
760
|
# Prepare the new Unsteady Flow File entry line
|
691
761
|
new_unsteady_line = f"Unsteady File=u{new_unsteady_num}\n"
|
@@ -705,29 +775,32 @@ class RasPlan:
|
|
705
775
|
|
706
776
|
if insertion_index is not None:
|
707
777
|
lines.insert(insertion_index, new_unsteady_line)
|
778
|
+
logging.info(f"Inserted new unsteady flow line at index {insertion_index}")
|
708
779
|
else:
|
709
780
|
# Try to insert after the last Unsteady Flow File entry
|
710
781
|
unsteady_indices = [i for i, line in enumerate(lines) if unsteady_file_pattern.match(line.strip())]
|
711
782
|
if unsteady_indices:
|
712
783
|
last_unsteady_index = unsteady_indices[-1]
|
713
784
|
lines.insert(last_unsteady_index + 1, new_unsteady_line)
|
785
|
+
logging.info(f"Inserted new unsteady flow line after index {last_unsteady_index}")
|
714
786
|
else:
|
715
787
|
# Append at the end if no Unsteady Flow File entries exist
|
716
788
|
lines.append(new_unsteady_line)
|
789
|
+
logging.info(f"Appended new unsteady flow line at the end of the project file")
|
717
790
|
|
718
|
-
|
719
|
-
|
720
|
-
|
791
|
+
try:
|
792
|
+
# Write the updated lines back to the project file
|
793
|
+
with open(ras_obj.prj_file, 'w') as f:
|
794
|
+
f.writelines(lines)
|
795
|
+
logging.info(f"Updated {ras_obj.prj_file} with new unsteady flow file u{new_unsteady_num}")
|
796
|
+
except IOError as e:
|
797
|
+
logging.error(f"Failed to write updated project file: {e}")
|
798
|
+
raise
|
721
799
|
|
722
|
-
print(f"Updated {ras_obj.prj_file} with new unsteady flow file u{new_unsteady_num}")
|
723
800
|
new_unsteady = new_unsteady_num
|
724
801
|
|
725
|
-
# Store the project folder path
|
726
|
-
project_folder = ras_obj.project_folder
|
727
|
-
hecras_path = ras_obj.ras_exe_path
|
728
|
-
|
729
802
|
# Re-initialize the ras global object
|
730
|
-
ras_obj.initialize(project_folder,
|
803
|
+
ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
|
731
804
|
|
732
805
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
733
806
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
@@ -768,14 +841,19 @@ class RasPlan:
|
|
768
841
|
new_flow_path = ras_obj.project_folder / f"{ras_obj.project_name}.f{new_flow_num}"
|
769
842
|
|
770
843
|
if not template_flow_path.exists():
|
844
|
+
logging.error(f"Template steady flow file '{template_flow_path}' does not exist.")
|
771
845
|
raise FileNotFoundError(f"Template steady flow file '{template_flow_path}' does not exist.")
|
772
846
|
|
773
847
|
shutil.copy(template_flow_path, new_flow_path)
|
774
|
-
|
848
|
+
logging.info(f"Copied {template_flow_path} to {new_flow_path}")
|
775
849
|
|
776
850
|
# Read the contents of the project file
|
777
|
-
|
778
|
-
|
851
|
+
try:
|
852
|
+
with open(ras_obj.prj_file, 'r') as f:
|
853
|
+
lines = f.readlines()
|
854
|
+
except FileNotFoundError:
|
855
|
+
logging.error(f"Project file not found: {ras_obj.prj_file}")
|
856
|
+
raise
|
779
857
|
|
780
858
|
# Prepare the new Steady Flow File entry line
|
781
859
|
new_flow_line = f"Flow File=f{new_flow_num}\n"
|
@@ -795,28 +873,32 @@ class RasPlan:
|
|
795
873
|
|
796
874
|
if insertion_index is not None:
|
797
875
|
lines.insert(insertion_index, new_flow_line)
|
876
|
+
logging.info(f"Inserted new steady flow line at index {insertion_index}")
|
798
877
|
else:
|
799
878
|
# Try to insert after the last Steady Flow File entry
|
800
879
|
flow_indices = [i for i, line in enumerate(lines) if flow_file_pattern.match(line.strip())]
|
801
880
|
if flow_indices:
|
802
881
|
last_flow_index = flow_indices[-1]
|
803
882
|
lines.insert(last_flow_index + 1, new_flow_line)
|
883
|
+
logging.info(f"Inserted new steady flow line after index {last_flow_index}")
|
804
884
|
else:
|
805
885
|
# Append at the end if no Steady Flow File entries exist
|
806
886
|
lines.append(new_flow_line)
|
887
|
+
logging.info(f"Appended new steady flow line at the end of the project file")
|
807
888
|
|
808
|
-
|
809
|
-
|
810
|
-
|
889
|
+
try:
|
890
|
+
# Write the updated lines back to the project file
|
891
|
+
with open(ras_obj.prj_file, 'w') as f:
|
892
|
+
f.writelines(lines)
|
893
|
+
logging.info(f"Updated {ras_obj.prj_file} with new steady flow file f{new_flow_num}")
|
894
|
+
except IOError as e:
|
895
|
+
logging.error(f"Failed to write updated project file: {e}")
|
896
|
+
raise
|
811
897
|
|
812
|
-
print(f"Updated {ras_obj.prj_file} with new steady flow file f{new_flow_num}")
|
813
898
|
new_steady = new_flow_num
|
814
899
|
|
815
|
-
# Store the project folder path
|
816
|
-
project_folder = ras_obj.project_folder
|
817
|
-
|
818
900
|
# Re-initialize the ras global object
|
819
|
-
ras_obj.initialize(project_folder, ras_obj.ras_exe_path)
|
901
|
+
ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
|
820
902
|
|
821
903
|
ras_obj.plan_df = ras_obj.get_plan_entries()
|
822
904
|
ras_obj.geom_df = ras_obj.get_geom_entries()
|
@@ -825,322 +907,6 @@ class RasPlan:
|
|
825
907
|
|
826
908
|
return new_steady
|
827
909
|
|
828
|
-
|
829
|
-
@staticmethod
|
830
|
-
def clone_geom(template_geom, ras_object=None):
|
831
|
-
"""
|
832
|
-
Copy geometry files from a template, find the next geometry number,
|
833
|
-
and update the project file accordingly.
|
834
|
-
|
835
|
-
Parameters:
|
836
|
-
template_geom (str): Geometry number to be used as a template (e.g., '01')
|
837
|
-
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
838
|
-
|
839
|
-
Returns:
|
840
|
-
str: New geometry number (e.g., '03')
|
841
|
-
|
842
|
-
Note:
|
843
|
-
This function updates the ras object's dataframes after modifying the project structure.
|
844
|
-
"""
|
845
|
-
ras_obj = ras_object or ras
|
846
|
-
ras_obj.check_initialized()
|
847
|
-
|
848
|
-
# Update geometry entries without reinitializing the entire project
|
849
|
-
ras_obj.geom_df = ras_obj.get_prj_entries('Geom') # Call the correct function to get updated geometry entries
|
850
|
-
print(f"Updated geometry entries:\n{ras_obj.geom_df}")
|
851
|
-
|
852
|
-
# Clone Functions to copy unsteady, flow, and geometry files from templates
|
853
|
-
|
854
|
-
@staticmethod
|
855
|
-
def clone_plan(template_plan, new_plan_shortid=None, ras_object=None):
|
856
|
-
"""
|
857
|
-
Create a new plan file based on a template and update the project file.
|
858
|
-
|
859
|
-
Parameters:
|
860
|
-
template_plan (str): Plan number to use as template (e.g., '01')
|
861
|
-
new_plan_shortid (str, optional): New short identifier for the plan file
|
862
|
-
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
863
|
-
|
864
|
-
Returns:
|
865
|
-
str: New plan number
|
866
|
-
|
867
|
-
Revision Notes:
|
868
|
-
- Updated to insert new plan entry in the correct position
|
869
|
-
- Improved error handling and logging
|
870
|
-
- Updated to use get_prj_entries('Plan') for the latest entries
|
871
|
-
- Added print statements for progress tracking
|
872
|
-
|
873
|
-
Example:
|
874
|
-
>>> ras_plan = RasPlan()
|
875
|
-
>>> new_plan_number = ras_plan.clone_plan('01', new_plan_shortid='New Plan')
|
876
|
-
>>> print(f"New plan created with number: {new_plan_number}")
|
877
|
-
"""
|
878
|
-
ras_obj = ras_object or ras
|
879
|
-
ras_obj.check_initialized()
|
880
|
-
|
881
|
-
# Update plan entries without reinitializing the entire project
|
882
|
-
ras_obj.plan_df = ras_obj.get_prj_entries('Plan')
|
883
|
-
|
884
|
-
new_plan_num = RasPlan.get_next_number(ras_obj.plan_df['plan_number'])
|
885
|
-
template_plan_path = ras_obj.project_folder / f"{ras_obj.project_name}.p{template_plan}"
|
886
|
-
new_plan_path = ras_obj.project_folder / f"{ras_obj.project_name}.p{new_plan_num}"
|
887
|
-
|
888
|
-
if not template_plan_path.exists():
|
889
|
-
raise FileNotFoundError(f"Template plan file '{template_plan_path}' does not exist.")
|
890
|
-
|
891
|
-
shutil.copy(template_plan_path, new_plan_path)
|
892
|
-
print(f"Copied {template_plan_path} to {new_plan_path}")
|
893
|
-
|
894
|
-
with open(new_plan_path, 'r') as f:
|
895
|
-
plan_lines = f.readlines()
|
896
|
-
|
897
|
-
shortid_pattern = re.compile(r'^Short Identifier=(.*)$', re.IGNORECASE)
|
898
|
-
for i, line in enumerate(plan_lines):
|
899
|
-
match = shortid_pattern.match(line.strip())
|
900
|
-
if match:
|
901
|
-
current_shortid = match.group(1)
|
902
|
-
if new_plan_shortid is None:
|
903
|
-
new_shortid = (current_shortid + "_copy")[:24]
|
904
|
-
else:
|
905
|
-
new_shortid = new_plan_shortid[:24]
|
906
|
-
plan_lines[i] = f"Short Identifier={new_shortid}\n"
|
907
|
-
break
|
908
|
-
|
909
|
-
with open(new_plan_path, 'w') as f:
|
910
|
-
f.writelines(plan_lines)
|
911
|
-
|
912
|
-
print(f"Updated short identifier in {new_plan_path}")
|
913
|
-
|
914
|
-
with open(ras_obj.prj_file, 'r') as f:
|
915
|
-
lines = f.readlines()
|
916
|
-
|
917
|
-
# Prepare the new Plan File entry line
|
918
|
-
new_plan_line = f"Plan File=p{new_plan_num}\n"
|
919
|
-
|
920
|
-
# Find the correct insertion point for the new Plan File entry
|
921
|
-
plan_file_pattern = re.compile(r'^Plan File=p(\d+)', re.IGNORECASE)
|
922
|
-
insertion_index = None
|
923
|
-
for i, line in enumerate(lines):
|
924
|
-
match = plan_file_pattern.match(line.strip())
|
925
|
-
if match:
|
926
|
-
current_number = int(match.group(1))
|
927
|
-
if current_number < int(new_plan_num):
|
928
|
-
continue
|
929
|
-
else:
|
930
|
-
insertion_index = i
|
931
|
-
break
|
932
|
-
|
933
|
-
if insertion_index is not None:
|
934
|
-
lines.insert(insertion_index, new_plan_line)
|
935
|
-
else:
|
936
|
-
# Try to insert after the last Plan File entry
|
937
|
-
plan_indices = [i for i, line in enumerate(lines) if plan_file_pattern.match(line.strip())]
|
938
|
-
if plan_indices:
|
939
|
-
last_plan_index = plan_indices[-1]
|
940
|
-
lines.insert(last_plan_index + 1, new_plan_line)
|
941
|
-
else:
|
942
|
-
# Append at the end if no Plan File entries exist
|
943
|
-
lines.append(new_plan_line)
|
944
|
-
|
945
|
-
# Write the updated lines back to the project file
|
946
|
-
with open(ras_obj.prj_file, 'w') as f:
|
947
|
-
f.writelines(lines)
|
948
|
-
|
949
|
-
print(f"Updated {ras_obj.prj_file} with new plan p{new_plan_num}")
|
950
|
-
new_plan = new_plan_num
|
951
|
-
|
952
|
-
# Store the project folder path
|
953
|
-
project_folder = ras_obj.project_folder
|
954
|
-
|
955
|
-
# Re-initialize the ras global object
|
956
|
-
ras_obj.initialize(project_folder, ras_obj.ras_exe_path)
|
957
|
-
return new_plan
|
958
|
-
|
959
|
-
|
960
|
-
@staticmethod
|
961
|
-
def clone_unsteady(template_unsteady, ras_object=None):
|
962
|
-
"""
|
963
|
-
Copy unsteady flow files from a template, find the next unsteady number,
|
964
|
-
and update the project file accordingly.
|
965
|
-
|
966
|
-
Parameters:
|
967
|
-
template_unsteady (str): Unsteady flow number to be used as a template (e.g., '01')
|
968
|
-
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
969
|
-
|
970
|
-
Returns:
|
971
|
-
str: New unsteady flow number (e.g., '03')
|
972
|
-
|
973
|
-
Example:
|
974
|
-
>>> ras_plan = RasPlan()
|
975
|
-
>>> new_unsteady_num = ras_plan.clone_unsteady('01')
|
976
|
-
>>> print(f"New unsteady flow file created: u{new_unsteady_num}")
|
977
|
-
|
978
|
-
Revision Notes:
|
979
|
-
- Updated to insert new unsteady flow entry in the correct position
|
980
|
-
- Improved error handling and logging
|
981
|
-
- Removed dst_folder parameter as it's not needed (using project folder)
|
982
|
-
- Added handling for corresponding .hdf files
|
983
|
-
- Updated to use get_prj_entries('Unsteady') for the latest entries
|
984
|
-
"""
|
985
|
-
ras_obj = ras_object or ras
|
986
|
-
ras_obj.check_initialized()
|
987
|
-
|
988
|
-
# Update unsteady entries without reinitializing the entire project
|
989
|
-
ras_obj.unsteady_df = ras_obj.get_prj_entries('Unsteady')
|
990
|
-
|
991
|
-
new_unsteady_num = RasPlan.get_next_number(ras_obj.unsteady_df['unsteady_number'])
|
992
|
-
template_unsteady_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{template_unsteady}"
|
993
|
-
new_unsteady_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{new_unsteady_num}"
|
994
|
-
|
995
|
-
if not template_unsteady_path.exists():
|
996
|
-
raise FileNotFoundError(f"Template unsteady file '{template_unsteady_path}' does not exist.")
|
997
|
-
|
998
|
-
shutil.copy(template_unsteady_path, new_unsteady_path)
|
999
|
-
print(f"Copied {template_unsteady_path} to {new_unsteady_path}")
|
1000
|
-
|
1001
|
-
# Copy the corresponding .hdf file if it exists
|
1002
|
-
template_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{template_unsteady}.hdf"
|
1003
|
-
new_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{new_unsteady_num}.hdf"
|
1004
|
-
if template_hdf_path.exists():
|
1005
|
-
shutil.copy(template_hdf_path, new_hdf_path)
|
1006
|
-
print(f"Copied {template_hdf_path} to {new_hdf_path}")
|
1007
|
-
else:
|
1008
|
-
print(f"No corresponding .hdf file found for '{template_unsteady_path}'. Skipping '.hdf' copy.")
|
1009
|
-
|
1010
|
-
with open(ras_obj.prj_file, 'r') as f:
|
1011
|
-
lines = f.readlines()
|
1012
|
-
|
1013
|
-
# Prepare the new Unsteady Flow File entry line
|
1014
|
-
new_unsteady_line = f"Unsteady File=u{new_unsteady_num}\n"
|
1015
|
-
|
1016
|
-
# Find the correct insertion point for the new Unsteady Flow File entry
|
1017
|
-
unsteady_file_pattern = re.compile(r'^Unsteady File=u(\d+)', re.IGNORECASE)
|
1018
|
-
insertion_index = None
|
1019
|
-
for i, line in enumerate(lines):
|
1020
|
-
match = unsteady_file_pattern.match(line.strip())
|
1021
|
-
if match:
|
1022
|
-
current_number = int(match.group(1))
|
1023
|
-
if current_number < int(new_unsteady_num):
|
1024
|
-
continue
|
1025
|
-
else:
|
1026
|
-
insertion_index = i
|
1027
|
-
break
|
1028
|
-
|
1029
|
-
if insertion_index is not None:
|
1030
|
-
lines.insert(insertion_index, new_unsteady_line)
|
1031
|
-
else:
|
1032
|
-
# Try to insert after the last Unsteady Flow File entry
|
1033
|
-
unsteady_indices = [i for i, line in enumerate(lines) if unsteady_file_pattern.match(line.strip())]
|
1034
|
-
if unsteady_indices:
|
1035
|
-
last_unsteady_index = unsteady_indices[-1]
|
1036
|
-
lines.insert(last_unsteady_index + 1, new_unsteady_line)
|
1037
|
-
else:
|
1038
|
-
# Append at the end if no Unsteady Flow File entries exist
|
1039
|
-
lines.append(new_unsteady_line)
|
1040
|
-
|
1041
|
-
# Write the updated lines back to the project file
|
1042
|
-
with open(ras_obj.prj_file, 'w') as f:
|
1043
|
-
f.writelines(lines)
|
1044
|
-
|
1045
|
-
print(f"Updated {ras_obj.prj_file} with new unsteady flow file u{new_unsteady_num}")
|
1046
|
-
new_unsteady = new_unsteady_num
|
1047
|
-
|
1048
|
-
# Store the project folder path
|
1049
|
-
project_folder = ras_obj.project_folder
|
1050
|
-
hecras_path = ras_obj.ras_exe_path
|
1051
|
-
|
1052
|
-
# Re-initialize the ras global object
|
1053
|
-
ras_obj.initialize(project_folder, hecras_path)
|
1054
|
-
|
1055
|
-
return new_unsteady
|
1056
|
-
|
1057
|
-
@staticmethod
|
1058
|
-
def clone_steady(template_flow, ras_object=None):
|
1059
|
-
"""
|
1060
|
-
Copy steady flow files from a template, find the next flow number,
|
1061
|
-
and update the project file accordingly.
|
1062
|
-
|
1063
|
-
Parameters:
|
1064
|
-
template_flow (str): Flow number to be used as a template (e.g., '01')
|
1065
|
-
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
1066
|
-
|
1067
|
-
Returns:
|
1068
|
-
str: New flow number (e.g., '03')
|
1069
|
-
|
1070
|
-
Example:
|
1071
|
-
>>> ras_plan = RasPlan()
|
1072
|
-
>>> new_flow_num = ras_plan.clone_steady('01')
|
1073
|
-
>>> print(f"New steady flow file created: f{new_flow_num}")
|
1074
|
-
|
1075
|
-
Revision Notes:
|
1076
|
-
- Updated to insert new steady flow entry in the correct position
|
1077
|
-
- Improved error handling and logging
|
1078
|
-
- Added handling for corresponding .hdf files
|
1079
|
-
- Updated to use get_prj_entries('Flow') for the latest entries
|
1080
|
-
"""
|
1081
|
-
ras_obj = ras_object or ras
|
1082
|
-
ras_obj.check_initialized()
|
1083
|
-
|
1084
|
-
# Update flow entries without reinitializing the entire project
|
1085
|
-
ras_obj.flow_df = ras_obj.get_prj_entries('Flow')
|
1086
|
-
|
1087
|
-
new_flow_num = RasPlan.get_next_number(ras_obj.flow_df['flow_number'])
|
1088
|
-
template_flow_path = ras_obj.project_folder / f"{ras_obj.project_name}.f{template_flow}"
|
1089
|
-
new_flow_path = ras_obj.project_folder / f"{ras_obj.project_name}.f{new_flow_num}"
|
1090
|
-
|
1091
|
-
if not template_flow_path.exists():
|
1092
|
-
raise FileNotFoundError(f"Template steady flow file '{template_flow_path}' does not exist.")
|
1093
|
-
|
1094
|
-
shutil.copy(template_flow_path, new_flow_path)
|
1095
|
-
print(f"Copied {template_flow_path} to {new_flow_path}")
|
1096
|
-
|
1097
|
-
# Read the contents of the project file
|
1098
|
-
with open(ras_obj.prj_file, 'r') as f:
|
1099
|
-
lines = f.readlines()
|
1100
|
-
|
1101
|
-
# Prepare the new Steady Flow File entry line
|
1102
|
-
new_flow_line = f"Flow File=f{new_flow_num}\n"
|
1103
|
-
|
1104
|
-
# Find the correct insertion point for the new Steady Flow File entry
|
1105
|
-
flow_file_pattern = re.compile(r'^Flow File=f(\d+)', re.IGNORECASE)
|
1106
|
-
insertion_index = None
|
1107
|
-
for i, line in enumerate(lines):
|
1108
|
-
match = flow_file_pattern.match(line.strip())
|
1109
|
-
if match:
|
1110
|
-
current_number = int(match.group(1))
|
1111
|
-
if current_number < int(new_flow_num):
|
1112
|
-
continue
|
1113
|
-
else:
|
1114
|
-
insertion_index = i
|
1115
|
-
break
|
1116
|
-
|
1117
|
-
if insertion_index is not None:
|
1118
|
-
lines.insert(insertion_index, new_flow_line)
|
1119
|
-
else:
|
1120
|
-
# Try to insert after the last Steady Flow File entry
|
1121
|
-
flow_indices = [i for i, line in enumerate(lines) if flow_file_pattern.match(line.strip())]
|
1122
|
-
if flow_indices:
|
1123
|
-
last_flow_index = flow_indices[-1]
|
1124
|
-
lines.insert(last_flow_index + 1, new_flow_line)
|
1125
|
-
else:
|
1126
|
-
# Append at the end if no Steady Flow File entries exist
|
1127
|
-
lines.append(new_flow_line)
|
1128
|
-
|
1129
|
-
# Write the updated lines back to the project file
|
1130
|
-
with open(ras_obj.prj_file, 'w') as f:
|
1131
|
-
f.writelines(lines)
|
1132
|
-
|
1133
|
-
print(f"Updated {ras_obj.prj_file} with new steady flow file f{new_flow_num}")
|
1134
|
-
new_steady = new_flow_num
|
1135
|
-
|
1136
|
-
# Store the project folder path
|
1137
|
-
project_folder = ras_obj.project_folder
|
1138
|
-
|
1139
|
-
# Re-initialize the ras global object
|
1140
|
-
ras_obj.initialize(project_folder, ras_obj.ras_exe_path)
|
1141
|
-
|
1142
|
-
return new_steady
|
1143
|
-
|
1144
910
|
@staticmethod
|
1145
911
|
def clone_geom(template_geom, ras_object=None):
|
1146
912
|
"""
|
@@ -1162,11 +928,13 @@ class RasPlan:
|
|
1162
928
|
|
1163
929
|
# Update geometry entries without reinitializing the entire project
|
1164
930
|
ras_obj.geom_df = ras_obj.get_prj_entries('Geom')
|
931
|
+
logging.debug(f"Updated geometry entries:\n{ras_obj.geom_df}")
|
1165
932
|
|
1166
933
|
template_geom_filename = f"{ras_obj.project_name}.g{template_geom}"
|
1167
934
|
template_geom_path = ras_obj.project_folder / template_geom_filename
|
1168
935
|
|
1169
936
|
if not template_geom_path.is_file():
|
937
|
+
logging.error(f"Template geometry file '{template_geom_path}' does not exist.")
|
1170
938
|
raise FileNotFoundError(f"Template geometry file '{template_geom_path}' does not exist.")
|
1171
939
|
|
1172
940
|
next_geom_number = RasPlan.get_next_number(ras_obj.geom_df['geom_number'])
|
@@ -1175,19 +943,23 @@ class RasPlan:
|
|
1175
943
|
new_geom_path = ras_obj.project_folder / new_geom_filename
|
1176
944
|
|
1177
945
|
shutil.copyfile(template_geom_path, new_geom_path)
|
1178
|
-
|
946
|
+
logging.info(f"Copied '{template_geom_path}' to '{new_geom_path}'.")
|
1179
947
|
|
1180
948
|
# Handle HDF file copy
|
1181
|
-
template_hdf_path =
|
1182
|
-
new_hdf_path =
|
949
|
+
template_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.g{template_geom}.hdf"
|
950
|
+
new_hdf_path = ras_obj.project_folder / f"{ras_obj.project_name}.g{next_geom_number}.hdf"
|
1183
951
|
if template_hdf_path.is_file():
|
1184
952
|
shutil.copyfile(template_hdf_path, new_hdf_path)
|
1185
|
-
|
953
|
+
logging.info(f"Copied '{template_hdf_path}' to '{new_hdf_path}'.")
|
1186
954
|
else:
|
1187
|
-
|
955
|
+
logging.warning(f"Template geometry HDF file '{template_hdf_path}' does not exist. Skipping '.hdf' copy.")
|
1188
956
|
|
1189
|
-
|
1190
|
-
|
957
|
+
try:
|
958
|
+
with open(ras_obj.prj_file, 'r') as file:
|
959
|
+
lines = file.readlines()
|
960
|
+
except FileNotFoundError:
|
961
|
+
logging.error(f"Project file not found: {ras_obj.prj_file}")
|
962
|
+
raise
|
1191
963
|
|
1192
964
|
# Prepare the new Geometry File entry line
|
1193
965
|
new_geom_line = f"Geom File=g{next_geom_number}\n"
|
@@ -1207,21 +979,28 @@ class RasPlan:
|
|
1207
979
|
|
1208
980
|
if insertion_index is not None:
|
1209
981
|
lines.insert(insertion_index, new_geom_line)
|
982
|
+
logging.info(f"Inserted new geometry line at index {insertion_index}")
|
1210
983
|
else:
|
1211
984
|
# Try to insert after the last Geometry File entry
|
1212
985
|
geom_indices = [i for i, line in enumerate(lines) if geom_file_pattern.match(line.strip())]
|
1213
986
|
if geom_indices:
|
1214
987
|
last_geom_index = geom_indices[-1]
|
1215
988
|
lines.insert(last_geom_index + 1, new_geom_line)
|
989
|
+
logging.info(f"Inserted new geometry line after index {last_geom_index}")
|
1216
990
|
else:
|
1217
991
|
# Append at the end if no Geometry File entries exist
|
1218
992
|
lines.append(new_geom_line)
|
993
|
+
logging.info(f"Appended new geometry line at the end of the project file")
|
1219
994
|
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
995
|
+
try:
|
996
|
+
# Write the updated lines back to the project file
|
997
|
+
with open(ras_obj.prj_file, 'w') as file:
|
998
|
+
file.writelines(lines)
|
999
|
+
logging.info(f"Updated {ras_obj.prj_file} with new geometry file g{next_geom_number}")
|
1000
|
+
except IOError as e:
|
1001
|
+
logging.error(f"Failed to write updated project file: {e}")
|
1002
|
+
raise
|
1223
1003
|
|
1224
|
-
print(f"Updated {ras_obj.prj_file} with new geometry file g{next_geom_number}")
|
1225
1004
|
new_geom = next_geom_number
|
1226
1005
|
|
1227
1006
|
# Update all dataframes in the ras object
|
@@ -1230,13 +1009,10 @@ class RasPlan:
|
|
1230
1009
|
ras_obj.flow_df = ras_obj.get_flow_entries()
|
1231
1010
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
1232
1011
|
|
1233
|
-
|
1012
|
+
logging.debug(f"Updated geometry entries:\n{ras_obj.geom_df}")
|
1234
1013
|
|
1235
1014
|
return new_geom
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1015
|
+
|
1240
1016
|
@staticmethod
|
1241
1017
|
def get_next_number(existing_numbers):
|
1242
1018
|
"""
|
@@ -1264,3 +1040,188 @@ class RasPlan:
|
|
1264
1040
|
else:
|
1265
1041
|
break
|
1266
1042
|
return f"{next_number:02d}"
|
1043
|
+
|
1044
|
+
|
1045
|
+
@staticmethod
|
1046
|
+
def get_plan_value(
|
1047
|
+
plan_number_or_path: Union[str, Path],
|
1048
|
+
key: str,
|
1049
|
+
ras_object=None
|
1050
|
+
) -> Any:
|
1051
|
+
"""
|
1052
|
+
Retrieve a specific value from a HEC-RAS plan file.
|
1053
|
+
|
1054
|
+
Parameters:
|
1055
|
+
plan_number_or_path (Union[str, Path]): The plan number (1 to 99) or full path to the plan file
|
1056
|
+
key (str): The key to retrieve from the plan file
|
1057
|
+
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
1058
|
+
|
1059
|
+
Returns:
|
1060
|
+
Any: The value associated with the specified key
|
1061
|
+
|
1062
|
+
Raises:
|
1063
|
+
ValueError: If the plan file is not found
|
1064
|
+
IOError: If there's an error reading the plan file
|
1065
|
+
|
1066
|
+
Available keys and their expected types:
|
1067
|
+
- 'description' (str): Plan description
|
1068
|
+
- 'computation_interval' (str): Time value for computational time step (e.g., '5SEC', '2MIN')
|
1069
|
+
- 'dss_file' (str): Name of the DSS file used
|
1070
|
+
- 'flow_file' (str): Name of the flow input file
|
1071
|
+
- 'friction_slope_method' (int): Method selection for friction slope (e.g., 1, 2)
|
1072
|
+
- 'geom_file' (str): Name of the geometry input file
|
1073
|
+
- 'mapping_interval' (str): Time interval for mapping output
|
1074
|
+
- 'plan_file' (str): Name of the plan file
|
1075
|
+
- 'plan_title' (str): Title of the simulation plan
|
1076
|
+
- 'program_version' (str): Version number of HEC-RAS
|
1077
|
+
- 'run_htab' (int): Flag to run HTab module (-1 or 1)
|
1078
|
+
- 'run_post_process' (int): Flag to run post-processing (-1 or 1)
|
1079
|
+
- 'run_sediment' (int): Flag to run sediment transport module (0 or 1)
|
1080
|
+
- 'run_unet' (int): Flag to run unsteady network module (-1 or 1)
|
1081
|
+
- 'run_wqnet' (int): Flag to run water quality module (0 or 1)
|
1082
|
+
- 'short_identifier' (str): Short name or ID for the plan
|
1083
|
+
- 'simulation_date' (str): Start and end dates/times for simulation
|
1084
|
+
- 'unet_d1_cores' (int): Number of cores used in 1D calculations
|
1085
|
+
- 'unet_use_existing_ib_tables' (int): Flag for using existing internal boundary tables (-1, 0, or 1)
|
1086
|
+
- 'unet_1d_methodology' (str): 1D calculation methodology
|
1087
|
+
- 'unet_d2_solver_type' (str): 2D solver type
|
1088
|
+
- 'unet_d2_name' (str): Name of the 2D area
|
1089
|
+
- 'run_rasmapper' (int): Flag to run RASMapper for floodplain mapping (-1 for off, 0 for on)
|
1090
|
+
|
1091
|
+
Example:
|
1092
|
+
>>> computation_interval = RasPlan.get_plan_value("01", "computation_interval")
|
1093
|
+
>>> print(f"Computation interval: {computation_interval}")
|
1094
|
+
"""
|
1095
|
+
ras_obj = ras_object or ras
|
1096
|
+
ras_obj.check_initialized()
|
1097
|
+
|
1098
|
+
valid_keys = {
|
1099
|
+
'description', 'computation_interval', 'dss_file', 'flow_file', 'friction_slope_method',
|
1100
|
+
'geom_file', 'mapping_interval', 'plan_file', 'plan_title', 'program_version',
|
1101
|
+
'run_htab', 'run_post_process', 'run_sediment', 'run_unet', 'run_wqnet',
|
1102
|
+
'short_identifier', 'simulation_date', 'unet_d1_cores', 'unet_use_existing_ib_tables',
|
1103
|
+
'unet_1d_methodology', 'unet_d2_solver_type', 'unet_d2_name', 'run_rasmapper'
|
1104
|
+
}
|
1105
|
+
|
1106
|
+
if key not in valid_keys:
|
1107
|
+
logging.warning(f"Unknown key: {key}. Valid keys are: {', '.join(valid_keys)}\n Add more keys and explanations in get_plan_value() as needed.")
|
1108
|
+
|
1109
|
+
plan_file_path = Path(plan_number_or_path)
|
1110
|
+
if not plan_file_path.is_file():
|
1111
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object=ras_obj)
|
1112
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
1113
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
1114
|
+
|
1115
|
+
try:
|
1116
|
+
with open(plan_file_path, 'r') as file:
|
1117
|
+
content = file.read()
|
1118
|
+
except IOError as e:
|
1119
|
+
logging.error(f"Error reading plan file {plan_file_path}: {e}")
|
1120
|
+
raise
|
1121
|
+
|
1122
|
+
if key == 'description':
|
1123
|
+
match = re.search(r'Begin DESCRIPTION(.*?)END DESCRIPTION', content, re.DOTALL)
|
1124
|
+
return match.group(1).strip() if match else None
|
1125
|
+
else:
|
1126
|
+
pattern = f"{key.replace('_', ' ').title()}=(.*)"
|
1127
|
+
match = re.search(pattern, content)
|
1128
|
+
if match:
|
1129
|
+
return match.group(1).strip()
|
1130
|
+
else:
|
1131
|
+
logging.error(f"Key '{key}' not found in the plan file.")
|
1132
|
+
return None
|
1133
|
+
|
1134
|
+
@staticmethod
|
1135
|
+
def update_plan_value(
|
1136
|
+
plan_number_or_path: Union[str, Path],
|
1137
|
+
key: str,
|
1138
|
+
value: Any,
|
1139
|
+
ras_object=None
|
1140
|
+
) -> None:
|
1141
|
+
"""
|
1142
|
+
Update a specific key-value pair in a HEC-RAS plan file.
|
1143
|
+
|
1144
|
+
Parameters:
|
1145
|
+
plan_number_or_path (Union[str, Path]): The plan number (1 to 99) or full path to the plan file
|
1146
|
+
key (str): The key to update in the plan file
|
1147
|
+
value (Any): The new value to set for the key
|
1148
|
+
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
1149
|
+
|
1150
|
+
Raises:
|
1151
|
+
ValueError: If the plan file is not found
|
1152
|
+
IOError: If there's an error reading or writing the plan file
|
1153
|
+
|
1154
|
+
Note: See the docstring of get_plan_value for a full list of available keys and their types.
|
1155
|
+
|
1156
|
+
Example:
|
1157
|
+
>>> RasPlan.update_plan_value("01", "computation_interval", "10SEC")
|
1158
|
+
>>> RasPlan.update_plan_value("/path/to/plan.p01", "run_htab", 1)
|
1159
|
+
>>> RasPlan.update_plan_value("01", "run_rasmapper", 0) # Turn on Floodplain Mapping
|
1160
|
+
"""
|
1161
|
+
ras_obj = ras_object or ras
|
1162
|
+
ras_obj.check_initialized()
|
1163
|
+
|
1164
|
+
valid_keys = {
|
1165
|
+
'description', 'computation_interval', 'dss_file', 'flow_file', 'friction_slope_method',
|
1166
|
+
'geom_file', 'mapping_interval', 'plan_file', 'plan_title', 'program_version',
|
1167
|
+
'run_htab', 'run_post_process', 'run_sediment', 'run_unet', 'run_wqnet',
|
1168
|
+
'short_identifier', 'simulation_date', 'unet_d1_cores', 'unet_use_existing_ib_tables',
|
1169
|
+
'unet_1d_methodology', 'unet_d2_solver_type', 'unet_d2_name', 'run_rasmapper'
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
if key not in valid_keys:
|
1173
|
+
logging.warning(f"Unknown key: {key}. Valid keys are: {', '.join(valid_keys)}")
|
1174
|
+
|
1175
|
+
plan_file_path = Path(plan_number_or_path)
|
1176
|
+
if not plan_file_path.is_file():
|
1177
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object)
|
1178
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
1179
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
1180
|
+
|
1181
|
+
try:
|
1182
|
+
with open(plan_file_path, 'r') as file:
|
1183
|
+
lines = file.readlines()
|
1184
|
+
except IOError as e:
|
1185
|
+
logging.error(f"Error reading plan file {plan_file_path}: {e}")
|
1186
|
+
raise
|
1187
|
+
|
1188
|
+
# Special handling for description
|
1189
|
+
if key == 'description':
|
1190
|
+
description_start = None
|
1191
|
+
description_end = None
|
1192
|
+
for i, line in enumerate(lines):
|
1193
|
+
if line.strip() == 'Begin DESCRIPTION':
|
1194
|
+
description_start = i
|
1195
|
+
elif line.strip() == 'END DESCRIPTION':
|
1196
|
+
description_end = i
|
1197
|
+
break
|
1198
|
+
if description_start is not None and description_end is not None:
|
1199
|
+
lines[description_start+1:description_end] = [f"{value}\n"]
|
1200
|
+
else:
|
1201
|
+
lines.append(f"Begin DESCRIPTION\n{value}\nEND DESCRIPTION\n")
|
1202
|
+
else:
|
1203
|
+
# For other keys
|
1204
|
+
pattern = f"{key.replace('_', ' ').title()}="
|
1205
|
+
updated = False
|
1206
|
+
for i, line in enumerate(lines):
|
1207
|
+
if line.startswith(pattern):
|
1208
|
+
lines[i] = f"{pattern}{value}\n"
|
1209
|
+
updated = True
|
1210
|
+
break
|
1211
|
+
if not updated:
|
1212
|
+
logging.error(f"Key '{key}' not found in the plan file.")
|
1213
|
+
return
|
1214
|
+
|
1215
|
+
try:
|
1216
|
+
with open(plan_file_path, 'w') as file:
|
1217
|
+
file.writelines(lines)
|
1218
|
+
logging.info(f"Updated {key} in plan file: {plan_file_path}")
|
1219
|
+
except IOError as e:
|
1220
|
+
logging.error(f"Error writing to plan file {plan_file_path}: {e}")
|
1221
|
+
raise
|
1222
|
+
|
1223
|
+
# Refresh RasPrj dataframes
|
1224
|
+
ras_obj.plan_df = ras_obj.get_plan_entries()
|
1225
|
+
ras_obj.geom_df = ras_obj.get_geom_entries()
|
1226
|
+
ras_obj.flow_df = ras_obj.get_flow_entries()
|
1227
|
+
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|