From 9827a8864ba0df0a4e148b4a7ac3225df7b80728 Mon Sep 17 00:00:00 2001
From: lx <ex_lixiang17@cosl.com.cn>
Date: 星期四, 07 八月 2025 18:09:41 +0800
Subject: [PATCH] upload

---
 ErrorAnalysis.Service/ErrorRatioCalc.cs     |  174 +++++++++++----------
 ErrorAnalysis.UI/FrmMain.cs                 |   31 ++-
 ErrorAnalysis.Service/COMergeCalcService.cs |  181 +++++++++++++++++++---
 ErrorAnalysis.UI/FrmMain.Designer.cs        |   35 +++
 ErrorAnalysis.Service/PDEVCalcService.cs    |    8 
 ErrorAnalysis.UI/Utility/UnitConvert.cs     |   13 +
 6 files changed, 309 insertions(+), 133 deletions(-)

diff --git a/ErrorAnalysis.Service/COMergeCalcService.cs b/ErrorAnalysis.Service/COMergeCalcService.cs
index 08c53d8..f3d90d5 100644
--- a/ErrorAnalysis.Service/COMergeCalcService.cs
+++ b/ErrorAnalysis.Service/COMergeCalcService.cs
@@ -1,4 +1,5 @@
 锘縰sing ErrorAnalysis.Repository;
+using Microsoft.VisualBasic;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -22,9 +23,9 @@
         const int _oWinStartIndex = (int)(4.88 / _gg + _offset); // org
         const int _oWinEndIndex = (int)(6.36 / _gg + _offset); // width = 6.36-4.88=1.48
 
-        const double _coef = 1e8 * 2.54 * 2.54 * 4 * 6 * 2 * 11 * 77 * 0.2 / 0.6;
+        //const double _coef = 1e8 * 2.54 * 2.54 * 4 * 6 * 2 * 11 * 77 * 0.2 / 0.6;
 
