Skip to main content

Database → EPANET Synchronization

Feature: Real-time database-to-EPANET synchronization Date: October 16-17, 2025 Status: ✅ Implemented and Complete Purpose: Apply database changes to EPANET model before simulations


Table of Contents​

  1. Overview
  2. Strategy
  3. Implementation
  4. Service Architecture
  5. Testing
  6. Limitations

1. Overview​

1.1 Purpose​

Enable database changes to flow directly to EPANET before running hydraulic and quality simulations, eliminating the need for manual INP file regeneration.

1.2 Problem Solved​

Before:

  • Network loaded from .inp files only
  • Database modifications not reflected in simulations
  • Manual INP file export required after every change
  • Simulation results didn't reflect database state

After:

  • Database changes automatically applied to EPANET model
  • Simulations use current database values
  • No manual export required
  • Database is source of truth

1.3 Solution Approach​

Chosen: Option 1 - Update EPANET In-Memory Model

Apply database changes to EPANET's in-memory model after loading from INP file, before running simulation.

┌─────────────┐
│ Load from │
│ INP file │
└──────┬──────┘
│
▼
┌─────────────┐
│ Apply DB │ ← New synchronization step
│ Changes │
└──────┬──────┘
│
▼
┌─────────────┐
│ Run Sim │
└─────────────┘

Why this approach:

  • ✅ Minimal code changes
  • ✅ Leverages existing EPANET setter functions
  • ✅ INP file remains as a baseline/template
  • ✅ Database becomes source of truth for changes
  • ✅ No binary format dependencies

2. Strategy​

2.1 Data Categories​

Global Network Data (All Records Synced):

  • Junctions, Tanks, Reservoirs (all nodes)
  • Pipes, Valves, Pumps (all links via LinkProperty table)
  • Patterns (all patterns)
  • Curves (all curves)
  • Node and Link properties

Scenario-Specific Data (Needs Special Handling):

  • Options (demand model, solver settings)
  • Time Parameters (duration, timesteps)
  • Controls (simple and rule-based)

2.2 Update Flow​

2.3 Property Mapping​

Nodes: | Database | EPANET Property Code | Entity | Sync Method | |----------|---------------------|--------|-------------| | Elevation | EN_ELEVATION (0) | All Nodes | SyncJunctionsAsync | | BaseDemand | EN_BASEDEMAND (1) | Junctions | SyncJunctionsAsync | | Pattern | EN_PATTERN (2) | Junctions/Reservoirs | SyncJunctionsAsync/ReservoirsAsync | | InitLevel | EN_INITLEVEL (21) | Tanks | SyncTanksAsync | | MinLevel | EN_MINLEVEL (20) | Tanks | SyncTanksAsync | | MaxLevel | EN_MAXLEVEL (22) | Tanks | SyncTanksAsync | | Diameter | EN_TANKDIAM (23) | Tanks | SyncTanksAsync | | VolCurve | EN_VOLCURVE (26) | Tanks | SyncTanksAsync |

Links: | Database | EPANET Property Code | Entity | Sync Method | |----------|---------------------|--------|-------------| | Diameter | EN_DIAMETER (0) | Pipes/Valves | SyncPipesAsync/ValvesAsync | | Length | EN_LENGTH (1) | Pipes | SyncPipesAsync | | Roughness | EN_ROUGHNESS (2) | Pipes | SyncPipesAsync | | MinorLoss | EN_MINORLOSS (3) | Pipes/Valves | SyncPipesAsync/ValvesAsync | | InitSetting | EN_INITSETTING (4) | Valves/Pumps | SyncValvesAsync/LinkPropertiesAsync | | InitStatus | EN_INITSTATUS (11) | All Links | SyncLinkPropertiesAsync | | KWall | EN_KWALL (23) | Pipes | SyncLinkPropertiesAsync | | PumpCurve | EN_PUMP_HCURVE (14) | Pumps | SyncLinkPropertiesAsync |


3. Implementation​

3.1 Core Infrastructure ✅​

New Service: NetworkSynchronizationService.cs

Location: epanetconsolecore/EpanetConsoleCore/Services/

Main Interface:

