ras-commander 0.64.0__py3-none-any.whl → 0.66.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 +1487 -1471
 - ras_commander/RasPrj.py +263 -126
 - ras_commander/RasUtils.py +22 -3
 - ras_commander/__init__.py +1 -1
 - {ras_commander-0.64.0.dist-info → ras_commander-0.66.0.dist-info}/METADATA +1 -1
 - {ras_commander-0.64.0.dist-info → ras_commander-0.66.0.dist-info}/RECORD +9 -9
 - {ras_commander-0.64.0.dist-info → ras_commander-0.66.0.dist-info}/LICENSE +0 -0
 - {ras_commander-0.64.0.dist-info → ras_commander-0.66.0.dist-info}/WHEEL +0 -0
 - {ras_commander-0.64.0.dist-info → ras_commander-0.66.0.dist-info}/top_level.txt +0 -0
 
    
        ras_commander/RasPrj.py
    CHANGED
    
    | 
         @@ -160,93 +160,96 @@ class RasPrj: 
     | 
|
| 
       160 
160 
     | 
    
         
             
                    Load project data from the HEC-RAS project file.
         
     | 
| 
       161 
161 
     | 
    
         | 
| 
       162 
162 
     | 
    
         
             
                    This method initializes DataFrames for plan, flow, unsteady, and geometry entries
         
     | 
| 
       163 
     | 
    
         
            -
                     
     | 
| 
       164 
     | 
    
         
            -
                    Also extracts unsteady_number and geometry_number from plan files and adds them to plan_df.
         
     | 
| 
      
 163 
     | 
    
         
            +
                    and ensures all required columns are present with appropriate paths.
         
     | 
| 
       165 
164 
     | 
    
         
             
                    """
         
     | 
| 
       166 
     | 
    
         
            -
                     
     | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
      
 165 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 166 
     | 
    
         
            +
                        # Load data frames
         
     | 
| 
      
 167 
     | 
    
         
            +
                        self.unsteady_df = self._get_prj_entries('Unsteady')
         
     | 
| 
      
 168 
     | 
    
         
            +
                        self.plan_df = self._get_prj_entries('Plan')
         
     | 
| 
      
 169 
     | 
    
         
            +
                        self.flow_df = self._get_prj_entries('Flow')
         
     | 
| 
      
 170 
     | 
    
         
            +
                        self.geom_df = self.get_geom_entries()
         
     | 
| 
      
 171 
     | 
    
         
            +
                        
         
     | 
| 
      
 172 
     | 
    
         
            +
                        # Ensure required columns exist
         
     | 
| 
      
 173 
     | 
    
         
            +
                        self._ensure_required_columns()
         
     | 
| 
      
 174 
     | 
    
         
            +
                        
         
     | 
| 
      
 175 
     | 
    
         
            +
                        # Set paths for geometry and flow files
         
     | 
| 
      
 176 
     | 
    
         
            +
                        self._set_file_paths()
         
     | 
| 
      
 177 
     | 
    
         
            +
                        
         
     | 
| 
      
 178 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 179 
     | 
    
         
            +
                        logger.error(f"Error loading project data: {e}")
         
     | 
| 
      
 180 
     | 
    
         
            +
                        raise
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                def _ensure_required_columns(self):
         
     | 
| 
      
 183 
     | 
    
         
            +
                    """Ensure all required columns exist in plan_df."""
         
     | 
| 
      
 184 
     | 
    
         
            +
                    required_columns = [
         
     | 
| 
      
 185 
     | 
    
         
            +
                        'plan_number', 'unsteady_number', 'geometry_number',
         
     | 
| 
      
 186 
     | 
    
         
            +
                        'Geom File', 'Geom Path', 'Flow File', 'Flow Path', 'full_path'
         
     | 
| 
      
 187 
     | 
    
         
            +
                    ]
         
     | 
| 
       171 
188 
     | 
    
         | 
| 
       172 
     | 
    
         
            -
                     
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
      
 189 
     | 
    
         
            +
                    for col in required_columns:
         
     | 
| 
      
 190 
     | 
    
         
            +
                        if col not in self.plan_df.columns:
         
     | 
| 
      
 191 
     | 
    
         
            +
                            self.plan_df[col] = None
         
     | 
| 
       174 
192 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
                     
     | 
| 
      
 193 
     | 
    
         
            +
                    if not self.plan_df['full_path'].any():
         
     | 
| 
      
 194 
     | 
    
         
            +
                        self.plan_df['full_path'] = self.plan_df['plan_number'].apply(
         
     | 
| 
      
 195 
     | 
    
         
            +
                            lambda x: str(self.project_folder / f"{self.project_name}.p{x}")
         
     | 
| 
      
 196 
     | 
    
         
            +
                        )
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                def _set_file_paths(self):
         
     | 
| 
      
 199 
     | 
    
         
            +
                    """Set geometry and flow paths in plan_df."""
         
     | 
| 
       176 
200 
     | 
    
         
             
                    for idx, row in self.plan_df.iterrows():
         
     | 
| 
       177 
201 
     | 
    
         
             
                        try:
         
     | 
| 
       178 
     | 
    
         
            -
                             
     | 
| 
       179 
     | 
    
         
            -
                             
     | 
| 
       180 
     | 
    
         
            -
                             
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
                                 
     | 
| 
       183 
     | 
    
         
            -
                                    logger.info(f"Plan {row['plan_number']} uses geometry file {geom_number}")
         
     | 
| 
      
 202 
     | 
    
         
            +
                            self._set_geom_path(idx, row)
         
     | 
| 
      
 203 
     | 
    
         
            +
                            self._set_flow_path(idx, row)
         
     | 
| 
      
 204 
     | 
    
         
            +
                            
         
     | 
| 
      
 205 
     | 
    
         
            +
                            if not self.suppress_logging:
         
     | 
| 
      
 206 
     | 
    
         
            +
                                logger.info(f"Plan {row['plan_number']} paths set up")
         
     | 
| 
       184 
207 
     | 
    
         
             
                        except Exception as e:
         
     | 
| 
       185 
208 
     | 
    
         
             
                            logger.error(f"Error processing plan file {row['plan_number']}: {e}")
         
     | 
| 
       186 
209 
     | 
    
         | 
| 
       187 
     | 
    
         
            -
                def  
     | 
| 
       188 
     | 
    
         
            -
                    """
         
     | 
| 
       189 
     | 
    
         
            -
                     
     | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
                    Parameters:
         
     | 
