lx
2025-09-05 0b108f56b2d0c35d01ee361dc593119ac52a14e1
ErrorAnalysis.Service/ErrorRatioCalc.cs
@@ -1,7 +1,10 @@
using ErrorAnalysis.Repository;
using ErrorAnalysis.Repository.Entity;
using ErrorAnalysis.Service;
using ErrorAnalysis.Service.Model;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -10,24 +13,44 @@
{
    public class ErrorRatioCalc
    {
        public static ErrorRatioResult GetErrorRatioResult(string modelID, double porosity, double sw, double depth, bool lockSpeed, double speed, double targetErrorRatio, out double testSpeed, int pass = 0)
        private static (double slope, double intercept) _lineCoef;
        private static (double a, double b, double c) _parabolaCoef;
        public static ErrorRatioResult GetErrorRatioResult(string modelID, double porosity, double sw, double depth, bool lockSpeed, double speed, double yieldCounting, double targetErrorRatio, out double testSpeed, out ProcessingDataModel processingData, int pass = 0, double nearCofe = 0.65, double farCofe = 0.35)
        {
            processingData = new ProcessingDataModel { Depth = depth, NearPDEVCoef = nearCofe, FarPDEVCoef = farCofe, NearTO = yieldCounting };
            var result = new ErrorRatioResult
            {
                CWOL = new List<double[]>(),
                OWOL = new List<double[]>(),
                ErrorRatios = new List<ErrorRatio>()
            };
            result.COModel = RepositoryInstance.Instance.COModelRepository?.GetCOModel(modelID);
            (result.CWOL, result.OWOL) = GetOilWaterLine(modelID);
            processingData.OilLine = result.CWOL;
            processingData.WaterLine = result.OWOL;
            (processingData.OilLineOrigin, processingData.WaterLineOrgin) = GetOilWaterLine(modelID, true);
            processingData.FarSpecData = COMergeCalcService.GetFarSpectrum(modelID, porosity, sw);
            processingData.NearSpecData = COMergeCalcService.GetNearSpectrum(modelID, porosity, sw);
            var nearCROR = COMergeCalcService.GetNearCOORResult(modelID, porosity, sw);
            var farCROR = COMergeCalcService.GetFarCOORResult(modelID, porosity, sw);
            processingData.NearCR = nearCROR.Item1;
            processingData.NearOR = nearCROR.Item2;
            processingData.FarCR = farCROR.Item1;
            processingData.FarOR = farCROR.Item2;
            var mDelta = GetMDelta(porosity, out double oilPoint, out double waterPoint);
            processingData.OilPoint = oilPoint;
            processingData.WaterPoint = waterPoint;
            if (lockSpeed)
            {
                List<double[]> cWOL;
                List<double[]> oWOL;
                var firstErrorPassM = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, out cWOL, out oWOL);
                result.CWOL = cWOL;
                result.OWOL = oWOL;
                var mergePDEV = Math.Sqrt(160 * speed / (0.0762 * 36 * depth)) * Math.Sqrt((Math.Pow(nearCofe, 2) / yieldCounting * (Math.Pow(nearCROR.Item1 / nearCROR.Item2, 2)) * (1 / nearCROR.Item1 + 1 / nearCROR.Item2)) + (Math.Pow(farCofe, 2) / yieldCounting / 2.2 * (Math.Pow(farCROR.Item1 / farCROR.Item2, 2)) * (1 / farCROR.Item1 + 1 / farCROR.Item2)));
                var firstErrorPassM = new ErrorRatio { Pass = 1, ErrorRatioValue = mergePDEV / mDelta };
                result.ErrorRatios.Clear();
                result.ErrorRatios.Add(firstErrorPassM);
                if (firstErrorPassM.ErrorRatioValue > targetErrorRatio)
                {
                    var targetPass = Convert.ToInt32(Math.Ceiling(Math.Pow(firstErrorPassM.ErrorRatioValue / targetErrorRatio, 2)));
@@ -36,21 +59,29 @@
                        result.ErrorRatios.Add(new ErrorRatio { Pass = i, ErrorRatioValue = firstErrorPassM.ErrorRatioValue / Math.Sqrt(i) });
                    }
                }
                processingData.Speed = speed;
            }
            else
            {
                var firstTargetErrorRatio = targetErrorRatio * Math.Sqrt(pass);
                List<double[]> cWOL;
                List<double[]> oWOL;
                var firstErrorPass = new ErrorRatio();
                speed = 2;
                do
                {
                    firstErrorPass = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, out cWOL, out oWOL);
                    result.CWOL = cWOL;
                    result.OWOL = oWOL;
                    speed -= 0.01;
                } while (firstErrorPass.ErrorRatioValue != 0 && firstErrorPass.ErrorRatioValue > firstTargetErrorRatio);
                processingData.Speed = speed = Math.Pow(firstTargetErrorRatio /
                     (Math.Sqrt(160 / (0.0762 * depth * 36d)) *
                     1d / mDelta) /
                     Math.Sqrt((Math.Pow(nearCofe, 2) / yieldCounting * (Math.Pow(nearCROR.Item1 / nearCROR.Item2, 2)) * (1 / nearCROR.Item1 + 1 / nearCROR.Item2)) + (Math.Pow(farCofe, 2) / yieldCounting / 2.2 * (Math.Pow(farCROR.Item1 / farCROR.Item2, 2)) * (1 / farCROR.Item1 + 1 / farCROR.Item2)))
                     , 2);
                var firstErrorPass = new ErrorRatio { Pass = 1, ErrorRatioValue = firstTargetErrorRatio };
                //var firstErrorPass = new ErrorRatio();
                //speed = 0.05;
                //do
                //{
                //    firstErrorPass = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, yieldCounting, nearCofe, farCofe);
                //    result.CWOL = cWOL;
                //    result.OWOL = oWOL;
                //    speed -= 0.0001;
                //} while (firstErrorPass.ErrorRatioValue != 0 && firstErrorPass.ErrorRatioValue > firstTargetErrorRatio);
                result.ErrorRatios.Clear();
                result.ErrorRatios.Add(firstErrorPass);
                for (int i = 2; i <= pass; i++)
@@ -64,95 +95,146 @@
        }
        private static ErrorRatio GetFirstErrorRatio(string modelID, double porosity, double sw, double speed, double depth, out List<double[]> cWOL, out List<double[]> oWOL)
        private static ErrorRatio GetFirstErrorRatio(string modelID, double porosity, double sw, double speed, double depth, double yieldCounting, double nearCofe, double farCofe, out double oilPoint, out double waterPoint)
        {
            if (porosity > 40)
                throw new InvalidDataException("Porosity value out of range!");
            cWOL = new List<double[]>();
            oWOL = new List<double[]>();
            var cWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 0);
            var oWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 100);
            var mergePDEV = PDEVCalcService.GetMergePDEV(modelID, porosity, sw, speed, depth);
            var wolResType = cWolRes?.GetType();
            try
            {
                //if (sw > 0 && sw < 100)
                //{
                //    foreach (var wolProperty in wolResType.GetProperties())
                //    {
                //        if (wolProperty.Name.Contains("WLPu"))
                //        {
                //            //if (wolProperty.Name != "WLPu0")
                //            //{
                //            var interC = Utility.Interpolate(sw, 100, 0, 0, (double)wolProperty.GetValue(cWolRes));
                //            var interO = Utility.Interpolate(sw, 0, 0, 100, (double)wolProperty.GetValue(oWolRes));
                //            wolProperty.SetValue(cWolRes, interC);
                //            wolProperty.SetValue(oWolRes, interO);
                //            //}
                //            //else
                //            //{
                //            //    var interC = Utility.Interpolate(sw, 100, 0, 0, (double)wolProperty.GetValue(cWolRes));
                //            //    var interO = Utility.Interpolate(sw, 0, 0, 100, (double)wolProperty.GetValue(oWolRes));
                //            //    var interVal = Utility.Interpolate(sw, 0, interC, 100, interO);
                //            //    wolProperty.SetValue(cWolRes, interVal);
                //            //    wolProperty.SetValue(oWolRes, interVal);
                //            //}
                var mDelta = GetMDelta(porosity, out oilPoint, out waterPoint);
                //        }
                //    }
                //}
                var mergePDEVOld = PDEVCalcService.GetMergePDEV(modelID, porosity, sw, speed, depth, yieldCounting, nearCofe, farCofe);
                foreach (var wolProperty in wolResType.GetProperties())
                {
                    if (wolProperty.Name.Contains("WLPu"))
                    {
                        cWOL.Add([Convert.ToDouble(wolProperty.Name.Replace("WLPu", "")), Convert.ToDouble(wolProperty.GetValue(cWolRes))]);
                        oWOL.Add([Convert.ToDouble(wolProperty.Name.Replace("WLPu", "")), Convert.ToDouble(wolProperty.GetValue(oWolRes))]);
                    }
                }
                //var nearCROR = COMergeCalcService.GetNearCOORResult(modelID, porosity, sw);
                //var farCROR = COMergeCalcService.GetFarCOORResult(modelID, porosity, sw);
                double cRes = 0;
                double oRes = 0;
                //var mergePDEV = Math.Sqrt(160 * speed / (0.0762 * 36 * depth)) * Math.Sqrt((Math.Pow(nearCofe, 2) / yieldCounting * (Math.Pow(nearCROR.Item1 / nearCROR.Item2, 2)) * (1 / nearCROR.Item1 + 1 / nearCROR.Item2)) + (Math.Pow(farCofe, 2) / yieldCounting / 2.2 * (Math.Pow(farCROR.Item1 / farCROR.Item2, 2)) * (1 / farCROR.Item1 + 1 / farCROR.Item2)));
                if (porosity % 5 == 0)
                {
                    var poroFiledName = $"WLPu" + porosity;
                    var property = wolResType.GetProperty(poroFiledName);
                    cRes = Convert.ToDouble(property.GetValue(cWolRes));
                    oRes = Convert.ToDouble(property.GetValue(oWolRes));
                }
                else
                {
                    var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
                    var ceilingProperty = wolResType.GetProperty($"WLPu" + ceilingPorosity);
                    var ceilingC = Convert.ToDouble(ceilingProperty.GetValue(cWolRes));
                    var ceilingO = Convert.ToDouble(ceilingProperty.GetValue(oWolRes));
                    var floorPorosity = Math.Floor(porosity / 5) * 5;
                    var floorProperty = wolResType.GetProperty($"WLPu" + floorPorosity);
                    var floorC = Convert.ToDouble(floorProperty.GetValue(cWolRes));
                    var floorO = Convert.ToDouble(floorProperty.GetValue(oWolRes));
                    cRes = Utility.Interpolate(porosity, floorPorosity, floorC, ceilingPorosity, ceilingC);
                    oRes = Utility.Interpolate(porosity, floorPorosity, floorO, ceilingPorosity, ceilingO);
                }
                var errorRatio = mergePDEV /( cRes > oRes ? (cRes - oRes) : (oRes - cRes));
                var errorRatio = mergePDEVOld / mDelta;
                return new ErrorRatio { Pass = 1, ErrorRatioValue = errorRatio };
            }
            catch
            {
                cWOL = null;
                oWOL = null;
                oilPoint = 0;
                waterPoint = 0;
                return new ErrorRatio { Pass = 0, ErrorRatioValue = 0 };
            }
        }
        private static double GetMDelta(double porosity, out double oilPoint, out double waterPoint)
        {
            oilPoint = Utility.GetParabolaValue(porosity, _parabolaCoef.a, _parabolaCoef.b, _parabolaCoef.c);
            waterPoint = Utility.GetLineValue(porosity, _lineCoef.slope, _lineCoef.intercept);
            //var cWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 0);
            //var oWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 100);
            //var wolResType = cWolRes?.GetType();
            //double cRes = 0;
            //double oRes = 0;
            //if (porosity % 5 == 0)
            //{
            //    var poroFiledName = $"WLPu" + porosity;
            //    var property = wolResType.GetProperty(poroFiledName);
            //    cRes = Convert.ToDouble(property.GetValue(cWolRes));
            //    oRes = Convert.ToDouble(property.GetValue(oWolRes));
            //}
            //else
            //{
            //    var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
            //    var ceilingProperty = wolResType.GetProperty($"WLPu" + ceilingPorosity);
            //    var ceilingC = Convert.ToDouble(ceilingProperty.GetValue(cWolRes));
            //    var ceilingO = Convert.ToDouble(ceilingProperty.GetValue(oWolRes));
            //    var floorPorosity = Math.Floor(porosity / 5) * 5;
            //    var floorProperty = wolResType.GetProperty($"WLPu" + floorPorosity);
            //    var floorC = Convert.ToDouble(floorProperty.GetValue(cWolRes));
            //    var floorO = Convert.ToDouble(floorProperty.GetValue(oWolRes));
            //    cRes = Utility.Interpolate(porosity, floorPorosity, floorC, ceilingPorosity, ceilingC);
            //    oRes = Utility.Interpolate(porosity, floorPorosity, floorO, ceilingPorosity, ceilingO);
            //}
            return oilPoint - waterPoint;
        }
        private static (List<double[]>, List<double[]>) GetOilWaterLine(string modelID, bool original = false)
        {
            var cWOL = new List<double[]>();
            var oWOL = new List<double[]>();
            var fitModel = original ? GetModel(modelID) : GetFitModel(modelID);
            var cWolRes = fitModel.Item1;
            var oWolRes = fitModel.Item2;
            var wolResType = cWolRes?.GetType();
            foreach (var wolProperty in wolResType.GetProperties())
            {
                if (wolProperty.Name.Contains("WLPu"))
                {
                    cWOL.Add([Convert.ToDouble(wolProperty.Name.Replace("WLPu", "")), Convert.ToDouble(wolProperty.GetValue(cWolRes))]);
                    oWOL.Add([Convert.ToDouble(wolProperty.Name.Replace("WLPu", "")), Convert.ToDouble(wolProperty.GetValue(oWolRes))]);
                }
            }
            return (cWOL, oWOL);
        }
        private static (COWOLTable, COWOLTable) GetModel(string modelID)
        {
            var oilWol = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 0);
            var waterWol = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 100);
            return (oilWol, waterWol);
        }
        private static (COWOLTable, COWOLTable) GetFitModel(string modelID)
        {
            var oilWol = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 0);
            var waterWol = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 100);
            var wolResType = oilWol?.GetType();
            List<PointF> oilPoints = [];
            List<PointF> waterPoints = [];
            foreach (var wolProperty in wolResType.GetProperties())
            {
                if (wolProperty.Name.Contains("WLPu"))
                {
                    var porosity = float.Parse(wolProperty.Name.Replace("WLPu", ""));
                    var oilVal = (double)wolProperty.GetValue(oilWol);
                    var waterVal = (double)wolProperty.GetValue(waterWol);
                    oilPoints.Add(new PointF(porosity, (float)oilVal));
                    waterPoints.Add(new PointF(porosity, (float)waterVal));
                }
            }
            _lineCoef = Utility.FitLine(waterPoints.ToArray());
            _parabolaCoef = Utility.FitParabola(oilPoints.ToArray());
            //var texCoef = Utility.quadraticLine(oilPoints.Select(o => Convert.ToDouble(o.X)).ToArray(), oilPoints.Select(o => Convert.ToDouble(o.Y)).ToArray());
            double initDiff = 0;
            foreach (var wolProperty in wolResType.GetProperties())
            {
                if (wolProperty.Name.Contains("WLPu"))
                {
                    var porosity = double.Parse(wolProperty.Name.Replace("WLPu", ""));
                    var waterVal = Utility.GetLineValue(porosity, _lineCoef.slope, _lineCoef.intercept);
                    var oilVal = Utility.GetParabolaValue(porosity, _parabolaCoef.a, _parabolaCoef.b, _parabolaCoef.c);
                    //var testoilVal = Utility.GetParabolaValue(porosity, texCoef.a, texCoef.b, texCoef.c);
                    if (porosity == 0)
                    {
                        initDiff = oilVal - waterVal;
                    }
                    wolProperty.SetValue(waterWol, waterVal + initDiff);
                    wolProperty.SetValue(oilWol, oilVal);
                }
            }
            return (oilWol, waterWol);
        }
    }
}