-        private static (double, double) GetFarInterplolateResult(string modelId, double porosity, double sw, double speed, double depth)
+        private static (double, double) GetFarInterplolateResult(string modelId, double porosity, double sw, double speed, double depth, double yieldCounting)
         {
             var cWinLength = _cWinEndIndex - _cWinStartIndex + 1;
             var oWinLength = _oWinEndIndex - _oWinStartIndex + 1;
@@ -32,20 +33,25 @@
             var waterLine = RepositoryInstance.Instance.COFarResultRepository?.GetCOFarResult(modelId, (int)porosity, 100);
             if (oilLine == null || waterLine == null)
                 throw new InvalidDataException("COFarResult Line is null");
-            var oilLineValArr = oilLine.InelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
-            var waterLineValArr = waterLine.InelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var oilLineValArr = oilLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var waterLineValArr = waterLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
 
-            var oilLineC = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() * _coef / speed * depth * 0.07;
-            var oilLineO = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() * _coef / speed * depth * 0.07;
+            var oilLineCR = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / oilLineValArr.Sum();
+            var oilLineOR = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / oilLineValArr.Sum();
 
-            var waterLineC = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() * _coef / speed * depth * 0.07;
-            var waterLineO = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() * _coef / speed * depth * 0.07;
+            var oilLineC = 36d / 160 * oilLineCR * yieldCounting / speed * depth * 0.0762;
+            var oilLineO = 36d / 160 * oilLineOR * yieldCounting / speed * depth * 0.0762;
+
+            var waterLineCR = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineOR = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineC = 36d / 160 * waterLineCR * yieldCounting / speed * depth * 0.0762;
+            var waterLineO = 36d / 160 * waterLineOR * yieldCounting / speed * depth * 0.0762;
 
             double cRes = 0, oRes = 0;
             if (sw == 0)
             {
-                cRes = waterLineC;
-                oRes = waterLineO;
+                cRes = oilLineC;
+                oRes = oilLineO;
             }
             else if (sw == 100)
             {
@@ -60,7 +66,7 @@
             return (cRes, oRes);
         }
 
-        private static (double, double) GetNearInterplolateResult(string modelId, double porosity, double sw, double speed, double depth)
+        private static (double, double) GetNearInterplolateResult(string modelId, double porosity, double sw, double speed, double depth, double yieldCounting)
         {
             var cWinLength = _cWinEndIndex - _cWinStartIndex + 1;
             var oWinLength = _oWinEndIndex - _oWinStartIndex + 1;
@@ -68,14 +74,18 @@
             var waterLine = RepositoryInstance.Instance.CONearResultRepository?.GetCONearResult(modelId, (int)porosity, 100);
             if (oilLine == null || waterLine == null)
                 throw new InvalidDataException("COFarResult Line is null");
-            var oilLineValArr = oilLine.InelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
-            var waterLineValArr = waterLine.InelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var oilLineValArr = oilLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var waterLineValArr = waterLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
 
-            var oilLineC = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() * _coef / speed * depth * 0.07;
-            var oilLineO = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() * _coef / speed * depth * 0.07;
+            var oilLineCR = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / oilLineValArr.Sum();
+            var oilLineOR = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / oilLineValArr.Sum();
+            var oilLineC = (36d / 160) * oilLineCR * yieldCounting / speed * depth * 0.0762;
+            var oilLineO = (36d / 160) * oilLineOR * yieldCounting / speed * depth * 0.0762;
 
-            var waterLineC = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() * _coef / speed * depth * 0.07;
-            var waterLineO = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() * _coef / speed * depth * 0.07;
+            var waterLineCR = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineOR = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineC = (36d / 160) * waterLineCR * yieldCounting / speed * depth * 0.0762;
+            var waterLineO = (36d / 160) * waterLineOR * yieldCounting / speed * depth * 0.0762;
 
             double cRes = 0, oRes = 0;
             if (sw == 0)
@@ -108,7 +118,7 @@
         /// <param name="depth">娣卞害</param>
         /// <returns>纰冲拰姘у��</returns>
         /// <exception cref="InvalidDataException">瀛旈殭搴﹁秴杩囪寖鍥�</exception>
-        public static (double, double) GetFarMergeCOResult(string modelId, double porosity, double sw, double speed, double depth)
+        public static (double, double) GetFarMergeCOResult(string modelId, double porosity, double sw, double speed, double depth, double yieldCounting)
         {
             if (porosity > 40)
                 throw new InvalidDataException("Porosity value out of range!");
@@ -116,15 +126,15 @@
             (double, double) result = (0, 0);
             if (porosity % 5 == 0)
             {
-                result = GetFarInterplolateResult(modelId, porosity, sw, speed, depth);
+                result = GetFarInterplolateResult(modelId, porosity, sw, speed, depth, yieldCounting);
             }
             else
             {
                 var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
-                var ceilingResult = GetFarInterplolateResult(modelId, ceilingPorosity, sw, speed, depth);
+                var ceilingResult = GetFarInterplolateResult(modelId, ceilingPorosity, sw, speed, depth, yieldCounting);
 
                 var floorPorosity = Math.Floor(porosity / 5) * 5;
-                var floorResult = GetFarInterplolateResult(modelId, floorPorosity, sw, speed, depth);
+                var floorResult = GetFarInterplolateResult(modelId, floorPorosity, sw, speed, depth, yieldCounting);
 
                 result.Item1 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item1, ceilingPorosity, ceilingResult.Item1);
                 result.Item2 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item2, ceilingPorosity, ceilingResult.Item2);
@@ -144,7 +154,7 @@
         /// <param name="depth">娣卞害</param>
         /// <returns>纰虫垨姘у��</returns>
         /// <exception cref="InvalidDataException">瀛旈殭搴﹁秴杩囪寖鍥�</exception>
-        public static (double, double) GetNearMergeCOResult(string modelId, double porosity, double sw, double speed, double depth)
+        public static (double, double) GetNearMergeCOResult(string modelId, double porosity, double sw, double speed, double depth, double yieldCounting)
         {
             if (porosity > 40)
                 throw new InvalidDataException("Porosity value out of range!");
@@ -152,20 +162,141 @@
             (double, double) result = (0, 0);
             if (porosity % 5 == 0)
             {
-                result = GetNearInterplolateResult(modelId, porosity, sw, speed, depth);
+                result = GetNearInterplolateResult(modelId, porosity, sw, speed, depth, yieldCounting);
             }
             else
             {
                 var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
-                var ceilingResult = GetNearInterplolateResult(modelId, ceilingPorosity, sw, speed, depth);
+                var ceilingResult = GetNearInterplolateResult(modelId, ceilingPorosity, sw, speed, depth, yieldCounting);
 
                 var floorPorosity = Math.Floor(porosity / 5) * 5;
-                var floorResult = GetNearInterplolateResult(modelId, floorPorosity, sw, speed, depth);
+                var floorResult = GetNearInterplolateResult(modelId, floorPorosity, sw, speed, depth, yieldCounting);
 
                 result.Item1 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item1, ceilingPorosity, ceilingResult.Item1);
                 result.Item2 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item2, ceilingPorosity, ceilingResult.Item2);
             }
             return result;
         }