| 
       193 
     | 
    
         
            -
                    -----------
         
     | 
| 
       194 
     | 
    
         
            -
                    plan_file_path : str or Path
         
     | 
| 
       195 
     | 
    
         
            -
                        Path to the plan file.
         
     | 
| 
       196 
     | 
    
         
            -
                    
         
     | 
| 
       197 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       198 
     | 
    
         
            -
                    --------
         
     | 
| 
       199 
     | 
    
         
            -
                    tuple: (str, str)
         
     | 
| 
       200 
     | 
    
         
            -
                        A tuple containing (geometry_number, full_hdf_path) or (None, None) if not found.
         
     | 
| 
       201 
     | 
    
         
            -
                        geometry_number is the number after 'g' in the Geom File value
         
     | 
| 
       202 
     | 
    
         
            -
                        full_hdf_path is the path to the geometry HDF file
         
     | 
| 
       203 
     | 
    
         
            -
                    """
         
     | 
| 
       204 
     | 
    
         
            -
                    content, encoding = read_file_with_fallback_encoding(plan_file_path)
         
     | 
| 
       205 
     | 
    
         
            -
                    
         
     | 
| 
       206 
     | 
    
         
            -
                    if content is None:
         
     | 
| 
       207 
     | 
    
         
            -
                        return None, None
         
     | 
| 
       208 
     | 
    
         
            -
                    
         
     | 
| 
       209 
     | 
    
         
            -
                    try:
         
     | 
| 
       210 
     | 
    
         
            -
                        match = re.search(r'Geom File=g(\d+)', content)
         
     | 
| 
       211 
     | 
    
         
            -
                        if match:
         
     | 
| 
       212 
     | 
    
         
            -
                            geom_number = match.group(1)  # This gets just the number after 'g'
         
     | 
| 
       213 
     | 
    
         
            -
                            geom_file = f"g{geom_number}"
         
     | 
| 
       214 
     | 
    
         
            -
                            geom_hdf_path = self.project_folder / f"{self.project_name}.{geom_file}.hdf"
         
     | 
| 
       215 
     | 
    
         
            -
                            if geom_hdf_path.exists():
         
     | 
| 
       216 
     | 
    
         
            -
                                return geom_number, str(geom_hdf_path)
         
     | 
| 
       217 
     | 
    
         
            -
                    except Exception as e:
         
     | 
| 
       218 
     | 
    
         
            -
                        logger.error(f"Error extracting geometry number from {plan_file_path}: {e}")
         
     | 
| 
       219 
     | 
    
         
            -
                    
         
     | 
| 
       220 
     | 
    
         
            -
                    return None, None
         
     | 
| 
      
 210 
     | 
    
         
            +
                def _set_geom_path(self, idx: int, row: pd.Series):
         
     | 
| 
      
 211 
     | 
    
         
            +
                    """Set geometry path for a plan entry."""
         
     | 
| 
      
 212 
     | 
    
         
            +
                    if pd.notna(row['Geom File']):
         
     | 
| 
      
 213 
     | 
    
         
            +
                        geom_path = self.project_folder / f"{self.project_name}.g{row['Geom File']}"
         
     | 
| 
      
 214 
     | 
    
         
            +
                        self.plan_df.at[idx, 'Geom Path'] = str(geom_path)
         
     | 
| 
       221 
215 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                def  
     | 
| 
      
 216 
     | 
    
         
            +
                def _set_flow_path(self, idx: int, row: pd.Series):
         
     | 
| 
      
 217 
     | 
    
         
            +
                    """Set flow path for a plan entry."""
         
     | 
| 
      
 218 
     | 
    
         
            +
                    if pd.notna(row['Flow File']):
         
     | 
| 
      
 219 
     | 
    
         
            +
                        prefix = 'u' if pd.notna(row['unsteady_number']) else 'f'
         
     | 
| 
      
 220 
     | 
    
         
            +
                        flow_path = self.project_folder / f"{self.project_name}.{prefix}{row['Flow File']}"
         
     | 
| 
      
 221 
     | 
    
         
            +
                        self.plan_df.at[idx, 'Flow Path'] = str(flow_path)
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                def _get_geom_file_for_plan(self, plan_number):
         
     | 
| 
       223 
224 
     | 
    
         
             
                    """
         
     | 
| 
       224 
     | 
    
         
            -
                     
     | 
| 
      
 225 
     | 
    
         
            +
                    Get the geometry file path for a given plan number.
         
     | 
| 
       225 
226 
     | 
    
         | 
| 
       226 
     | 
    
         
            -
                     
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
                    plan_file_path : str or Path
         
     | 
| 
       229 
     | 
    
         
            -
                        Path to the plan file.
         
     | 
| 
      
 227 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 228 
     | 
    
         
            +
                        plan_number (str): The plan number to find the geometry file for.
         
     | 
| 
       230 
229 
     | 
    
         | 
| 
       231 
230 
     | 
    
         
             
                    Returns:
         
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
                    str or None
         
     | 
| 
       234 
     | 
    
         
            -
                        The Flow File value or None if not found.
         
     | 
| 
      
 231 
     | 
    
         
            +
                        str: The full path to the geometry HDF file, or None if not found.
         
     | 
| 
       235 
