Skip to main content

Scenario Configuration Refactoring

Feature: Reusable Configuration Presets Date: October 15, 2025 Status: ✅ Phase 1 Complete - Options Model Refactored Purpose: Eliminate field duplication and enable reusable configuration presets


Table of Contents

  1. Overview
  2. Problem & Solution
  3. Architecture Changes
  4. Implementation
  5. Migration
  6. Future Phases

1. Overview

1.1 Purpose

Refactor scenario configuration models to:

  • Eliminate field duplication between Scenario and Options
  • Enable reusable configuration presets
  • Improve user experience with preset selection
  • Standardize configuration management

1.2 Scope

Phase 1 (✅ Complete):

  • Options model refactoring
  • Removed duplicate PDA fields from Scenario
  • Scenario → Options FK relationship
  • Reusable options presets

Future Phases (Planned):

  • TimeParameters refactoring
  • Report refactoring
  • Configuration sets (grouped presets)

2. Problem & Solution

2.1 The Problem

Field Duplication:

// Scenario.cs (BEFORE)
public class Scenario
{
public string DemandModel { get; set; } // ❌ Duplicate
public double? PressureExponent { get; set; } // ❌ Duplicate
public double? MinimumPressure { get; set; } // ❌ Duplicate
public double? RequiredPressure { get; set; } // ❌ Duplicate
// ...
}

// Options.cs (BEFORE)
public class Options
{
public int? ScenarioId { get; set; } // ❌ Wrong direction
public string DemandModel { get; set; } // ❌ Duplicate
public double? PressureExponent { get; set; } // ❌ Duplicate
public double? MinimumPressure { get; set; } // ❌ Duplicate
public double? RequiredPressure { get; set; } // ❌ Duplicate
// ...
}

Issues:

  • Data duplication across models
  • Unclear ownership (Options→Scenario or Scenario→Options?)
  • No configuration reuse capability
  • Complex update logic
  • Risk of inconsistency

2.2 The Solution

Single Source of Truth:

// Scenario.cs (AFTER)
public class Scenario
{
public int? OptionsId { get; set; } // ✅ FK to Options
public Options Options { get; set; } // ✅ Navigation property
// PDA fields removed - now in Options only
}

// Options.cs (AFTER)
public class Options
{
public int Id { get; set; }
public string Name { get; set; } // ✅ "PDA Standard", "DDA High Accuracy"
public string Description { get; set; } // ✅ Description of preset
public bool IsDefault { get; set; } // ✅ Mark default preset

// Demand model fields (single source)
public string DemandModel { get; set; }
public double? PressureExponent { get; set; }
public double? MinimumPressure { get; set; }
public double? RequiredPressure { get; set; }

// Reverse navigation
public List<Scenario> Scenarios { get; set; }
}

Benefits:

  • ✅ Single source of truth for configuration
  • ✅ Reusable presets across scenarios
  • ✅ Clearer conceptual model
  • ✅ Better UX (select preset vs enter all fields)
  • ✅ Standardization encouraged

3. Architecture Changes

3.1 Relationship Change

Before:

Options ────► Scenario
(owns) (owned by)

After:

Scenario ────► Options
(uses) (preset)

(can be shared by multiple scenarios)

3.2 Database Schema

Foreign Key:

ALTER TABLE "Scenarios"
ADD COLUMN "OptionsId" INTEGER REFERENCES "Options"("Id") ON DELETE RESTRICT;

Indexes:

CREATE INDEX "IX_Scenario_OptionsId" ON "Scenarios"("OptionsId");
CREATE INDEX "IX_Options_Name" ON "Options"("Name");
CREATE INDEX "IX_Options_IsDefault" ON "Options"("IsDefault");

Delete Behavior: RESTRICT (prevents deletion if in use by scenarios)

3.3 Data Migration

Migration Strategy:

  1. Create "Default" options preset from existing default options
  2. For each scenario with custom options:
    • Create named preset (e.g., "Scenario 7 Options")
    • Link scenario to new preset
  3. Scenarios without custom options link to "Default" preset

4. Implementation

4.1 Backend Changes ✅

Models Updated:

  1. Options.cs - Added Name, Description, IsDefault; removed ScenarioId
  2. Scenario.cs - Added OptionsId FK; removed duplicate PDA fields
  3. EN2PostgresContext.cs - Configured relationships and indexes

API Controllers:

  1. OptionsController.cs - Refactored to manage presets

    • GET /api/options - List all presets
    • GET /api/options/default - Get default preset
    • POST /api/options - Create new preset
    • PUT /api/options/{id} - Update preset
    • DELETE /api/options/{id} - Delete (with usage check)
  2. ScenarioController.cs - Updated to use Options navigation

    • Include Options in queries
    • Map OptionsId and OptionsName in DTOs
    • Use Options for demand model fields

DTOs Updated:

  • OptionsDto.cs - Added Name, Description, IsDefault
  • ScenarioResponseDto.cs - Added OptionsId, OptionsName; embedded demand model from Options

4.2 Frontend Changes ✅

New Services:

  • optionsService.js - Full CRUD for options presets