+
+        public static (double, double) GetFarCROR(string modelId, double porosity, double sw)
+        {
+            var cWinLength = _cWinEndIndex - _cWinStartIndex + 1;
+            var oWinLength = _oWinEndIndex - _oWinStartIndex + 1;
+            var oilLine = RepositoryInstance.Instance.COFarResultRepository?.GetCOFarResult(modelId, (int)porosity, 0);
+            var waterLine = RepositoryInstance.Instance.COFarResultRepository?.GetCOFarResult(modelId, (int)porosity, 100);
+            if (oilLine == null || waterLine == null)
+                throw new InvalidDataException("COFarResult Line is null");
+            var oilLineValArr = oilLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var waterLineValArr = waterLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+
+            var oilLineCR = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / oilLineValArr.Sum();
+            var oilLineOR = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / oilLineValArr.Sum();
+
+            var waterLineCR = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineOR = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / waterLineValArr.Sum();
+
+            double cRes = 0, oRes = 0;
+            if (sw == 0)
+            {
+                cRes = oilLineCR;
+                oRes = oilLineOR;
+            }
+            else if (sw == 100)
+            {
+                cRes = waterLineCR;
+                oRes = waterLineOR;
+            }
+            else if (sw > 0 && sw < 100)
+            {
+                cRes = Utility.Interpolate(sw, 100, waterLineCR, 0, oilLineCR);
+                oRes = Utility.Interpolate(sw, 0, oilLineOR, 100, waterLineOR);
+            }
+            return (cRes, oRes);
+        }
+
+        public static (double, double) GetNearCROR(string modelId, double porosity, double sw)
+        {
+            var cWinLength = _cWinEndIndex - _cWinStartIndex + 1;
+            var oWinLength = _oWinEndIndex - _oWinStartIndex + 1;
+            var oilLine = RepositoryInstance.Instance.CONearResultRepository?.GetCONearResult(modelId, (int)porosity, 0);
+            var waterLine = RepositoryInstance.Instance.CONearResultRepository?.GetCONearResult(modelId, (int)porosity, 100);
+            if (oilLine == null || waterLine == null)
+                throw new InvalidDataException("COFarResult Line is null");
+            var oilLineValArr = oilLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+            var waterLineValArr = waterLine.CInelasticSpec.Split(',').Select(v => Convert.ToDouble(v)).ToArray();
+
+            var oilLineCR = oilLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / oilLineValArr.Sum();
+            var oilLineOR = oilLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / oilLineValArr.Sum();
+
+            var waterLineCR = waterLineValArr.Skip(_cWinStartIndex).Take(cWinLength).Sum() / waterLineValArr.Sum();
+            var waterLineOR = waterLineValArr.Skip(_oWinStartIndex).Take(oWinLength).Sum() / waterLineValArr.Sum();
+
+            double crRes = 0, orRes = 0;
+            if (sw == 0)
+            {
+                crRes = oilLineCR;
+                orRes = oilLineOR;
+            }
+            else if (sw == 100)
+            {
+                crRes = waterLineCR;
+                orRes = waterLineOR;
+            }
+            else if (sw > 0 && sw < 100)
+            {
+                crRes = Utility.Interpolate(sw, 100, waterLineCR, 0, oilLineCR);
+                orRes = Utility.Interpolate(sw, 0, oilLineOR, 100, waterLineOR);
+            }
+            return (crRes, orRes);
+        }
+
+        public static (double, double) GetFarCOORResult(string modelId, double porosity, double sw)
+        {
+            if (porosity > 40)
+                throw new InvalidDataException("Porosity value out of range!");
+
+            (double, double) result = (0, 0);
+            if (porosity % 5 == 0)
+            {
+                result = GetFarCROR(modelId, porosity, sw);
+            }
+            else
+            {
+                var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
+                var ceilingResult = GetFarCROR(modelId, ceilingPorosity, sw);
+
+                var floorPorosity = Math.Floor(porosity / 5) * 5;
+                var floorResult = GetFarCROR(modelId, floorPorosity, sw);
+
+                result.Item1 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item1, ceilingPorosity, ceilingResult.Item1);
+                result.Item2 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item2, ceilingPorosity, ceilingResult.Item2);
+            }
+            return result;
+        }
+
+        public static (double, double) GetNearCOORResult(string modelId, double porosity, double sw)
+        {
+            if (porosity > 40)
+                throw new InvalidDataException("Porosity value out of range!");
+
+            (double, double) result = (0, 0);
+            if (porosity % 5 == 0)
+            {
+                result = GetNearCROR(modelId, porosity, sw);
+            }
+            else
+            {
+                var ceilingPorosity = Math.Ceiling(porosity / 5) * 5;
+                var ceilingResult = GetNearCROR(modelId, ceilingPorosity, sw);
+
+                var floorPorosity = Math.Floor(porosity / 5) * 5;
+                var floorResult = GetNearCROR(modelId, floorPorosity, sw);
+
+                result.Item1 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item1, ceilingPorosity, ceilingResult.Item1);
+                result.Item2 = Utility.Interpolate(porosity, floorPorosity, floorResult.Item2, ceilingPorosity, ceilingResult.Item2);
+            }
+            return result;
+        }
+
     }
 }