232 
     | 
    
         
             
                    """
         
     | 
| 
      
 233 
     | 
    
         
            +
                    plan_file_path = self.project_folder / f"{self.project_name}.p{plan_number}"
         
     | 
| 
       236 
234 
     | 
    
         
             
                    content, encoding = read_file_with_fallback_encoding(plan_file_path)
         
     | 
| 
       237 
235 
     | 
    
         | 
| 
       238 
236 
     | 
    
         
             
                    if content is None:
         
     | 
| 
       239 
237 
     | 
    
         
             
                        return None
         
     | 
| 
       240 
238 
     | 
    
         | 
| 
       241 
239 
     | 
    
         
             
                    try:
         
     | 
| 
       242 
     | 
    
         
            -
                         
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
      
 240 
     | 
    
         
            +
                        for line in content.splitlines():
         
     | 
| 
      
 241 
     | 
    
         
            +
                            if line.startswith("Geom File="):
         
     | 
| 
      
 242 
     | 
    
         
            +
                                geom_file = line.strip().split('=')[1]
         
     | 
| 
      
 243 
     | 
    
         
            +
                                geom_hdf_path = self.project_folder / f"{self.project_name}.{geom_file}.hdf"
         
     | 
| 
      
 244 
     | 
    
         
            +
                                if geom_hdf_path.exists():
         
     | 
| 
      
 245 
     | 
    
         
            +
                                    return str(geom_hdf_path)
         
     | 
| 
      
 246 
     | 
    
         
            +
                                else:
         
     | 
| 
      
 247 
     | 
    
         
            +
                                    return None
         
     | 
| 
       245 
248 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
       246 
     | 
    
         
            -
                        logger.error(f"Error  
     | 
| 
       247 
     | 
    
         
            -
                    
         
     | 
| 
      
 249 
     | 
    
         
            +
                        logger.error(f"Error reading plan file for geometry: {e}")
         
     | 
| 
       248 
250 
     | 
    
         
             
                    return None
         
     | 
| 
       249 
251 
     | 
    
         | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
       250 
253 
     | 
    
         
             
                @staticmethod
         
     | 
| 
       251 
254 
     | 
    
         
             
                @log_call
         
     | 
| 
       252 
255 
     | 
    
         
             
                def get_plan_value(
         
     | 
| 
         @@ -431,80 +434,106 @@ class RasPrj: 
     | 
|
| 
       431 
434 
     | 
    
         | 
| 
       432 
435 
     | 
    
         
             
                    Returns:
         
     | 
| 
       433 
436 
     | 
    
         
             
                        pd.DataFrame: A DataFrame containing the extracted entries.
         
     | 
| 
      
 437 
     | 
    
         
            +
                    
         
     | 
| 
      
 438 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 439 
     | 
    
         
            +
                        Exception: If there's an error reading or processing the project file.
         
     | 
| 
       434 
440 
     | 
    
         
             
                    """
         
     | 
| 
       435 
441 
     | 
    
         
             
                    entries = []
         
     | 
| 
       436 
442 
     | 
    
         
             
                    pattern = re.compile(rf"{entry_type} File=(\w+)")
         
     | 
| 
       437 
443 
     | 
    
         | 
| 
       438 
444 
     | 
    
         
             
                    try:
         
     | 
| 
       439 
     | 
    
         
            -
                        with open(self.prj_file, 'r') as file:
         
     | 
| 
      
 445 
     | 
    
         
            +
                        with open(self.prj_file, 'r', encoding='utf-8') as file:
         
     | 
| 
       440 
446 
     | 
    
         
             
                            for line in file:
         
     | 
| 
       441 
447 
     | 
    
         
             
                                match = pattern.match(line.strip())
         
     | 
| 
       442 
448 
     | 
    
         
             
                                if match:
         
     | 
| 
       443 
449 
     | 
    
         
             
                                    file_name = match.group(1)
         
     | 
| 
       444 
450 
     | 
    
         
             
                                    full_path = str(self.project_folder / f"{self.project_name}.{file_name}")
         
     | 
| 
       445 
     | 
    
         
            -
                                    entry_number = file_name[1:] 
     | 
| 
      
 451 
     | 
    
         
            +
                                    entry_number = file_name[1:]
         
     | 
| 
       446 
452 
     | 
    
         | 
| 
       447 
453 
     | 
    
         
             
                                    entry = {
         
     | 
| 
       448 
454 
     | 
    
         
             
                                        f'{entry_type.lower()}_number': entry_number,
         
     | 
| 
       449 
455 
     | 
    
         
             
                                        'full_path': full_path
         
     | 
| 
       450 
456 
     | 
    
         
             
                                    }
         
     | 
| 
       451 
457 
     | 
    
         | 
| 
      
 458 
     | 
    
         
            +
                                    # Handle Unsteady entries
         
     | 
| 
       452 
459 
     | 
    
         
             
                                    if entry_type == 'Unsteady':
         
     | 
| 
       453 
     | 
    
         
            -
                                        entry 
     | 
| 
       454 
     | 
    
         
            -
                                        unsteady_info = self._parse_unsteady_file(Path(full_path))
         
     | 
| 
       455 
     | 
    
         
            -
                                        entry.update(unsteady_info)
         
     | 
| 
      
 460 
     | 
    
         
            +
                                        entry.update(self._process_unsteady_entry(entry_number, full_path))
         
     | 
| 
       456 
461 
     | 
    
         
             
                                    else:
         
     | 
| 
       457 
     | 
    
         
            -
                                        entry.update( 
     | 
| 
       458 
     | 
    
         
            -
                                            'unsteady_number': None,
         
     | 
| 
       459 
     | 
    
         
            -
                                            'geometry_number': None,
         
     | 
| 
       460 
     | 
    
         
            -
                                            'Short Identifier': None,
         
     | 
| 
       461 
     | 
    
         
            -
                                            'Simulation Date': None
         
     | 
| 
       462 
     | 
    
         
            -
                                        })
         
     | 
| 
      
 462 
     | 
    
         
            +
                                        entry.update(self._process_default_entry())
         
     | 
| 
       463 
463 
     | 
    
         | 
| 
      
 464 
     | 
    
         
            +
                                    # Handle Plan entries
         
     | 
| 
       464 
465 
     | 
    
         
             
                                    if entry_type == 'Plan':
         
     | 
| 
       465 
     | 
    
         
            -
                                         
     | 
| 
       466 
     | 
    
         
            -
                                        if plan_info:
         
     | 
| 
       467 
     | 
    
         
            -
                                            # Handle Flow File (unsteady) number
         
     | 
| 
       468 
     | 
    
         
            -
                                            flow_file = plan_info.get('Flow File')
         
     | 
| 
       469 
     | 
    
         
            -
                                            if flow_file and flow_file.startswith('u'):
         
     | 
| 
       470 
     | 
    
         
            -
                                                entry['unsteady_number'] = flow_file[1:]
         
     | 
| 
       471 
     | 
    
         
            -
                                            else:
         
     | 
| 
       472 
     | 
    
         
            -
                                                entry['unsteady_number'] = flow_file
         
     | 
| 
       473 
     | 
    
         
            -
                                                
         
     | 
| 
       474 
     | 
    
         
            -
                                            # Handle Geom File number
         
     | 
| 
       475 
     | 
    
         
            -
                                            geom_file = plan_info.get('Geom File')
         
     | 
| 
       476 
     | 
    
         
            -
                                            if geom_file and geom_file.startswith('g'):
         
     | 
| 
       477 
     | 
    
         
            -
                                                entry['geometry_number'] = geom_file[1:]
         
     | 
