Architecture ============= System Overview --------------- pyhnhtools uses a three-layer architecture to support multiple analysis modules with a shared GUI: .. code-block:: text ┌────────────────────────────────────────┐ │ qt_gui.py (Generic PyQt5 Framework) │ │ - Layout, widgets, visualization │ │ - No solver-specific logic │ └────────────────┬───────────────────────┘ │ ┌───────────┴──────────────┐ │ │ ┌────▼─────────────────┐ ┌───▼──────────────────┐ │ std_step_1d_gui.py │ │ culverts_gui.py │ │ (Integration Layer) │ │ (Integration Layer) │ │ Workflow logic │ │ Module-specific UI │ └────┬─────────────────┘ └───┬──────────────────┘ │ │ ┌────▼──────────────┐ ┌──────▼────────────────┐ │ standard_step_1d_ │ │ culverts_adapter.py │ │ adapter.py │ │ (Data Adapter) │ │ (Data Adapter) │ │ │ └────┬──────────────┘ └──────┬────────────────┘ │ │ ┌────▼──────────────┐ ┌──────▼────────────────┐ │ std_step_1d/ │ │ culverts/ │ │ (Core Solver) │ │ (Core Solver) │ └───────────────────┘ └───────────────────────┘ Layer Descriptions ------------------ **Layer 1: qt_gui.py (Generic GUI Framework)** The core GUI framework handles: - Window layout and organization - Tab and widget management - User interaction routing - Common visualization patterns Key principle: **No solver-specific logic here** **Layer 2: {module}_gui.py (Integration Interface)** Module-specific integration layer handles: - Module workflow orchestration - Converting GUI inputs to solver inputs - Converting solver outputs to GUI display - Plotting module-specific results Examples: - ``std_step_1d_gui.py`` → StandardStep1DInterface - ``culverts_gui.py`` → CulvertsInterface **Layer 3: {module}_adapter.py (Data Adapter)** Bridges module API and GUI: - Serializes/deserializes data (JSON, CSV) - Transforms data between formats - Handles file I/O **Layer 4: Core Solver** Pure computation with no GUI knowledge: - ``std_step_1d/`` - Standard step 1D solver - ``culverts/`` - Culvert analysis module Data Flow --------- Typical execution flow: 1. **User Input** → GUI collects parameters 2. **Interface** → std_step_1d_gui.py receives input 3. **Adapter** → standard_step_1d_adapter.py formats data 4. **Solver** → std_step_1d module runs analysis 5. **Adapter** → Converts results back to Python objects 6. **Interface** → Updates GUI displays 7. **GUI** → Shows results to user Module Integration ------------------ To add a new module (e.g., unsteady flow): 1. **Create Integration Layer** - Copy ``std_step_1d_gui.py`` template - Rename to ``unsteady_gui.py`` - Adapt methods for unsteady workflow - Implement ``UnsteadyInterface`` class 2. **Create Data Adapter** (if needed) - New file: ``unsteady_adapter.py`` - Handle model serialization - Call core unsteady solver 3. **Register in qt_gui.py** - Import new interface - Instantiate in BackwaterWidget - Create tab for unsteady analysis - Wire button clicks 4. **Benefits** - One GUI serves all modules - Each module has independent logic - Easy to add new modules - No code duplication Package Structure ----------------- .. code-block:: text pyhnhtools/ ├── __init__.py # Package initialization ├── gui/ │ ├── qt_gui.py # Core GUI framework (1,799 lines) │ ├── std_step_1d_gui.py # Template & working example │ ├── culverts_gui.py # Template for different workflows │ ├── app.py # Application launcher │ └── __init__.py ├── standard_step_1d_adapter.py # Data adapter ├── std_step_1d/ # Core solver │ ├── __init__.py │ ├── solver.py │ └── ... ├── culverts/ # Culvert analysis module │ ├── __init__.py │ └── ... ├── parsers/ # HEC-RAS parsers │ ├── __init__.py │ └── ... └── ... Design Principles ------------------ **Separation of Concerns** Each layer has single responsibility: - GUI manages interface, not computation - Interfaces manage workflow, not data format - Adapters manage serialization, not solving - Solvers manage computation, not UI **No Circular Dependencies** Data flows downward only: - GUI → Interfaces → Adapters → Solvers - Solvers never import GUI - Adapters never import interfaces **Extensibility** Add new solvers without modifying GUI: - New solver = new interface class - qt_gui.py just gets one new reference - Minimal changes to existing code **Maintainability** Clear boundaries make debugging easier: - GUI issues isolated to qt_gui.py - Workflow issues in {module}_gui.py - Data issues in {module}_adapter.py - Computation issues in {module}/ API Consistency --------------- All module interfaces follow same pattern: .. code-block:: python class {Module}Interface: def __init__(self, gui_widget) def setup() # Initialize def load_model(filepath) # Load data def new_model() # Create empty def run_solver() # Execute def validate_model() # Pre-checks def save_model(filepath) # Save data This consistency makes code predictable. Technology Stack ---------------- - **GUI Framework**: PyQt5 (desktop application) - **Plotting**: Plotly (interactive visualization) - **Data**: NumPy, SciPy (numerical computation) - **Serialization**: JSON (human-readable configs) - **Build**: Setuptools (package distribution) - **Documentation**: Sphinx + Read the Docs Performance Considerations --------------------------- **Geometry Processing** Cross-section interpolation scales with point count: - <100 points: Instant - 100-1000 points: <1 second - >1000 points: May slow GUI **Solver Execution** Standard step solver scales with section count: - 5-20 sections: Milliseconds - 20-100 sections: <1 second - >100 sections: Variable by geometry **GUI Responsiveness** For large models, consider: - Run solver in background thread - Reduce plot complexity for preview - Use command-line API for batch jobs Future Expansion ---------------- The architecture supports: - **Unsteady Flow Module** (time-varying boundary conditions) - **Hydrology Module** (basin and routing analysis) - **Multiple Backends** (different solvers for same problem type) - **Plugin System** (user-defined modules) - **Cloud Integration** (REST API frontend) See :doc:`../developer/module_integration` for details.