diff --git a/ErrorAnalysis.Service/ErrorRatioCalc.cs b/ErrorAnalysis.Service/ErrorRatioCalc.cs
index f34ab06..633a272 100644
--- a/ErrorAnalysis.Service/ErrorRatioCalc.cs
+++ b/ErrorAnalysis.Service/ErrorRatioCalc.cs
@@ -1,4 +1,5 @@
 锘縰sing ErrorAnalysis.Repository;
+using ErrorAnalysis.Service;
 using ErrorAnalysis.Service.Model;
 using System;
 using System.Collections.Generic;
@@ -10,7 +11,7 @@
 {
     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)
+        public static ErrorRatioResult GetErrorRatioResult(string modelID, double porosity, double sw, double depth, bool lockSpeed, double speed, double yieldCounting, double targetErrorRatio, out double testSpeed, int pass = 0, double nearCofe = 0.65, double farCofe = 0.35)
         {
             var result = new ErrorRatioResult
             {
@@ -24,7 +25,8 @@
             {
                 List<double[]> cWOL;
                 List<double[]> oWOL;
-                var firstErrorPassM = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, out cWOL, out oWOL);
+                var firstErrorPassM = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, yieldCounting, nearCofe, farCofe);
+                (cWOL, oWOL) = GetOilWaterLine(modelID);
                 result.CWOL = cWOL;
                 result.OWOL = oWOL;
                 result.ErrorRatios.Add(firstErrorPassM);
@@ -42,15 +44,32 @@
                 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);
+                (cWOL, oWOL) = GetOilWaterLine(modelID);
+                result.CWOL = cWOL;
+                result.OWOL = oWOL;
+
+                var nearCROR = COMergeCalcService.GetNearCOORResult(modelID, porosity, sw);
+                var farCROR = COMergeCalcService.GetFarCOORResult(modelID, porosity, sw);
+                var mDelta = GetMDelta(modelID, porosity);
+
+                speed = Math.Pow(firstTargetErrorRatio /
+                    Math.Sqrt(1 / (0.0762 * depth * (36d / 160))) *
+                    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) * (Math.Pow(farCROR.Item1 / farCROR.Item2, 2)) * (1 / farCROR.Item1 + 1 / farCROR.Item2))
+                    , 2);
+
+                var firstErrorPass = new ErrorRatio { Pass = 1, ErrorRatioValue = firstTargetErrorRatio };
+
+                var firstErrorPassM = GetFirstErrorRatio(modelID, porosity, sw, speed, depth, yieldCounting, nearCofe, farCofe);
+                //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 +83,80 @@
 
         }
 