public class NetworkSynchronizationService
{
private readonly EpanetWrapper _epanetWrapper;
private readonly EN2PostgresContext _context;
private readonly HTLogger _logger;

public NetworkSynchronizationService(
EpanetWrapper epanetWrapper,
EN2PostgresContext context,
HTLogger logger)
{
_epanetWrapper = epanetWrapper;
_context = context;
_logger = logger;
}

// Main synchronization method
public async Task SyncAllFromDatabaseAsync()
{
await SyncNodesAsync();
await SyncLinksAsync();
await SyncPatternsAsync();
await SyncCurvesAsync();
await SyncOptionsAsync();
await SyncTimeParametersAsync();
}
}

3.2 Synchronization Methods​

Node Synchronization​

// Junctions: Elevation, BaseDemand, Pattern
private async Task SyncJunctionsAsync()
{
var junctions = await _context.Junctions.ToListAsync();
foreach (var junction in junctions)
{
int index = _epanetWrapper.EN_getnodeindex(junction.Id);
if (index <= 0) continue;

_epanetWrapper.EN_setnodevalue(index, EN_ELEVATION, junction.Elevation);
_epanetWrapper.EN_setnodevalue(index, EN_BASEDEMAND, junction.BaseDemand);

if (!string.IsNullOrEmpty(junction.Pattern))
{
int patIndex = _epanetWrapper.EN_getpatternindex(junction.Pattern);
if (patIndex > 0)
_epanetWrapper.EN_setnodevalue(index, EN_PATTERN, patIndex);
}
}
}
// Uses LinkProperty table for flexible property updates
private async Task SyncLinkPropertiesAsync()
{
var linkProperties = await _context.LinkProperties.ToListAsync();
foreach (var prop in linkProperties)
{
int index = _epanetWrapper.EN_getlinkindex(prop.LinkId);
if (index <= 0) continue;

_epanetWrapper.EN_setlinkvalue(index, prop.LinkPropertyTypeId, prop.LinkValue);
}
}

Pattern & Curve Bulk Updates​

// Efficient bulk pattern update
public int EN_setpattern(int index, double[] values, int len)
{
return EpanetImport.EN_setpattern(_project, index, values, len);
}

// Efficient bulk curve update
public int EN_setcurve(int index, double[] xValues, double[] yValues, int nPoints)
{
return EpanetImport.EN_setcurve(_project, index, xValues, yValues, nPoints);
}

3.3 Integration Point​

File: epanetconsolecore/EpanetConsoleCore/Program.cs

Integrated at line 251 (after startup, before simulation):

// Load network from INP file
bool startupSuccess = epanetWrapper.Startup(doQuality, msxFilename);
if (!startupSuccess) { /* error handling */ }

// ✨ Synchronize network from database
logger.LogInformation("Synchronizing network from database...");
scenario.CurrentStep = "Synchronizing network from database";
scenario.ProgressPercentage = 7;
await enContext.SaveChangesAsync();

var syncService = new NetworkSynchronizationService(epanetWrapper, enContext, logger);
try
{
await syncService.SyncAllFromDatabaseAsync();
logger.LogInformation("Network synchronization completed");
}
catch (Exception syncEx)
{
logger.LogError($"Network synchronization failed: {syncEx.Message}");
// Continue with INP values (partial sync better than crash)
}

// Continue with simulation...

4. Service Architecture​

4.1 Synchronization Categories​

Implemented Methods:

  1. Node Sync Methods:

    • SyncJunctionsAsync() - Elevation, BaseDemand, Pattern
    • SyncTanksAsync() - Elevation, Diameter, Levels, VolCurve
    • SyncReservoirsAsync() - InitialHead, Pattern
    • SyncNodePropertiesAsync() - InitQual, Emitter, TankBulkCoeff
    • SyncTankMixingAsync() - Mixing model and fraction
  2. Link Sync Methods:

    • SyncPipesAsync() - Diameter, Length, Roughness, MinorLoss
    • SyncValvesAsync() - Diameter, ValveType, InitSetting, MinorLoss
    • SyncLinkPropertiesAsync() - Pump properties, KWall, InitStatus, etc.
  3. Pattern & Curve Methods:

    • SyncPatternsAsync() - Updates existing patterns (bulk operation)
    • SyncCurvesAsync() - Updates existing curves (bulk operation)
  4. Configuration Methods:

    • SyncOptionsAsync() - Trials, Accuracy, QualityTolerance
    • SyncTimeParametersAsync() - Duration, timesteps (partial)

4.2 Scenario-Specific Handling​

Options:

  • Base sync uses default options
  • SaveInpFileMode applies scenario-specific options via SyncOptionsFromObject()
  • Regular runs apply demand model via WriteDemandModel()