| 
       478 
     | 
    
         
            -
                                            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')
         
     | 
| 
       483 
     | 
    
         
            -
                                            
         
     | 
| 
       484 
     | 
    
         
            -
                                            # Update remaining fields
         
     | 
| 
       485 
     | 
    
         
            -
                                            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']:
         
     | 
| 
       488 
     | 
    
         
            -
                                                    entry[key] = value
         
     | 
| 
      
 466 
     | 
    
         
            +
                                        entry.update(self._process_plan_entry(entry_number, full_path))
         
     | 
| 
       489 
467 
     | 
    
         | 
| 
       490 
     | 
    
         
            -
                                        # Add HDF results path
         
     | 
| 
       491 
     | 
    
         
            -
                                        hdf_results_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
         
     | 
| 
       492 
     | 
    
         
            -
                                        entry['HDF_Results_Path'] = str(hdf_results_path) if hdf_results_path.exists() else None
         
     | 
| 
       493 
     | 
    
         
            -
             
     | 
| 
       494 
468 
     | 
    
         
             
                                    entries.append(entry)
         
     | 
| 
       495 
     | 
    
         
            -
                    
         
     | 
| 
       496 
     | 
    
         
            -
                        df = pd.DataFrame(entries)
         
     | 
| 
       497 
     | 
    
         
            -
                        
         
     | 
| 
       498 
     | 
    
         
            -
                        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]
         
     | 
| 
       503 
469 
     | 
    
         | 
| 
       504 
     | 
    
         
            -
                         
     | 
| 
      
 470 
     | 
    
         
            +
                        df = pd.DataFrame(entries)
         
     | 
| 
      
 471 
     | 
    
         
            +
                        return self._format_dataframe(df, entry_type)
         
     | 
| 
      
 472 
     | 
    
         
            +
                    
         
     | 
| 
       505 
473 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
      
 474 
     | 
    
         
            +
                        logger.error(f"Error in _get_prj_entries for {entry_type}: {e}")
         
     | 
| 
       506 
475 
     | 
    
         
             
                        raise
         
     | 
| 
       507 
476 
     | 
    
         | 
| 
      
 477 
     | 
    
         
            +
                def _process_unsteady_entry(self, entry_number: str, full_path: str) -> dict:
         
     | 
| 
      
 478 
     | 
    
         
            +
                    """Process unsteady entry data."""
         
     | 
| 
      
 479 
     | 
    
         
            +
                    entry = {'unsteady_number': entry_number}
         
     | 
| 
      
 480 
     | 
    
         
            +
                    unsteady_info = self._parse_unsteady_file(Path(full_path))
         
     | 
| 
      
 481 
     | 
    
         
            +
                    entry.update(unsteady_info)
         
     | 
| 
      
 482 
     | 
    
         
            +
                    return entry
         
     | 
| 
      
 483 
     | 
    
         
            +
             
     | 
| 
      
 484 
     | 
    
         
            +
                def _process_default_entry(self) -> dict:
         
     | 
| 
      
 485 
     | 
    
         
            +
                    """Process default entry data."""
         
     | 
| 
      
 486 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 487 
     | 
    
         
            +
                        'unsteady_number': None,
         
     | 
| 
      
 488 
     | 
    
         
            +
                        'geometry_number': None
         
     | 
| 
      
 489 
     | 
    
         
            +
                    }
         
     | 
| 
      
 490 
     | 
    
         
            +
             
     | 
| 
      
 491 
     | 
    
         
            +
                def _process_plan_entry(self, entry_number: str, full_path: str) -> dict:
         
     | 
| 
      
 492 
     | 
    
         
            +
                    """Process plan entry data."""
         
     | 
| 
      
 493 
     | 
    
         
            +
                    entry = {}
         
     | 
| 
      
 494 
     | 
    
         
            +
                    plan_info = self._parse_plan_file(Path(full_path))
         
     | 
| 
      
 495 
     | 
    
         
            +
                    
         
     | 
| 
      
 496 
     | 
    
         
            +
                    if plan_info:
         
     | 
| 
      
 497 
     | 
    
         
            +
                        entry.update(self._process_flow_file(plan_info))
         
     | 
| 
      
 498 
     | 
    
         
            +
                        entry.update(self._process_geom_file(plan_info))
         
     | 
| 
      
 499 
     | 
    
         
            +
                        
         
     | 
| 
      
 500 
     | 
    
         
            +
                        # Add remaining plan info
         
     | 
| 
      
 501 
     | 
    
         
            +
                        for key, value in plan_info.items():
         
     | 
| 
      
 502 
     | 
    
         
            +
                            if key not in ['Flow File', 'Geom File']:
         
     | 
| 
      
 503 
     | 
    
         
            +
                                entry[key] = value
         
     | 
| 
      
 504 
     | 
    
         
            +
                        
         
     | 
| 
      
 505 
     | 
    
         
            +
                        # Add HDF results path
         
     | 
| 
      
 506 
     | 
    
         
            +
                        hdf_results_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
         
     | 
| 
      
 507 
     | 
    
         
            +
                        entry['HDF_Results_Path'] = str(hdf_results_path) if hdf_results_path.exists() else None
         
     | 
| 
      
 508 
     | 
    
         
            +
                    
         
     | 
| 
      
 509 
     | 
    
         
            +
                    return entry
         
     | 
| 
      
 510 
     | 
    
         
            +
             
     | 
| 
      
 511 
     | 
    
         
            +
                def _process_flow_file(self, plan_info: dict) -> dict:
         
     | 
| 
      
 512 
     | 
    
         
            +
                    """Process flow file information from plan info."""
         
     | 
| 
      
 513 
     | 
    
         
            +
                    flow_file = plan_info.get('Flow File')
         
     | 
| 
      
 514 
     | 
    
         
            +
                    if flow_file and flow_file.startswith('u'):
         
     | 
| 
      
 515 
     | 
    
         
            +
                        return {
         
     | 
| 
      
 516 
     | 
    
         
            +
                            'unsteady_number': flow_file[1:],
         
     | 
| 
      
 517 
     | 
    
         
            +
                            'Flow File': flow_file[1:]
         
     | 
| 
      
 518 
     | 
    
         
            +
                        }
         
     | 
| 
      
 519 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 520 
     | 
    
         
            +
                        'unsteady_number': None,
         
     | 
| 
      
 521 
     | 
    
         
            +
                        'Flow File': flow_file[1:] if flow_file and flow_file.startswith('f') else None
         
     | 
