///<summary>Creates new segments in outflow links from nodes.</summary> /// <param name="dt">Step duration in seconds.</param> private void Release(long dt) { foreach (QualityLink qL in _links) { if (qL.Flow == 0.0) { continue; } // Find flow volume released to link from upstream node // (NOTE: Flow volume is allowed to be > link volume.) QualityNode qN = qL.UpStreamNode; double q = Math.Abs(qL.Flow); double v = q * dt; // Include source contribution in quality released from node. double c = qN.Quality + qN.SourceContribution; // If link has a last seg, check if its quality // differs from that of the flow released from node. if (qL.Segments.Count > 0) { QualitySegment seg = qL.Segments.Last.Value; // Quality of seg close to that of node if (Math.Abs(seg.C - c) < _net.Ctol) { seg.C = (seg.C * seg.V + c * v) / (seg.V + v); seg.V += v; } else // Otherwise add a new seg to end of link { qL.Segments.AddLast(new QualitySegment(v, c)); } } else // If link has no segs then add a new one. { qL.Segments.AddLast(new QualitySegment(qL.LinkVolume, c)); } } }
///<summary>Initializes WQ solver system</summary> public QualitySim(EpanetNetwork net, TraceSource ignored) { // this.log = log; _net = net; _nodes = net.Nodes.Select(QualityNode.Create).ToArray(); _tanks = _nodes.OfType <QualityTank>().ToArray(); _juncs = _nodes.Where(x => !(x is QualityTank)).ToArray(); _links = net.Links.Select(n => new QualityLink(net.Nodes, _nodes, n)).ToArray(); /* * this.nodes = new List<QualityNode>(net.Nodes.Count); * this.links = new List<QualityLink>(net.Links.Count); * this.tanks = new List<QualityTank>(net.Tanks.Count()); * this.juncs = new List<QualityNode>(net.Junctions.Count()); * * foreach (Node n in net.Nodes) { * QualityNode qN = QualityNode.Create(n); * * this.nodes.Add(qN); * * var tank = qN as QualityTank; * * if (tank != null) * this.tanks.Add(tank); * else * this.juncs.Add(qN); * } * * foreach (Link n in net.Links) * this.links.Add(new QualityLink(net.Nodes, this.nodes, n)); * */ _bucf = 1.0; _tucf = 1.0; _reactflag = false; _qualflag = _net.QualFlag; if (_qualflag != QualType.NONE) { if (_qualflag == QualType.TRACE) { foreach (QualityNode qN in _nodes) { if (qN.Node.Name.Equals(_net.TraceNode, StringComparison.OrdinalIgnoreCase)) { _traceNode = qN; _traceNode.Quality = 100.0; break; } } } if (_net.Diffus > 0.0) { _sc = _net.Viscos / _net.Diffus; } else { _sc = 0.0; } _bucf = GetUcf(_net.BulkOrder); _tucf = GetUcf(_net.TankOrder); _reactflag = GetReactflag(); } _wbulk = 0.0; _wwall = 0.0; _wtank = 0.0; _wsource = 0.0; _htime = 0; _rtime = _net.RStart; _qtime = 0; _nperiods = 0; _elevUnits = _net.FieldsMap.GetUnits(FieldType.ELEV); }
///<summary>Initializes water quality segments.</summary> private void Initsegs() { foreach (QualityLink qL in _links) { qL.FlowDir = true; if (qL.Flow < 0.0) { qL.FlowDir = false; } qL.Segments.Clear(); double c; // Find quality of downstream node QualityNode j = qL.DownStreamNode; if (j is QualityTank) { c = ((QualityTank)j).Concentration; } else { c = j.Quality; } // Fill link with single segment with this quality qL.Segments.AddLast(new QualitySegment(qL.LinkVolume, c)); } // Initialize segments in tanks that use them foreach (QualityTank qT in _tanks) { Tank tank = (Tank)qT.Node; // Skip reservoirs & complete mix tanks if (tank.Type == NodeType.RESERV || tank.MixModel == MixType.MIX1) { continue; } double c = qT.Concentration; qT.Segments.Clear(); // Add 2 segments for 2-compartment model if (tank.MixModel == MixType.MIX2) { double v = Math.Max(0, qT.Volume - tank.V1Max); qT.Segments.AddLast(new QualitySegment(v, c)); v = qT.Volume - v; qT.Segments.AddLast(new QualitySegment(v, c)); } else { // Add one segment for FIFO & LIFO models double v = qT.Volume; qT.Segments.AddLast(new QualitySegment(v, c)); } } }
///<summary>Accumulates mass flow at nodes and updates nodal quality.</summary> /// <param name="dt">Step duration in seconds.</param> private void Accumulate(long dt) { // Re-set memory used to accumulate mass & volume foreach (QualityNode qN in _nodes) { qN.VolumeIn = 0; qN.MassIn = 0; qN.SourceContribution = 0; } foreach (QualityLink qL in _links) { QualityNode j = qL.DownStreamNode; // Downstream node if (qL.Segments.Count > 0) // Accumulate concentrations { j.MassIn = j.MassIn + qL.Segments.First.Value.C; j.VolumeIn = j.VolumeIn + 1; } j = qL.UpStreamNode; if (qL.Segments.Count > 0) // Upstream node { // Accumulate concentrations j.MassIn = j.MassIn + qL.Segments.Last.Value.C; j.VolumeIn = j.VolumeIn + 1; } } foreach (QualityNode qN in _nodes) { if (qN.VolumeIn > 0.0) { qN.SourceContribution = qN.MassIn / qN.VolumeIn; } } // Move mass from first segment of each pipe into downstream node foreach (QualityNode qN in _nodes) { qN.VolumeIn = 0; qN.MassIn = 0; } foreach (QualityLink qL in _links) { QualityNode j = qL.DownStreamNode; double v = Math.Abs(qL.Flow) * dt; while (v > 0.0) { if (qL.Segments.Count == 0) { break; } QualitySegment seg = qL.Segments.First.Value; // Volume transported from this segment is // minimum of flow volume & segment volume // (unless leading segment is also last segment) double vseg = seg.V; vseg = Math.Min(vseg, v); if (qL.Segments.Count == 1) { vseg = v; } double cseg = seg.C; j.VolumeIn = j.VolumeIn + vseg; j.MassIn = j.MassIn + vseg * cseg; v -= vseg; // If all of segment's volume was transferred, then // replace leading segment with the one behind it // (Note that the current seg is recycled for later use.) if (v >= 0.0 && vseg >= seg.V) { qL.Segments.RemoveFirst(); } else { seg.V -= vseg; } } } }