Updated Components:

  1. ScenarioEditDialog.jsx - Complete rewrite of options section

    • Removed 50+ lines of Demand Model fields
    • Added Options dropdown to select preset
    • Added preview of selected preset
    • Shows PDA/DDA parameters from selected preset
  2. ScenariosList.jsx - Updated state management

    • Added optionsPresets to state
    • Load presets on mount
    • Pass to edit dialog
  3. ScenarioDetailsDialog.jsx - Display options name

    • Shows selected preset name
    • Shows demand model type
    • Shows PDA parameters if applicable

4.3 Simulation Integration ✅

EpanetConsoleCore:

  • Updated to load Options with scenario
  • WriteDemandModel() uses scenario.Options
  • Backward compatible with null options (uses default)

NetworkManager:

  • Parses options from INP file
  • Creates "Default" preset automatically
  • Sets IsDefault = true

5. Migration

5.1 Data Migration Required

From:

-- Old structure
Scenario: Id=7, DemandModel='PDA', PressureExponent=0.5, ...
Options: Id=1, ScenarioId=7, DemandModel='PDA', PressureExponent=0.5, ...

To:

-- New structure
Options: Id=1, Name='Default', IsDefault=true, DemandModel='DDA', ...
Options: Id=2, Name='PDA Standard', IsDefault=false, DemandModel='PDA', PressureExponent=0.5, ...
Scenario: Id=7, OptionsId=2, (no DemandModel field), ...

5.2 Migration Script

-- Step 1: Add new columns to Options
ALTER TABLE "Options" ADD COLUMN "Name" VARCHAR(100);
ALTER TABLE "Options" ADD COLUMN "Description" TEXT;
ALTER TABLE "Options" ADD COLUMN "IsDefault" BOOLEAN DEFAULT false;

-- Step 2: Create default preset
INSERT INTO "Options" (Name, Description, IsDefault, DemandModel, ...)
VALUES ('Default', 'Default EPANET options', true, 'DDA', ...);

-- Step 3: Add OptionsId to Scenarios
ALTER TABLE "Scenarios" ADD COLUMN "OptionsId" INTEGER;

-- Step 4: Link all scenarios to default
UPDATE "Scenarios" SET "OptionsId" = (SELECT "Id" FROM "Options" WHERE "IsDefault" = true);

-- Step 5: Drop old columns from Scenarios
ALTER TABLE "Scenarios" DROP COLUMN "DemandModel";
ALTER TABLE "Scenarios" DROP COLUMN "PressureExponent";
ALTER TABLE "Scenarios" DROP COLUMN "MinimumPressure";
ALTER TABLE "Scenarios" DROP COLUMN "RequiredPressure";

-- Step 6: Drop ScenarioId from Options
ALTER TABLE "Options" DROP COLUMN "ScenarioId";

-- Step 7: Add FK constraint
ALTER TABLE "Scenarios"
ADD CONSTRAINT "FK_Scenarios_Options"
FOREIGN KEY ("OptionsId") REFERENCES "Options"("Id") ON DELETE RESTRICT;

-- Step 8: Add indexes
CREATE INDEX "IX_Scenario_OptionsId" ON "Scenarios"("OptionsId");
CREATE INDEX "IX_Options_Name" ON "Options"("Name");
CREATE INDEX "IX_Options_IsDefault" ON "Options"("IsDefault");

6. Future Phases

6.1 Phase 2: TimeParameters Refactoring

Status: Planned Effort: Medium

Apply same pattern to TimeParameters:

  • Remove from Scenario if duplicated
  • Create reusable TimeParameters presets
  • Scenario → TimeParametersId FK

6.2 Phase 3: Report Refactoring

Status: Planned Effort: Medium

Apply same pattern to Report:

  • Create reusable Report presets
  • Scenario → ReportId FK

6.3 Phase 4: Configuration Sets

Status: Future Enhancement Effort: Medium-High

Group related configurations:

public class ConfigurationSet
{
public int Id { get; set; }
public string Name { get; set; }
public int OptionsId { get; set; }
public int TimeParametersId { get; set; }
public int ReportId { get; set; }
}

public class Scenario
{
public int? ConfigurationSetId { get; set; }
// OR individual FKs for flexibility
}

Benefits:

  • Single selection for complete configuration
  • "Quick Analysis", "Full Simulation", "High Accuracy" sets
  • Even better UX

Summary

Phase 1 Complete:

  • Options model refactored successfully
  • Duplicate fields removed from Scenario
  • Reusable presets enabled
  • Frontend updated with preset selector
  • Backend fully supports new architecture
  • Database migration planned

Improvements Delivered:

  • Clearer data model
  • Better code maintainability
  • Enhanced user experience
  • Standardization enabled
  • Reduced duplication

Production Ready:

  • All components updated
  • API tested
  • Frontend tested
  • Migration script ready
  • Backward compatible (null options uses default)

⏸️ Future Work:

  • TimeParameters refactoring (Phase 2)
  • Report refactoring (Phase 3)
  • Configuration Sets (Phase 4)

Related Documentation:

  • Database Schema: See architecture documentation
  • API Reference: Available in Swagger UI
  • Frontend Components: See architecture docs

Status: ✅ Phase 1 Complete and Production Ready Last Updated: October 15, 2025