| 
      
 522 
     | 
    
         
            +
                    }
         
     | 
| 
      
 523 
     | 
    
         
            +
             
     | 
| 
      
 524 
     | 
    
         
            +
                def _process_geom_file(self, plan_info: dict) -> dict:
         
     | 
| 
      
 525 
     | 
    
         
            +
                    """Process geometry file information from plan info."""
         
     | 
| 
      
 526 
     | 
    
         
            +
                    geom_file = plan_info.get('Geom File')
         
     | 
| 
      
 527 
     | 
    
         
            +
                    if geom_file and geom_file.startswith('g'):
         
     | 
| 
      
 528 
     | 
    
         
            +
                        return {
         
     | 
| 
      
 529 
     | 
    
         
            +
                            'geometry_number': geom_file[1:],
         
     | 
| 
      
 530 
     | 
    
         
            +
                            'Geom File': geom_file[1:]
         
     | 
| 
      
 531 
     | 
    
         
            +
                        }
         
     | 
| 
      
 532 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 533 
     | 
    
         
            +
                        'geometry_number': None,
         
     | 
| 
      
 534 
     | 
    
         
            +
                        'Geom File': None
         
     | 
| 
      
 535 
     | 
    
         
            +
                    }
         
     | 
| 
      
 536 
     | 
    
         
            +
             
     | 
| 
       508 
537 
     | 
    
         
             
                def _parse_unsteady_file(self, unsteady_file_path):
         
     | 
| 
       509 
538 
     | 
    
         
             
                    """
         
     | 
| 
       510 
539 
     | 
    
         
             
                    Parse an unsteady flow file and extract critical information.
         
     | 
| 
         @@ -895,27 +924,135 @@ class RasPrj: 
     | 
|
| 
       895 
924 
     | 
    
         
             
                    return bc_info, unparsed_lines
         
     | 
| 
       896 
925 
     | 
    
         | 
| 
       897 
926 
     | 
    
         
             
                @log_call
         
     | 
| 
       898 
     | 
    
         
            -
                def  
     | 
| 
      
 927 
     | 
    
         
            +
                def _format_dataframe(self, df, entry_type):
         
     | 
| 
       899 
928 
     | 
    
         
             
                    """
         
     | 
| 
       900 
     | 
    
         
            -
                     
     | 
| 
      
 929 
     | 
    
         
            +
                    Format the DataFrame according to the desired column structure.
         
     | 
| 
      
 930 
     | 
    
         
            +
                    
         
     | 
| 
      
 931 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 932 
     | 
    
         
            +
                        df (pd.DataFrame): The DataFrame to format.
         
     | 
| 
      
 933 
     | 
    
         
            +
                        entry_type (str): The type of entry (e.g., 'Plan', 'Flow', 'Unsteady', 'Geom').
         
     | 
| 
       901 
934 
     | 
    
         | 
| 
       902 
935 
     | 
    
         
             
                    Returns:
         
     | 
| 
       903 
     | 
    
         
            -
             
     | 
| 
       904 
     | 
    
         
            -
                     
     | 
| 
       905 
     | 
    
         
            -
             
     | 
| 
      
 936 
     | 
    
         
            +
                        pd.DataFrame: The formatted DataFrame.
         
     | 
| 
      
 937 
     | 
    
         
            +
                    """
         
     | 
| 
      
 938 
     | 
    
         
            +
                    if df.empty:
         
     | 
| 
      
 939 
     | 
    
         
            +
                        return df
         
     | 
| 
       906 
940 
     | 
    
         | 
| 
       907 
     | 
    
         
            -
                     
     | 
| 
       908 
     | 
    
         
            -
             
     | 
| 
       909 
     | 
    
         
            -
             
     | 
| 
      
 941 
     | 
    
         
            +
                    if entry_type == 'Plan':
         
     | 
| 
      
 942 
     | 
    
         
            +
                        # Define column groups
         
     | 
| 
      
 943 
     | 
    
         
            +
                        first_cols = ['plan_number', 'unsteady_number', 'geometry_number']
         
     | 
| 
      
 944 
     | 
    
         
            +
                        plan_key_cols = [
         
     | 
| 
      
 945 
     | 
    
         
            +
                            'Plan Title', 'Program Version', 'Short Identifier', 'Simulation Date',
         
     | 
| 
      
 946 
     | 
    
         
            +
                            'Computation Interval', 'Mapping Interval', 'Run HTab', 'Run UNet', 
         
     | 
| 
      
 947 
     | 
    
         
            +
                            'Run Sediment', 'Run PostProcess', 'Run WQNet', 'UNET Use Existing IB Tables', 
         
     | 
| 
      
 948 
     | 
    
         
            +
                            'HDF_Results_Path', 'UNET 1D Methodology'
         
     | 
| 
      
 949 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 950 
     | 
    
         
            +
                        file_path_cols = ['Geom File', 'Geom Path', 'Flow File', 'Flow Path']
         
     | 
| 
      
 951 
     | 
    
         
            +
                        
         
     | 
| 
      
 952 
     | 
    
         
            +
                        # Build column list
         
     | 
| 
      
 953 
     | 
    
         
            +
                        all_cols = first_cols.copy()
         
     | 
| 
      
 954 
     | 
    
         
            +
                        all_cols.extend([col for col in plan_key_cols if col in df.columns])
         
     | 
| 
      
 955 
     | 
    
         
            +
                        all_cols.extend([col for col in df.columns if col not in all_cols + file_path_cols + ['full_path']])
         
     | 
| 
      
 956 
     | 
    
         
            +
                        all_cols.extend(file_path_cols)
         
     | 
| 
      
 957 
     | 
    
         
            +
                        
         
     | 
| 
      
 958 
     | 
    
         
            +
                        # Rename and fill missing columns
         
     | 
| 
      
 959 
     | 
    
         
            +
                        df = df.rename(columns={f'{entry_type.lower()}_number': 'plan_number'})
         
     | 
| 
      
 960 
     | 
    
         
            +
                        for col in all_cols:
         
     | 
| 
      
 961 
     | 
    
         
            +
                            if col not in df.columns:
         
     | 
| 
      
 962 
     | 
    
         
            +
                                df[col] = None
         
     | 
| 
      
 963 
     | 
    
         
            +
                        
         
     | 
| 
      
 964 
     | 
    
         
            +
                        # Add full_path if present
         
     | 
| 
      
 965 
     | 
    
         
            +
                        if 'full_path' in df.columns:
         
     | 
| 
      
 966 
     | 
    
         
            +
                            all_cols.append('full_path')
         
     | 
| 
      
 967 
     | 
    
         
            +
                        
         
     | 
| 
      
 968 
     | 
    
         
            +
                        return df[[col for col in all_cols if col in df.columns]]
         
     | 
| 
      
 969 
     | 
    
         
            +
                    
         
     | 
| 
      
 970 
     | 
    
         
            +
                    return df
         
     | 
| 
      
 971 
     | 
    
         
            +
             
     | 
| 
      
 972 
     | 
    
         
            +
                @log_call
         
     | 
| 
      
 973 
     | 
    
         
            +
                def _get_prj_entries(self, entry_type):
         
     | 
| 
       910 
974 
     | 
    
         
             
                    """
         
     | 
