using IStation.Epanet.Enums; using System.Collections; namespace IStation.Epanet.Network.Structures { ///2D graph used to map volume, pump, efficiency and head loss curves. public sealed class Curve : Element, IEnumerable { private readonly List _points = new List(); public Curve(string name) : base(name) { } public override ElementType ElementType => ElementType.CURVE; /// Computes intercept and slope of head v. flow curve at current flow. /// /// Flow value. /// Head at zero flow (y-intercept). /// dHead/dFlow (slope). public void GetCoeff(FieldsMap fMap, double q, out double h0, out double r) { q *= fMap.GetUnits(FieldType.FLOW); int npts = _points.Count; var k2 = 0; while (k2 < npts && _points[k2].X < q) k2++; if (k2 == 0) k2++; else if (k2 == npts) k2--; int k1 = k2 - 1; r = (_points[k2].Y - _points[k1].Y) / (_points[k2].X - _points[k1].X); h0 = _points[k1].Y - r * _points[k1].X; h0 /= fMap.GetUnits(FieldType.HEAD); r *= fMap.GetUnits(FieldType.FLOW) / fMap.GetUnits(FieldType.HEAD); } ///Curve type. public CurveType Type { get; set; } //TODO: parse it correctly /// Compute the linear interpolation of a 2d cartesian graph. /// The abscissa value. /// The interpolated value. public double Interpolate(double x) { var p = _points; int m = _points.Count - 1; if (x <= p[0].X) return p[0].Y; for (int i = 1; i <= m; i++) { if (p[i].X >= x) { double dx = p[i].X - p[i - 1].X; double dy = p[i].Y - p[i - 1].Y; if (Math.Abs(dx) < Constants.TINY) return p[i].Y; return p[i].Y - (p[i].X - x) * dy / dx; } } return p[m].Y; } #region partial implementation of IList public void Add(double x, double y) { _points.Add(new EnPoint(x, y)); } IEnumerator IEnumerable.GetEnumerator() { return _points.GetEnumerator(); } public IEnumerator GetEnumerator() { return _points.GetEnumerator(); } public int Count => _points.Count; public EnPoint this[int index] => _points[index]; #endregion } }