ras-commander 0.64.0__py3-none-any.whl → 0.65.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/RasPlan.py CHANGED
@@ -105,11 +105,10 @@ class RasPlan:
105
105
  ras_obj = ras_object or ras
106
106
  ras_obj.check_initialized()
107
107
 
108
- # Ensure plan_number and new_geom are strings
109
108
  plan_number = str(plan_number).zfill(2)
110
109
  new_geom = str(new_geom).zfill(2)
111
110
 
112
- # Before doing anything, make sure the plan, geom, flow, and unsteady dataframes are current
111
+ # Update all dataframes
113
112
  ras_obj.plan_df = ras_obj.get_plan_entries()
114
113
  ras_obj.geom_df = ras_obj.get_geom_entries()
115
114
  ras_obj.flow_df = ras_obj.get_flow_entries()
@@ -119,18 +118,15 @@ class RasPlan:
119
118
  logger.error(f"Geometry {new_geom} not found in project.")
120
119
  raise ValueError(f"Geometry {new_geom} not found in project.")
121
120
 
122
- # Update the geometry for the specified plan
123
- ras_obj.plan_df.loc[ras_obj.plan_df['plan_number'] == plan_number, 'geom_number'] = new_geom
121
+ # Update all geometry-related columns
122
+ mask = ras_obj.plan_df['plan_number'] == plan_number
123
+ ras_obj.plan_df.loc[mask, 'geometry_number'] = new_geom
124
+ ras_obj.plan_df.loc[mask, 'Geom File'] = new_geom
125
+ geom_path = ras_obj.project_folder / f"{ras_obj.project_name}.g{new_geom}"
126
+ ras_obj.plan_df.loc[mask, 'Geom Path'] = str(geom_path)
124
127
 
125
- logger.info(f"Geometry for plan {plan_number} set to {new_geom}")
126
- logger.debug("Updated plan DataFrame:")
127
- logger.debug(ras_obj.plan_df)
128
-
129
- # Update the project file
130
- prj_file_path = ras_obj.prj_file
131
- RasUtils.update_file(prj_file_path, RasPlan._update_geom_in_file, plan_number, new_geom)
132
-
133
- # Re-initialize the ras object to reflect changes
128
+ # Update project file and reinitialize
129
+ RasUtils.update_file(ras_obj.prj_file, RasPlan._update_geom_in_file, plan_number, new_geom)
134
130
  ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
135
131
 
136
132
  return ras_obj.plan_df
@@ -177,21 +173,26 @@ class RasPlan:
177
173
  ras_obj = ras_object or ras
178
174
  ras_obj.check_initialized()
179
175
 
180
- # Update the flow dataframe in the ras instance to ensure it is current
181
176
  ras_obj.flow_df = ras_obj.get_flow_entries()
182
177
 
183
178
  if new_steady_flow_number not in ras_obj.flow_df['flow_number'].values:
184
179
  raise ValueError(f"Steady flow number {new_steady_flow_number} not found in project file.")
185
180
 
186
- # Resolve the full path of the plan file
187
181
  plan_file_path = RasPlan.get_plan_path(plan_number, ras_obj)
188
182
  if not plan_file_path:
189
183
  raise FileNotFoundError(f"Plan file not found: {plan_number}")
190
184
 
191
185
  RasUtils.update_file(plan_file_path, RasPlan._update_steady_in_file, new_steady_flow_number)
192
186
 
193
- # Update the ras object's dataframes
187
+ # Update dataframes and additional columns
194
188
  ras_obj.plan_df = ras_obj.get_plan_entries()
189
+ mask = ras_obj.plan_df['plan_number'] == plan_number
190
+ flow_path = ras_obj.project_folder / f"{ras_obj.project_name}.f{new_steady_flow_number}"
191
+ ras_obj.plan_df.loc[mask, 'Flow File'] = new_steady_flow_number
192
+ ras_obj.plan_df.loc[mask, 'Flow Path'] = str(flow_path)
193
+ ras_obj.plan_df.loc[mask, 'unsteady_number'] = None
194
+
195
+ # Update remaining dataframes
195
196
  ras_obj.geom_df = ras_obj.get_geom_entries()
196
197
  ras_obj.flow_df = ras_obj.get_flow_entries()
197
198
  ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
