The challenge of simulating a river network with sequential diversions, return flows, and competing demands governed by priorities is non-trivial. By moving the allocation decision to an external optimizer, we can achieve optimal water delivery in a way that is challenging to replicate with explicit rules-based logic.
The Challenge: Sequential Diversions with Return Flows and Priorities
Our case study involves a river system with three reaches (N1, N2, N3) and three diversion points (Div A, Div B, Div C). The complexity arises from two factors:
- Return Flows: Diversions A and B are inefficient; a portion of the water is consumed and the remainder returns to the river downstream. The return flow from A re-enters Reach N2, and the return flow from B re-enters Reach N3.
- Priorities: The demands have a priority order that must be respected during allocation: Div B (Highest) > Div A (Medium) > Div C (Lowest).
When inflows are low, we need a mechanism to simultaneously determine the maximum possible flow to Div A, Div B, and Div C while respecting the sequential flow limits, accounting for the downstream effects of return flows, and strictly adhering to the priority order.
Flow diagram from the GoldSim model |
GSPy and Linear Programming
I'm using the GSPy bridge, a DLL that operates dynamically with a GoldSim model to send the current state of the system to the Python script:
- Inflows: T1, T2, T3
- Demands: Target_A, Target_B, Target_C
- Efficiencies: EA, EB
The Python script, river_optimizer.py
, uses the PuLP library to formulate and solve the allocation problem as a Weighted Deficit Minimization Linear Program.
PuLP Formulation
In the Python script, we define three sets of variables: the Decision Variables Div_X, which are the flows we're solving for, and the Shortage Variables SX.
1. The Objective Function (Minimization)
To enforce the priority order (B > A > C), we assign weights to the Shortage variables. Minimizing this function forces the solver to first satisfy the highest priority demand (B), then A, then C.
Minimize (3.0 * S_B) + (2.0 * S_A) + (1.0 * S_C) + (0.01 * Q_Exit)
The small weighting on the exit flow Q_Exit is included for stability, but the shortage weights dominate the solution.
2. Constraints (Demand & Mass Balance)
The constraints ensure that the solver respects the physical limits and the demand targets:
Demand Constraints: For each demand, the flow diverted plus any shortage must equal the target:
Div_X + S_X = Target_X
Reach Limits (N1): The diversion cannot exceed the inflow at that point:
Div_A <= T_1
Reach Limits (N2): Flow available at N2 is the remainder from N1 plus the tributary T_2 and the return flow R_A:
Div_B <= (T_1 - Div_A) + T_2 + R_A
Reach Limits (N3): Flow available at N3 is the remainder from N2 plus the tributary T_3 and the return flow R_B:
Div_C <= (N2 Flow - Div_B) + T_3 + R_B
GSPy Integration and Interface
The GoldSim-Python bridge handles the data transfer.
The GSPy Interface
The External element in GoldSim is configured to send 8 scalar inputs and receive 3 scalar outputs. The order in the GoldSim interface must exactly match the order defined in the GSPy JSON configuration.
Data Flow | GoldSim Variable | Python Index / Return | Type |
---|---|---|---|
Input | T1, T2, T3 | args[0], args[1], args[2] |
Scalar |
Target_A, Target_B, Target_C | args[3], args[4], args[5] |
Scalar | |
E_A, E_B | args[6], args[7] |
Scalar | |
... | |||
Output | Div_A, Div_B, Div_C | return (opt_A, opt_B, opt_C) |
Scalar |
Performance Tip
For production models that call the optimizer hundreds of thousands of times, remember to set the log_level
in your GSPy JSON configuration to 0
(ERROR only). This minimizes the log file overhead and significantly improves simulation speed.
Conclusion
By integrating a dedicated optimization solver, we've modeled a complex, priority-based water allocation system with return flows, which is a challenging problem for traditional simulation methods. This effective integration between GoldSim, GSPy, and PuLP allows the GoldSim model to ask, "What is the best allocation?" at every time step, moving from rules-based simulation to true dynamic optimization.
Time History result plots from the model |
This approach is flexible and can be adapted to solve various resource allocation problems, from power dispatch to material blending, all within the robust probabilistic framework of GoldSim.
Want to try this model?
You can download the complete example package (including the GoldSim model, the GSPy configuration, and the Python PuLP script) from our cloud storage here: Download: GSPy PuLP Optimization Example (ZIP)
Note on Setup: You must have a 64-bit Python installation (3.8+ recommended) and the NumPy and PuLP packages installed. The downloaded DLL and JSON file must have the same names and be in the same directory to function correctly. Please see the GSPy documentation for more instructions on configuration and troubleshooting Python paths.
No comments:
Post a Comment