-        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)
         {
             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 mergePDEV = 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))]);
-                    }
-                }
-
-                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);
-                }
-
-                var errorRatio = mergePDEV /( cRes > oRes ? (cRes - oRes) : (oRes - cRes));
+                var errorRatio = mergePDEV / GetMDelta(modelID, porosity);
 
                 return new ErrorRatio { Pass = 1, ErrorRatioValue = errorRatio };
             }
             catch
             {
-                cWOL = null;
-                oWOL = null;
                 return new ErrorRatio { Pass = 0, ErrorRatioValue = 0 };
             }
         }
+
+        private static double GetMDelta(string modelID, double porosity)
+        {
+            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 cRes - oRes;
+        }
+
+        private static (List<double[]>, List<double[]>) GetOilWaterLine(string modelID)
+        {
+            var cWOL = new List<double[]>();
+            var oWOL = new List<double[]>();
+
+            var cWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 0);
+            var oWolRes = RepositoryInstance.Instance.COWOLRepository?.GetWOL(modelID, 100);
+            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);
+        }
     }
 }
\ No newline at end of file
diff --git a/ErrorAnalysis.Service/PDEVCalcService.cs b/ErrorAnalysis.Service/PDEVCalcService.cs
index ccfc05f..d198b27 100644
--- a/ErrorAnalysis.Service/PDEVCalcService.cs
+++ b/ErrorAnalysis.Service/PDEVCalcService.cs
@@ -8,15 +8,15 @@
 {
     public class PDEVCalcService
     {
-        public static double GetMergePDEV(string modelId, double porosity, double sw, double speed, double depth)
+        public static double GetMergePDEV(string modelId, double porosity, double sw, double speed, double depth, double yieldCounting, double nearCofe, double farCofe)
         {
-            var farResult = COMergeCalcService.GetFarMergeCOResult(modelId, porosity, sw, speed, depth);
+            var farResult = COMergeCalcService.GetFarMergeCOResult(modelId, porosity, sw, speed, depth, yieldCounting);
             var farPDEV = CalcPDEV(farResult.Item1, farResult.Item2);
 
-            var nearResult = COMergeCalcService.GetNearMergeCOResult(modelId, porosity, sw, speed, depth);
+            var nearResult = COMergeCalcService.GetNearMergeCOResult(modelId, porosity, sw, speed, depth, yieldCounting);
             var nearPDEV = CalcPDEV(nearResult.Item1, nearResult.Item2);
 
-            return nearPDEV * 0.65 + farPDEV * 0.35;
+            return Math.Sqrt(Math.Pow(nearPDEV * nearCofe, 2) + Math.Pow(farPDEV * farCofe, 2));
         }
 
         private static double CalcPDEV(double c, double o) => Math.Sqrt(Math.Pow(c / o, 2) * (1 / c + 1 / o));
diff --git a/ErrorAnalysis.UI/FrmMain.Designer.cs b/ErrorAnalysis.UI/FrmMain.Designer.cs
index c074c68..ff9c9d3 100644
--- a/ErrorAnalysis.UI/FrmMain.Designer.cs
+++ b/ErrorAnalysis.UI/FrmMain.Designer.cs
@@ -120,9 +120,11 @@
             cmbLithology = new ComboBox();
             label16 = new Label();
             tpPlan = new TabPage();
+            label7 = new Label();
             cmbTargetLoggingIntervalUnit = new ComboBox();
             label5 = new Label();
             chkAlphaProcessing = new CheckBox();
+            nudYieldCounting = new NumericUpDown();
             nudAlphaProcessingWin = new NumericUpDown();
             nudDepth = new NumericUpDown();
             nudTargetLoggingInterval = new NumericUpDown();
@@ -157,6 +159,7 @@
             ((System.ComponentModel.ISupportInitialize)nudSw).BeginInit();
             ((System.ComponentModel.ISupportInitialize)nudPorosity).BeginInit();
             tpPlan.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)nudYieldCounting).BeginInit();
             ((System.ComponentModel.ISupportInitialize)nudAlphaProcessingWin).BeginInit();
             ((System.ComponentModel.ISupportInitialize)nudDepth).BeginInit();
             ((System.ComponentModel.ISupportInitialize)nudTargetLoggingInterval).BeginInit();