@@ -227,37 +228,37 @@ class RasPlan:
227
228
  ras_obj = ras_object or ras
228
229
  ras_obj.check_initialized()
229
230
 
230
- # Update the unsteady dataframe in the ras instance to ensure it is current
231
231
  ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
232
232
 
233
233
  if new_unsteady_flow_number not in ras_obj.unsteady_df['unsteady_number'].values:
234
234
  raise ValueError(f"Unsteady number {new_unsteady_flow_number} not found in project file.")
235
235
 
236
- # Get the full path of the plan file
237
236
  plan_file_path = RasPlan.get_plan_path(plan_number, ras_obj)
238
237
  if not plan_file_path:
239
238
  raise FileNotFoundError(f"Plan file not found: {plan_number}")
240
239
 
241
- def update_unsteady_in_file(lines):
242
- updated_lines = []
243
- for line in lines:
244
- if line.startswith("Flow File="):
245
- updated_lines.append(f"Flow File=u{new_unsteady_flow_number}\n")
246
- else:
247
- updated_lines.append(line)
248
- return updated_lines
249
-
250
240
  try:
251
- RasUtils.update_file(plan_file_path, update_unsteady_in_file)
241
+ RasUtils.update_file(plan_file_path, lambda lines: [
242
+ f"Flow File=u{new_unsteady_flow_number}\n" if line.startswith("Flow File=") else line
243
+ for line in lines
244
+ ])
245
+
246
+ # Update dataframes and additional columns
247
+ ras_obj.plan_df = ras_obj.get_plan_entries()
248
+ mask = ras_obj.plan_df['plan_number'] == plan_number
249
+ flow_path = ras_obj.project_folder / f"{ras_obj.project_name}.u{new_unsteady_flow_number}"
250
+ ras_obj.plan_df.loc[mask, 'Flow File'] = new_unsteady_flow_number
251
+ ras_obj.plan_df.loc[mask, 'Flow Path'] = str(flow_path)
252
+ ras_obj.plan_df.loc[mask, 'unsteady_number'] = new_unsteady_flow_number
253
+
254
+ # Update remaining dataframes
255
+ ras_obj.geom_df = ras_obj.get_geom_entries()
256
+ ras_obj.flow_df = ras_obj.get_flow_entries()
257
+ ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
258
+
252
259
  except Exception as e:
253
260
  raise Exception(f"Failed to update unsteady flow file: {e}")
254
261
 
255
- # Update the ras object's dataframes
256
- ras_obj.plan_df = ras_obj.get_plan_entries()
257
- ras_obj.geom_df = ras_obj.get_geom_entries()
258
- ras_obj.flow_df = ras_obj.get_flow_entries()
259
- ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
260
-
261
262
  @staticmethod
262
263
  @log_call
263
264
  def set_num_cores(plan_number, num_cores, ras_object=None):
@@ -610,36 +611,31 @@ class RasPlan:
610
611
  ras_obj = ras_object or ras
611
612
  ras_obj.check_initialized()
612
613
 
613
- # Update plan entries without reinitializing the entire project
614
614
  ras_obj.plan_df = ras_obj.get_prj_entries('Plan')
615
-
616
615
  new_plan_num = RasPlan.get_next_number(ras_obj.plan_df['plan_number'])
616
+
617
617
  template_plan_path = ras_obj.project_folder / f"{ras_obj.project_name}.p{template_plan}"
618
618
  new_plan_path = ras_obj.project_folder / f"{ras_obj.project_name}.p{new_plan_num}"
619
619
 
620
- def update_shortid(lines):
621
- shortid_pattern = re.compile(r'^Short Identifier=(.*)$', re.IGNORECASE)
622
- for i, line in enumerate(lines):
623
- match = shortid_pattern.match(line.strip())
624
- if match:
625
- current_shortid = match.group(1)
626
- if new_plan_shortid is None:
627
- new_shortid = (current_shortid + "_copy")[:24]
628
- else:
629
- new_shortid = new_plan_shortid[:24]
630
- lines[i] = f"Short Identifier={new_shortid}\n"
631
- break
632
- return lines
633
-
634
- # Use RasUtils to clone the file and update the short identifier
635
- RasUtils.clone_file(template_plan_path, new_plan_path, update_shortid)
636
-
637
- # Use RasUtils to update the project file
620
+ # Clone file and update project file
621
+ RasUtils.clone_file(template_plan_path, new_plan_path,
622
+ lambda lines: [
623
+ f"Short Identifier={new_plan_shortid or (match.group(1) + '_copy')[:24]}\n"
624
+ if (match := re.match(r'^Short Identifier=(.*)$', line.strip(), re.IGNORECASE))
625
+ else line
626
+ for line in lines
627
+ ])
628
+
638
629
  RasUtils.update_project_file(ras_obj.prj_file, 'Plan', new_plan_num, ras_object=ras_obj)