| 
       911 
     | 
    
         
            -
                     
     | 
| 
      
 975 
     | 
    
         
            +
                    Extract entries of a specific type from the HEC-RAS project file.
         
     | 
| 
      
 976 
     | 
    
         
            +
                    """
         
     | 
| 
      
 977 
     | 
    
         
            +
                    entries = []
         
     | 
| 
      
 978 
     | 
    
         
            +
                    pattern = re.compile(rf"{entry_type} File=(\w+)")
         
     | 
| 
      
 979 
     | 
    
         
            +
             
     | 
| 
      
 980 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 981 
     | 
    
         
            +
                        with open(self.prj_file, 'r') as file:
         
     | 
| 
      
 982 
     | 
    
         
            +
                            for line in file:
         
     | 
| 
      
 983 
     | 
    
         
            +
                                match = pattern.match(line.strip())
         
     | 
| 
      
 984 
     | 
    
         
            +
                                if match:
         
     | 
| 
      
 985 
     | 
    
         
            +
                                    file_name = match.group(1)
         
     | 
| 
      
 986 
     | 
    
         
            +
                                    full_path = str(self.project_folder / f"{self.project_name}.{file_name}")
         
     | 
| 
      
 987 
     | 
    
         
            +
                                    entry = self._create_entry(entry_type, file_name, full_path)
         
     | 
| 
      
 988 
     | 
    
         
            +
                                    entries.append(entry)
         
     | 
| 
       912 
989 
     | 
    
         | 
| 
       913 
     | 
    
         
            -
             
     | 
| 
       914 
     | 
    
         
            -
                    unsteady_plans = self.plan_df[self.plan_df['unsteady_number'].notna()].copy()
         
     | 
| 
      
 990 
     | 
    
         
            +
                        return self._format_dataframe(pd.DataFrame(entries), entry_type)
         
     | 
| 
       915 
991 
     | 
    
         | 
| 
       916 
     | 
    
         
            -
                     
     | 
| 
      
 992 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 993 
     | 
    
         
            +
                        logger.error(f"Error in _get_prj_entries for {entry_type}: {e}")
         
     | 
| 
      
 994 
     | 
    
         
            +
                        raise
         
     | 
| 
      
 995 
     | 
    
         
            +
             
     | 
| 
      
 996 
     | 
    
         
            +
                def _create_entry(self, entry_type, file_name, full_path):
         
     | 
| 
      
 997 
     | 
    
         
            +
                    """Helper method to create entry dictionary."""
         
     | 
| 
      
 998 
     | 
    
         
            +
                    entry_number = file_name[1:]
         
     | 
| 
      
 999 
     | 
    
         
            +
                    entry = {
         
     | 
| 
      
 1000 
     | 
    
         
            +
                        f'{entry_type.lower()}_number': entry_number,
         
     | 
| 
      
 1001 
     | 
    
         
            +
                        'full_path': full_path,
         
     | 
| 
      
 1002 
     | 
    
         
            +
                        'unsteady_number': None,
         
     | 
| 
      
 1003 
     | 
    
         
            +
                        'geometry_number': None
         
     | 
| 
      
 1004 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1005 
     | 
    
         
            +
                    
         
     | 
| 
      
 1006 
     | 
    
         
            +
                    if entry_type == 'Unsteady':
         
     | 
| 
      
 1007 
     | 
    
         
            +
                        entry['unsteady_number'] = entry_number
         
     | 
| 
      
 1008 
     | 
    
         
            +
                        entry.update(self._parse_unsteady_file(Path(full_path)))
         
     | 
| 
      
 1009 
     | 
    
         
            +
                    elif entry_type == 'Plan':
         
     | 
| 
      
 1010 
     | 
    
         
            +
                        self._update_plan_entry(entry, entry_number, full_path)
         
     | 
| 
       917 
1011 
     | 
    
         | 
| 
       918 
     | 
    
         
            -
                    return  
     | 
| 
      
 1012 
     | 
    
         
            +
                    return entry
         
     | 
| 
      
 1013 
     | 
    
         
            +
             
     | 
| 
      
 1014 
     | 
    
         
            +
                def _update_plan_entry(self, entry, entry_number, full_path):
         
     | 
| 
      
 1015 
     | 
    
         
            +
                    """Helper method to update plan entry with additional information."""
         
     | 
| 
      
 1016 
     | 
    
         
            +
                    plan_info = self._parse_plan_file(Path(full_path))
         
     | 
| 
      
 1017 
     | 
    
         
            +
                    if plan_info:
         
     | 
| 
      
 1018 
     | 
    
         
            +
                        # Handle Flow File
         
     | 
| 
      
 1019 
     | 
    
         
            +
                        flow_file = plan_info.get('Flow File')
         
     | 
| 
      
 1020 
     | 
    
         
            +
                        if flow_file:
         
     | 
| 
      
 1021 
     | 
    
         
            +
                            if flow_file.startswith('u'):
         
     | 
| 
      
 1022 
     | 
    
         
            +
                                entry.update({'unsteady_number': flow_file[1:], 'Flow File': flow_file[1:]})
         
     | 
| 
      
 1023 
     | 
    
         
            +
                            else:
         
     | 
| 
      
 1024 
     | 
    
         
            +
                                entry['Flow File'] = flow_file[1:] if flow_file.startswith('f') else None
         
     | 
| 
      
 1025 
     | 
    
         
            +
                        
         
     | 
| 
      
 1026 
     | 
    
         
            +
                        # Handle Geom File
         
     | 
| 
      
 1027 
     | 
    
         
            +
                        geom_file = plan_info.get('Geom File')
         
     | 
| 
      
 1028 
     | 
    
         
            +
                        if geom_file and geom_file.startswith('g'):
         
     | 
| 
      
 1029 
     | 
    
         
            +
                            entry.update({'geometry_number': geom_file[1:], 'Geom File': geom_file[1:]})
         
     | 
| 
      
 1030 
     | 
    
         
            +
                        
         
     | 
| 
      
 1031 
     | 
    
         
            +
                        # Add remaining plan info
         
     | 
| 
      
 1032 
     | 
    
         
            +
                        entry.update({k: v for k, v in plan_info.items() if k not in ['Flow File', 'Geom File']})
         
     | 
| 
      
 1033 
     | 
    
         
            +
                        
         
     | 
| 
      
 1034 
     | 
    
         
            +
                        # Add HDF results path
         
     | 
| 
      
 1035 
     | 
    
         
            +
                        hdf_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
         
     | 
| 
      
 1036 
     | 
    
         
            +
                        entry['HDF_Results_Path'] = str(hdf_path) if hdf_path.exists() else None
         
     | 
| 
      
 1037 
     | 
    
         
            +
             
     | 
| 
      
 1038 
     | 
    
         
            +
                def _set_plan_paths(self):
         
     | 
| 
      
 1039 
     | 
    
         
            +
                    """Helper method to set paths in plan_df."""
         
     | 
| 
      
 1040 
     | 
    
         
            +
                    if not self.plan_df['full_path'].any():
         
     | 
| 
      
 1041 
     | 
    
         
            +
                        self.plan_df['full_path'] = self.plan_df['plan_number'].apply(
         
     | 
| 
      
 1042 
     | 
    
         
            +
                            lambda x: str(self.project_folder / f"{self.project_name}.p{x}")
         
     | 
| 
      
 1043 
     | 
    
         
            +
                        )
         
     | 
| 
      
 1044 
     | 
    
         
            +
                    
         
     | 
| 
      
 1045 
     | 
    
         
            +
                    for idx, row in self.plan_df.iterrows():
         
     | 
| 
      
 1046 
     | 
    
         
            +
                        if pd.notna(row['Geom File']) and pd.isna(row.get('Geom Path')):
         
     | 
| 
      
 1047 
     | 
    
         
            +
                            self.plan_df.at[idx, 'Geom Path'] = str(self.project_folder / f"{self.project_name}.g{row['Geom File']}")
         
     | 
| 
      
 1048 
     | 
    
         
            +
                        
         
     | 
| 
      
 1049 
     | 
    
         
            +
                        if pd.notna(row['Flow File']) and pd.isna(row.get('Flow Path')):
         
     | 
| 
      
 1050 
     | 
    
         
            +
                            prefix = 'u' if pd.notna(row['unsteady_number']) else 'f'
         
     | 
| 
      
 1051 
     | 
    
         
            +
                            self.plan_df.at[idx, 'Flow Path'] = str(self.project_folder / f"{self.project_name}.{prefix}{row['Flow File']}")
         
     | 
| 
      
 1052 
     | 
    
         
            +
                        
         
     | 
| 
      
 1053 
     | 
    
         
            +
                        if not self.suppress_logging:
         
     | 
| 
      
 1054 
     | 
    
         
            +
                            logger.info(f"Plan {row['plan_number']} paths set up")
         
     | 
| 
      
 1055 
     | 
    
         
            +
             
     | 
| 
       919 
1056 
     | 
    
         | 
| 
       920 
1057 
     | 
    
         
             
            # Create a global instance named 'ras'
         
     | 
| 
       921 
1058 
     | 
    
         
             
            # Defining the global instance allows the init_ras_project function to initialize the project.
         
     | 
| 
         @@ -987,7 +1124,7 @@ def get_ras_exe(ras_version=None): 
     | 
|
| 
       987 
1124 
     | 
    
         
             
                Args:
         
     | 
| 
       988 
1125 
     | 
    
         
             
                    ras_version (str, optional): Either a version number or a full path to the HEC-RAS executable.
         
     | 
| 
       989 
1126 
     | 
    
         
             
                                                 If None, the function will first check the global 'ras' object for a path.
         
     | 
| 
       990 
     | 
    
         
            -
                                                  
     | 
| 
      
 1127 
     | 
    
         
            +
                                                 or a default path.
         
     | 
| 
       991 
1128 
     | 
    
         | 
| 
       992 
1129 
     | 
    
         
             
                Returns:
         
     | 
| 
       993 
1130 
     | 
    
         
             
                    str: The full path to the HEC-RAS executable.
         
     | 
    
        ras_commander/RasUtils.py
    CHANGED
    
    | 
         @@ -216,30 +216,49 @@ class RasUtils: 
     | 
|
| 
       216 
216 
     | 
    
         
             
                    Returns:
         
     | 
| 
       217 
217 
     | 
    
         
             
                    Path: Full path to the plan file
         
     | 
| 
       218 
218 
     | 
    
         | 
| 
      
 219 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 220 
     | 
    
         
            +
                    ValueError: If plan number is not between 1 and 99
         
     | 
| 
      
 221 
     | 
    
         
            +
                    TypeError: If input type is invalid
         
     | 
| 
      
 222 
     | 
    
         
            +
                    FileNotFoundError: If the plan file does not exist
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
       219 
224 
     | 
    
         
             
                    Example:
         
     | 
| 
       220 
225 
     | 
    
         
             
                    >>> plan_path = RasUtils.get_plan_path(1)
         
     | 
| 
       221 
226 
     | 
    
         
             
                    >>> print(f"Plan file path: {plan_path}")
         
     | 
| 
       222 
227 
     | 
    
         
             
                    >>> plan_path = RasUtils.get_plan_path("path/to/plan.p01")
         
     | 
| 
       223 
228 
     | 
    
         
             
                    >>> print(f"Plan file path: {plan_path}")
         
     | 
| 
       224 
229 
     | 
    
         
             
                    """
         
     | 