@@ -293,7 +296,7 @@
             // 
             // nudSpeed
             // 
-            nudSpeed.DecimalPlaces = 1;
+            nudSpeed.DecimalPlaces = 2;
             nudSpeed.Increment = new decimal(new int[] { 1, 0, 0, 65536 });
             nudSpeed.Location = new Point(54, 16);
             nudSpeed.Maximum = new decimal(new int[] { 9999999, 0, 0, 0 });
@@ -1095,10 +1098,12 @@
             // 
             // tpPlan
             // 
+            tpPlan.Controls.Add(label7);
             tpPlan.Controls.Add(cmbTargetLoggingIntervalUnit);
             tpPlan.Controls.Add(label5);
             tpPlan.Controls.Add(chkAlphaProcessing);
             tpPlan.Controls.Add(label22);
+            tpPlan.Controls.Add(nudYieldCounting);
             tpPlan.Controls.Add(nudAlphaProcessingWin);
             tpPlan.Controls.Add(pnlLockPass);
             tpPlan.Controls.Add(nudDepth);
@@ -1117,6 +1122,15 @@
             tpPlan.TabIndex = 3;
             tpPlan.Text = "Plan";
             tpPlan.UseVisualStyleBackColor = true;
+            // 
+            // label7
+            // 
+            label7.AutoSize = true;
+            label7.Location = new Point(457, 102);
+            label7.Name = "label7";
+            label7.Size = new Size(90, 17);
+            label7.TabIndex = 17;
+            label7.Text = "Yield counting";
             // 
             // cmbTargetLoggingIntervalUnit
             // 
@@ -1140,16 +1154,26 @@
             // chkAlphaProcessing
             // 
             chkAlphaProcessing.AutoSize = true;
-            chkAlphaProcessing.Location = new Point(421, 69);
+            chkAlphaProcessing.Location = new Point(419, 43);
             chkAlphaProcessing.Name = "chkAlphaProcessing";
             chkAlphaProcessing.Size = new Size(128, 21);
             chkAlphaProcessing.TabIndex = 15;
             chkAlphaProcessing.Text = "Alpha processing";
             chkAlphaProcessing.UseVisualStyleBackColor = true;
             // 
+            // nudYieldCounting
+            // 
+            nudYieldCounting.DecimalPlaces = 2;
+            nudYieldCounting.Increment = new decimal(new int[] { 100, 0, 0, 0 });
+            nudYieldCounting.Location = new Point(565, 100);
+            nudYieldCounting.Maximum = new decimal(new int[] { 99999999, 0, 0, 0 });
+            nudYieldCounting.Name = "nudYieldCounting";
+            nudYieldCounting.Size = new Size(82, 23);
+            nudYieldCounting.TabIndex = 13;
+            // 
             // nudAlphaProcessingWin
             // 
-            nudAlphaProcessingWin.Location = new Point(555, 98);
+            nudAlphaProcessingWin.Location = new Point(565, 70);
             nudAlphaProcessingWin.Maximum = new decimal(new int[] { 99999999, 0, 0, 0 });
             nudAlphaProcessingWin.Name = "nudAlphaProcessingWin";
             nudAlphaProcessingWin.Size = new Size(47, 23);
@@ -1175,7 +1199,7 @@
             // label54
             // 
             label54.AutoSize = true;
-            label54.Location = new Point(391, 100);
+            label54.Location = new Point(389, 72);
             label54.Name = "label54";
             label54.Size = new Size(158, 17);
             label54.TabIndex = 12;