639
-
640
- # Re-initialize the ras global object
641
630
  ras_obj.initialize(ras_obj.project_folder, ras_obj.ras_exe_path)
631
+
632
+ # Copy additional columns from template to new plan
633
+ template_row = ras_obj.plan_df[ras_obj.plan_df['plan_number'] == template_plan].iloc[0]
634
+ for col in ['Geom File', 'Geom Path', 'Flow File', 'Flow Path']:
635
+ if col in template_row.index:
636
+ ras_obj.plan_df.loc[ras_obj.plan_df['plan_number'] == new_plan_num, col] = template_row[col]
642
637
 
638
+ # Update all dataframes
643
639
  ras_obj.plan_df = ras_obj.get_plan_entries()
644
640
  ras_obj.geom_df = ras_obj.get_geom_entries()
645
641
  ras_obj.flow_df = ras_obj.get_flow_entries()
@@ -890,7 +886,8 @@ class RasPlan:
890
886
  'Run HTab', 'Run Post Process', 'Run Sediment', 'Run UNET', 'Run WQNET',
891
887
  'Short Identifier', 'Simulation Date', 'UNET D1 Cores', 'UNET D2 Cores', 'PS Cores',
892
888
  'UNET Use Existing IB Tables', 'UNET 1D Methodology', 'UNET D2 Solver Type',
893
- 'UNET D2 Name', 'Run RASMapper', 'Run HTab', 'Run UNET'
889
+ 'UNET D2 Name', 'Run RASMapper', 'Run HTab', 'Run UNet', 'Run PostProcess',
890
+ 'Run WQNet', 'UNET P2 Cores', 'Run Post Process', 'Run WQNET'
894
891
  }
895
892
 
896
893
  if key not in supported_plan_keys:
ras_commander/RasPrj.py CHANGED
@@ -162,6 +162,7 @@ class RasPrj:
162
162
  This method initializes DataFrames for plan, flow, unsteady, and geometry entries
163
163
  by calling the _get_prj_entries method for each entry type.
164
164
  Also extracts unsteady_number and geometry_number from plan files and adds them to plan_df.