Controls:

  • Simple controls: Written via existing WriteControls() method
  • Rule-based controls: Written via existing WriteControls() method
  • Already scenario-aware

4.3 Audit Results ✅​

Global Data Synchronization:

  • ✅ All nodes synced (Junctions, Tanks, Reservoirs)
  • ✅ All links synced (Pipes, Valves, Pumps)
  • ✅ All patterns synced
  • ✅ All curves synced
  • ✅ All properties synced

Scenario-Specific Handling:

  • ✅ Options handled correctly (SaveInpFileMode fixed)
  • ✅ Controls already scenario-aware
  • ⚠️ TimeParameters partially implemented (string parsing pending)

5. Testing​

5.1 Test Scenarios​

Test Case 1: Pipe Property Changes

-- Change pipe diameter in database
UPDATE "Pipes" SET "Diameter" = 12 WHERE "Id" = 'P-1';

-- Run simulation
-- Expected: EPANET uses diameter=12, not INP file value

Test Case 2: Tank Level Changes

-- Change tank initial level
UPDATE "Tanks" SET "InitLevel" = 50 WHERE "Id" = 'T-1';

-- Run simulation
-- Expected: Tank starts at level 50

Test Case 3: Pattern Changes

-- Update pattern multipliers
UPDATE "DataPattern" SET "Pattern" = 1.5 WHERE "Pattern_Id" = 'PAT1' AND "SeqNum" = 1;

-- Run simulation
-- Expected: Pattern uses new multipliers

5.2 Verification Points​

✅ Verify synchronization happens:

  • Check logs for "Synchronizing network from database..."
  • Check logs for "Network synchronization completed"
  • Progress bar updates to 7% during sync

✅ Verify values applied:

  • Compare EPANET results with expected values
  • Check that database values override INP values
  • Verify scenario-specific options are used

✅ Verify error handling:

  • Sync failures logged but don't crash simulation
  • Partial sync better than no sync
  • Clear error messages

6. Limitations​

6.1 Current Limitations​

Cannot Add New Elements:

  • Cannot add new junctions, pipes, etc. via database
  • Can only update existing elements from INP file
  • INP file defines network topology

String Parsing Pending:

  • Time parameters stored as strings ("24 HOURS")
  • Need parsing logic to convert to seconds
  • Currently skipped in sync

Pattern/Curve Creation:

  • Cannot add new patterns or curves
  • Can only update existing ones
  • Pattern/Curve IDs must exist in INP

6.2 Design Rationale​

Why INP as baseline:

  • Network topology is complex (connectivity graph)
  • EPANET requires valid network structure
  • Easier to load valid baseline then update values
  • Avoids topology validation complexity

Why not sync new elements:

  • Adding nodes/links requires topology validation
  • Must ensure network connectivity
  • Graph algorithms complex and error-prone
  • Future enhancement if needed

7. Future Enhancements​

7.1 Time Parameter Parsing​

Status: Planned Effort: Low (1-2 hours)

Parse time strings and apply to EPANET:

// Parse "24 HOURS" → 86400 seconds
// Parse "5 DAYS" → 432000 seconds
// Parse "30 MINUTES" → 1800 seconds

7.2 Topology Updates​

Status: Future Consideration Effort: High (weeks)

Support adding/removing network elements:

  • Add new junctions/pipes via database
  • Validate network connectivity
  • Update topology in EPANET
  • Complex graph algorithms required

7.3 Differential Sync​

Status: Optimization Idea Effort: Medium (days)

Only sync changed records:

  • Track modified timestamp on entities
  • Sync only records changed since last sync
  • Performance improvement for large networks

Summary​

✅ Implemented:

  • NetworkSynchronizationService with comprehensive sync methods
  • Automatic sync before every simulation
  • Scenario-specific options handling
  • Error handling and logging
  • Audit of all sync methods

✅ Benefits:

  • Database is source of truth
  • Real-time updates reflected in simulations
  • No manual INP export required
  • Improved workflow efficiency

✅ Production Ready:

  • Error handling prevents crashes
  • Logging for troubleshooting
  • Backward compatible (INP still works)
  • Tested with multiple scenarios

⏸️ Future Work:

  • Time parameter string parsing
  • Consider topology updates if needed
  • Performance optimization for large networks

Related Documentation:

  • Implementation Plan: Available in archive (detailed step-by-step)
  • EPANET API Reference: See EpanetWrapper documentation
  • Database Schema: See architecture documentation

Status: ✅ Complete and Production Ready Last Updated: October 17, 2025