@@ -1267,6 +1291,7 @@
             ((System.ComponentModel.ISupportInitialize)nudPorosity).EndInit();
             tpPlan.ResumeLayout(false);
             tpPlan.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)nudYieldCounting).EndInit();
             ((System.ComponentModel.ISupportInitialize)nudAlphaProcessingWin).EndInit();
             ((System.ComponentModel.ISupportInitialize)nudDepth).EndInit();
             ((System.ComponentModel.ISupportInitialize)nudTargetLoggingInterval).EndInit();
@@ -1378,5 +1403,7 @@
         private TableLayoutPanel tableLayoutPanel1;
         private ProgressBar pbLoadDB;
         private RichTextBox txtAnalysisResult;
+        private Label label7;
+        private NumericUpDown nudYieldCounting;
     }
 }
diff --git a/ErrorAnalysis.UI/FrmMain.cs b/ErrorAnalysis.UI/FrmMain.cs
index 00ae909..5b0a9e4 100644
--- a/ErrorAnalysis.UI/FrmMain.cs
+++ b/ErrorAnalysis.UI/FrmMain.cs
@@ -138,7 +138,13 @@
 
             if (!decimal.TryParse(nudOilDensity.Value.ToString(), out decimal oilDensity) || nudOilDensity.Value <= 0)
             {
-                MessageBox.Show("Please enter a oil density");
+                MessageBox.Show("Please enter a reasonable oil density");
+                return;
+            }
+
+            if (!double.TryParse(nudYieldCounting.Value.ToString(), out double yieldCounting) || nudYieldCounting.Value <= 0)
+            {
+                MessageBox.Show("Please enter a reasonable yield counting");
                 return;
             }
 
@@ -163,12 +169,12 @@
                     MessageBox.Show("Please enter a reasonable speed");
                     return;
                 }
-                var calcSpeed = cmbSpeedUnit.Text == "ft/hr" ? UnitConvert.FtHr2MMin(speed) : UnitConvert.MHR2MMin(speed);
+                var calcSpeed = cmbSpeedUnit.Text == "ft/hr" ? UnitConvert.FtHr2MS(speed) : UnitConvert.MHR2MS(speed);
 
-                var result = ErrorRatioCalc.GetErrorRatioResult(_model.ModelID, porosity, sw, depth, true, calcSpeed, targetErrorRatio / 100, out _);
+                var result = ErrorRatioCalc.GetErrorRatioResult(_model.ModelID, porosity, sw, depth, true, calcSpeed, yieldCounting, targetErrorRatio / 100, out _);
                 Plot(result);
 
-                double totalTime = Math.Round((targetLoggingInterval / calcSpeed / 60), 2);
+                double totalTime = Math.Round((targetLoggingInterval / calcSpeed / 60 / 60), 2);
                 var errorRate = Math.Round(result.ErrorRatios.Min(r => r.ErrorRatioValue) * 100, 2);
                 txtAnalysisResult.AppendText("A total of ");
                 AppendText(txtAnalysisResult, result.ErrorRatios.Count.ToString(), ColorTranslator.FromHtml("#8B0000"));
@@ -179,7 +185,7 @@
                 txtAnalysisResult.AppendText(" the estimated total job duration is ");
                 AppendText(txtAnalysisResult, $"{totalTime} hours.", ColorTranslator.FromHtml("#8B0000"));
 
-                _reportModel = CreateReportModel(totalTime.ToString(), result.ErrorRatios.Count.ToString(), Math.Round(UnitConvert.MMin2FtHr(calcSpeed), 2).ToString(), errorRate.ToString());
+                _reportModel = CreateReportModel(totalTime.ToString(), result.ErrorRatios.Count.ToString(), Math.Round(UnitConvert.MS2FtHr(calcSpeed), 2).ToString(), errorRate.ToString());
             }
             else
             {
@@ -189,12 +195,12 @@
                     return;
                 }
                 double speed;
-                var result = ErrorRatioCalc.GetErrorRatioResult(_model.ModelID, porosity, sw, depth, false, 0, targetErrorRatio / 100, out speed, pass);
+                var result = ErrorRatioCalc.GetErrorRatioResult(_model.ModelID, porosity, sw, depth, false, 0, yieldCounting, targetErrorRatio / 100, out speed, pass);
                 Plot(result);
 
