///<summary>Get the shortest time step to activate the control.</summary> private long GetRequiredTimeStep(EpanetNetwork net, long htime, long tstep) { long t = 0; // Node control if (Node != null) { if (!(Node is SimulationTank)) // Check if node is a tank { return(tstep); } double h = _node.SimHead; // Current tank grade double q = _node.SimDemand; // Flow into tank if (Math.Abs(q) <= Constants.QZERO) { return(tstep); } if ((h < Grade && Type == ControlType.HILEVEL && q > 0.0) || // Tank below hi level & filling (h > Grade && Type == ControlType.LOWLEVEL && q < 0.0)) // Tank above low level & emptying { SimulationTank tank = (SimulationTank)Node; double v = tank.FindVolume(net.FieldsMap, Grade) - tank.SimVolume; t = (long)Math.Round(v / q); // Time to reach level } } // Time control if (Type == ControlType.TIMER) { if (Time > htime) { t = Time - htime; } } // Time-of-day control if (Type == ControlType.TIMEOFDAY) { long t1 = (htime + net.Tstart) % Constants.SECperDAY; long t2 = Time; if (t2 >= t1) { t = t2 - t1; } else { t = Constants.SECperDAY - t1 + t2; } } // Revise time step if (t > 0 && t < tstep) { SimulationLink link = Link; // Check if rule actually changes link status or setting if (link != null && (link.Type > LinkType.PIPE && link.SimSetting != Setting) || (link.SimStatus != Status)) { tstep = t; } } return(tstep); }
/// <summary>Implements simple controls based on time or tank levels.</summary> public static int StepActions( TraceSource log, EpanetNetwork net, IEnumerable <SimulationControl> controls, long htime) { int setsum = 0; // Examine each control statement foreach (SimulationControl control in controls) { bool reset = false; // Make sure that link is defined if (control.Link == null) { continue; } // Link is controlled by tank level var node = control.Node as SimulationTank; if (node != null) { double h = node.SimHead; double vplus = Math.Abs(node.SimDemand); SimulationTank tank = node; double v1 = tank.FindVolume(net.FieldsMap, h); double v2 = tank.FindVolume(net.FieldsMap, control.Grade); if (control.Type == ControlType.LOWLEVEL && v1 <= v2 + vplus) { reset = true; } if (control.Type == ControlType.HILEVEL && v1 >= v2 - vplus) { reset = true; } } // Link is time-controlled if (control.Type == ControlType.TIMER) { if (control.Time == htime) { reset = true; } } // Link is time-of-day controlled if (control.Type == ControlType.TIMEOFDAY) { if ((htime + net.Tstart) % Constants.SECperDAY == control.Time) { reset = true; } } // Update link status & pump speed or valve setting if (reset) { StatType s1, s2; SimulationLink link = control.Link; if (link.SimStatus <= StatType.CLOSED) { s1 = StatType.CLOSED; } else { s1 = StatType.OPEN; } s2 = control.Status; double k1 = link.SimSetting; double k2 = k1; if (control.Link.Type > LinkType.PIPE) { k2 = control.Setting; } if (s1 != s2 || k1 != k2) { link.SimStatus = s2; link.SimSetting = k2; if (net.StatFlag != StatFlag.NO) { LogControlAction(log, control, htime); } setsum++; } } } return(setsum); }
/// <summary> /// Updates next time step by checking if any rules will fire before then; /// also updates tank levels. /// </summary> public static void MinimumTimeStep( EpanetNetwork net, TraceSource log, SimulationRule[] rules, List <SimulationTank> tanks, long htime, long tstep, double dsystem, out long tstepOut, out long htimeOut) { long dt; // Normal time increment for rule evaluation long dt1; // Actual time increment for rule evaluation // Find interval of time for rule evaluation long tnow = htime; // Start of time interval for rule evaluation long tmax = tnow + tstep; // End of time interval for rule evaluation //If no rules, then time increment equals current time step if (rules.Length == 0) { dt = tstep; dt1 = dt; } else { // Otherwise, time increment equals rule evaluation time step and // first actual increment equals time until next even multiple of // Rulestep occurs. dt = net.RuleStep; dt1 = net.RuleStep - tnow % net.RuleStep; } // Make sure time increment is no larger than current time step dt = Math.Min(dt, tstep); dt1 = Math.Min(dt1, tstep); if (dt1 == 0) { dt1 = dt; } // Step through time, updating tank levels, until either // a rule fires or we reach the end of evaluation period. // // Note: we are updating the global simulation time (Htime) // here because it is used by functions in RULES.C(this class) // to evaluate rules when checkrules() is called. // It is restored to its original value after the // rule evaluation process is completed (see below). // Also note that dt1 will equal dt after the first // time increment is taken. do { htime += dt1; // Update simulation clock SimulationTank.StepWaterLevels(tanks, net.FieldsMap, dt1); // Find new tank levels if (Check(net, rules, log, htime, dt1, dsystem) != 0) { break; // Stop if rules fire } dt = Math.Min(dt, tmax - htime); // Update time increment dt1 = dt; // Update actual increment }while (dt > 0); //Compute an updated simulation time step (*tstep) // and return simulation time to its original value tstepOut = htime - tnow; htimeOut = tnow; }
/// <summary>Closes link flowing into full or out of empty tank.</summary> private void TankStatus(EpanetNetwork net) { double q = flow; SimulationNode n1 = First; SimulationNode n2 = Second; // Make node n1 be the tank if (!(n1 is SimulationTank)) { if (!(n2 is SimulationTank)) { return; // neither n1 or n2 is a tank } // N2 is a tank, swap ! SimulationNode n = n1; n1 = n2; n2 = n; q = -q; } double h = n1.SimHead - n2.SimHead; SimulationTank tank = (SimulationTank)n1; // Skip reservoirs & closed links if (tank.Area == 0.0 || status <= StatType.CLOSED) { return; } // If tank full, then prevent flow into it if (tank.SimHead >= tank.Hmax - net.HTol) { //Case 1: Link is a pump discharging into tank if (Type == LinkType.PUMP) { if (Second == n1) { status = StatType.TEMPCLOSED; } } else if (CvStatus(net, StatType.OPEN, h, q) == StatType.CLOSED) { // Case 2: Downstream head > tank head status = StatType.TEMPCLOSED; } } // If tank empty, then prevent flow out of it if (tank.SimHead <= tank.Hmin + net.HTol) { // Case 1: Link is a pump discharging from tank if (Type == LinkType.PUMP) { if (First == n1) { status = StatType.TEMPCLOSED; } } // Case 2: Tank head > downstream head else if (CvStatus(net, StatType.CLOSED, h, q) == StatType.OPEN) { status = StatType.TEMPCLOSED; } } }
/// <summary>Checks if numerical condition on a variable is true.</summary> private bool CheckValue(FieldsMap fMap, double dsystem) { const double TOL = 0.001D; double x; SimulationLink link = _object as SimulationLink; SimulationNode node = _object as SimulationNode; switch (_variable) { case Varwords.DEMAND: if ((Objects)_object == Objects.SYSTEM) { x = dsystem * fMap.GetUnits(FieldType.DEMAND); } else { x = node.SimDemand * fMap.GetUnits(FieldType.DEMAND); } break; case Varwords.HEAD: case Varwords.GRADE: x = node.SimHead * fMap.GetUnits(FieldType.HEAD); break; case Varwords.PRESSURE: x = (node.SimHead - node.Elevation) * fMap.GetUnits(FieldType.PRESSURE); break; case Varwords.LEVEL: x = (node.SimHead - node.Elevation) * fMap.GetUnits(FieldType.HEAD); break; case Varwords.FLOW: x = Math.Abs(link.SimFlow) * fMap.GetUnits(FieldType.FLOW); break; case Varwords.SETTING: if (double.IsNaN(link.SimSetting)) { return(false); } x = link.SimSetting; switch (link.Type) { case LinkType.PRV: case LinkType.PSV: case LinkType.PBV: x *= fMap.GetUnits(FieldType.PRESSURE); break; case LinkType.FCV: x *= fMap.GetUnits(FieldType.FLOW); break; } break; case Varwords.FILLTIME: { if (!(_object is SimulationTank)) { return(false); } SimulationTank tank = (SimulationTank)_object; if (tank.IsReservoir) { return(false); } if (tank.SimDemand <= Constants.TINY) { return(false); } x = (tank.Vmax - tank.SimVolume) / tank.SimDemand; break; } case Varwords.DRAINTIME: { if (!(_object is SimulationTank)) { return(false); } SimulationTank tank = (SimulationTank)_object; if (tank.IsReservoir) { return(false); } if (tank.SimDemand >= -Constants.TINY) { return(false); } x = (tank.Vmin - tank.SimVolume) / tank.SimDemand; break; } default: return(false); } switch (_relop) { case Operators.EQ: if (Math.Abs(x - _value) > TOL) { return(false); } break; case Operators.NE: if (Math.Abs(x - _value) < TOL) { return(false); } break; case Operators.LT: if (x > _value + TOL) { return(false); } break; case Operators.LE: if (x > _value - TOL) { return(false); } break; case Operators.GT: if (x < _value - TOL) { return(false); } break; case Operators.GE: if (x < _value + TOL) { return(false); } break; } return(true); }