165
+ Now includes Geom Path, Flow Path, and other required columns.
165
166
  """
166
167
  # Load unsteady first to ensure consistent handling of unsteady numbers
167
168
  self.unsteady_df = self._get_prj_entries('Unsteady')
@@ -169,18 +170,25 @@ class RasPrj:
169
170
  self.flow_df = self._get_prj_entries('Flow')
170
171
  self.geom_df = self.get_geom_entries()
171
172
 
172
- # Initialize Geom_File column
173
- self.plan_df['Geom_File'] = None
174
-
175
- # Set geometry HDF paths
173
+ # Set geometry and flow paths
176
174
  for idx, row in self.plan_df.iterrows():
177
175
  try:
178
- plan_file_path = row['full_path']
179
- geom_number, geom_hdf_path = self._get_geom_file_from_plan(plan_file_path)
180
- if geom_number:
181
- self.plan_df.at[idx, 'Geom_File'] = geom_hdf_path
176
+ # Add Geom Path
177
+ if row['Geom File'] is not None:
178
+ geom_hdf_path = self.project_folder / f"{self.project_name}.g{row['Geom File']}"
179
+ self.plan_df.at[idx, 'Geom Path'] = str(geom_hdf_path)
180
+
181
+ # Add Flow Path
182
+ if row['Flow File'] is not None:
183
+ # Determine if this is a steady or unsteady flow file
184
+ if row['unsteady_number'] is not None:
185
+ flow_path = self.project_folder / f"{self.project_name}.u{row['Flow File']}"
186
+ else:
187
+ flow_path = self.project_folder / f"{self.project_name}.f{row['Flow File']}"
188
+ self.plan_df.at[idx, 'Flow Path'] = str(flow_path)
189
+
182
190
  if not self.suppress_logging:
183
- logger.info(f"Plan {row['plan_number']} uses geometry file {geom_number}")
191
+ logger.info(f"Plan {row['plan_number']} paths set up")
184
192
  except Exception as e:
185
193
  logger.error(f"Error processing plan file {row['plan_number']}: {e}")
186
194
 
@@ -456,9 +464,7 @@ class RasPrj:
456
464
  else:
457
465
  entry.update({
458
466
  'unsteady_number': None,
459
- 'geometry_number': None,
460
- 'Short Identifier': None,
461
- 'Simulation Date': None
467
+ 'geometry_number': None
462
468
  })
463
469
 
464
470
  if entry_type == 'Plan':
@@ -468,23 +474,23 @@ class RasPrj:
468
474
  flow_file = plan_info.get('Flow File')
469
475
  if flow_file and flow_file.startswith('u'):
470
476
  entry['unsteady_number'] = flow_file[1:]
477
+ entry['Flow File'] = flow_file[1:]
471
478
  else:
472
- entry['unsteady_number'] = flow_file
479
+ entry['unsteady_number'] = None
480
+ entry['Flow File'] = flow_file[1:] if flow_file and flow_file.startswith('f') else None
473
481
 
474
482
  # Handle Geom File number
475
483
  geom_file = plan_info.get('Geom File')
476
484
  if geom_file and geom_file.startswith('g'):
477
485
  entry['geometry_number'] = geom_file[1:]
486
+ entry['Geom File'] = geom_file[1:]
478
487
  else:
479
- entry['geometry_number'] = geom_file
480
-
481
- entry['Short Identifier'] = plan_info.get('Short Identifier')
482
- entry['Simulation Date'] = plan_info.get('Simulation Date')
488
+ entry['geometry_number'] = None
489
+ entry['Geom File'] = None
483
490
 
484
- # Update remaining fields
491
+ # Add all plan key information with exact same names
485
492
  for key, value in plan_info.items():
486
- if key not in ['unsteady_number', 'geometry_number', 'Short Identifier',
487
- 'Simulation Date', 'Flow File', 'Geom File']:
493
+ if key not in ['Flow File', 'Geom File']:
488
494
  entry[key] = value
489
495
 
490
496
  # Add HDF results path
@@ -496,13 +502,47 @@ class RasPrj:
496
502
  df = pd.DataFrame(entries)
497
503
 
498
504
  if not df.empty and entry_type == 'Plan':
499
- first_cols = [f'{entry_type.lower()}_number', 'unsteady_number', 'geometry_number',
500
- 'Short Identifier', 'Simulation Date']
501
- other_cols = [col for col in df.columns if col not in first_cols]
502
- df = df[first_cols + other_cols]
505
+ # Set required column order
506
+ first_cols = ['plan_number', 'unsteady_number', 'geometry_number']
507
+
508
+ # Standard plan key columns in the exact order specified
509
+ plan_key_cols = [
510
+ 'Plan Title', 'Program Version', 'Short Identifier', 'Simulation Date',
511
+ 'Std Step Tol', 'Computation Interval', 'Output Interval', 'Instantaneous Interval',
512
+ 'Mapping Interval', 'Run HTab', 'Run UNet', 'Run Sediment', 'Run PostProcess',
513
+ 'Run WQNet', 'Run RASMapper', 'UNET Use Existing IB Tables', 'HDF_Results_Path',
514
+ 'UNET 1D Methodology', 'Write IC File', 'Write IC File at Fixed DateTime',
515
+ 'IC Time', 'Write IC File Reoccurance', 'Write IC File at Sim End'
516
+ ]
517
+
518
+ # Additional convenience columns
519
+ file_path_cols = ['Geom File', 'Geom Path', 'Flow File', 'Flow Path']
520
+
521
+ # Build the final column list
522
+ all_cols = first_cols.copy()
523
+ for col in plan_key_cols:
524
+ if col in df.columns:
525
+ all_cols.append(col)
526
+
527
+ # Add any remaining columns not explicitly specified
528
+ other_cols = [col for col in df.columns if col not in all_cols + file_path_cols + ['full_path']]
529
+ all_cols.extend(other_cols)
530
+ all_cols.extend(file_path_cols)
531
+
532
+ # Rename plan_number column
533
+ df = df.rename(columns={f'{entry_type.lower()}_number': 'plan_number'})
534
+
535
+ # Fill in missing columns with None
536
+ for col in all_cols:
537
+ if col not in df.columns:
538
+ df[col] = None
539
+
540
+ # Return DataFrame with specified column order
541
+ return df[all_cols]
503
542
 
504
543
  return df
505
544
  except Exception as e:
545
+ logger.error(f"Error in _get_prj_entries for {entry_type}: {e}")
506
546
  raise
507
547
 
508
548
  def _parse_unsteady_file(self, unsteady_file_path):
ras_commander/__init__.py CHANGED
@@ -10,7 +10,7 @@ try:
10
10
  __version__ = version("ras-commander")
11
11
  except PackageNotFoundError:
12
12
  # package is not installed
13
- __version__ = "0.64.0"
13
+ __version__ = "0.65.0"
14
14
 
15
15
  # Set up logging
16
16
  setup_logging()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ras-commander
3
- Version: 0.64.0
3
+ Version: 0.65.0
4
4
  Summary: A Python library for automating HEC-RAS 6.x operations
5
5
  Home-page: https://github.com/gpt-cmdr/ras-commander
6
6
  Author: William M. Katzenmeyer, P.E., C.F.M.
@@ -21,14 +21,14 @@ ras_commander/RasExamples.py,sha256=6IZ96LcAsk5LYFehdD0zDW5wyZWxQa6OQu2N9upxWXA,
21
21
  ras_commander/RasGeo.py,sha256=M0sVNKlWmmbve8iMXLWq25WgbxqLWBo7_1oDg_rALzU,5607
22
22
  ras_commander/RasGpt.py,sha256=N_7p2nucWrBBXdB2k2ZKvOeOdXNmFD9dIY3W7_5i5nw,1206
23
23
  ras_commander/RasMapper.py,sha256=LO_blvQnd4pwkEU8A30-RoE-CYIoU3s_fNLHBoM8ljw,613
24
- ras_commander/RasPlan.py,sha256=TYxFh3d6ZDqeytFcwEmtlo8TQ79wLmalo7HohBUYf40,62118
25
- ras_commander/RasPrj.py,sha256=KVDz4SKq-axe4AZv27H1ACrPov-ERHM9esl51p6Crfc,42560
24
+ ras_commander/RasPlan.py,sha256=9RbOzQyz0_BCRa-gds_ca3_DImNMeX6n5NslkjpfT1w,62477
25
+ ras_commander/RasPrj.py,sha256=b0d0A5WKkUXRtQobUcDPBTTGymtM_EWZwFbpOrNjqfY,44731
26
26
  ras_commander/RasToGo.py,sha256=TKujfaV1xQhFaOddF4g2ogGy6ky-CLlfelSMPD2J3Nk,1223
27
27
  ras_commander/RasUnsteady.py,sha256=KfCXAag-_bPwwS3JbPZH-s4hbaoHACO0mlRnGrzbFgA,32092
28
28
  ras_commander/RasUtils.py,sha256=P2-aBL61kdRINsjnBpstZVD6VVc7hI_D3RUXqr6ldmc,34863
29
- ras_commander/__init__.py,sha256=KGJQL7tKKaKdvY8Gtfm1QUsCX3vVxXZXF5EVStGVyk8,2001
30
- ras_commander-0.64.0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
31
- ras_commander-0.64.0.dist-info/METADATA,sha256=u9uEBRrgkNsXiKrPRZDXAmhtUdCn1nVEQzIHRDDUe8c,26233
32
- ras_commander-0.64.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
33
- ras_commander-0.64.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
34
- ras_commander-0.64.0.dist-info/RECORD,,
29
+ ras_commander/__init__.py,sha256=Aojks8MKjFZceg9TEaTqrr6RsgLeJAh6iZrRZVoOdg0,2001
30
+ ras_commander-0.65.0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
31
+ ras_commander-0.65.0.dist-info/METADATA,sha256=7Ddh7KnY46exroA1XG0YC77l3YKdQJqiNZuyZIUtOPE,26233
32
+ ras_commander-0.65.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
33
+ ras_commander-0.65.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
34
+ ras_commander-0.65.0.dist-info/RECORD,,