| 
       225 
     | 
    
         
            -
                    
         
     | 
| 
      
 230 
     | 
    
         
            +
                    # Validate RAS object
         
     | 
| 
       226 
231 
     | 
    
         
             
                    ras_obj = ras_object or ras
         
     | 
| 
       227 
232 
     | 
    
         
             
                    ras_obj.check_initialized()
         
     | 
| 
       228 
233 
     | 
    
         | 
| 
      
 234 
     | 
    
         
            +
                    # Handle direct file path input
         
     | 
| 
       229 
235 
     | 
    
         
             
                    plan_path = Path(current_plan_number_or_path)
         
     | 
| 
       230 
236 
     | 
    
         
             
                    if plan_path.is_file():
         
     | 
| 
       231 
237 
     | 
    
         
             
                        logger.info(f"Using provided plan file path: {plan_path}")
         
     | 
| 
       232 
238 
     | 
    
         
             
                        return plan_path
         
     | 
| 
       233 
239 
     | 
    
         | 
| 
      
 240 
     | 
    
         
            +
                    # Handle plan number input
         
     | 
| 
       234 
241 
     | 
    
         
             
                    try:
         
     | 
| 
       235 
     | 
    
         
            -
                         
     | 
| 
      
 242 
     | 
    
         
            +
                        plan_num = int(current_plan_number_or_path)
         
     | 