-                double totalTime = Math.Round((targetLoggingInterval / speed / 60), 2);
+                double totalTime = Math.Round((targetLoggingInterval / speed / 60 / 60), 2);
                 var errorRate = Math.Round(result.ErrorRatios.Min(r => r.ErrorRatioValue) * 100, 2);
-                var speedFr = Math.Round(UnitConvert.MMin2FtHr(speed), 2);
+                var speedFr = Math.Round(UnitConvert.MS2FtHr(speed), 2);
 
                 txtAnalysisResult.AppendText("A total of ");
                 AppendText(txtAnalysisResult, result.ErrorRatios.Count.ToString(), ColorTranslator.FromHtml("#8B0000"));
@@ -307,9 +313,9 @@
             if (curModels.Count == 0)
                 return;
 
-            var casingFulids = curModels.Select(x => x.CasingFluid == "0" ? "water" : x.CasingFluid == "100" ? "oil" : x.CasingFluid == "200" ? "gas" : "none").Distinct().ToArray();
+            var casingFulids = curModels.Select(x => x.CasingFluid == "999" ? "none" : x.CasingFluid == "100" ? "oil" : x.CasingFluid == "200" ? "gas" : "water").Distinct().ToArray();
 
-            if (casingFulids.Length == 1 && casingFulids[0] == "999")
+            if (casingFulids.Length == 1 && casingFulids[0] == "none")
             {
                 cmbCasingHoldUp.Enabled = false;
                 nudCasingHoldUp.Enabled = false;
@@ -322,7 +328,7 @@
                 cmbCasingHoldUp.Items.AddRange(casingFulids);
             }
 
-            var screenFulids = curModels.Select(x => x.ScreenFluid == "0" ? "water" : x.ScreenFluid == "100" ? "oil" : x.ScreenFluid == "200" ? "gas" : "none").Distinct().ToArray();
+            var screenFulids = curModels.Select(x => x.ScreenFluid == "999" ? "none" : x.ScreenFluid == "100" ? "oil" : x.ScreenFluid == "200" ? "gas" : "water").Distinct().ToArray();
 
             if (screenFulids.Length == 1 && screenFulids[0] == "none")
             {
@@ -336,7 +342,7 @@
                 cmbScreenHoldUp.Items.AddRange(screenFulids);
             }
 
-            var tubeFluids = curModels.Select(x => x.TubeFluid == "0" ? "water" : x.TubeFluid == "100" ? "oil" : x.TubeFluid == "200" ? "gas" : "none").Distinct().ToArray();
+            var tubeFluids = curModels.Select(x => x.TubeFluid == "999" ? "none" : x.TubeFluid == "100" ? "oil" : x.TubeFluid == "200" ? "gas" : "water").Distinct().ToArray();
             if (tubeFluids.Length == 1 && tubeFluids[0] == "none")
             {
                 cmbTubeHoldUp.Enabled = false;
@@ -487,6 +493,7 @@
             }
         }
         #endregion
+
         private List<COModelTable> GetFilterResult()
         {
             var type = typeof(COModelTable);
diff --git a/ErrorAnalysis.UI/Utility/UnitConvert.cs b/ErrorAnalysis.UI/Utility/UnitConvert.cs
index 0ebbb64..8b77059 100644
--- a/ErrorAnalysis.UI/Utility/UnitConvert.cs
+++ b/ErrorAnalysis.UI/Utility/UnitConvert.cs
@@ -12,10 +12,17 @@
 
         public static double M2Ft(double val) => val * 3.281;
 
-        public static double FtHr2MMin(double val) => val / 3.281 / 60;
+        public static double FtHr2MS(double val) => val / 3.281 / 60/60;
 
-        public static double MHR2MMin(double val) => val /60;
+        public static double MHR2MS(double val) => val /60/60;
 
-        public static double MMin2FtHr(double val) => val * 3.281 * 60;
+        public static double MS2FtHr(double val) => val * 3.281 * 60*60;
+
+        /// <summary>
+        /// 璁$畻YieldCounting澶囩敤
+        /// </summary>
+        /// <param name="yieldCounting">浜ч</param>
+        /// <returns>浜ч</returns>
+        public static double CalcYieldCounting(double yieldCounting) => yieldCounting;
     }
 }

--
Gitblit v1.9.3