using MathNet.Numerics.LinearAlgebra.Double; // 引入 Math.NET Numerics 库进行多项式拟合
|
using MathNet.Numerics;
|
using MathNet.Numerics.LinearAlgebra;
|
using System;
|
using System.Collections.Generic;
|
using System.Drawing;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Windows.Forms.DataVisualization.Charting;
|
using CommonBase;
|
|
namespace Hydro.MapView
|
{
|
public class Dataset
|
{
|
public static List<string> listString = new List<string>() { "流量扬程曲线", "流量功率曲线", "流量效率曲线" };
|
public Dataset(string name, PumpViewModel pump, int Degree = 2)
|
{
|
Name = name;
|
this.pump = pump;
|
this.degree = Degree;
|
}
|
|
public override string ToString()
|
{
|
StringBuilder txt = new StringBuilder();
|
foreach (PointF point in _data)
|
{
|
txt.AppendLine($"{Name}\t{point.X}\t{point.Y}");
|
}
|
return txt.ToString();
|
}
|
//public Dataset()
|
//{
|
|
//}
|
public Color Color
|
{
|
get
|
{
|
switch (Name)
|
{
|
case "流量扬程曲线":
|
return Color.Blue;
|
//break;
|
case "流量功率曲线":
|
return Color.Orange;
|
//break;
|
case "流量效率曲线":
|
return Color.Red;
|
//break;
|
default:
|
return Color.Blue;
|
}
|
|
}
|
|
}
|
public string Name { get; set; }
|
|
|
public List<PointF> _data = new List<PointF>();
|
|
int FitTimes = 2;
|
|
public List<PointF> Data
|
{
|
get
|
{
|
|
if (pump == null || pump.额定转速 == 0) return _data;
|
float eg = (float)pump.当前转速 / (float)pump.额定转速;
|
if (eg >= 1) return _data;
|
else
|
{
|
switch (Name)
|
{
|
case "流量扬程曲线":
|
return _data.Select(p => { p.X *= eg; p.Y *= eg * eg; return p; }).ToList();
|
break;
|
case "流量功率曲线":
|
return _data.Select(p => { p.X *= eg; p.Y *= eg * eg * eg; return p; }).ToList();
|
break;
|
case "流量效率曲线":
|
var c1 = pump.Datasets["流量扬程曲线"];
|
var c2 = pump.Datasets["流量功率曲线"];
|
if (c1.IsFitted && c2.IsFitted && c1.Data.Count == c2.Data.Count)
|
{
|
List<PointF> points = new List<PointF>();
|
for (int i = 0; i < c1.Data.Count; i++)
|
{
|
float x = c1.Data[i].X;
|
float y = x * c1.Data[i].Y / c2.Data[i].Y / 3.6f;
|
points.Add(new PointF(x, y));
|
|
//功率=流量* 扬程 / 效率 / 3.6f
|
//效率=流量* 扬程 / 功率 / 3.6f
|
}
|
return points;
|
}
|
else
|
{
|
return _data;
|
}
|
|
break;
|
default:
|
return _data;
|
}
|
|
|
|
}
|
}
|
set
|
{
|
//float eg = (float)pump.当前转速 / (float)pump.额定转速;
|
//if (eg < 1)
|
//{
|
// MessageBox.Show("非额定转速,拒绝修改");
|
// return;
|
//}
|
IsFitted = false;
|
_data = value;
|
}
|
}
|
|
public int degree { get { return FitTimes; } set { if (FitTimes != value) this.IsFitted = false; FitTimes = value; } }
|
|
public List<double> ForumParams = null;
|
|
public DenseVector coefficients = null; // 存储多项式系数
|
|
public static double ErrNum { get { return -1; } }
|
|
public static double stepNum { get { return 800; } }
|
|
private double step { get { return (range_X.Max - range_X.Min) / stepNum; } }
|
|
|
[NonSerialized]
|
public PumpViewModel pump = null;
|
|
|
|
/// <summary>
|
/// 不需要读取
|
/// </summary>
|
public string Legend
|
{
|
get
|
{
|
switch (Name)
|
{
|
case "流量扬程曲线":
|
return "Legend1";
|
case "流量功率曲线":
|
return "Legend1";
|
case "流量效率曲线":
|
return "Legend1";
|
|
}
|
return "Legend1";
|
}
|
}
|
|
public AxisType axisType
|
{
|
get
|
{
|
switch (Name)
|
{
|
case "流量扬程曲线":
|
return AxisType.Primary;
|
default:
|
return AxisType.Secondary;
|
|
}
|
}
|
}
|
|
public void CurveFit()
|
{
|
|
if (!HasData)
|
{
|
IsFitted = false;
|
return;
|
}
|
|
coefficients = MathNet.Numerics.Fit.Polynomial(Data.Select(p => (double)p.X).ToArray(), Data.Select(p => (double)p.Y).ToArray(), degree);
|
|
if (range_X == null)
|
{
|
range_X = new Range(double.MaxValue, double.MinValue);
|
Data.ForEach(p => {
|
if (range_X.Min > p.X) range_X.Min = p.X;
|
if (range_X.Max < p.X) range_X.Max = p.X;
|
});
|
}
|
IsFitted = true;
|
}
|
public double Evaluate(double x)
|
|
{
|
if (!IsFitted) return ErrNum;
|
// 计算多项式在点 x 处的取值
|
double y = 0;
|
for (int i = 0; i < coefficients.Count; i++)
|
{
|
y += coefficients[i] * Math.Pow(x, i);
|
}
|
return y;
|
}
|
|
|
// 定义 solve 方法来解多项式方程
|
//double Solve(double y)
|
//{
|
// FittedCurve
|
//}
|
double Solve(double y)
|
{
|
List<PointF> points = FittedCurve.ToList();
|
PointF prevPoint = PointF.Empty;
|
PointF nextPoint = PointF.Empty;
|
|
foreach (var point in points)
|
{
|
if (point.Y == y)
|
{
|
return point.X;
|
}
|
else if (point.Y > y)
|
{
|
nextPoint = point;
|
}
|
else
|
{
|
prevPoint = point;
|
break;
|
}
|
}
|
|
if (prevPoint.IsEmpty)
|
{
|
return points[points.Count - 1].X; //throw new Exception("No valid interpolation range found.");
|
}
|
else if (nextPoint.IsEmpty)
|
{
|
return points[0].X;
|
}
|
|
double xDiff = nextPoint.X - prevPoint.X;
|
double yDiff = nextPoint.Y - prevPoint.Y;
|
double slope = yDiff / xDiff;
|
double x = prevPoint.X + (y - prevPoint.Y) / slope;
|
if (double.IsNaN(x)) return ErrNum;
|
else
|
return x;
|
}
|
|
public double Evaluate_Solve(double y)
|
{
|
if (!IsFitted) return ErrNum;
|
if (degree == 2) // 多项式次数
|
{
|
if (!is_Yvalue_validate(y)) return ErrNum;
|
|
|
double a = coefficients[degree]; // 二次项系数
|
double b = coefficients[degree - 1]; // 一次项系数
|
double c = coefficients[0] - y; // 常数项系数减去给定的 y 值
|
|
// 求解二次方程 ax^2 + bx + c = 0
|
double delta = b * b - 4 * a * c;
|
|
double x1 = (-b + Math.Sqrt(delta)) / (2 * a);
|
double x2 = (-b - Math.Sqrt(delta)) / (2 * a);
|
|
// 取两个解中符合实际情况的那个(可以根据具体应用场景来确定)
|
double x = (x2 >= x1) ? x2 : x1;
|
if (double.IsNaN(x)) return ErrNum;
|
else
|
return x;
|
}
|
else
|
{
|
return Solve(y);
|
}
|
}
|
|
|
public string getForum
|
{
|
get
|
{
|
if (coefficients == null) return null;
|
// 生成多项式的字符串表示形式
|
string polyStr = "y=";
|
for (int i = coefficients.Count - 1; i >= 0; i--)
|
{
|
if (coefficients[i] == 0) continue;
|
if (i == coefficients.Count - 1)
|
{
|
polyStr += coefficients[i].ToString() + "x^" + i;
|
}
|
else if (i == 1)
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString() + "x";
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString() + "x";
|
}
|
}
|
else if (i == 0)
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString();
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString();
|
}
|
}
|
else
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString() + "x^" + i;
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString() + "x^" + i;
|
}
|
}
|
}
|
if (polyStr == "") polyStr = "0";
|
|
if (degree == 2)
|
{
|
double a = coefficients[degree];
|
double b = coefficients[degree - 1];
|
double c = coefficients[0];
|
double delta = b * b - 4 * a * c;
|
double minExtremePoint = -b / (2 * a); // 计算导函数的零点
|
polyStr += $" maxX:{minExtremePoint.ToString("0.0")}";
|
}
|
|
return polyStr;
|
}
|
|
}
|
public double[] getForumNum
|
{
|
get
|
{
|
if (coefficients == null) return null;
|
List<double> list = new List<double>();
|
for (int i = coefficients.Count - 1; i >= 0; i--)
|
{
|
list.Add(coefficients[i]);
|
}
|
return list.ToArray();
|
}
|
|
}
|
public double YMax
|
{
|
get
|
{
|
if (!IsFitted) return ErrNum;
|
double a = coefficients[degree];
|
double b = coefficients[degree - 1];
|
double c = coefficients[0];
|
double delta = b * b - 4 * a * c;
|
double minExtremePoint = -b / (2 * a); // 计算导函数的零点
|
double maxValue = 0;
|
if (a < 0)
|
|
maxValue = -delta / (4 * a) + c;
|
else
|
maxValue = a * minExtremePoint * minExtremePoint + b * minExtremePoint + c; // 计算最小值
|
return maxValue;
|
}
|
}
|
public double YdataMax
|
{
|
get
|
{
|
double max = -99999;
|
foreach (var p in Data)
|
{
|
if (max < p.Y) max = p.Y;
|
}
|
return max;
|
}
|
}
|
public bool is_Yvalue_validate(double y)
|
{
|
if (!IsFitted) return false;
|
double a = coefficients[degree];
|
if (a < 0)
|
|
return y <= YMax;
|
else
|
return y >= YMax;
|
}
|
|
/// <summary>
|
/// X显示的范围
|
/// </summary>
|
public Range range_X;
|
|
|
/// <summary>
|
/// Y显示的范围
|
/// </summary>
|
public Range range_Y;
|
|
|
|
|
public bool HasData { get { return Data.Count >= 3; } }
|
|
public bool IsFitted = false;
|
public List<PointF> FittedCurve
|
{
|
get
|
{
|
if (range_X == null) return null;
|
List<PointF> doubles = new List<PointF>();
|
for (double x = range_X.Min; x <= range_X.Max; x += step)
|
{
|
double y = Evaluate(x);
|
//chart.Series[1].Points.AddXY(x, y);
|
doubles.Add(new PointF((float)x, (float)y));
|
}
|
return doubles;
|
|
}
|
|
}
|
public List<PointF> FittedCurvebyRange(Range range)
|
{
|
List<PointF> doubles = new List<PointF>();
|
|
if (range == null)
|
{
|
range = new Range();
|
range.Min = range_X.Min;
|
range.Max = range_X.Max;
|
}
|
var step = (range.Max - range.Min) / stepNum;
|
for (double x = range.Min; x <= range.Max; x += step)
|
{
|
double y = Evaluate(x);
|
//chart.Series[1].Points.AddXY(x, y);
|
doubles.Add(new PointF((float)x, (float)y));
|
}
|
return doubles;
|
}
|
|
|
}
|
}
|