| 
      
 243 
     | 
    
         
            +
                        if not 1 <= plan_num <= 99:
         
     | 
| 
      
 244 
     | 
    
         
            +
                            raise ValueError(f"Plan number must be between 1 and 99, got: {plan_num}")
         
     | 
| 
      
 245 
     | 
    
         
            +
                        current_plan_number = f"{plan_num:02d}"  # Ensure two-digit format
         
     | 
| 
       236 
246 
     | 
    
         
             
                        logger.debug(f"Converted plan number to two-digit format: {current_plan_number}")
         
     | 
| 
       237 
     | 
    
         
            -
                    except ValueError:
         
     | 
| 
      
 247 
     | 
    
         
            +
                    except (ValueError, TypeError) as e:
         
     | 
| 
      
 248 
     | 
    
         
            +
                        if isinstance(e, TypeError):
         
     | 
| 
      
 249 
     | 
    
         
            +
                            logger.error(f"Invalid input type: {type(current_plan_number_or_path)}. Expected string, number, or Path.")
         
     | 
| 
      
 250 
     | 
    
         
            +
                            raise TypeError(f"Invalid input type: {type(current_plan_number_or_path)}. Expected string, number, or Path.")
         
     | 
| 
       238 
251 
     | 
    
         
             
                        logger.error(f"Invalid plan number: {current_plan_number_or_path}. Expected a number from 1 to 99.")
         
     | 
| 
       239 
252 
     | 
    
         
             
                        raise ValueError(f"Invalid plan number: {current_plan_number_or_path}. Expected a number from 1 to 99.")
         
     | 
| 
       240 
253 
     | 
    
         | 
| 
      
 254 
     | 
    
         
            +
                    # Construct and validate plan path
         
     | 
| 
       241 
255 
     | 
    
         
             
                    plan_name = f"{ras_obj.project_name}.p{current_plan_number}"
         
     | 
| 
       242 
256 
     | 
    
         
             
                    full_plan_path = ras_obj.project_folder / plan_name
         
     | 
| 
      
 257 
     | 
    
         
            +
                    
         
     | 
| 
      
 258 
     | 
    
         
            +
                    if not full_plan_path.exists():
         
     | 
| 
      
 259 
     | 
    
         
            +
                        logger.error(f"Plan file does not exist: {full_plan_path}")
         
     | 
| 
      
 260 
     | 
    
         
            +
                        raise FileNotFoundError(f"Plan file does not exist: {full_plan_path}")
         
     | 
| 
      
 261 
     | 
    
         
            +
                    
         
     | 
| 
       243 
262 
     | 
    
         
             
                    logger.info(f"Constructed plan file path: {full_plan_path}")
         
     | 
| 
       244 
263 
     | 
    
         
             
                    return full_plan_path
         
     | 
| 
       245 
264 
     | 
    
         | 
    
        ras_commander/__init__.py
    CHANGED
    
    
| 
         @@ -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= 
     | 
| 
       25 
     | 
    
         
            -
            ras_commander/RasPrj.py,sha256= 
     | 
| 
      
 24 
     | 
    
         
            +
            ras_commander/RasPlan.py,sha256=0THxeB9ldeDuS4of2ruYG7Abbc5jz2729y5qOHntMuI,61664
         
     | 
| 
      
 25 
     | 
    
         
            +
            ras_commander/RasPrj.py,sha256=gQoGG1Mqsoj5W1oVlulqhA0kQ5eD69vI96uihnY-kLE,47744
         
     | 
| 
       26 
26 
     | 
    
         
             
            ras_commander/RasToGo.py,sha256=TKujfaV1xQhFaOddF4g2ogGy6ky-CLlfelSMPD2J3Nk,1223
         
     | 
| 
       27 
27 
     | 
    
         
             
            ras_commander/RasUnsteady.py,sha256=KfCXAag-_bPwwS3JbPZH-s4hbaoHACO0mlRnGrzbFgA,32092
         
     | 
| 
       28 
     | 
    
         
            -
            ras_commander/RasUtils.py,sha256= 
     | 
| 
       29 
     | 
    
         
            -
            ras_commander/__init__.py,sha256= 
     | 
| 
       30 
     | 
    
         
            -
            ras_commander-0. 
     | 
| 
       31 
     | 
    
         
            -
            ras_commander-0. 
     | 
| 
       32 
     | 
    
         
            -
            ras_commander-0. 
     | 
| 
       33 
     | 
    
         
            -
            ras_commander-0. 
     | 
| 
       34 
     | 
    
         
            -
            ras_commander-0. 
     | 
| 
      
 28 
     | 
    
         
            +
            ras_commander/RasUtils.py,sha256=0fm4IIs0LH1dgDj3pGd66mR82DhWLEkRKUvIo2M_5X0,35886
         
     | 
| 
      
 29 
     | 
    
         
            +
            ras_commander/__init__.py,sha256=KICL5peRDDjE2BXEFVDMmtDZ4TKHtNhHh5eQgTSdqqs,2001
         
     | 
| 
      
 30 
     | 
    
         
            +
            ras_commander-0.66.0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
         
     | 
| 
      
 31 
     | 
    
         
            +
            ras_commander-0.66.0.dist-info/METADATA,sha256=-jPNaFlPs0iCcscfGjO31DwF3-LT632rbiunJQmYzoM,26233
         
     | 
| 
      
 32 
     | 
    
         
            +
            ras_commander-0.66.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
         
     | 
| 
      
 33 
     | 
    
         
            +
            ras_commander-0.66.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
         
     | 
| 
      
 34 
     | 
    
         
            +
            ras_commander-0.66.0.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |