From d8f0e09b26c2e09b3f2f9f00aeba9564f5a46e03 Mon Sep 17 00:00:00 2001 From: vlad dalevich Date: Tue, 10 Jun 2025 20:52:10 +0700 Subject: [PATCH 1/7] add test for calculateDepth --- .../CalculateDepthTestNew.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 T2dMath.Pipeline.Test/CalculateDepthTestNew.cs diff --git a/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs b/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs new file mode 100644 index 0000000..cce28dc --- /dev/null +++ b/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs @@ -0,0 +1,49 @@ +using System; +using Xunit; +using T2dMath.Pipeline; + +namespace T2dMath.Tests +{ + public class PipelinesTests + { + [Fact] + public void CalculateDepth_CalculatesDepthWithFixZaboy() + { + // arrange + double[] time_surface = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + double[] block_height = new double[] { 0, 0.1, 0.2, 0.21, 0.2, 0.19, 0.2, 0.21, 0.2, 0.19 }; + double[] weight = new double[] { 0, 5, 10, 10, 10, 5, 0, 0, 0, 0 }; + double[] depth = new double[] { 0, 0.5, 1.0, 1.5, 2.0, 2.1, 2.2, 2.2, 2.2, 2.2 }; + double[] fstop = null; + double[] length_drill_pipe = new double[] { 1.5, 1.5 }; // итого 3.0 + double length_tool = 0.5; + int min_time = 1000; + + // act + Pipelines.CalculateDepth( + time_surface, + block_height, + weight, + min_time, + ref fstop, + length_drill_pipe, + length_tool, + ref depth, + out var stop_intervals, + out var kmlt, + out var kmove + ); + + // assert + Assert.NotNull(depth); + Assert.Equal(time_surface.Length, depth.Length); + + double expected_zaboy = length_drill_pipe[0] + length_drill_pipe[1] + length_tool; + double max_depth = depth[^1]; + + Assert.True(max_depth <= expected_zaboy + 0.1, $"Глубина должна быть меньше или равна забою (ожидалось ≈ {expected_zaboy})"); + Assert.NotNull(kmlt); + Assert.NotNull(kmove); + } + } +} From 7760c4d8270eb468bb266641fe2c1a052351a6f7 Mon Sep 17 00:00:00 2001 From: vlad dalevich Date: Wed, 11 Jun 2025 22:36:27 +0700 Subject: [PATCH 2/7] test and specification --- T2dMath.Pipeline/Time2DepthLogging.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/T2dMath.Pipeline/Time2DepthLogging.cs b/T2dMath.Pipeline/Time2DepthLogging.cs index 7564f75..cd5b37e 100644 --- a/T2dMath.Pipeline/Time2DepthLogging.cs +++ b/T2dMath.Pipeline/Time2DepthLogging.cs @@ -29,7 +29,28 @@ public delegate double[] AlgorithmAvarage( object[] param ); - // + /// + /// Преобразует входные кривые, заданные во временной шкале, в кривые по глубине. + /// Учитываются остановки, вращения, параметры трансформации и шаг глубинной сетки. + /// + /// Массив временных отметок, соответствующих измерениям инструмента. + /// Словарь временных кривых: ключ — имя кривой, значение — массив значений. + /// Параметры расчета: могут включать фильтры, коэффициенты и т.д. + /// Список кривых, подлежащих расчету во временной области. + /// Описание преобразований для выходных кривых. + /// Временные метки, соответствующие известным глубинам. + /// Глубинные значения, соответствующие времени в . + /// Признаки остановок инструмента (0.0 или меньше - нет, больше 0.0 - есть). + /// Признаки включенного ротора (0.0 — выключен, 1.0 — включен). + /// Список кривых, подлежащих расчету в глубинной области. + /// Шаг равномерной глубинной сетки. Должен быть > 0.Add commentMore actions + /// Результирующая глубинная сетка, равномерная по . + /// Результирующие кривые по глубине: ключ — имя кривой, значение — массив значений. + /// (Необязательно) Колбэк для отслеживания прогресса выполнения. + /// (Необязательно) Колбэк для вывода вспомогательной информации. + /// Выбрасывается при некорректном шаге или несогласованных массивах. + /// Если не найдена требуемая входная кривая. + /// Если не удалось выполнить трансформацию или интерполяцию. public static void Time2DepthLogging( double[] time_tool, From 42bbf7f3020022e8e10b067f5f61ef416e221909 Mon Sep 17 00:00:00 2001 From: vlad dalevich Date: Thu, 3 Jul 2025 12:30:34 +0700 Subject: [PATCH 3/7] change code style --- .editorconfig | 52 ++++++++++++++++++++++++++++++ .gitattributes | 4 +++ .github/workflows/dotnet-test.yml | 53 ++++++++++++++++++++++++++++--- 3 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..19e4ae2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,52 @@ +# .editorconfig for a C# project + +# Indicates that this is the root file and no parent files should be considered +root = true + +# General settings for all files +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +# Settings for C# files +[*.cs] +# Use predefined types (e.g. int instead of Int32) when the type is obvious +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +# Do not require "this." qualification for members +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Opening brace on a new line for all types (methods, classes, etc.) +csharp_new_line_before_open_brace = all:warning + +# Enforce modifier order (e.g. public static void → correct order) +csharp_preferred_modifier_order = public, protected, private, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:error + +# Use 'var' for built-in types and when the type is apparent +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:suggestion + +# Sort 'using' directives with System namespaces first +dotnet_sort_system_directives_first = true + +# Do not enforce any specific file header +file_header_template = unset + +# Require braces on a new line and discourage single-line embedded statements +csharp_style_allow_embedded_statements_on_same_line = false:warning + +# Indentation rules for switch/case and labels +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Set maximum line length (optional but often recommended) +max_line_length = 120 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f8f4795 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +.cs text eol=lf +*.csproj text eol=lf +*.yml text eol=lf +*.xml text eol=lf \ No newline at end of file diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index 97d7717..055372c 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -2,19 +2,20 @@ name: .NET Tests on: push: - branches: - - main pull_request: - branches: - - main + + jobs: build-and-test: runs-on: ubuntu-latest + steps: - name: Checkout repository uses: actions/checkout@v4 + with: + token: ${{ secrets.CODE_STYLE }} - name: Setup .NET uses: actions/setup-dotnet@v3 @@ -24,6 +25,50 @@ jobs: - name: Restore dependencies run: dotnet restore T2dMath.sln + - name: Format code + run: dotnet format whitespace T2dMath.sln + + - name: Format code style + run: dotnet format style T2dMath.sln + + - name: Verify formatting + run: dotnet format T2dMath.sln --verify-no-changes + + - name: Check token + env: + GITHUB_TOKEN: ${{ secrets.CODE_STYLE }} + run: echo "Token length is ${#GITHUB_TOKEN}" + + - name: Show context info + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + run: | + echo "Branch: $BRANCH_NAME" + git remote -v + git status + + - name: Commit and push formatting changes + env: + GITHUB_TOKEN: ${{ secrets.CODE_STYLE }} + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + run: | + echo "Branch: $BRANCH_NAME" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git checkout -B "$BRANCH_NAME" + + git add . + git commit -m "Auto-format code" || echo "No changes to commit" + + git pull --rebase origin "$BRANCH_NAME" + + git push origin "$BRANCH_NAME" + + + + - name: Build the project run: dotnet build T2dMath.sln --configuration Release --no-restore From 55fbe62df8ffda8315839b9445432ae8c2b4b8bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 05:31:51 +0000 Subject: [PATCH 4/7] Auto-format code --- T2dMath.ILogAlgorithm/IGridTransformation.cs | 30 +- T2dMath.ILogAlgorithm/ILogAlgorithm.cs | 22 +- T2dMath.ILogAlgorithm/ILogTransformation.cs | 22 +- .../T2dMath.ILogAlgorithm.csproj | 16 +- .../Abstract/ALogOneCurveInput.cs | 40 +- .../Abstract/ALogPoint2Point.cs | 55 +- .../Abstract/ALogTransformation.cs | 47 +- T2dMath.LogAlgorithm/LogAlgorithmDoubler.cs | 37 +- T2dMath.LogAlgorithm/LogAlgorithmPolynom.cs | 123 +- T2dMath.LogAlgorithm/LogAlgorithmVikDfRok.cs | 54 +- .../T2dMath.LogAlgorithm.csproj | 22 +- .../CalculateDepthTestSimple.cs | 99 +- T2dMath.Pipeline.Test/TestFullWorkflow.cs | 21 +- T2dMath.Pipeline.Test/TestPipeline.cs | 24 +- T2dMath.Pipeline.Test/TestPipelineSimple.cs | 159 +- T2dMath.Pipeline/CalculateDepth.cs | 279 ++- T2dMath.Pipeline/PipelinesInit.cs | 557 +++-- T2dMath.Pipeline/Time2DepthLogging.cs | 113 +- T2dMath.Test/CurveToStopsNewTest.cs | 11 +- .../SetAbsoluteDepthByPromerVlasovTest.cs | 257 ++- T2dMath.Test/TestBuildGrid.cs | 11 +- T2dMath.Test/TestCalculateStopsByDepth.cs | 34 +- T2dMath.Test/TestDepthGridNoStopsAvg.cs | 36 +- T2dMath.Test/TestGetInterval.cs | 134 +- T2dMath.Test/TestPipelineFunc.cs | 493 +++-- T2dMath.UtilsTest.Test/PromerParserTest.cs | 16 +- .../T2dMath.UtilsTest.Test.csproj | 4 +- T2dMath.UtilsTest.Test/TestParserLasTime.cs | 36 +- T2dMath.UtilsTest.Test/TestParserTool.cs | 16 +- T2dMath.UtilsTest.Test/TestParserXML.cs | 12 +- T2dMath.UtilsTest/ParseLasTime.cs | 529 +++-- T2dMath.UtilsTest/ParseXML.cs | 33 +- T2dMath.UtilsTest/PromerParser.cs | 37 +- T2dMath.UtilsTest/Tools/DoubleComparer.cs | 47 +- T2dMath.UtilsTest/Tools/ParserTool.cs | 23 +- .../Tools/StopIntervalComparer.cs | 17 +- .../Tools\320\241onfiguration.cs" | 168 +- T2dMath/CalculateSpeedMoving.cs | 100 +- T2dMath/CalculateStopsByDepth.cs | 47 +- .../DefineGoodQualityRecordingIntervals.cs | 39 +- T2dMath/DepthGridNoStopsAvg.cs | 99 +- T2dMath/SimulationHoistBlockMovement.cs | 23 +- T2dMath/T2DMathCalculateDepth.cs | 1847 ++++++++--------- T2dMath/T2dMath.cs | 1412 ++++++------- 44 files changed, 3358 insertions(+), 3843 deletions(-) diff --git a/T2dMath.ILogAlgorithm/IGridTransformation.cs b/T2dMath.ILogAlgorithm/IGridTransformation.cs index 8638ba0..59cc01d 100644 --- a/T2dMath.ILogAlgorithm/IGridTransformation.cs +++ b/T2dMath.ILogAlgorithm/IGridTransformation.cs @@ -1,16 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace T2dMath.ILogAlgorithm -{ - public interface IGridTransformation - { - double[] Transform( - double[] irregular_depth, - double[] curve_value, - double[] regular_depth_grid, - object[] param - ); - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace T2dMath.ILogAlgorithm { + public interface IGridTransformation { + double[] Transform( + double[] irregular_depth, + double[] curve_value, + double[] regular_depth_grid, + object[] param + ); + } +} diff --git a/T2dMath.ILogAlgorithm/ILogAlgorithm.cs b/T2dMath.ILogAlgorithm/ILogAlgorithm.cs index c114776..6e57758 100644 --- a/T2dMath.ILogAlgorithm/ILogAlgorithm.cs +++ b/T2dMath.ILogAlgorithm/ILogAlgorithm.cs @@ -1,12 +1,10 @@ -using System; - -namespace T2dMath.IAlgorithm -{ - /// - /// - /// - public interface ILogAlgorithm - { - double[] AlgorithmProcessor(double[] reference_curve, double[][] curves_in, object[] param); - } -} +using System; + +namespace T2dMath.IAlgorithm { + /// + /// + /// + public interface ILogAlgorithm { + double[] AlgorithmProcessor(double[] reference_curve, double[][] curves_in, object[] param); + } +} diff --git a/T2dMath.ILogAlgorithm/ILogTransformation.cs b/T2dMath.ILogAlgorithm/ILogTransformation.cs index 4c2036c..812b75e 100644 --- a/T2dMath.ILogAlgorithm/ILogTransformation.cs +++ b/T2dMath.ILogAlgorithm/ILogTransformation.cs @@ -1,12 +1,10 @@ -using System; - -namespace T2dMath.ILogAlgorithm -{ - /// - /// - /// - public interface ILogTransformation - { - double[] Transformation(double[] reference_curve, double[][] input_curves, object[] param); - } -} +using System; + +namespace T2dMath.ILogAlgorithm { + /// + /// + /// + public interface ILogTransformation { + double[] Transformation(double[] reference_curve, double[][] input_curves, object[] param); + } +} diff --git a/T2dMath.ILogAlgorithm/T2dMath.ILogAlgorithm.csproj b/T2dMath.ILogAlgorithm/T2dMath.ILogAlgorithm.csproj index 41a1665..e3160cd 100644 --- a/T2dMath.ILogAlgorithm/T2dMath.ILogAlgorithm.csproj +++ b/T2dMath.ILogAlgorithm/T2dMath.ILogAlgorithm.csproj @@ -1,8 +1,8 @@ - - - Library - net8.0 - enable - true - - + + + Library + net8.0 + enable + true + + diff --git a/T2dMath.LogAlgorithm/Abstract/ALogOneCurveInput.cs b/T2dMath.LogAlgorithm/Abstract/ALogOneCurveInput.cs index c292e57..311ff13 100644 --- a/T2dMath.LogAlgorithm/Abstract/ALogOneCurveInput.cs +++ b/T2dMath.LogAlgorithm/Abstract/ALogOneCurveInput.cs @@ -1,22 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using T2dMath.LogAlgorithm.Abstract; - -namespace T2dMath.IAlgorithm -{ - public abstract class ALogOneCurveInput : ALogTransformation - { - protected abstract double[] Calculate(double[] reference_curve, double[] input_curve); - - protected override double[] Calculate(double[] reference_curve, double[][] input_curves) - { - if (input_curves.Length != 1) - { - return Enumerable.Repeat(double.NaN, reference_curve.Length).ToArray(); - } - return Calculate(reference_curve, input_curves[0]); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using T2dMath.LogAlgorithm.Abstract; + +namespace T2dMath.IAlgorithm { + public abstract class ALogOneCurveInput : ALogTransformation { + protected abstract double[] Calculate(double[] reference_curve, double[] input_curve); + + protected override double[] Calculate(double[] reference_curve, double[][] input_curves) { + if (input_curves.Length != 1) { + return Enumerable.Repeat(double.NaN, reference_curve.Length).ToArray(); + } + return Calculate(reference_curve, input_curves[0]); + } + } +} diff --git a/T2dMath.LogAlgorithm/Abstract/ALogPoint2Point.cs b/T2dMath.LogAlgorithm/Abstract/ALogPoint2Point.cs index 51d505b..161b12a 100644 --- a/T2dMath.LogAlgorithm/Abstract/ALogPoint2Point.cs +++ b/T2dMath.LogAlgorithm/Abstract/ALogPoint2Point.cs @@ -1,29 +1,26 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace T2dMath.IAlgorithm -{ - /// - /// Алгоритмы поточечного пересчёта одной кривой в другую - /// - public abstract class ALogPoint2Point : ALogOneCurveInput - { - protected abstract double Calculate(double reference_curve, double input_curve); - - protected override double[] Calculate(double[] reference_curve, double[] input_curve) - { - var n = reference_curve.Length; - - double[] result = Enumerable - .Range(0, n) - .AsParallel() - .Select(i => Calculate(reference_curve[i], input_curve[0])) - .ToArray(); - - return result; - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace T2dMath.IAlgorithm { + /// + /// Алгоритмы поточечного пересчёта одной кривой в другую + /// + public abstract class ALogPoint2Point : ALogOneCurveInput { + protected abstract double Calculate(double reference_curve, double input_curve); + + protected override double[] Calculate(double[] reference_curve, double[] input_curve) { + var n = reference_curve.Length; + + double[] result = Enumerable + .Range(0, n) + .AsParallel() + .Select(i => Calculate(reference_curve[i], input_curve[0])) + .ToArray(); + + return result; + } + } +} diff --git a/T2dMath.LogAlgorithm/Abstract/ALogTransformation.cs b/T2dMath.LogAlgorithm/Abstract/ALogTransformation.cs index 02b4782..83704e0 100644 --- a/T2dMath.LogAlgorithm/Abstract/ALogTransformation.cs +++ b/T2dMath.LogAlgorithm/Abstract/ALogTransformation.cs @@ -1,25 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using T2dMath.ILogAlgorithm; - -namespace T2dMath.LogAlgorithm.Abstract -{ - public abstract class ALogTransformation : ILogTransformation - { - protected virtual void InitOptions(object[] param) { } - - protected abstract double[] Calculate(double[] reference_curve, double[][] curves_in); - - public double[] Transformation( - double[] reference_curve, - double[][] input_curves, - object[] param - ) - { - InitOptions(param); - return Calculate(reference_curve, input_curves); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using T2dMath.ILogAlgorithm; + +namespace T2dMath.LogAlgorithm.Abstract { + public abstract class ALogTransformation : ILogTransformation { + protected virtual void InitOptions(object[] param) { } + + protected abstract double[] Calculate(double[] reference_curve, double[][] curves_in); + + public double[] Transformation( + double[] reference_curve, + double[][] input_curves, + object[] param + ) { + InitOptions(param); + return Calculate(reference_curve, input_curves); + } + } +} diff --git a/T2dMath.LogAlgorithm/LogAlgorithmDoubler.cs b/T2dMath.LogAlgorithm/LogAlgorithmDoubler.cs index 7b5e528..3adcae5 100644 --- a/T2dMath.LogAlgorithm/LogAlgorithmDoubler.cs +++ b/T2dMath.LogAlgorithm/LogAlgorithmDoubler.cs @@ -1,20 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; -using T2dMath.IAlgorithm; - -namespace T2dMath.LogAlgorithm -{ - /// - /// Алгоритм дублирования кривой - /// - public class LogAlgorithmDoubler : ALogOneCurveInput - { - protected override double[] Calculate(double[] reference_curve, double[] input_curve) - { - double[] result = new double[input_curve.Length]; - Array.Copy(input_curve, result, input_curve.Length); - return result; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using T2dMath.IAlgorithm; + +namespace T2dMath.LogAlgorithm { + /// + /// Алгоритм дублирования кривой + /// + public class LogAlgorithmDoubler : ALogOneCurveInput { + protected override double[] Calculate(double[] reference_curve, double[] input_curve) { + double[] result = new double[input_curve.Length]; + Array.Copy(input_curve, result, input_curve.Length); + return result; + } + } +} diff --git a/T2dMath.LogAlgorithm/LogAlgorithmPolynom.cs b/T2dMath.LogAlgorithm/LogAlgorithmPolynom.cs index 422798b..b14e46c 100644 --- a/T2dMath.LogAlgorithm/LogAlgorithmPolynom.cs +++ b/T2dMath.LogAlgorithm/LogAlgorithmPolynom.cs @@ -1,67 +1,56 @@ -using System; -using System.Globalization; -using T2dMath.IAlgorithm; - -namespace T2dMath.LogAlgorithm -{ - /// - /// - /// - public class LogAlgorithmPolynom : ALogPoint2Point - { - double[] coefficients; - - protected override void InitOptions(object[] param) - { - coefficients = new double[param.Length]; - for (int i = 0; i < param.Length; i++) - { - try - { - if (param[i] is double) - { - coefficients[i] = (double)param[i]; - continue; - } - if (param[i] is string) - { - coefficients[i] = double.Parse( - (string)param[i], - CultureInfo.InvariantCulture - ); - continue; - } - coefficients[i] = double.NaN; - } - catch - { - coefficients[i] = double.NaN; - } - } - } - - // Метод для вычисления полинома по схеме Горнера - // coefficients - массив коэффициентов полинома от старшей степени к младшей - // x - значение переменной, для которой вычисляется полином - private static double HornerScheme(double[] coefficients, double x) - { - double result = coefficients[0]; // Начинаем со старшего коэффициента - - for (int i = 1; i < coefficients.Length; i++) - { - result = result * x + coefficients[i]; // Умножаем на x и прибавляем следующий коэффициент - } - - return result; - } - - // Пример: вычисление полинома 2x^3 - 6x^2 + 2x - 1 для x = 3 - //double[] coefficients = { 2, -6, 2, -1 }; // Коэффициенты: 2, -6, 2, -1 - //double x = 3; - //double value = HornerScheme(coefficients, x); - protected override double Calculate(double reference_curve, double value) - { - return HornerScheme(coefficients, value); - } - } -} +using System; +using System.Globalization; +using T2dMath.IAlgorithm; + +namespace T2dMath.LogAlgorithm { + /// + /// + /// + public class LogAlgorithmPolynom : ALogPoint2Point { + double[] coefficients; + + protected override void InitOptions(object[] param) { + coefficients = new double[param.Length]; + for (int i = 0; i < param.Length; i++) { + try { + if (param[i] is double) { + coefficients[i] = (double)param[i]; + continue; + } + if (param[i] is string) { + coefficients[i] = double.Parse( + (string)param[i], + CultureInfo.InvariantCulture + ); + continue; + } + coefficients[i] = double.NaN; + } + catch { + coefficients[i] = double.NaN; + } + } + } + + // Метод для вычисления полинома по схеме Горнера + // coefficients - массив коэффициентов полинома от старшей степени к младшей + // x - значение переменной, для которой вычисляется полином + private static double HornerScheme(double[] coefficients, double x) { + double result = coefficients[0]; // Начинаем со старшего коэффициента + + for (int i = 1; i < coefficients.Length; i++) { + result = result * x + coefficients[i]; // Умножаем на x и прибавляем следующий коэффициент + } + + return result; + } + + // Пример: вычисление полинома 2x^3 - 6x^2 + 2x - 1 для x = 3 + //double[] coefficients = { 2, -6, 2, -1 }; // Коэффициенты: 2, -6, 2, -1 + //double x = 3; + //double value = HornerScheme(coefficients, x); + protected override double Calculate(double reference_curve, double value) { + return HornerScheme(coefficients, value); + } + } +} diff --git a/T2dMath.LogAlgorithm/LogAlgorithmVikDfRok.cs b/T2dMath.LogAlgorithm/LogAlgorithmVikDfRok.cs index 792a81d..e755876 100644 --- a/T2dMath.LogAlgorithm/LogAlgorithmVikDfRok.cs +++ b/T2dMath.LogAlgorithm/LogAlgorithmVikDfRok.cs @@ -1,29 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Transactions; -using T2dMath.LogAlgorithm; - -namespace T2dMath.LogAlgorithm -{ - public class LogAlgorithmVikDfRok : LogAlgorithmPolynom - { - private Dictionary base_coeff = new Dictionary() - { - { "DF05", new object[] { 0.0 } }, - }; - - protected override void InitOptions(object[] param) - { - if ( - param.Length != 1 - || !(param[0] is string) - || !base_coeff.ContainsKey((string)param[0]) - ) - { - base.InitOptions(new object[] { double.NaN }); - } - base.InitOptions(base_coeff[(string)param[0]]); - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Transactions; +using T2dMath.LogAlgorithm; + +namespace T2dMath.LogAlgorithm { + public class LogAlgorithmVikDfRok : LogAlgorithmPolynom { + private Dictionary base_coeff = new Dictionary() + { + { "DF05", new object[] { 0.0 } }, + }; + + protected override void InitOptions(object[] param) { + if ( + param.Length != 1 + || !(param[0] is string) + || !base_coeff.ContainsKey((string)param[0]) + ) { + base.InitOptions(new object[] { double.NaN }); + } + base.InitOptions(base_coeff[(string)param[0]]); + } + } +} diff --git a/T2dMath.LogAlgorithm/T2dMath.LogAlgorithm.csproj b/T2dMath.LogAlgorithm/T2dMath.LogAlgorithm.csproj index b268e59..969dc38 100644 --- a/T2dMath.LogAlgorithm/T2dMath.LogAlgorithm.csproj +++ b/T2dMath.LogAlgorithm/T2dMath.LogAlgorithm.csproj @@ -1,11 +1,11 @@ - - - Library - net8.0 - enable - true - - - - - + + + Library + net8.0 + enable + true + + + + + diff --git a/T2dMath.Pipeline.Test/CalculateDepthTestSimple.cs b/T2dMath.Pipeline.Test/CalculateDepthTestSimple.cs index 75d4b60..cfe7573 100644 --- a/T2dMath.Pipeline.Test/CalculateDepthTestSimple.cs +++ b/T2dMath.Pipeline.Test/CalculateDepthTestSimple.cs @@ -1,51 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Xunit; - -namespace T2dMath.Pipeline.Test -{ - public class CalculateDepthTestSimple - { - [Fact] - public void CalculateDepth_CalcStopsAndPromer() - { - double[] time_surface = { 10000, 20000, 30000, 40000 }; - double[] block_height = null; - double[] weight = null; - double[] stop = null; - int min_time = 5000; - double[] length_drill_pipe = { 24 }; - double length_tool = 100; - double[] depth = { 2000, 2000, 2012, 2012 }; - double[] expected_depth = { 100, 100, 124, 124 }; - - Pipelines.CalculateDepth( - time_surface, - block_height, - weight, - min_time, - ref stop, - length_drill_pipe, - length_tool, - ref depth, - out (double time_start, double time_end)[] idx_stop_interval, - out double[] kmlt, - out double[] kmove); - - Assert.NotNull(stop); - Assert.Equal(new double[] { 0, 0, 1, 1 }, stop); - Assert.Equal(expected_depth.Length, depth.Length); - for (int i = 0; i < depth.Length; i++) - Assert.Equal(expected_depth[i], depth[i], 6); - - Assert.Single(kmlt); - Assert.Single(kmove); - Assert.Equal(2.0, kmlt[0], 6); - Assert.Equal(-3900, kmove[0], 6); - - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace T2dMath.Pipeline.Test { + public class CalculateDepthTestSimple { + [Fact] + public void CalculateDepth_CalcStopsAndPromer() { + double[] time_surface = { 10000, 20000, 30000, 40000 }; + double[] block_height = null; + double[] weight = null; + double[] stop = null; + int min_time = 5000; + double[] length_drill_pipe = { 24 }; + double length_tool = 100; + double[] depth = { 2000, 2000, 2012, 2012 }; + double[] expected_depth = { 100, 100, 124, 124 }; + + Pipelines.CalculateDepth( + time_surface, + block_height, + weight, + min_time, + ref stop, + length_drill_pipe, + length_tool, + ref depth, + out (double time_start, double time_end)[] idx_stop_interval, + out double[] kmlt, + out double[] kmove); + + Assert.NotNull(stop); + Assert.Equal(new double[] { 0, 0, 1, 1 }, stop); + Assert.Equal(expected_depth.Length, depth.Length); + for (int i = 0; i < depth.Length; i++) + Assert.Equal(expected_depth[i], depth[i], 6); + + Assert.Single(kmlt); + Assert.Single(kmove); + Assert.Equal(2.0, kmlt[0], 6); + Assert.Equal(-3900, kmove[0], 6); + + } + } +} diff --git a/T2dMath.Pipeline.Test/TestFullWorkflow.cs b/T2dMath.Pipeline.Test/TestFullWorkflow.cs index 71029de..c94c03e 100644 --- a/T2dMath.Pipeline.Test/TestFullWorkflow.cs +++ b/T2dMath.Pipeline.Test/TestFullWorkflow.cs @@ -1,24 +1,21 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Xml.Linq; using System.Text.Json; +using System.Xml.Linq; using T2dMath.Pipeline; using T2dMath.Utils; -using Xunit; using T2dMath.UtilsTest; +using Xunit; -namespace T2dMath.Pipeline.Test -{ - public class FullPipelineTests - { +namespace T2dMath.Pipeline.Test { + public class FullPipelineTests { private const string path = "../../../../logs/"; [Fact] - public void Test_Two_Pipelines_WithParsedData() - { + public void Test_Two_Pipelines_WithParsedData() { var nameFileDataTool = Path.Combine(path, "1", "logging.gti.las"); var nameFileTool = Path.Combine(path, "1", "tool.device"); var nameFilePromer = Path.Combine(path, "1", "custom_promer.promer"); @@ -110,8 +107,7 @@ out double[] kmove Assert.Contains(curve_out["GR"], v => !double.IsNaN(v)); // 💾 Сохранение результатов - var outputData = new - { + var outputData = new { Depth = depth_out, Curves = curve_out, Parameters = param @@ -121,8 +117,7 @@ out double[] kmove Directory.CreateDirectory(outputDirectory); string outputFilePath = Path.Combine(outputDirectory, "FullPipelineOutput.json"); - var jsonOptions = new JsonSerializerOptions - { + var jsonOptions = new JsonSerializerOptions { WriteIndented = true, NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals }; diff --git a/T2dMath.Pipeline.Test/TestPipeline.cs b/T2dMath.Pipeline.Test/TestPipeline.cs index 187a9aa..4d0a3d2 100644 --- a/T2dMath.Pipeline.Test/TestPipeline.cs +++ b/T2dMath.Pipeline.Test/TestPipeline.cs @@ -8,15 +8,12 @@ using T2dMath.Utils; using Xunit; -namespace T2dMath.Pipeline.Test -{ - public class PipelineTests - { +namespace T2dMath.Pipeline.Test { + public class PipelineTests { private const string path = "../../../../logs/"; [Fact] - public void Test_Pipeline_WithParsedData() - { + public void Test_Pipeline_WithParsedData() { var nameFileDataTool = Path.Combine(path, "1", "3351_39.mpp.las"); var nameFileTool = Path.Combine(path, "1", "tool.device"); var nameFileTimeDepth = Path.Combine(path, "1", "Ti_Dept.GTI.LAS"); @@ -54,8 +51,7 @@ out Dictionary curvesDepths }; Dictionary param = new Dictionary(); - foreach (var p in parametersTools) - { + foreach (var p in parametersTools) { param.Add(p.Key, p.Value); } @@ -71,10 +67,8 @@ string[] name_params { "IK05", offsetIK05 }, }; - foreach (var cur_curve_in in dataTools) - { - if (cur_curve_in.Key != "TIME") - { + foreach (var cur_curve_in in dataTools) { + if (cur_curve_in.Key != "TIME") { curve_in[cur_curve_in.Key] = cur_curve_in.Value; measure_point[cur_curve_in.Key] = 0; } @@ -125,8 +119,7 @@ string[] name_params ); //TODO вынести этот кусок в отдельную функцию - var outputData = new - { + var outputData = new { Depth = depth_out, Curves = curve_out, Parameters = param, @@ -137,8 +130,7 @@ string[] name_params string outputFilePath = Path.Combine(outputDirectory, "PipelineTestOutput.json"); - var options = new JsonSerializerOptions - { + var options = new JsonSerializerOptions { WriteIndented = true, NumberHandling = System .Text diff --git a/T2dMath.Pipeline.Test/TestPipelineSimple.cs b/T2dMath.Pipeline.Test/TestPipelineSimple.cs index a2d6e37..d97ec26 100644 --- a/T2dMath.Pipeline.Test/TestPipelineSimple.cs +++ b/T2dMath.Pipeline.Test/TestPipelineSimple.cs @@ -1,81 +1,78 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using T2dMath.Pipeline; -using Xunit; - -namespace T2dMath.Pipeline.Test -{ - public class TestPipelineSimple - { - [Fact] - public void TestPipeline_Simple() - { - var date = new DateTime(2000, 1, 1); - double[] time_tool = - { - date.AddMilliseconds(1000).ToOADate(), - date.AddMilliseconds(1001).ToOADate(), - date.AddMilliseconds(1002).ToOADate(), - }; - double[] depth_surface = { 2000, 2001, 2002 }; - - var param = new Dictionary() { { "KNNS", 1.23 }, { "GGFG1", 4.56 } }; - - var curve_in = new Dictionary { { "GR", [45, -999.25, 47] } }; - - var calculate_time_curve = Array.Empty<( - string name_curve, - Pipelines.AlgorithmProcessor alg, - string[] name_curves, - string[] name_params - )>(); - var calculate_depth_curve = Array.Empty<( - string name_curve, - Pipelines.AlgorithmProcessor alg, - string[] name_curves, - string[] name_params - )>(); - - Dictionary measure_point = new Dictionary - { - { "GR", 0.0 }, - }; - - double[] time_surface = time_tool; - double[] depth = depth_surface; - double[] block_height = new double[0]; - double[] weight = new double[0]; - double[] stop = new double[0]; - double[] length_drill_pipe = new double[0]; - double step_grid = 0.1; - - Pipelines.Time2DepthLogging( - time_tool, - curve_in, - param, - calculate_time_curve, - measure_point, - time_surface, - depth, - block_height, - weight, - stop, - length_drill_pipe, - calculate_depth_curve, - step_grid, - out double[] depth_out, - out Dictionary curve_out, - progress: null, - info: (level, message, obj) => Console.WriteLine($"{level}: {message}") - ); - - Assert.NotNull(depth_out); - Assert.True(depth_out.Length > 0); - Assert.NotNull(curve_out); - Assert.True(curve_out.ContainsKey("GR")); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using T2dMath.Pipeline; +using Xunit; + +namespace T2dMath.Pipeline.Test { + public class TestPipelineSimple { + [Fact] + public void TestPipeline_Simple() { + var date = new DateTime(2000, 1, 1); + double[] time_tool = + { + date.AddMilliseconds(1000).ToOADate(), + date.AddMilliseconds(1001).ToOADate(), + date.AddMilliseconds(1002).ToOADate(), + }; + double[] depth_surface = { 2000, 2001, 2002 }; + + var param = new Dictionary() { { "KNNS", 1.23 }, { "GGFG1", 4.56 } }; + + var curve_in = new Dictionary { { "GR", [45, -999.25, 47] } }; + + var calculate_time_curve = Array.Empty<( + string name_curve, + Pipelines.AlgorithmProcessor alg, + string[] name_curves, + string[] name_params + )>(); + var calculate_depth_curve = Array.Empty<( + string name_curve, + Pipelines.AlgorithmProcessor alg, + string[] name_curves, + string[] name_params + )>(); + + Dictionary measure_point = new Dictionary + { + { "GR", 0.0 }, + }; + + double[] time_surface = time_tool; + double[] depth = depth_surface; + double[] block_height = new double[0]; + double[] weight = new double[0]; + double[] stop = new double[0]; + double[] length_drill_pipe = new double[0]; + double step_grid = 0.1; + + Pipelines.Time2DepthLogging( + time_tool, + curve_in, + param, + calculate_time_curve, + measure_point, + time_surface, + depth, + block_height, + weight, + stop, + length_drill_pipe, + calculate_depth_curve, + step_grid, + out double[] depth_out, + out Dictionary curve_out, + progress: null, + info: (level, message, obj) => Console.WriteLine($"{level}: {message}") + ); + + Assert.NotNull(depth_out); + Assert.True(depth_out.Length > 0); + Assert.NotNull(curve_out); + Assert.True(curve_out.ContainsKey("GR")); + } + } +} diff --git a/T2dMath.Pipeline/CalculateDepth.cs b/T2dMath.Pipeline/CalculateDepth.cs index 4ecb47b..d7d7b6d 100644 --- a/T2dMath.Pipeline/CalculateDepth.cs +++ b/T2dMath.Pipeline/CalculateDepth.cs @@ -1,145 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using static System.Formats.Asn1.AsnWriter; - -namespace T2dMath.Pipeline -{ - public partial class Pipelines - { - public static void CalculateDepth( - double[] time_surface, - double[] block_height, - double[] weight, - int min_time, - ref double[] fstop, - double[] length_drill_pipe, - double length_tool, - ref double[] depth, - out (double time_start, double time_end)[] stop_intervals, - out double[] kmlt, - out double[] kmove - ) - { - if (time_surface == null) - throw new ArgumentException("Массив time_surface не может быть null"); - - /*if (depth == null && length_drill_pipe == null) - throw new ArgumentException("Нужно задать либо depth, либо length_drill_pipe");*/ - - if (length_drill_pipe != null && length_drill_pipe.Any(d => double.IsNaN(d) || d <= 0)) - throw new ArgumentException("Все длины труб должны быть > 0 и не NaN"); - - const int GATE_MS = 20000; // ширина окна, мс - const double CRITERIA = 5; // чувствительность к изменению веса - - int n = time_surface.Length; - - if (fstop != null) - { - stop_intervals = LogMath.CurveToStops(time_surface, fstop); - } - else if (depth != null) - { - //int min_time = 5 * 1000; - double eps = 1e-12; - stop_intervals = LogMath.CalculateStopsByDepth(time_surface, depth, min_time, eps); - //double[] stops_curve = new double[time_surface.Length]; - - fstop = LogMath.StopsToCurve(stop_intervals.Length, stop_intervals, time_surface); - - } - else if (weight != null && block_height != null) - { - //throw new NotImplementedException(); - //AlmazMath.CalculateStopsByWeightStatistic(time, weight, 20000, 5, stops); - //AlmazMath.FilterMoveNoOperation(time, blockMovement, stops); - //AlmazMath.CorrectStartAndStopMove(time, blockMovement, weight, stops); - stop_intervals = LogMath.CalculateStopsStatisticVlasov(time_surface, weight, GATE_MS, CRITERIA); - //stop_intervals = LogMath.CalculateStopsStatisticVlasovAuto(time_surface, weight); - stop_intervals = LogMath.FilterMoveNoOperation(time_surface, block_height, stop_intervals); - stop_intervals = LogMath.CorrectStartAndStopMove(time_surface, block_height, weight, stop_intervals); - fstop = LogMath.StopsToCurve(stop_intervals.Length, stop_intervals, time_surface); - } - - else - { - throw new ArgumentException("По входным данным нельзя вычислить стоянки"); - } - if (depth == null) - { - //AlmazMath.CalculateDepthOnRazrezData(time, blockMovement, stops, depth); - depth = LogMath.CalculateDepthOnRazrezData(time_surface, block_height, stop_intervals); - } - - if (depth != null && length_drill_pipe != null) - { - // Необходимо посадить - int m = stop_intervals.Length; - //(int idx_start, int idx_stop)[] stopidx_intervals = LogMath. - //double[] stopsStart = stop_intervals.Select(t => time_surface[t.idx_start]).ToArray(); - //double[] stopsFinish = stop_intervals.Select(t => time_surface[t.idx_finish]).ToArray(); - double[] stopsStart = stop_intervals.Select(t => t.time_start).ToArray(); - double[] stopsFinish = stop_intervals.Select(t => t.time_end).ToArray(); - - LogMath.CorrectStopsFixZaboy( - n, - time_surface, - depth, - m, - //stopsStart, - //stopsFinish, - stop_intervals, - epsZaboy: 0.1, - length_tool, - length_drill_pipe, - out int mFixed, - out (double start, double finish)[] time_intervals_fixed); - - int npipe = length_drill_pipe.Length; - double[] njoint = new double[n]; - kmlt = new double[Math.Max(0, mFixed - 1)]; - kmove = new double[Math.Max(0, mFixed - 1)]; - double[] deformationPipe = new double[npipe]; - int numStopPipe, numStartPipe; - - int ret = LogMath.SetAbsoluteDepthByPromerVlasov( - n, - time_surface, - depth, - njoint, - mFixed, - time_intervals_fixed, - npipe, - length_drill_pipe, - maxFullPipe: npipe, - lengthDevice: length_tool, - epsZaboy: 0.1, - kmlt, - kmove, - out numStartPipe, - out numStopPipe, - deformationPipe); - - if (ret != 0) - throw new InvalidOperationException($"Ошибка в расчете глубины: код {ret}"); - } - else if (depth == null) - { - // Вычисление глубины на основе промеров - var zaboy = length_drill_pipe.Sum() + length_tool; - depth = new double[n]; - for (int i = 0; i < n; i++) - depth[i] = zaboy; - - kmlt = Array.Empty(); - kmove = Array.Empty(); - } - else - { - throw new InvalidOperationException("Недостаточно данных для вычисления глубины"); - } - //throw new NotImplementedException(); - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Linq; +using static System.Formats.Asn1.AsnWriter; + +namespace T2dMath.Pipeline { + public partial class Pipelines { + public static void CalculateDepth( + double[] time_surface, + double[] block_height, + double[] weight, + int min_time, + ref double[] fstop, + double[] length_drill_pipe, + double length_tool, + ref double[] depth, + out (double time_start, double time_end)[] stop_intervals, + out double[] kmlt, + out double[] kmove + ) { + if (time_surface == null) + throw new ArgumentException("Массив time_surface не может быть null"); + + /*if (depth == null && length_drill_pipe == null) + throw new ArgumentException("Нужно задать либо depth, либо length_drill_pipe");*/ + + if (length_drill_pipe != null && length_drill_pipe.Any(d => double.IsNaN(d) || d <= 0)) + throw new ArgumentException("Все длины труб должны быть > 0 и не NaN"); + + const int GATE_MS = 20000; // ширина окна, мс + const double CRITERIA = 5; // чувствительность к изменению веса + + int n = time_surface.Length; + + if (fstop != null) { + stop_intervals = LogMath.CurveToStops(time_surface, fstop); + } + else if (depth != null) { + //int min_time = 5 * 1000; + double eps = 1e-12; + stop_intervals = LogMath.CalculateStopsByDepth(time_surface, depth, min_time, eps); + //double[] stops_curve = new double[time_surface.Length]; + + fstop = LogMath.StopsToCurve(stop_intervals.Length, stop_intervals, time_surface); + + } + else if (weight != null && block_height != null) { + //throw new NotImplementedException(); + //AlmazMath.CalculateStopsByWeightStatistic(time, weight, 20000, 5, stops); + //AlmazMath.FilterMoveNoOperation(time, blockMovement, stops); + //AlmazMath.CorrectStartAndStopMove(time, blockMovement, weight, stops); + stop_intervals = LogMath.CalculateStopsStatisticVlasov(time_surface, weight, GATE_MS, CRITERIA); + //stop_intervals = LogMath.CalculateStopsStatisticVlasovAuto(time_surface, weight); + stop_intervals = LogMath.FilterMoveNoOperation(time_surface, block_height, stop_intervals); + stop_intervals = LogMath.CorrectStartAndStopMove(time_surface, block_height, weight, stop_intervals); + fstop = LogMath.StopsToCurve(stop_intervals.Length, stop_intervals, time_surface); + } + + else { + throw new ArgumentException("По входным данным нельзя вычислить стоянки"); + } + if (depth == null) { + //AlmazMath.CalculateDepthOnRazrezData(time, blockMovement, stops, depth); + depth = LogMath.CalculateDepthOnRazrezData(time_surface, block_height, stop_intervals); + } + + if (depth != null && length_drill_pipe != null) { + // Необходимо посадить + int m = stop_intervals.Length; + //(int idx_start, int idx_stop)[] stopidx_intervals = LogMath. + //double[] stopsStart = stop_intervals.Select(t => time_surface[t.idx_start]).ToArray(); + //double[] stopsFinish = stop_intervals.Select(t => time_surface[t.idx_finish]).ToArray(); + double[] stopsStart = stop_intervals.Select(t => t.time_start).ToArray(); + double[] stopsFinish = stop_intervals.Select(t => t.time_end).ToArray(); + + LogMath.CorrectStopsFixZaboy( + n, + time_surface, + depth, + m, + //stopsStart, + //stopsFinish, + stop_intervals, + epsZaboy: 0.1, + length_tool, + length_drill_pipe, + out int mFixed, + out (double start, double finish)[] time_intervals_fixed); + + int npipe = length_drill_pipe.Length; + double[] njoint = new double[n]; + kmlt = new double[Math.Max(0, mFixed - 1)]; + kmove = new double[Math.Max(0, mFixed - 1)]; + double[] deformationPipe = new double[npipe]; + int numStopPipe, numStartPipe; + + int ret = LogMath.SetAbsoluteDepthByPromerVlasov( + n, + time_surface, + depth, + njoint, + mFixed, + time_intervals_fixed, + npipe, + length_drill_pipe, + maxFullPipe: npipe, + lengthDevice: length_tool, + epsZaboy: 0.1, + kmlt, + kmove, + out numStartPipe, + out numStopPipe, + deformationPipe); + + if (ret != 0) + throw new InvalidOperationException($"Ошибка в расчете глубины: код {ret}"); + } + else if (depth == null) { + // Вычисление глубины на основе промеров + var zaboy = length_drill_pipe.Sum() + length_tool; + depth = new double[n]; + for (int i = 0; i < n; i++) + depth[i] = zaboy; + + kmlt = Array.Empty(); + kmove = Array.Empty(); + } + else { + throw new InvalidOperationException("Недостаточно данных для вычисления глубины"); + } + //throw new NotImplementedException(); + } + } +} diff --git a/T2dMath.Pipeline/PipelinesInit.cs b/T2dMath.Pipeline/PipelinesInit.cs index 10769ea..ac44d7d 100644 --- a/T2dMath.Pipeline/PipelinesInit.cs +++ b/T2dMath.Pipeline/PipelinesInit.cs @@ -1,287 +1,270 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace T2dMath.Pipeline -{ - public partial class Pipelines - { - public delegate double[] AlgorithmProcessor( - double[] reference_curve, - double[][] curves_in, - object[] param - ); - - /// - /// Делегат для передачи информации о прогрессе вычисления - /// - /// Значение 0..1 - прогрессс вычисления - /// true - продолжать работу алгоритма, false - остановить - public delegate bool ProgressCallback(double progress); - - public enum LevelInfo - { - Info, - Warning, - Error, - } - - public delegate void InfoCallback(LevelInfo level, string message, object obj = null); - - public static void InterlinkCurve( - double[] timeMPP, - double[] valueMPP, - double[] timeAction, - double[] depthAction, - double[] depthGrid, - double delayTime, - double measurepoint, - out double[] resCurve - ) - { - // Предусловие на корректную работу функции - if ( - timeAction.Length == 0 - || timeMPP.Length != valueMPP.Length - || timeAction.Length != depthAction.Length - ) - { - throw new ApplicationException(); - } - double[] time_curve = new double[timeMPP.Length]; - - Array.Copy(timeMPP, time_curve, timeMPP.Length); - - // Смещение времени зарегистрированного сигнала - for (int i = 0; i < time_curve.Length; i++) - { - time_curve[i] -= delayTime; - } - - LogMath.IntervalsIntersection( - (timeAction[0], timeAction[^1]), - time_curve, - out (LogMath.ActionIntersection status, int idx_start, int idx_end) intersection - ); - - if (intersection.status == LogMath.ActionIntersection.NoIntersection) - { - throw new Exception("Данные глубиномера не пересекаются с данными МПП."); - } - - int lengthIntersection = intersection.idx_end - intersection.idx_start + 1; - - double[] timeMPP_cut = new double[lengthIntersection]; - double[] valueMPP_cut = new double[lengthIntersection]; - - // Вырезаем кусок данных в интервале времен наземных датчиков - Array.ConstrainedCopy( - time_curve, - intersection.idx_start, - timeMPP_cut, - 0, - lengthIntersection - ); - Array.ConstrainedCopy( - valueMPP, - intersection.idx_start, - valueMPP_cut, - 0, - lengthIntersection - ); - - LogMath.FilterCurveNotNull(ref timeMPP_cut, ref valueMPP_cut); - - // Привязка глубины к времени - LogMath.MatchDepthToLine( - timeMPP_cut, - timeAction, - depthAction, - measurepoint, - out double[] depthMPP_cut - ); - - double[] dataRes = new double[depthGrid.Length]; - - if (valueMPP_cut.Length > 0) - { - LogMath.BuildMonotoneLogSmart( - depthMPP_cut, - timeMPP_cut, - valueMPP_cut, - out double[] depth_monoton, - out double[] value_monoton - ); - - // TODO используется только ОДИН алгоритм - LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthGrid, out resCurve); - } - else - { - resCurve = Enumerable.Repeat(double.NaN, depthGrid.Length).ToArray(); - } - } - - // Методы из первого файла - public static void Time2DepthLogging( - double[] time_tool, - Dictionary curve_in, - Dictionary param, - ( - string name_curve, - AlgorithmProcessor alg, - string[] name_curves, - string[] name_params - )[] calculate_time_curve, - Dictionary measure_point, - double[] time_surface, - double[] depth, - double[] block_height, - double[] weight, - double[] stop, - double[] length_drill_pipe, - ( - string name_curve, - AlgorithmProcessor alg, - string[] name_curves, - string[] name_params - )[] calculate_depth_curve, - double step_grid, - out double[] depth_out, - out Dictionary curve_out, - ProgressCallback progress = null, - InfoCallback info = null - ) - { - // длина прибора = макс точка записи + шаг сетки по глубине - double tool_length = measure_point.Values.Max(); - /*(from n in measure_point orderby n.Value descending, n.Key select n).First().Value - + step_grid;*/ - var curve_time = new Dictionary(curve_in); - - info?.Invoke(LevelInfo.Info, "Стартовал процесс увязки"); - - (double start, double finish)[] time_interval_stops; - - if (stop == null) - { - int minTime = 5000; //стоянка не может быть меньше 5-ти секунд - double eps = 0.1; //погрешность глубины для определения стоянок - - info?.Invoke( - LevelInfo.Warning, - "Данные о стоянках не найдены. Запущен алгоритм автоматического определения стоянок." - ); - - time_interval_stops = LogMath.CalculateStopsByDepth( - time_surface, - depth, - minTime, - eps - ); - - info?.Invoke( - LevelInfo.Info, - "Определено " + time_interval_stops.Length + " стоянок" - ); - } - else - { - time_interval_stops = LogMath.CurveToStops(time_surface, stop); - } - - // TODO - // Добавить проверку на монотонность IsMonotoneTime(time_tool, time_surface); - - // Логика обработки данных, анализ действий, привязка данных по глубине - info?.Invoke(LevelInfo.Info, "Определение интервалов действия(спуск/подъём)"); - var direction = LogMath.AnalysisActionSimpleVlasov(time_surface, depth); - - (int idx_start, int idx_end) idx_action = (0, time_surface.Length - 1); - - DateTime start_action = DateTime.FromOADate(time_surface[0]); - DateTime end_action = DateTime.FromOADate(time_surface[^1]); - - string formatData = "yyyy-MM-dd-HH:mm:ss.fff"; - string action_str = direction == LogMath.ToolDirectionMoving.Up ? "подъём" : "спуск"; - info?.Invoke( - LevelInfo.Info, - "Определен интервал c " - + start_action.ToString(formatData) - + " по " - + end_action.ToString(formatData) - + " (" - + action_str - + ")" - ); - - int lengthAction = idx_action.idx_end - idx_action.idx_start + 1; - var timeAction = new double[lengthAction]; - Array.ConstrainedCopy(time_surface, idx_action.idx_start, timeAction, 0, lengthAction); - var depthAction = new double[lengthAction]; - Array.ConstrainedCopy(depth, idx_action.idx_start, depthAction, 0, lengthAction); - - info?.Invoke(LevelInfo.Info, "Расчет сетки по глубине"); - - //Logger написать с какой по какую глубину и шаг - depth_out = LogMath.BuildGrid(depthAction, step_grid, tool_length); - double depthGridStart = depth_out[0]; - double depthGridEnd = depth_out[^1]; - info?.Invoke( - LevelInfo.Info, - "Результирующая сетка по глубине:\n" - + "STRT = " - + depthGridStart - + "\tSTOP = " - + depthGridEnd - + "\tSTEP = " - + step_grid - ); - - //Проверка что хотя бы 3 значения в глубинах - if (depth.Length > 2) - { - // TODO Добавить предобработку временных данных - info?.Invoke(LevelInfo.Info, "Увязка данных по глубине."); - curve_out = new Dictionary(); - - // Увязка расчетных кривых - foreach (var cur_measure_point in measure_point) - { - var name_curve = cur_measure_point.Key; - try - { - // TODO Необходимо передать переменную через аргумент - double delayTime = 0; - - var curve_processing = curve_time[name_curve]; - InterlinkCurve( - time_tool, - curve_processing, - timeAction, - depthAction, - depth_out, - delayTime, - cur_measure_point.Value, - out double[] resCurve - ); - curve_out.Add(name_curve, resCurve); - info?.Invoke(LevelInfo.Info, name_curve + " - OK."); - } - catch (Exception err) - { - info?.Invoke(LevelInfo.Warning, name_curve + " - Ошибка. " + err.Message); - } - } - - // TODO Добавить постобработку глубинных данных - info?.Invoke(LevelInfo.Info, "Увязка по глубине завершена."); - } - else - { - // TODO Нормально обработать это состояние - throw new ApplicationException(); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace T2dMath.Pipeline { + public partial class Pipelines { + public delegate double[] AlgorithmProcessor( + double[] reference_curve, + double[][] curves_in, + object[] param + ); + + /// + /// Делегат для передачи информации о прогрессе вычисления + /// + /// Значение 0..1 - прогрессс вычисления + /// true - продолжать работу алгоритма, false - остановить + public delegate bool ProgressCallback(double progress); + + public enum LevelInfo { + Info, + Warning, + Error, + } + + public delegate void InfoCallback(LevelInfo level, string message, object obj = null); + + public static void InterlinkCurve( + double[] timeMPP, + double[] valueMPP, + double[] timeAction, + double[] depthAction, + double[] depthGrid, + double delayTime, + double measurepoint, + out double[] resCurve + ) { + // Предусловие на корректную работу функции + if ( + timeAction.Length == 0 + || timeMPP.Length != valueMPP.Length + || timeAction.Length != depthAction.Length + ) { + throw new ApplicationException(); + } + double[] time_curve = new double[timeMPP.Length]; + + Array.Copy(timeMPP, time_curve, timeMPP.Length); + + // Смещение времени зарегистрированного сигнала + for (int i = 0; i < time_curve.Length; i++) { + time_curve[i] -= delayTime; + } + + LogMath.IntervalsIntersection( + (timeAction[0], timeAction[^1]), + time_curve, + out (LogMath.ActionIntersection status, int idx_start, int idx_end) intersection + ); + + if (intersection.status == LogMath.ActionIntersection.NoIntersection) { + throw new Exception("Данные глубиномера не пересекаются с данными МПП."); + } + + int lengthIntersection = intersection.idx_end - intersection.idx_start + 1; + + double[] timeMPP_cut = new double[lengthIntersection]; + double[] valueMPP_cut = new double[lengthIntersection]; + + // Вырезаем кусок данных в интервале времен наземных датчиков + Array.ConstrainedCopy( + time_curve, + intersection.idx_start, + timeMPP_cut, + 0, + lengthIntersection + ); + Array.ConstrainedCopy( + valueMPP, + intersection.idx_start, + valueMPP_cut, + 0, + lengthIntersection + ); + + LogMath.FilterCurveNotNull(ref timeMPP_cut, ref valueMPP_cut); + + // Привязка глубины к времени + LogMath.MatchDepthToLine( + timeMPP_cut, + timeAction, + depthAction, + measurepoint, + out double[] depthMPP_cut + ); + + double[] dataRes = new double[depthGrid.Length]; + + if (valueMPP_cut.Length > 0) { + LogMath.BuildMonotoneLogSmart( + depthMPP_cut, + timeMPP_cut, + valueMPP_cut, + out double[] depth_monoton, + out double[] value_monoton + ); + + // TODO используется только ОДИН алгоритм + LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthGrid, out resCurve); + } + else { + resCurve = Enumerable.Repeat(double.NaN, depthGrid.Length).ToArray(); + } + } + + // Методы из первого файла + public static void Time2DepthLogging( + double[] time_tool, + Dictionary curve_in, + Dictionary param, + ( + string name_curve, + AlgorithmProcessor alg, + string[] name_curves, + string[] name_params + )[] calculate_time_curve, + Dictionary measure_point, + double[] time_surface, + double[] depth, + double[] block_height, + double[] weight, + double[] stop, + double[] length_drill_pipe, + ( + string name_curve, + AlgorithmProcessor alg, + string[] name_curves, + string[] name_params + )[] calculate_depth_curve, + double step_grid, + out double[] depth_out, + out Dictionary curve_out, + ProgressCallback progress = null, + InfoCallback info = null + ) { + // длина прибора = макс точка записи + шаг сетки по глубине + double tool_length = measure_point.Values.Max(); + /*(from n in measure_point orderby n.Value descending, n.Key select n).First().Value + + step_grid;*/ + var curve_time = new Dictionary(curve_in); + + info?.Invoke(LevelInfo.Info, "Стартовал процесс увязки"); + + (double start, double finish)[] time_interval_stops; + + if (stop == null) { + int minTime = 5000; //стоянка не может быть меньше 5-ти секунд + double eps = 0.1; //погрешность глубины для определения стоянок + + info?.Invoke( + LevelInfo.Warning, + "Данные о стоянках не найдены. Запущен алгоритм автоматического определения стоянок." + ); + + time_interval_stops = LogMath.CalculateStopsByDepth( + time_surface, + depth, + minTime, + eps + ); + + info?.Invoke( + LevelInfo.Info, + "Определено " + time_interval_stops.Length + " стоянок" + ); + } + else { + time_interval_stops = LogMath.CurveToStops(time_surface, stop); + } + + // TODO + // Добавить проверку на монотонность IsMonotoneTime(time_tool, time_surface); + + // Логика обработки данных, анализ действий, привязка данных по глубине + info?.Invoke(LevelInfo.Info, "Определение интервалов действия(спуск/подъём)"); + var direction = LogMath.AnalysisActionSimpleVlasov(time_surface, depth); + + (int idx_start, int idx_end) idx_action = (0, time_surface.Length - 1); + + DateTime start_action = DateTime.FromOADate(time_surface[0]); + DateTime end_action = DateTime.FromOADate(time_surface[^1]); + + string formatData = "yyyy-MM-dd-HH:mm:ss.fff"; + string action_str = direction == LogMath.ToolDirectionMoving.Up ? "подъём" : "спуск"; + info?.Invoke( + LevelInfo.Info, + "Определен интервал c " + + start_action.ToString(formatData) + + " по " + + end_action.ToString(formatData) + + " (" + + action_str + + ")" + ); + + int lengthAction = idx_action.idx_end - idx_action.idx_start + 1; + var timeAction = new double[lengthAction]; + Array.ConstrainedCopy(time_surface, idx_action.idx_start, timeAction, 0, lengthAction); + var depthAction = new double[lengthAction]; + Array.ConstrainedCopy(depth, idx_action.idx_start, depthAction, 0, lengthAction); + + info?.Invoke(LevelInfo.Info, "Расчет сетки по глубине"); + + //Logger написать с какой по какую глубину и шаг + depth_out = LogMath.BuildGrid(depthAction, step_grid, tool_length); + double depthGridStart = depth_out[0]; + double depthGridEnd = depth_out[^1]; + info?.Invoke( + LevelInfo.Info, + "Результирующая сетка по глубине:\n" + + "STRT = " + + depthGridStart + + "\tSTOP = " + + depthGridEnd + + "\tSTEP = " + + step_grid + ); + + //Проверка что хотя бы 3 значения в глубинах + if (depth.Length > 2) { + // TODO Добавить предобработку временных данных + info?.Invoke(LevelInfo.Info, "Увязка данных по глубине."); + curve_out = new Dictionary(); + + // Увязка расчетных кривых + foreach (var cur_measure_point in measure_point) { + var name_curve = cur_measure_point.Key; + try { + // TODO Необходимо передать переменную через аргумент + double delayTime = 0; + + var curve_processing = curve_time[name_curve]; + InterlinkCurve( + time_tool, + curve_processing, + timeAction, + depthAction, + depth_out, + delayTime, + cur_measure_point.Value, + out double[] resCurve + ); + curve_out.Add(name_curve, resCurve); + info?.Invoke(LevelInfo.Info, name_curve + " - OK."); + } + catch (Exception err) { + info?.Invoke(LevelInfo.Warning, name_curve + " - Ошибка. " + err.Message); + } + } + + // TODO Добавить постобработку глубинных данных + info?.Invoke(LevelInfo.Info, "Увязка по глубине завершена."); + } + else { + // TODO Нормально обработать это состояние + throw new ApplicationException(); + } + } + } +} diff --git a/T2dMath.Pipeline/Time2DepthLogging.cs b/T2dMath.Pipeline/Time2DepthLogging.cs index 7564f75..b740814 100644 --- a/T2dMath.Pipeline/Time2DepthLogging.cs +++ b/T2dMath.Pipeline/Time2DepthLogging.cs @@ -1,58 +1,55 @@ -using System.Collections.Generic; -using System.Linq; -using T2dMath.ILogAlgorithm; - -namespace T2dMath.Pipeline -{ - using AlgCurveInfo = ( - string name_curve, - ILogTransformation alg, - string[] name_curves, - string[] name_params - ); - using CurveInfoTransformation = ( - string name_curve, - double offset, - double time_delay, - bool use_only_rotor, - IGridTransformation alg_avg, - string[] name_params_avg - ); - - public partial class Pipelines - { - //depth_monoton, value_monoton, depthGrid, param_alg - public delegate double[] AlgorithmAvarage( - double[] depth_monotone, - double[] value_monoton, - double[] depthGrid, - object[] param - ); - - // - - public static void Time2DepthLogging( - double[] time_tool, - IDictionary input_curves, - Dictionary input_parameters, - AlgCurveInfo[] calculate_time_curve, - CurveInfoTransformation[] curve_out_param, - double[] time_surface, - double[] depth, - double[] f_stop, - double[] f_rotor, - AlgCurveInfo[] calculate_depth_curve, - double step_grid, - out double[] depth_out, - out Dictionary curve_out, - ProgressCallback progress = null, - InfoCallback info = null - ) - { - IEnumerable t = new double[5]; - var r = t.ElementAt(0); - depth_out = null; - curve_out = null; - } - } -} +using System.Collections.Generic; +using System.Linq; +using T2dMath.ILogAlgorithm; + +namespace T2dMath.Pipeline { + using AlgCurveInfo = ( + string name_curve, + ILogTransformation alg, + string[] name_curves, + string[] name_params + ); + using CurveInfoTransformation = ( + string name_curve, + double offset, + double time_delay, + bool use_only_rotor, + IGridTransformation alg_avg, + string[] name_params_avg + ); + + public partial class Pipelines { + //depth_monoton, value_monoton, depthGrid, param_alg + public delegate double[] AlgorithmAvarage( + double[] depth_monotone, + double[] value_monoton, + double[] depthGrid, + object[] param + ); + + // + + public static void Time2DepthLogging( + double[] time_tool, + IDictionary input_curves, + Dictionary input_parameters, + AlgCurveInfo[] calculate_time_curve, + CurveInfoTransformation[] curve_out_param, + double[] time_surface, + double[] depth, + double[] f_stop, + double[] f_rotor, + AlgCurveInfo[] calculate_depth_curve, + double step_grid, + out double[] depth_out, + out Dictionary curve_out, + ProgressCallback progress = null, + InfoCallback info = null + ) { + IEnumerable t = new double[5]; + var r = t.ElementAt(0); + depth_out = null; + curve_out = null; + } + } +} diff --git a/T2dMath.Test/CurveToStopsNewTest.cs b/T2dMath.Test/CurveToStopsNewTest.cs index 6d244ed..69cecab 100644 --- a/T2dMath.Test/CurveToStopsNewTest.cs +++ b/T2dMath.Test/CurveToStopsNewTest.cs @@ -4,10 +4,8 @@ using T2dMath.UtilsTest.Tools; using Xunit; -namespace TestT2dMath -{ - public class CurveToStopsTheoryTests - { +namespace TestT2dMath { + public class CurveToStopsTheoryTests { public static IEnumerable TestCases => new[] { new object[] @@ -53,11 +51,10 @@ public class CurveToStopsTheoryTests public void CurveToStops_ShouldMatchExpected( double[] time, double[] stps, - (double, double)[] expected) - { + (double, double)[] expected) { var result = LogMath.CurveToStops(time, stps); Assert.Equal(expected.Length, result.Length); Assert.Equal(expected, result, new StopIntervalComparer(1e-12)); } } -} \ No newline at end of file +} diff --git a/T2dMath.Test/SetAbsoluteDepthByPromerVlasovTest.cs b/T2dMath.Test/SetAbsoluteDepthByPromerVlasovTest.cs index 0caed6a..6d76114 100644 --- a/T2dMath.Test/SetAbsoluteDepthByPromerVlasovTest.cs +++ b/T2dMath.Test/SetAbsoluteDepthByPromerVlasovTest.cs @@ -1,131 +1,126 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Xunit; - -namespace T2dMath.Test -{ - public class SetAbsoluteDepthByPromerVlasovTest - { - [Fact] - public void Test_NoStops_ShouldReturnDefaultDepths() - { - int n = 5; - double[] time = { 0, 1, 2, 3, 4 }; - double[] depth = { 10, 10, 10, 10, 10 }; - double[] njoint = new double[n]; - int nstops = 0; - //double[] stopsStart = Array.Empty(); - //double[] stopsFinish = Array.Empty(); - (double start, double finish)[] time_intervals = Array.Empty<(double, double)>(); - int npipe = 2; - double[] lengthPipe = { 5, 5 }; - int maxFullPipe = 2; - double lengthDevice = 10; - double epsZaboy = 0.1; - double[] kgrip = new double[nstops]; - double[] kmove = new double[nstops]; - int numStartPipe, numStopPipe; - double[] deformationPipe = new double[npipe]; - - int result = LogMath.SetAbsoluteDepthByPromerVlasov( - n, time, depth, njoint, - nstops, time_intervals, - npipe, lengthPipe, maxFullPipe, - lengthDevice, epsZaboy, - kgrip, kmove, - out numStartPipe, out numStopPipe, - deformationPipe); - - Assert.Equal(0, result); - // With no stops, depth should be set to device length - foreach (var d in depth) - Assert.Equal(lengthDevice + lengthPipe.Sum(), d); - Assert.Equal(0, numStartPipe); - Assert.Equal(npipe - 1, numStopPipe); - foreach (var def in deformationPipe) - Assert.Equal(-1, def); - } - - [Fact] - public void Test_SingleStop_ShouldAdjustDepthsAndIndices() - { - int n = 4; - double[] time = { 0, 1, 2, 3 }; - double[] depth = { 0, 0, 0, 0 }; - double[] njoint = new double[n]; - int nstops = 1; - //double[] stopsStart = { 1 }; - //double[] stopsFinish = { 2 }; - (double start, double finish)[] time_intervals = { (1, 2) }; - int npipe = 3; - double[] lengthPipe = { 4, 4, 4 }; - int maxFullPipe = 1; - double lengthDevice = 2; - double epsZaboy = 0.5; - double[] kgrip = new double[nstops]; - double[] kmove = new double[nstops]; - int numStartPipe, numStopPipe; - double[] deformationPipe = new double[npipe]; - - int result = LogMath.SetAbsoluteDepthByPromerVlasov( - n, time, depth, njoint, - nstops, time_intervals, - npipe, lengthPipe, maxFullPipe, - lengthDevice, epsZaboy, - kgrip, kmove, - out numStartPipe, out numStopPipe, - deformationPipe); - - Assert.Equal(0, result); - // On stop interval [1,2], depth should equal device + lengthPipe[0] - double expectedDepth = lengthDevice + lengthPipe[0]; - Assert.Equal(expectedDepth, depth[1]); - Assert.Equal(expectedDepth, depth[2]); - // Before and after stop also same depth - Assert.Equal(expectedDepth, depth[0]); - Assert.Equal(expectedDepth, depth[3]); - // njoint indices should be 1 on indices 1 and 2 - Assert.Equal(1, njoint[0]); - Assert.Equal(1, njoint[1]); - Assert.Equal(1, njoint[2]); - Assert.Equal(-127, njoint[3]); - } - - [Fact] - public void Test_InvalidPipeIndex_ShouldReturnErrorCode() - { - int n = 2; - double[] time = { 0, 1 }; - double[] depth = { 0, 0 }; - double[] njoint = new double[n]; - int nstops = 1; - //double[] stopsStart = { 0 }; - //double[] stopsFinish = { 1 }; - (double start, double finish)[] time_intervals = { (0, 1) }; - int npipe = 1; - double[] lengthPipe = { 0 }; - int maxFullPipe = -1; // invalid - double lengthDevice = 0; - double epsZaboy = 0; - double[] kgrip = new double[nstops]; - double[] kmove = new double[nstops]; - int numStartPipe, numStopPipe; - double[] deformationPipe = new double[npipe]; - - int result = LogMath.SetAbsoluteDepthByPromerVlasov( - n, time, depth, njoint, - nstops, time_intervals, - npipe, lengthPipe, maxFullPipe, - lengthDevice, epsZaboy, - kgrip, kmove, - out numStartPipe, out numStopPipe, - deformationPipe); - - Assert.Equal(57, result); - } - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace T2dMath.Test { + public class SetAbsoluteDepthByPromerVlasovTest { + [Fact] + public void Test_NoStops_ShouldReturnDefaultDepths() { + int n = 5; + double[] time = { 0, 1, 2, 3, 4 }; + double[] depth = { 10, 10, 10, 10, 10 }; + double[] njoint = new double[n]; + int nstops = 0; + //double[] stopsStart = Array.Empty(); + //double[] stopsFinish = Array.Empty(); + (double start, double finish)[] time_intervals = Array.Empty<(double, double)>(); + int npipe = 2; + double[] lengthPipe = { 5, 5 }; + int maxFullPipe = 2; + double lengthDevice = 10; + double epsZaboy = 0.1; + double[] kgrip = new double[nstops]; + double[] kmove = new double[nstops]; + int numStartPipe, numStopPipe; + double[] deformationPipe = new double[npipe]; + + int result = LogMath.SetAbsoluteDepthByPromerVlasov( + n, time, depth, njoint, + nstops, time_intervals, + npipe, lengthPipe, maxFullPipe, + lengthDevice, epsZaboy, + kgrip, kmove, + out numStartPipe, out numStopPipe, + deformationPipe); + + Assert.Equal(0, result); + // With no stops, depth should be set to device length + foreach (var d in depth) + Assert.Equal(lengthDevice + lengthPipe.Sum(), d); + Assert.Equal(0, numStartPipe); + Assert.Equal(npipe - 1, numStopPipe); + foreach (var def in deformationPipe) + Assert.Equal(-1, def); + } + + [Fact] + public void Test_SingleStop_ShouldAdjustDepthsAndIndices() { + int n = 4; + double[] time = { 0, 1, 2, 3 }; + double[] depth = { 0, 0, 0, 0 }; + double[] njoint = new double[n]; + int nstops = 1; + //double[] stopsStart = { 1 }; + //double[] stopsFinish = { 2 }; + (double start, double finish)[] time_intervals = { (1, 2) }; + int npipe = 3; + double[] lengthPipe = { 4, 4, 4 }; + int maxFullPipe = 1; + double lengthDevice = 2; + double epsZaboy = 0.5; + double[] kgrip = new double[nstops]; + double[] kmove = new double[nstops]; + int numStartPipe, numStopPipe; + double[] deformationPipe = new double[npipe]; + + int result = LogMath.SetAbsoluteDepthByPromerVlasov( + n, time, depth, njoint, + nstops, time_intervals, + npipe, lengthPipe, maxFullPipe, + lengthDevice, epsZaboy, + kgrip, kmove, + out numStartPipe, out numStopPipe, + deformationPipe); + + Assert.Equal(0, result); + // On stop interval [1,2], depth should equal device + lengthPipe[0] + double expectedDepth = lengthDevice + lengthPipe[0]; + Assert.Equal(expectedDepth, depth[1]); + Assert.Equal(expectedDepth, depth[2]); + // Before and after stop also same depth + Assert.Equal(expectedDepth, depth[0]); + Assert.Equal(expectedDepth, depth[3]); + // njoint indices should be 1 on indices 1 and 2 + Assert.Equal(1, njoint[0]); + Assert.Equal(1, njoint[1]); + Assert.Equal(1, njoint[2]); + Assert.Equal(-127, njoint[3]); + } + + [Fact] + public void Test_InvalidPipeIndex_ShouldReturnErrorCode() { + int n = 2; + double[] time = { 0, 1 }; + double[] depth = { 0, 0 }; + double[] njoint = new double[n]; + int nstops = 1; + //double[] stopsStart = { 0 }; + //double[] stopsFinish = { 1 }; + (double start, double finish)[] time_intervals = { (0, 1) }; + int npipe = 1; + double[] lengthPipe = { 0 }; + int maxFullPipe = -1; // invalid + double lengthDevice = 0; + double epsZaboy = 0; + double[] kgrip = new double[nstops]; + double[] kmove = new double[nstops]; + int numStartPipe, numStopPipe; + double[] deformationPipe = new double[npipe]; + + int result = LogMath.SetAbsoluteDepthByPromerVlasov( + n, time, depth, njoint, + nstops, time_intervals, + npipe, lengthPipe, maxFullPipe, + lengthDevice, epsZaboy, + kgrip, kmove, + out numStartPipe, out numStopPipe, + deformationPipe); + + Assert.Equal(57, result); + } + } + +} diff --git a/T2dMath.Test/TestBuildGrid.cs b/T2dMath.Test/TestBuildGrid.cs index 3f6e9bd..3be7831 100644 --- a/T2dMath.Test/TestBuildGrid.cs +++ b/T2dMath.Test/TestBuildGrid.cs @@ -1,11 +1,9 @@ -using T2dMath; +using T2dMath; using T2dMath.UtilsTest.Tools; using Xunit; -namespace TestT2dMath -{ - public class TestBuildGrid - { +namespace TestT2dMath { + public class TestBuildGrid { // Основной сценарий: нормальное направление (увеличение глубины) [Theory] [ @@ -56,8 +54,7 @@ public void BuildGrid_NormalDirection_CorrectGrid( double step, double toolLength, double[] expected - ) - { + ) { var result = LogMath.BuildGrid(depth, step, toolLength); Assert.Equal(expected, result, new DoubleComparer(1e-12)); } diff --git a/T2dMath.Test/TestCalculateStopsByDepth.cs b/T2dMath.Test/TestCalculateStopsByDepth.cs index 3621b89..94c73d2 100644 --- a/T2dMath.Test/TestCalculateStopsByDepth.cs +++ b/T2dMath.Test/TestCalculateStopsByDepth.cs @@ -2,13 +2,10 @@ using T2dMath; using Xunit; -namespace TestLogMath -{ - public class CalculateStopsByDepth - { +namespace TestLogMath { + public class CalculateStopsByDepth { [Fact] - public void BasicTest() - { + public void BasicTest() { double[] time = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; double[] depth = [1.0, 1.1, 1.0, 1.05, 1.0, 2.0, 2.1, 2.0, 1.0, 1.0]; int mintime = 15; @@ -24,8 +21,7 @@ public void BasicTest() } [Fact] - public void NoStopsTest() - { + public void NoStopsTest() { double[] time = [0, 10, 20, 30, 40]; double[] depth = [1.0, 2.0, 3.0, 4.0, 5.0]; int mintime = 15; @@ -37,8 +33,7 @@ public void NoStopsTest() } [Fact] - public void MultipleStopsTest() - { + public void MultipleStopsTest() { double[] time = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; double[] depth = [1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0]; int mintime = 15; @@ -54,8 +49,7 @@ public void MultipleStopsTest() } [Fact] - public void ShortStopsFilteredTest() - { + public void ShortStopsFilteredTest() { double[] time = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; double[] depth = [1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0]; int mintime = 25; @@ -69,8 +63,7 @@ public void ShortStopsFilteredTest() } [Fact] - public void InvalidInputTest() - { + public void InvalidInputTest() { double[] time = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; double[] depth = [1.0, 1.1, 1.0, 1.05, 1.0, 2.0, 2.1, 2.0, 1.0, 1.0]; int mintime = 15; @@ -85,8 +78,7 @@ public void InvalidInputTest() } [Fact] - public void EdgeCaseSinglePointTest() - { + public void EdgeCaseSinglePointTest() { double[] time = [0]; double[] depth = [1.0]; int mintime = 15; @@ -98,8 +90,7 @@ public void EdgeCaseSinglePointTest() } [Fact] - public void EdgeCaseAllSameDepthTest() - { + public void EdgeCaseAllSameDepthTest() { double[] time = [0, 10, 20, 30, 40]; double[] depth = [1.0, 1.0, 1.0, 1.0, 1.0]; int mintime = 15; @@ -110,11 +101,10 @@ public void EdgeCaseAllSameDepthTest() Assert.Single(result); Assert.Equal(0, result[0].start); Assert.Equal(40, result[0].finish); - } - + } + [Fact] - public void OnlyMainPointDepthTest() - { + public void OnlyMainPointDepthTest() { double[] time = [0, 10, 20, 30, 40, 50]; double[] depth = [1.0, 1.0, 5.0, 5.0, 10.0, 10.0]; int mintime = 5; diff --git a/T2dMath.Test/TestDepthGridNoStopsAvg.cs b/T2dMath.Test/TestDepthGridNoStopsAvg.cs index 982dff9..6a29b18 100644 --- a/T2dMath.Test/TestDepthGridNoStopsAvg.cs +++ b/T2dMath.Test/TestDepthGridNoStopsAvg.cs @@ -3,16 +3,13 @@ using T2dMath; using Xunit; -namespace TestT2dMath -{ - public class BuildLogCurveDepthGridNoStopsAvg - { +namespace TestT2dMath { + public class BuildLogCurveDepthGridNoStopsAvg { [Theory] [MemberData(nameof(GetImplementations))] public void WithFixedData( Func function - ) - { + ) { RunTest(function, [100, 200, 300, 400, 500], [10, 20, 30, 40, 50]); } @@ -20,8 +17,7 @@ Func function [MemberData(nameof(GetImplementations))] public void WithDescendingData( Func function - ) - { + ) { RunTest(function, [100, 200, 300, 400, 500], [50, 40, 30, 20, 10]); } @@ -29,8 +25,7 @@ Func function [MemberData(nameof(GetImplementations))] public void WithRepeatedValues( Func function - ) - { + ) { RunTest(function, [100, 200, 300, 400, 500], [20, 20, 20, 20, 20]); } @@ -38,8 +33,7 @@ Func function [MemberData(nameof(GetImplementations))] public void WithLinearData( Func function - ) - { + ) { RunTest(function, [100, 200, 300, 400, 500], [5, 15, 25, 35, 45]); } @@ -47,8 +41,7 @@ Func function [MemberData(nameof(GetImplementations))] public void WithSparseData( Func function - ) - { + ) { RunTest(function, [100, 500, 1000, 2000, 5000], [10, 50, 100, 200, 500]); } @@ -56,8 +49,7 @@ private void RunTest( Func function, double[] depth, double[] data - ) - { + ) { int n = depth.Length; double[] time = new double[n]; for (int i = 0; i < n; i++) @@ -70,15 +62,12 @@ double[] data long repetitions = 3; long totalTime = 0; - for (long i = 0; i < repetitions; i++) - { + for (long i = 0; i < repetitions; i++) { var stopwatch = Stopwatch.StartNew(); - try - { + try { function(n, depth, time, data, ngrid, depthGrid, outData); } - catch (Exception ex) - { + catch (Exception ex) { Console.WriteLine($"Exception: {ex.Message}\n{ex.StackTrace}"); throw; } @@ -96,8 +85,7 @@ double[] data public static TheoryData< Func - > GetImplementations() - { + > GetImplementations() { return new TheoryData< Func > diff --git a/T2dMath.Test/TestGetInterval.cs b/T2dMath.Test/TestGetInterval.cs index 4df26be..9aa6983 100644 --- a/T2dMath.Test/TestGetInterval.cs +++ b/T2dMath.Test/TestGetInterval.cs @@ -1,70 +1,64 @@ -using T2dMath; -using Xunit; - -namespace TestT2dMath -{ - public class TestGetInterval - { - private static double tol = 1e-10; - - /// - /// Проверка на данных при подъеме - /// - [Fact] - public void GetInterval_Up() - { - double[] grid = [4.0, 3.0, 2.0, 1.0]; - int idx = 1; - - LogMath.GetInterval(grid, idx, out var start, out var finish); - - Assert.Equal(3.5, start, tol); - Assert.Equal(2.5, finish, tol); - } - - /// - /// Проверка на данных при спуске - /// - [Fact] - public void GetInterval_Down() - { - double[] grid = [1.0, 2.0, 3.0, 4.0]; - int idx = 1; - - LogMath.GetInterval(grid, idx, out var start, out var finish); - - Assert.Equal(1.5, start, tol); - Assert.Equal(2.5, finish, tol); - } - - /// - /// Проверка на данных при спуске - /// - [Fact] - public void GetInterval_OnePoint() - { - double[] grid = [1.0]; - int idx = 0; - - LogMath.GetInterval(grid, idx, out var start, out var finish); - - Assert.Equal(1, start, tol); - Assert.Equal(1, finish, tol); - } - - /// - /// Проверка на данных при спуске - /// - [Fact] - public void GetInterval_Empty() - { - double[] grid = []; - int idx = 1; - - LogMath.GetInterval(grid, idx, out var start, out var finish); - - Assert.True(double.IsNaN(start)); - Assert.True(double.IsNaN(finish)); - } - } -} +using T2dMath; +using Xunit; + +namespace TestT2dMath { + public class TestGetInterval { + private static double tol = 1e-10; + + /// + /// Проверка на данных при подъеме + /// + [Fact] + public void GetInterval_Up() { + double[] grid = [4.0, 3.0, 2.0, 1.0]; + int idx = 1; + + LogMath.GetInterval(grid, idx, out var start, out var finish); + + Assert.Equal(3.5, start, tol); + Assert.Equal(2.5, finish, tol); + } + + /// + /// Проверка на данных при спуске + /// + [Fact] + public void GetInterval_Down() { + double[] grid = [1.0, 2.0, 3.0, 4.0]; + int idx = 1; + + LogMath.GetInterval(grid, idx, out var start, out var finish); + + Assert.Equal(1.5, start, tol); + Assert.Equal(2.5, finish, tol); + } + + /// + /// Проверка на данных при спуске + /// + [Fact] + public void GetInterval_OnePoint() { + double[] grid = [1.0]; + int idx = 0; + + LogMath.GetInterval(grid, idx, out var start, out var finish); + + Assert.Equal(1, start, tol); + Assert.Equal(1, finish, tol); + } + + /// + /// Проверка на данных при спуске + /// + [Fact] + public void GetInterval_Empty() { + double[] grid = []; + int idx = 1; + + LogMath.GetInterval(grid, idx, out var start, out var finish); + + Assert.True(double.IsNaN(start)); + Assert.True(double.IsNaN(finish)); + } + } +} diff --git a/T2dMath.Test/TestPipelineFunc.cs b/T2dMath.Test/TestPipelineFunc.cs index 488bcf4..a5f5527 100644 --- a/T2dMath.Test/TestPipelineFunc.cs +++ b/T2dMath.Test/TestPipelineFunc.cs @@ -1,256 +1,237 @@ -using T2dMath; -using Xunit; - -namespace TestT2dMath -{ - public class PipelineFuncTests - { - [Fact] - public void IntervalsIntersection_FullIntersection() - { - double[] time_curve = [2.0, 2.5, 3.0, 3.5, 4.0]; - var time_template_interval = (start: 1.5, end: 4.5); - - LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); - - Assert.Equal(LogMath.ActionIntersection.FullIntersection, intersection.status); - Assert.Equal(0, intersection.idx_start); - Assert.Equal(4, intersection.idx_end); - } - - [Fact] - public void IntervalsIntersection_PartialIntersection() - { - double[] time_curve = [0.5, 1.5, 2.5, 3.5, 4.5]; - var time_template_interval = (start: 2.0, end: 4.0); - - LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); - - Assert.Equal(LogMath.ActionIntersection.PartialIntersection, intersection.status); - Assert.Equal(2, intersection.idx_start); - Assert.Equal(3, intersection.idx_end); - } - - [Fact] - public void IntervalsIntersection_NoIntersection() - { - double[] time_curve = [5.0, 6.0, 7.0, 8.0, 9.0]; - var time_template_interval = (start: 1.0, end: 4.0); - - LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); - - Assert.Equal(LogMath.ActionIntersection.NoIntersection, intersection.status); - Assert.Equal(-1, intersection.idx_start); - Assert.Equal(-1, intersection.idx_end); - } - - [Fact] - public void FilterCurveNotNull_RemovesNaNValues() - { - double[] timeMPP = [1.0, 2.0, 3.0, 4.0]; - double[] valueMPP = [10.0, double.NaN, 30.0, double.NaN]; - - LogMath.FilterCurveNotNull(ref timeMPP, ref valueMPP); - - Assert.Equal(2, timeMPP.Length); - Assert.Equal(2, valueMPP.Length); - Assert.Equal(1.0, timeMPP[0]); - Assert.Equal(3.0, timeMPP[1]); - Assert.Equal(10.0, valueMPP[0]); - Assert.Equal(30.0, valueMPP[1]); - } - - [Fact] - public void InterpolateDepths_InterpolatesCorrectly() - { - double[] timeMPP = [2.5]; - double[] timeSurface = [2.0, 3.0]; - double[] depthSurface = [100.0, 150.0]; - double[] depthMPP = new double[timeMPP.Length]; - - LogMath.InterpolateDepths(timeMPP, timeSurface, depthSurface, depthMPP); - - Assert.Equal(125.0, depthMPP[0]); - } - - [Fact] - public void MatchDepthToLine_MatchesDepth() - { - double[] timeMPP = [1.0, 2.0, 3.0]; - double[] timeSurface = [0.0, 2.0, 4.0]; - double[] depthSurface = [100.0, 150.0, 200.0]; - double measurepoint = 50.0; - - LogMath.MatchDepthToLine( - timeMPP, - timeSurface, - depthSurface, - measurepoint, - out var depthMPP - ); - - Assert.Equal(3, depthMPP.Length); - Assert.NotNull(depthMPP); - } - - [Fact] - public void BuildMonotoneLogSmart_CreatesMonotoneLog() - { - double[] depth = [100.0, 110.0, 120.0, 130.0]; - double[] time = [1.0, 2.0, 3.0, 4.0]; - double[] data = [1.0, 2.0, 3.0, 4.0]; - - LogMath.BuildMonotoneLogSmart( - depth, - time, - data, - out var depth_monoton, - out var value_monoton - ); - - Assert.Equal(4, depth_monoton.Length); - Assert.Equal(4, value_monoton.Length); - } - - [Fact] - public void FilterOnePointNullValue_FixesNaNInMiddle() - { - double[] curve = [1.0, double.NaN, 3.0]; - - LogMath.FilterOnePointNullValue(curve); - - Assert.Equal(2.0, curve[1]); - } - - [Fact] - public void IntervalIntersectionDoublesVlasov_ValidIntersection() - { - double interval_start = 2.0; - double interval_end = 4.0; - double[] depthgrid = [1.0, 2.0, 3.0, 4.0, 5.0]; - - LogMath.IntervalIntersectionDoublesVlasov( - interval_start, - interval_end, - depthgrid, - out var idx_start, - out var idx_end - ); - - Assert.Equal(1, idx_start); - Assert.Equal(3, idx_end); - } - - [Fact] - public void IntervalIntersectionDoublesVlasov_ValidIntersectionRound() - { - double interval_start = 2.0; - double interval_end = 3.9; - double[] depthgrid = [1.0, 2.0, 3.0, 4.0, 5.0]; - - LogMath.IntervalIntersectionDoublesVlasov( - interval_start, - interval_end, - depthgrid, - out var idx_start, - out var idx_end - ); - - Assert.Equal(1, idx_start); - Assert.Equal(2, idx_end); - } - - [Fact] - public void GridDepthTransformateAvgFast_CorrectTransformation() - { - double[] depthgrid = [1.0, 2.0, 3.0]; - double[] depth_monoton = [1.0, 2.0, 3.0]; - double[] value_monoton = [10.0, 20.0, 30.0]; - double[] outdata = new double[depthgrid.Length]; - - LogMath.GridDepthTransformateAvgFast(depthgrid, depth_monoton, value_monoton, outdata); - - Assert.Equal(20.0, outdata[1]); - } - - [Fact] - public void LogInterpolateAvg_ValidData_ReturnsCorrectInterpolation() - { - const double tolerance = 1e-10; - double[] depth_monoton = { 10, 20, 30, 40, 50 }; - double[] value_monoton = { 5, 10, 15, 20, 25 }; - double[] depthgrid = { 15, 25, 35, 45 }; - - double[] expected = [5, 10, 15, 20]; - - LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); - Assert.Equal(expected[0], actual[0], tolerance); - Assert.Equal(expected[1], actual[1], tolerance); - Assert.Equal(expected[2], actual[2], tolerance); - Assert.Equal(expected[3], actual[3], tolerance); - } - - [Fact(Skip = "TODO Fail")] - public void LogInterpolateAvg_ValidData_ReturnsCorrectInterpolation_decrise() - { - const double tolerance = 1e-10; - double[] depth_monoton = { 50, 40, 30, 20, 10 }; - double[] value_monoton = { 25, 15, 15, 10, 5 }; - double[] depthgrid = { 45, 35, 25, 15 }; - - double[] expected = [20, 15, 10, 5]; - - LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); - Assert.Equal(expected[0], actual[0], tolerance); - Assert.Equal(expected[1], actual[1], tolerance); - Assert.Equal(expected[2], actual[2], tolerance); - Assert.Equal(expected[3], actual[3], tolerance); - } - - [Fact] - public void LogInterpolateAvg_EmptyDepthGrid_ReturnsNaN() - { - double[] depth_monoton = { 10, 20, 30, 40, 50 }; - double[] value_monoton = { 5, 10, 15, 20, 25 }; - double[] depthgrid = []; - - LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); - Assert.Empty(actual); - } - - [Fact] - public void LogInterpolateAvg_OutOfRangeDepthGrid_ReturnsNaN() - { - double[] depth_monoton = [10, 20, 30, 40, 50]; - double[] value_monoton = [5, 10, 15, 20, 25]; - double[] depthgrid = [100, 110]; - //double[] depthgrid = { 4, 56 }; - - LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); - - Assert.All(actual, value => Assert.True(double.IsNaN(value))); - } - - [Fact] - public void AnalysisActionSimpleVlasov_Up() - { - double[] time = { 1.0, 2.0, 3.0 }; - double[] depth = { 100.0, 90.0, 80.0 }; - - var result = LogMath.AnalysisActionSimpleVlasov(time, depth); - - Assert.Equal(LogMath.ToolDirectionMoving.Up, result); - } - - [Fact] - public void AnalysisActionSimpleVlasov_Down() - { - double[] time = [1.0, 2.0, 3.0]; - double[] depth = [80.0, 90.0, 100.0]; - - var result = LogMath.AnalysisActionSimpleVlasov(time, depth); - - Assert.Equal(LogMath.ToolDirectionMoving.Down, result); - } - } -} +using T2dMath; +using Xunit; + +namespace TestT2dMath { + public class PipelineFuncTests { + [Fact] + public void IntervalsIntersection_FullIntersection() { + double[] time_curve = [2.0, 2.5, 3.0, 3.5, 4.0]; + var time_template_interval = (start: 1.5, end: 4.5); + + LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); + + Assert.Equal(LogMath.ActionIntersection.FullIntersection, intersection.status); + Assert.Equal(0, intersection.idx_start); + Assert.Equal(4, intersection.idx_end); + } + + [Fact] + public void IntervalsIntersection_PartialIntersection() { + double[] time_curve = [0.5, 1.5, 2.5, 3.5, 4.5]; + var time_template_interval = (start: 2.0, end: 4.0); + + LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); + + Assert.Equal(LogMath.ActionIntersection.PartialIntersection, intersection.status); + Assert.Equal(2, intersection.idx_start); + Assert.Equal(3, intersection.idx_end); + } + + [Fact] + public void IntervalsIntersection_NoIntersection() { + double[] time_curve = [5.0, 6.0, 7.0, 8.0, 9.0]; + var time_template_interval = (start: 1.0, end: 4.0); + + LogMath.IntervalsIntersection(time_template_interval, time_curve, out var intersection); + + Assert.Equal(LogMath.ActionIntersection.NoIntersection, intersection.status); + Assert.Equal(-1, intersection.idx_start); + Assert.Equal(-1, intersection.idx_end); + } + + [Fact] + public void FilterCurveNotNull_RemovesNaNValues() { + double[] timeMPP = [1.0, 2.0, 3.0, 4.0]; + double[] valueMPP = [10.0, double.NaN, 30.0, double.NaN]; + + LogMath.FilterCurveNotNull(ref timeMPP, ref valueMPP); + + Assert.Equal(2, timeMPP.Length); + Assert.Equal(2, valueMPP.Length); + Assert.Equal(1.0, timeMPP[0]); + Assert.Equal(3.0, timeMPP[1]); + Assert.Equal(10.0, valueMPP[0]); + Assert.Equal(30.0, valueMPP[1]); + } + + [Fact] + public void InterpolateDepths_InterpolatesCorrectly() { + double[] timeMPP = [2.5]; + double[] timeSurface = [2.0, 3.0]; + double[] depthSurface = [100.0, 150.0]; + double[] depthMPP = new double[timeMPP.Length]; + + LogMath.InterpolateDepths(timeMPP, timeSurface, depthSurface, depthMPP); + + Assert.Equal(125.0, depthMPP[0]); + } + + [Fact] + public void MatchDepthToLine_MatchesDepth() { + double[] timeMPP = [1.0, 2.0, 3.0]; + double[] timeSurface = [0.0, 2.0, 4.0]; + double[] depthSurface = [100.0, 150.0, 200.0]; + double measurepoint = 50.0; + + LogMath.MatchDepthToLine( + timeMPP, + timeSurface, + depthSurface, + measurepoint, + out var depthMPP + ); + + Assert.Equal(3, depthMPP.Length); + Assert.NotNull(depthMPP); + } + + [Fact] + public void BuildMonotoneLogSmart_CreatesMonotoneLog() { + double[] depth = [100.0, 110.0, 120.0, 130.0]; + double[] time = [1.0, 2.0, 3.0, 4.0]; + double[] data = [1.0, 2.0, 3.0, 4.0]; + + LogMath.BuildMonotoneLogSmart( + depth, + time, + data, + out var depth_monoton, + out var value_monoton + ); + + Assert.Equal(4, depth_monoton.Length); + Assert.Equal(4, value_monoton.Length); + } + + [Fact] + public void FilterOnePointNullValue_FixesNaNInMiddle() { + double[] curve = [1.0, double.NaN, 3.0]; + + LogMath.FilterOnePointNullValue(curve); + + Assert.Equal(2.0, curve[1]); + } + + [Fact] + public void IntervalIntersectionDoublesVlasov_ValidIntersection() { + double interval_start = 2.0; + double interval_end = 4.0; + double[] depthgrid = [1.0, 2.0, 3.0, 4.0, 5.0]; + + LogMath.IntervalIntersectionDoublesVlasov( + interval_start, + interval_end, + depthgrid, + out var idx_start, + out var idx_end + ); + + Assert.Equal(1, idx_start); + Assert.Equal(3, idx_end); + } + + [Fact] + public void IntervalIntersectionDoublesVlasov_ValidIntersectionRound() { + double interval_start = 2.0; + double interval_end = 3.9; + double[] depthgrid = [1.0, 2.0, 3.0, 4.0, 5.0]; + + LogMath.IntervalIntersectionDoublesVlasov( + interval_start, + interval_end, + depthgrid, + out var idx_start, + out var idx_end + ); + + Assert.Equal(1, idx_start); + Assert.Equal(2, idx_end); + } + + [Fact] + public void GridDepthTransformateAvgFast_CorrectTransformation() { + double[] depthgrid = [1.0, 2.0, 3.0]; + double[] depth_monoton = [1.0, 2.0, 3.0]; + double[] value_monoton = [10.0, 20.0, 30.0]; + double[] outdata = new double[depthgrid.Length]; + + LogMath.GridDepthTransformateAvgFast(depthgrid, depth_monoton, value_monoton, outdata); + + Assert.Equal(20.0, outdata[1]); + } + + [Fact] + public void LogInterpolateAvg_ValidData_ReturnsCorrectInterpolation() { + const double tolerance = 1e-10; + double[] depth_monoton = { 10, 20, 30, 40, 50 }; + double[] value_monoton = { 5, 10, 15, 20, 25 }; + double[] depthgrid = { 15, 25, 35, 45 }; + + double[] expected = [5, 10, 15, 20]; + + LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); + Assert.Equal(expected[0], actual[0], tolerance); + Assert.Equal(expected[1], actual[1], tolerance); + Assert.Equal(expected[2], actual[2], tolerance); + Assert.Equal(expected[3], actual[3], tolerance); + } + + [Fact(Skip = "TODO Fail")] + public void LogInterpolateAvg_ValidData_ReturnsCorrectInterpolation_decrise() { + const double tolerance = 1e-10; + double[] depth_monoton = { 50, 40, 30, 20, 10 }; + double[] value_monoton = { 25, 15, 15, 10, 5 }; + double[] depthgrid = { 45, 35, 25, 15 }; + + double[] expected = [20, 15, 10, 5]; + + LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); + Assert.Equal(expected[0], actual[0], tolerance); + Assert.Equal(expected[1], actual[1], tolerance); + Assert.Equal(expected[2], actual[2], tolerance); + Assert.Equal(expected[3], actual[3], tolerance); + } + + [Fact] + public void LogInterpolateAvg_EmptyDepthGrid_ReturnsNaN() { + double[] depth_monoton = { 10, 20, 30, 40, 50 }; + double[] value_monoton = { 5, 10, 15, 20, 25 }; + double[] depthgrid = []; + + LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); + Assert.Empty(actual); + } + + [Fact] + public void LogInterpolateAvg_OutOfRangeDepthGrid_ReturnsNaN() { + double[] depth_monoton = [10, 20, 30, 40, 50]; + double[] value_monoton = [5, 10, 15, 20, 25]; + double[] depthgrid = [100, 110]; + //double[] depthgrid = { 4, 56 }; + + LogMath.LogInterpolateAvg(depth_monoton, value_monoton, depthgrid, out double[] actual); + + Assert.All(actual, value => Assert.True(double.IsNaN(value))); + } + + [Fact] + public void AnalysisActionSimpleVlasov_Up() { + double[] time = { 1.0, 2.0, 3.0 }; + double[] depth = { 100.0, 90.0, 80.0 }; + + var result = LogMath.AnalysisActionSimpleVlasov(time, depth); + + Assert.Equal(LogMath.ToolDirectionMoving.Up, result); + } + + [Fact] + public void AnalysisActionSimpleVlasov_Down() { + double[] time = [1.0, 2.0, 3.0]; + double[] depth = [80.0, 90.0, 100.0]; + + var result = LogMath.AnalysisActionSimpleVlasov(time, depth); + + Assert.Equal(LogMath.ToolDirectionMoving.Down, result); + } + } +} diff --git a/T2dMath.UtilsTest.Test/PromerParserTest.cs b/T2dMath.UtilsTest.Test/PromerParserTest.cs index 9554347..0d69627 100644 --- a/T2dMath.UtilsTest.Test/PromerParserTest.cs +++ b/T2dMath.UtilsTest.Test/PromerParserTest.cs @@ -1,16 +1,13 @@ -using System; +using System; using System.IO; -using Xunit; using T2dMath.UtilsTest; using T2dMath.UtilsTest.Tools; +using Xunit; -namespace T2dMath.UtilsTest.Test -{ - public class PromerFileParserTests - { +namespace T2dMath.UtilsTest.Test { + public class PromerFileParserTests { [Fact] - public void ParseLengths_SkipsFormatLineAndParsesCorrectly() - { + public void ParseLengths_SkipsFormatLineAndParsesCorrectly() { string input = @"NUM LENGTH - м @@ -30,8 +27,7 @@ 2 23.950 } [Fact] - public void ParseLengths_FromFile() - { + public void ParseLengths_FromFile() { string filepath = "../../../../logs/1/custom_promer.promer"; int expected_length = 138; diff --git a/T2dMath.UtilsTest.Test/T2dMath.UtilsTest.Test.csproj b/T2dMath.UtilsTest.Test/T2dMath.UtilsTest.Test.csproj index 19051ce..a688a24 100644 --- a/T2dMath.UtilsTest.Test/T2dMath.UtilsTest.Test.csproj +++ b/T2dMath.UtilsTest.Test/T2dMath.UtilsTest.Test.csproj @@ -9,7 +9,7 @@ - - + + diff --git a/T2dMath.UtilsTest.Test/TestParserLasTime.cs b/T2dMath.UtilsTest.Test/TestParserLasTime.cs index d3d3de3..b3a64c4 100644 --- a/T2dMath.UtilsTest.Test/TestParserLasTime.cs +++ b/T2dMath.UtilsTest.Test/TestParserLasTime.cs @@ -4,13 +4,10 @@ using T2dMath.Utils; using Xunit; -namespace Parser.Tests -{ - public class ParseLasTimeTests - { +namespace Parser.Tests { + public class ParseLasTimeTests { [Fact] - public void ParseLasFile_ParsesCorrectly() - { + public void ParseLasFile_ParsesCorrectly() { var tempFilePath = Path.GetTempFileName(); var lasContent = @" @@ -70,8 +67,7 @@ out Dictionary curves Assert.Equal(expectedTime.Length, actualTime.Length); const double timeTolerance = 1e-9; // Допуск для сравнения OADate - for (int i = 0; i < expectedTime.Length; i++) - { + for (int i = 0; i < expectedTime.Length; i++) { Assert.Equal(expectedTime[i], actualTime[i], timeTolerance); } @@ -80,14 +76,11 @@ out Dictionary curves var actualGR = curves["GR"]; Assert.Equal(expectedGR.Length, actualGR.Length); - for (int i = 0; i < expectedGR.Length; i++) - { - if (double.IsNaN(expectedGR[i])) - { + for (int i = 0; i < expectedGR.Length; i++) { + if (double.IsNaN(expectedGR[i])) { Assert.True(double.IsNaN(actualGR[i])); } - else - { + else { Assert.Equal(expectedGR[i], actualGR[i], 3); // 3 знака после запятой } } @@ -96,8 +89,7 @@ out Dictionary curves } [Fact] - public void ParseLasFile_ParsesCorrectly_without_file() - { + public void ParseLasFile_ParsesCorrectly_without_file() { var tempFilePath = Path.GetTempFileName(); var lasContent = @" @@ -156,8 +148,7 @@ out Dictionary curves Assert.Equal(expectedTime.Length, actualTime.Length); const double timeTolerance = 1e-9; // Допуск для сравнения OADate - for (int i = 0; i < expectedTime.Length; i++) - { + for (int i = 0; i < expectedTime.Length; i++) { Assert.Equal(expectedTime[i], actualTime[i], timeTolerance); } @@ -166,14 +157,11 @@ out Dictionary curves var actualGR = curves["GR"]; Assert.Equal(expectedGR.Length, actualGR.Length); - for (int i = 0; i < expectedGR.Length; i++) - { - if (double.IsNaN(expectedGR[i])) - { + for (int i = 0; i < expectedGR.Length; i++) { + if (double.IsNaN(expectedGR[i])) { Assert.True(double.IsNaN(actualGR[i]), $"Ожидалось NaN для GR[{i}]"); } - else - { + else { Assert.Equal(expectedGR[i], actualGR[i], 3); // 3 знака после запятой } } diff --git a/T2dMath.UtilsTest.Test/TestParserTool.cs b/T2dMath.UtilsTest.Test/TestParserTool.cs index 810c95c..cedfa15 100644 --- a/T2dMath.UtilsTest.Test/TestParserTool.cs +++ b/T2dMath.UtilsTest.Test/TestParserTool.cs @@ -1,15 +1,12 @@ -using System; +using System; using System.IO; -using Xunit; using T2dMath.UtilsTest; +using Xunit; -namespace T2dMath.UtilsTest.Test -{ - public class TestParserTool - { +namespace T2dMath.UtilsTest.Test { + public class TestParserTool { [Fact] - public void TestLoadFromStream_ValidXml_ReturnsCorrectConfiguration() - { + public void TestLoadFromStream_ValidXml_ReturnsCorrectConfiguration() { // Arrange string xmlContent = @" @@ -26,8 +23,7 @@ public void TestLoadFromStream_ValidXml_ReturnsCorrectConfiguration() "; // Act - using (var reader = new StringReader(xmlContent)) - { + using (var reader = new StringReader(xmlContent)) { var config = ParserTool.LoadFromStream(reader); // Assert diff --git a/T2dMath.UtilsTest.Test/TestParserXML.cs b/T2dMath.UtilsTest.Test/TestParserXML.cs index bfacbd8..83df49a 100644 --- a/T2dMath.UtilsTest.Test/TestParserXML.cs +++ b/T2dMath.UtilsTest.Test/TestParserXML.cs @@ -3,13 +3,10 @@ using T2dMath.Utils; using Xunit; -namespace Parser.Tests -{ - public class XmlParserTests - { +namespace Parser.Tests { + public class XmlParserTests { [Fact] - public void Parse_ParsesXmlCorrectly() - { + public void Parse_ParsesXmlCorrectly() { var tempFilePath = Path.GetTempFileName(); var xmlContent = @" @@ -62,8 +59,7 @@ public void Parse_ParsesXmlCorrectly() } [Fact] - public void Parse_HandlesMissingLengthAttribute() - { + public void Parse_HandlesMissingLengthAttribute() { var xmlContent = @" diff --git a/T2dMath.UtilsTest/ParseLasTime.cs b/T2dMath.UtilsTest/ParseLasTime.cs index 00baf3b..782e077 100644 --- a/T2dMath.UtilsTest/ParseLasTime.cs +++ b/T2dMath.UtilsTest/ParseLasTime.cs @@ -1,284 +1,245 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; - -namespace T2dMath.Utils -{ - public class ParseLasTime - { - public static void ParseLasFile( - string filePath, - out Dictionary wellInfo, - out Dictionary parameters, - out Dictionary curves - ) - { - using (var reader = new StreamReader(filePath)) - { - ParseLasFile(reader, out wellInfo, out parameters, out curves); - } - } - - public static void ParseLasFile( - TextReader in_stream, - out Dictionary wellInfo, - out Dictionary parameters, - out Dictionary curves - ) - { - wellInfo = new Dictionary(); - parameters = new Dictionary(); - curves = new Dictionary(); - - var currentSection = ""; - List curveNames = new List(); - List> dataColumns = new List>(); - - var culture = CultureInfo.InvariantCulture; - var reader = in_stream; - { - string line; - while ((line = reader.ReadLine()) != null) - { - line = line.Trim(); - - if (line.StartsWith("~")) - { - if (line.StartsWith("~W", StringComparison.InvariantCultureIgnoreCase)) - currentSection = "WELL"; - else if (line.StartsWith("~P", StringComparison.InvariantCultureIgnoreCase)) - currentSection = "PARAMETER"; - else if (line.StartsWith("~C", StringComparison.InvariantCultureIgnoreCase)) - currentSection = "CURVE"; - else if (line.StartsWith("~A", StringComparison.InvariantCultureIgnoreCase)) - currentSection = "ASCII"; - continue; - } - - if (currentSection == "WELL" && !line.StartsWith("#")) - { - var parts = line.Split(new[] { ':' }, 2); - if (parts.Length == 2) - { - var keyParts = parts[0].Split('.'); - var key = keyParts[0].Trim(); - - if (key == "NULL") - { - var tokens = parts[0] - .Substring(keyParts[0].Length + 1) - .Trim() - .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - if ( - tokens.Length > 0 - && double.TryParse( - tokens[0], - NumberStyles.Float, - culture, - out double nullValue - ) - ) - { - wellInfo[key] = nullValue; - } - } - else if (key == "DATE") - { - var dateParts = parts[0] - .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - if (dateParts.Length > 1) - { - wellInfo[key] = dateParts[1].Trim(); - } - else - { - wellInfo[key] = parts[1].Trim(); - } - } - } - } - - if (currentSection == "PARAMETER" && !line.StartsWith("#")) - { - var parts = line.Split(new[] { ':' }, 2); - if (parts.Length == 2) - { - var keyParts = parts[0].Split('.'); - var key = keyParts[0].Trim(); - - var tokens = parts[0] - .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - if ( - tokens.Length > 0 - && double.TryParse( - tokens.Last(), - NumberStyles.Float, - culture, - out double value - ) - ) - { - parameters[key] = value; - } - } - } - - if (currentSection == "CURVE" && !line.StartsWith("#")) - { - var mnemonic = line.Split('.')[0].Trim(); - curveNames.Add(mnemonic); - dataColumns.Add(new List()); - } - - if (currentSection == "ASCII" && !line.StartsWith("#")) - { - var values = line.Split( - new[] { ' ' }, - StringSplitOptions.RemoveEmptyEntries - ); - for (int i = 0; i < Math.Min(values.Length, dataColumns.Count); i++) - { - if ( - double.TryParse( - values[i], - NumberStyles.Float, - culture, - out double value - ) - ) - { - dataColumns[i].Add(value); - } - } - } - } - } - - for (int i = 0; i < curveNames.Count; i++) - { - curves[curveNames[i]] = dataColumns[i].ToArray(); - } - PostProcessing(wellInfo["DATE"].ToString(), wellInfo["NULL"].ToString(), curves); - } - - private static void PostProcessing( - string date_value, - string null_value, - Dictionary curves - ) - { - const double tolerance = 1e-12; - double dnull = double.Parse(null_value, CultureInfo.InvariantCulture); - string[] formats = { "dd-MMM-yyyy", "dd/MM/yyyy", "dd.MM.yyyy" }; //01-JAN-2000, 14/06/2012 - DateTime date = DateTime.ParseExact(date_value, formats, CultureInfo.InvariantCulture); - - //Конвертация времени в формат AODate - const string name_time_curve = "TIME"; - if (curves.ContainsKey(name_time_curve)) - { - var curve_time = curves[name_time_curve]; - for (int i = 0; i < curve_time.Length; i++) - { - curve_time[i] = date.AddMilliseconds(curve_time[i]).ToOADate(); - } - } - - // Замена NULL на NaN - foreach (var curve_record in curves) - { - double[] curve = curve_record.Value; - for (int i = 0; i < curve.Length; i++) - { - var value = curve[i]; - if (!double.IsNaN(value) && Math.Abs(value - dnull) < tolerance) - { - curve[i] = double.NaN; - } - } - } - } - - public static string FormatScientific(double value, double nullValue) - { - if (Math.Abs(value - nullValue) < 0.0001) - return "NULL"; - if (value == 0.0) - return "0.000000E+00"; - - var str = value.ToString("0.000000E+00", CultureInfo.InvariantCulture); - var parts = str.Split('E'); - if (parts.Length == 2) - { - if (int.TryParse(parts[1], out int exponent)) - { - return $"{parts[0]}E{exponent.ToString("+00;-00;+00")}"; - } - } - return str; - } - - public static void SaveToFile( - string filePath, - Dictionary wellInfo, - Dictionary parameters, - Dictionary curves, - double nullValue - ) - { - using (var writer = new StreamWriter(filePath)) - { - writer.WriteLine("WELL INFORMATION:"); - writer.WriteLine($"NULL. {nullValue}: NULL VALUES"); - if (wellInfo.TryGetValue("DATE", out var date)) - { - writer.WriteLine($"DATE. {date}: LOG DATE"); - } - writer.WriteLine(); - - writer.WriteLine("PARAMETER INFORMATION (FROM KNNS TO GGFG1):"); - var parameterKeys = parameters.Keys.ToList(); - if (parameterKeys.Contains("KNNS") && parameterKeys.Contains("GGFG1")) - { - var startIndex = parameterKeys.IndexOf("KNNS"); - var endIndex = parameterKeys.IndexOf("GGFG1"); - - for (int i = startIndex; i <= endIndex; i++) - { - var key = parameterKeys[i]; - if (parameters.TryGetValue(key, out var val) && val is double value) - { - writer.WriteLine($"{key}. {FormatScientific(value, nullValue)}"); - } - } - } - writer.WriteLine(); - - writer.WriteLine("CURVE DATA (ARRAY FORMAT):"); - foreach (var curve in curves) - { - writer.WriteLine($"\n{curve.Key}:"); - writer.WriteLine("["); - - for (int i = 0; i < curve.Value.Length; i++) - { - var value = curve.Value[i]; - writer.Write( - Math.Abs(value - nullValue) < 0.0001 - ? "NULL" - : value.ToString(CultureInfo.InvariantCulture) - ); - if (i < curve.Value.Length - 1) - writer.Write(", "); - - if ((i + 1) % 10 == 0) - writer.WriteLine(); - } - - writer.WriteLine("\n]"); - } - } - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace T2dMath.Utils { + public class ParseLasTime { + public static void ParseLasFile( + string filePath, + out Dictionary wellInfo, + out Dictionary parameters, + out Dictionary curves + ) { + using (var reader = new StreamReader(filePath)) { + ParseLasFile(reader, out wellInfo, out parameters, out curves); + } + } + + public static void ParseLasFile( + TextReader in_stream, + out Dictionary wellInfo, + out Dictionary parameters, + out Dictionary curves + ) { + wellInfo = new Dictionary(); + parameters = new Dictionary(); + curves = new Dictionary(); + + var currentSection = ""; + List curveNames = new List(); + List> dataColumns = new List>(); + + var culture = CultureInfo.InvariantCulture; + var reader = in_stream; + { + string line; + while ((line = reader.ReadLine()) != null) { + line = line.Trim(); + + if (line.StartsWith("~")) { + if (line.StartsWith("~W", StringComparison.InvariantCultureIgnoreCase)) + currentSection = "WELL"; + else if (line.StartsWith("~P", StringComparison.InvariantCultureIgnoreCase)) + currentSection = "PARAMETER"; + else if (line.StartsWith("~C", StringComparison.InvariantCultureIgnoreCase)) + currentSection = "CURVE"; + else if (line.StartsWith("~A", StringComparison.InvariantCultureIgnoreCase)) + currentSection = "ASCII"; + continue; + } + + if (currentSection == "WELL" && !line.StartsWith("#")) { + var parts = line.Split(new[] { ':' }, 2); + if (parts.Length == 2) { + var keyParts = parts[0].Split('.'); + var key = keyParts[0].Trim(); + + if (key == "NULL") { + var tokens = parts[0] + .Substring(keyParts[0].Length + 1) + .Trim() + .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if ( + tokens.Length > 0 + && double.TryParse( + tokens[0], + NumberStyles.Float, + culture, + out double nullValue + ) + ) { + wellInfo[key] = nullValue; + } + } + else if (key == "DATE") { + var dateParts = parts[0] + .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (dateParts.Length > 1) { + wellInfo[key] = dateParts[1].Trim(); + } + else { + wellInfo[key] = parts[1].Trim(); + } + } + } + } + + if (currentSection == "PARAMETER" && !line.StartsWith("#")) { + var parts = line.Split(new[] { ':' }, 2); + if (parts.Length == 2) { + var keyParts = parts[0].Split('.'); + var key = keyParts[0].Trim(); + + var tokens = parts[0] + .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if ( + tokens.Length > 0 + && double.TryParse( + tokens.Last(), + NumberStyles.Float, + culture, + out double value + ) + ) { + parameters[key] = value; + } + } + } + + if (currentSection == "CURVE" && !line.StartsWith("#")) { + var mnemonic = line.Split('.')[0].Trim(); + curveNames.Add(mnemonic); + dataColumns.Add(new List()); + } + + if (currentSection == "ASCII" && !line.StartsWith("#")) { + var values = line.Split( + new[] { ' ' }, + StringSplitOptions.RemoveEmptyEntries + ); + for (int i = 0; i < Math.Min(values.Length, dataColumns.Count); i++) { + if ( + double.TryParse( + values[i], + NumberStyles.Float, + culture, + out double value + ) + ) { + dataColumns[i].Add(value); + } + } + } + } + } + + for (int i = 0; i < curveNames.Count; i++) { + curves[curveNames[i]] = dataColumns[i].ToArray(); + } + PostProcessing(wellInfo["DATE"].ToString(), wellInfo["NULL"].ToString(), curves); + } + + private static void PostProcessing( + string date_value, + string null_value, + Dictionary curves + ) { + const double tolerance = 1e-12; + double dnull = double.Parse(null_value, CultureInfo.InvariantCulture); + string[] formats = { "dd-MMM-yyyy", "dd/MM/yyyy", "dd.MM.yyyy" }; //01-JAN-2000, 14/06/2012 + DateTime date = DateTime.ParseExact(date_value, formats, CultureInfo.InvariantCulture); + + //Конвертация времени в формат AODate + const string name_time_curve = "TIME"; + if (curves.ContainsKey(name_time_curve)) { + var curve_time = curves[name_time_curve]; + for (int i = 0; i < curve_time.Length; i++) { + curve_time[i] = date.AddMilliseconds(curve_time[i]).ToOADate(); + } + } + + // Замена NULL на NaN + foreach (var curve_record in curves) { + double[] curve = curve_record.Value; + for (int i = 0; i < curve.Length; i++) { + var value = curve[i]; + if (!double.IsNaN(value) && Math.Abs(value - dnull) < tolerance) { + curve[i] = double.NaN; + } + } + } + } + + public static string FormatScientific(double value, double nullValue) { + if (Math.Abs(value - nullValue) < 0.0001) + return "NULL"; + if (value == 0.0) + return "0.000000E+00"; + + var str = value.ToString("0.000000E+00", CultureInfo.InvariantCulture); + var parts = str.Split('E'); + if (parts.Length == 2) { + if (int.TryParse(parts[1], out int exponent)) { + return $"{parts[0]}E{exponent.ToString("+00;-00;+00")}"; + } + } + return str; + } + + public static void SaveToFile( + string filePath, + Dictionary wellInfo, + Dictionary parameters, + Dictionary curves, + double nullValue + ) { + using (var writer = new StreamWriter(filePath)) { + writer.WriteLine("WELL INFORMATION:"); + writer.WriteLine($"NULL. {nullValue}: NULL VALUES"); + if (wellInfo.TryGetValue("DATE", out var date)) { + writer.WriteLine($"DATE. {date}: LOG DATE"); + } + writer.WriteLine(); + + writer.WriteLine("PARAMETER INFORMATION (FROM KNNS TO GGFG1):"); + var parameterKeys = parameters.Keys.ToList(); + if (parameterKeys.Contains("KNNS") && parameterKeys.Contains("GGFG1")) { + var startIndex = parameterKeys.IndexOf("KNNS"); + var endIndex = parameterKeys.IndexOf("GGFG1"); + + for (int i = startIndex; i <= endIndex; i++) { + var key = parameterKeys[i]; + if (parameters.TryGetValue(key, out var val) && val is double value) { + writer.WriteLine($"{key}. {FormatScientific(value, nullValue)}"); + } + } + } + writer.WriteLine(); + + writer.WriteLine("CURVE DATA (ARRAY FORMAT):"); + foreach (var curve in curves) { + writer.WriteLine($"\n{curve.Key}:"); + writer.WriteLine("["); + + for (int i = 0; i < curve.Value.Length; i++) { + var value = curve.Value[i]; + writer.Write( + Math.Abs(value - nullValue) < 0.0001 + ? "NULL" + : value.ToString(CultureInfo.InvariantCulture) + ); + if (i < curve.Value.Length - 1) + writer.Write(", "); + + if ((i + 1) % 10 == 0) + writer.WriteLine(); + } + + writer.WriteLine("\n]"); + } + } + } + } +} diff --git a/T2dMath.UtilsTest/ParseXML.cs b/T2dMath.UtilsTest/ParseXML.cs index 4d2c741..1baab32 100644 --- a/T2dMath.UtilsTest/ParseXML.cs +++ b/T2dMath.UtilsTest/ParseXML.cs @@ -3,38 +3,31 @@ using System.Linq; using System.Xml.Linq; -namespace T2dMath.Utils -{ - public class ChannelInfo - { +namespace T2dMath.Utils { + public class ChannelInfo { public string? Name { get; set; } public int? Delay { get; set; } } - public class ElementInfo - { + public class ElementInfo { public string? Name { get; set; } public int Length { get; set; } public List Channels { get; set; } = new List(); } - public class XmlParser - { - public List Parse(string xmlContent) - { + public class XmlParser { + public List Parse(string xmlContent) { return XDocument .Parse(xmlContent) .Descendants("element") - .Select(element => new ElementInfo - { + .Select(element => new ElementInfo { Name = (string?)element.Attribute("name"), Length = (int)( element.Attribute("length") ?? throw new Exception("Missing 'length'") ), Channels = element .Elements("channel") - .Select(channel => new ChannelInfo - { + .Select(channel => new ChannelInfo { Name = (string?)channel.Attribute("name"), Delay = (int?)channel.Attribute("delay"), }) @@ -43,23 +36,19 @@ public List Parse(string xmlContent) .ToList(); } - public void PrintElements(List elements) - { - foreach (var element in elements) - { + public void PrintElements(List elements) { + foreach (var element in elements) { Console.WriteLine( $"element: {element.Name?.PadRight(20) ?? "null"} length: {element.Length}" ); - foreach (var channel in element.Channels) - { + foreach (var channel in element.Channels) { Console.WriteLine( $"\tchannel: {channel.Name?.PadRight(10) ?? "null"} delay: {channel.Delay?.ToString() ?? "null"}" ); } - if (!element.Channels.Any()) - { + if (!element.Channels.Any()) { Console.WriteLine("\t(don't have channels)"); } Console.WriteLine(); diff --git a/T2dMath.UtilsTest/PromerParser.cs b/T2dMath.UtilsTest/PromerParser.cs index 43d1cda..f99c03f 100644 --- a/T2dMath.UtilsTest/PromerParser.cs +++ b/T2dMath.UtilsTest/PromerParser.cs @@ -1,31 +1,24 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; -namespace T2dMath.UtilsTest -{ - public class PromerFileParser - { - public static double[] ParseLengths(string filepath) - { - using (StreamReader sr = new StreamReader(filepath)) - { +namespace T2dMath.UtilsTest { + public class PromerFileParser { + public static double[] ParseLengths(string filepath) { + using (StreamReader sr = new StreamReader(filepath)) { return ParseLengths(sr); } } - public static double[] ParseLengths(TextReader reader) - { + public static double[] ParseLengths(TextReader reader) { var lengths = new List(); bool dataStarted = false; int dataLinesToSkip = 3; // Пропустить: заголовок, единицы измерения, строку формата (0 0.0) string? line; - while ((line = reader.ReadLine()) != null) - { - if (dataLinesToSkip > 0) - { + while ((line = reader.ReadLine()) != null) { + if (dataLinesToSkip > 0) { dataLinesToSkip--; continue; } @@ -34,24 +27,20 @@ public static double[] ParseLengths(TextReader reader) if (string.IsNullOrEmpty(trimmed)) continue; - if (!dataStarted) - { - if (char.IsDigit(trimmed[0]) || trimmed[0] == '-') - { + if (!dataStarted) { + if (char.IsDigit(trimmed[0]) || trimmed[0] == '-') { dataStarted = true; } else continue; } - if (dataStarted) - { + if (dataStarted) { var parts = trimmed.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 2) continue; - if (double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double length)) - { + if (double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double length)) { lengths.Add(length); } } @@ -60,4 +49,4 @@ public static double[] ParseLengths(TextReader reader) return lengths.ToArray(); } } -} \ No newline at end of file +} diff --git a/T2dMath.UtilsTest/Tools/DoubleComparer.cs b/T2dMath.UtilsTest/Tools/DoubleComparer.cs index d34fb57..97207a6 100644 --- a/T2dMath.UtilsTest/Tools/DoubleComparer.cs +++ b/T2dMath.UtilsTest/Tools/DoubleComparer.cs @@ -1,26 +1,21 @@ -using System; -using System.Collections.Generic; - -namespace T2dMath.UtilsTest.Tools -{ - // Вспомогательный класс для сравнения double с точностью - public class DoubleComparer : IEqualityComparer - { - private readonly double _epsilon; - - public DoubleComparer(double epsilon) - { - _epsilon = epsilon; - } - - public bool Equals(double x, double y) - { - return (double.IsNaN(x) && double.IsNaN(y)) || Math.Abs(x - y) < _epsilon; - } - - public int GetHashCode(double obj) - { - return Math.Round(obj / _epsilon).GetHashCode(); - } - } -} +using System; +using System.Collections.Generic; + +namespace T2dMath.UtilsTest.Tools { + // Вспомогательный класс для сравнения double с точностью + public class DoubleComparer : IEqualityComparer { + private readonly double _epsilon; + + public DoubleComparer(double epsilon) { + _epsilon = epsilon; + } + + public bool Equals(double x, double y) { + return (double.IsNaN(x) && double.IsNaN(y)) || Math.Abs(x - y) < _epsilon; + } + + public int GetHashCode(double obj) { + return Math.Round(obj / _epsilon).GetHashCode(); + } + } +} diff --git a/T2dMath.UtilsTest/Tools/ParserTool.cs b/T2dMath.UtilsTest/Tools/ParserTool.cs index 65eadd6..2e08d31 100644 --- a/T2dMath.UtilsTest/Tools/ParserTool.cs +++ b/T2dMath.UtilsTest/Tools/ParserTool.cs @@ -1,37 +1,30 @@ using System; using System.Collections.Generic; using System.IO; -using System.Xml.Linq; using System.Linq; +using System.Xml.Linq; -namespace T2dMath.UtilsTest -{ - public class ParserTool - { - public static ToolsСonfiguration LoadFromFile(string filePath) - { - using (var reader = new StreamReader(filePath)) - { +namespace T2dMath.UtilsTest { + public class ParserTool { + public static ToolsСonfiguration LoadFromFile(string filePath) { + using (var reader = new StreamReader(filePath)) { return LoadFromStream(reader); } } - public static ToolsСonfiguration LoadFromStream(TextReader in_stream) - { + public static ToolsСonfiguration LoadFromStream(TextReader in_stream) { XDocument doc = XDocument.Load(in_stream); XElement device = doc.Root.Element("device"); var elements = device.Elements("element"); List tools = new List(); - foreach (var element in elements) - { + foreach (var element in elements) { string name = element.Attribute("name")?.Value ?? ""; int length = int.Parse(element.Attribute("length")?.Value ?? "0"); var channels = element.Elements("channel") - .Select(ch => - { + .Select(ch => { return new ChannelInfo( name: ch.Attribute("name")?.Value ?? "", mnem: ch.Attribute("name")?.Value ?? "", diff --git a/T2dMath.UtilsTest/Tools/StopIntervalComparer.cs b/T2dMath.UtilsTest/Tools/StopIntervalComparer.cs index 455cc3e..41f2174 100644 --- a/T2dMath.UtilsTest/Tools/StopIntervalComparer.cs +++ b/T2dMath.UtilsTest/Tools/StopIntervalComparer.cs @@ -1,29 +1,24 @@ using System; using System.Collections.Generic; -namespace T2dMath.UtilsTest.Tools -{ - public class StopIntervalComparer : IEqualityComparer<(double, double)> - { +namespace T2dMath.UtilsTest.Tools { + public class StopIntervalComparer : IEqualityComparer<(double, double)> { private readonly double _epsilon; - public StopIntervalComparer(double epsilon = 1e-12) - { + public StopIntervalComparer(double epsilon = 1e-12) { _epsilon = epsilon; } - public bool Equals((double, double) x, (double, double) y) - { + public bool Equals((double, double) x, (double, double) y) { return (Math.Abs(x.Item1 - y.Item1) < _epsilon) && (Math.Abs(x.Item2 - y.Item2) < _epsilon); } - public int GetHashCode((double, double) obj) - { + public int GetHashCode((double, double) obj) { int hash1 = Math.Round(obj.Item1 / _epsilon).GetHashCode(); int hash2 = Math.Round(obj.Item2 / _epsilon).GetHashCode(); return HashCode.Combine(hash1, hash2); } } -} \ No newline at end of file +} diff --git "a/T2dMath.UtilsTest/Tools\320\241onfiguration.cs" "b/T2dMath.UtilsTest/Tools\320\241onfiguration.cs" index a736269..26f12f0 100644 --- "a/T2dMath.UtilsTest/Tools\320\241onfiguration.cs" +++ "b/T2dMath.UtilsTest/Tools\320\241onfiguration.cs" @@ -1,86 +1,82 @@ -using System; -using System.Linq; - -namespace T2dMath.UtilsTest -{ - //name="SPEE" mnem="SPEE" offset="0" isfromtop="False" - //isrtservice="False" isazimuthal="False" isrtalgorithm="False" - //rtalgorithm="" show="on" alg="LPostAlgSpeed(HTIME)" order="10" unit="M/H" description - public record ChannelInfo( - string name, - string mnem, - int offset, - bool isfromtop, - bool isrtservice, - bool isazimuthal, - bool isrtalgorithm, - string rtalgorithm, - bool show, - string alg, - string avg_alg, - int order, - int delay, - string unit, - string description - ); - - public record CurveProcessingInfo( - string name, //Имя кривой - double measure_point, //Точка записи в метрах - double delay, - string time_algorithm_name, - string[] time_algorithm_input_curve, - object[] time_algorithm_parameters, - string average_algorithm_name, - object[] average_algorithm_parameters, - string depth_algorithm_name, - string[] depth_algorithm_input_curve, - object[] depth_algorithm_parameters - ); - - public record ToolInfo(string name, int length, string driver_name, ChannelInfo[] channels); - - public class ToolsСonfiguration - { - private ToolInfo[] tools; - - public ToolsСonfiguration(string name, ToolInfo[] _tools) - { - Name = name; - tools = _tools; - } - - public string Name { get; private set; } - public double Length => tools.Sum(t => t.length); - public string[] ToolsName => tools.Select(t => t.name).ToArray(); - // public double Length - // { - // get { throw new NotImplementedException(); } - // } - // public string[] ToolsName - // { - // get { throw new NotImplementedException(); } - // } - - // Список отсортирован по - public CurveProcessingInfo[] GetToolCurve(string name) - { - var tool = tools.FirstOrDefault(t => t.name == name); - if (tool == null) return Array.Empty(); - - return tool.channels.Select(ch => new CurveProcessingInfo( - name: ch.name, - measure_point: ch.offset, - delay: ch.delay, - time_algorithm_name: ch.alg, - time_algorithm_input_curve: Array.Empty(), - time_algorithm_parameters: Array.Empty(), - average_algorithm_name: ch.avg_alg, - average_algorithm_parameters: Array.Empty(), - depth_algorithm_name: "", - depth_algorithm_input_curve: Array.Empty(), - depth_algorithm_parameters: Array.Empty() - )).ToArray(); - } - } -} +using System; +using System.Linq; + +namespace T2dMath.UtilsTest { + //name="SPEE" mnem="SPEE" offset="0" isfromtop="False" + //isrtservice="False" isazimuthal="False" isrtalgorithm="False" + //rtalgorithm="" show="on" alg="LPostAlgSpeed(HTIME)" order="10" unit="M/H" description + public record ChannelInfo( + string name, + string mnem, + int offset, + bool isfromtop, + bool isrtservice, + bool isazimuthal, + bool isrtalgorithm, + string rtalgorithm, + bool show, + string alg, + string avg_alg, + int order, + int delay, + string unit, + string description + ); + + public record CurveProcessingInfo( + string name, //Имя кривой + double measure_point, //Точка записи в метрах + double delay, + string time_algorithm_name, + string[] time_algorithm_input_curve, + object[] time_algorithm_parameters, + string average_algorithm_name, + object[] average_algorithm_parameters, + string depth_algorithm_name, + string[] depth_algorithm_input_curve, + object[] depth_algorithm_parameters + ); + + public record ToolInfo(string name, int length, string driver_name, ChannelInfo[] channels); + + public class ToolsСonfiguration { + private ToolInfo[] tools; + + public ToolsСonfiguration(string name, ToolInfo[] _tools) { + Name = name; + tools = _tools; + } + + public string Name { get; private set; } + public double Length => tools.Sum(t => t.length); + public string[] ToolsName => tools.Select(t => t.name).ToArray(); + // public double Length + // { + // get { throw new NotImplementedException(); } + // } + // public string[] ToolsName + // { + // get { throw new NotImplementedException(); } + // } + + // Список отсортирован по + public CurveProcessingInfo[] GetToolCurve(string name) { + var tool = tools.FirstOrDefault(t => t.name == name); + if (tool == null) return Array.Empty(); + + return tool.channels.Select(ch => new CurveProcessingInfo( + name: ch.name, + measure_point: ch.offset, + delay: ch.delay, + time_algorithm_name: ch.alg, + time_algorithm_input_curve: Array.Empty(), + time_algorithm_parameters: Array.Empty(), + average_algorithm_name: ch.avg_alg, + average_algorithm_parameters: Array.Empty(), + depth_algorithm_name: "", + depth_algorithm_input_curve: Array.Empty(), + depth_algorithm_parameters: Array.Empty() + )).ToArray(); + } + } +} diff --git a/T2dMath/CalculateSpeedMoving.cs b/T2dMath/CalculateSpeedMoving.cs index ac81e64..78e9290 100644 --- a/T2dMath/CalculateSpeedMoving.cs +++ b/T2dMath/CalculateSpeedMoving.cs @@ -1,53 +1,47 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace T2dMath -{ - public partial class LogMath - { - /// - /// Вычисление скорости движения (ед. изм. глубины)/час - /// - /// - /// - /// - /// - public double[] CalculateSpeedMoving(double[] time, double[] depth) - { - if (time.Length != depth.Length) - { - throw new ArgumentException( - string.Format( - "(time.Length={0}) != (depth.Length={1})", - time.Length, - depth.Length - ) - ); - } - - if (time.Length == 0) - { - return new double[0]; - } - if (time.Length == 1) - { - return new double[] { 0 }; - } - - double[] spd = Enumerable - .Range(0, time.Length) - .AsParallel() - .Select(i => - i == 0 ? 0 : (depth[i] - depth[i - 1]) / ((time[i] - time[i - 1]) * 24) - ) - .ToArray(); - spd[0] = spd[1]; - return spd; - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace T2dMath { + public partial class LogMath { + /// + /// Вычисление скорости движения (ед. изм. глубины)/час + /// + /// + /// + /// + /// + public double[] CalculateSpeedMoving(double[] time, double[] depth) { + if (time.Length != depth.Length) { + throw new ArgumentException( + string.Format( + "(time.Length={0}) != (depth.Length={1})", + time.Length, + depth.Length + ) + ); + } + + if (time.Length == 0) { + return new double[0]; + } + if (time.Length == 1) { + return new double[] { 0 }; + } + + double[] spd = Enumerable + .Range(0, time.Length) + .AsParallel() + .Select(i => + i == 0 ? 0 : (depth[i] - depth[i - 1]) / ((time[i] - time[i - 1]) * 24) + ) + .ToArray(); + spd[0] = spd[1]; + return spd; + } + } +} diff --git a/T2dMath/CalculateStopsByDepth.cs b/T2dMath/CalculateStopsByDepth.cs index d27d375..1915bb8 100644 --- a/T2dMath/CalculateStopsByDepth.cs +++ b/T2dMath/CalculateStopsByDepth.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -namespace T2dMath -{ - public partial class LogMath - { +namespace T2dMath { + public partial class LogMath { //! Вычисление стоянок по весу на основе анализа спектра /*! @param[in] n количество отсчетов @@ -21,15 +19,12 @@ public static (double start, double finish)[] CalculateStopsByDepth( double[] depth, int mintime, double eps - ) - { + ) { int n = depth.Length; - if (n <= 0 || time == null || depth == null) - { + if (n <= 0 || time == null || depth == null) { throw new ArgumentException("Invalid input parameters."); } - if (time.Length != n || depth.Length != n) - { + if (time.Length != n || depth.Length != n) { throw new ArgumentException( "Time and depth arrays must have the same length as n." ); @@ -39,22 +34,18 @@ double eps List idxFinishStops = new List(); bool isCurrentlyStopped = false; - for (int i = 0; i < n - 1; i++) - { - if (Math.Abs(depth[i + 1] - depth[i]) < eps && !isCurrentlyStopped) - { + for (int i = 0; i < n - 1; i++) { + if (Math.Abs(depth[i + 1] - depth[i]) < eps && !isCurrentlyStopped) { isCurrentlyStopped = true; idxStartStops.Add(i); } - if (Math.Abs(depth[i + 1] - depth[i]) >= eps && isCurrentlyStopped) - { + if (Math.Abs(depth[i + 1] - depth[i]) >= eps && isCurrentlyStopped) { isCurrentlyStopped = false; idxFinishStops.Add(i); } } - if (isCurrentlyStopped) - { + if (isCurrentlyStopped) { idxFinishStops.Add(n - 1); } @@ -75,8 +66,7 @@ double eps ); var timeIntervals = new (double start, double finish)[nstops]; - for (int i = 0; i < nstops; i++) - { + for (int i = 0; i < nstops; i++) { timeIntervals[i] = (start[i], finish[i]); } @@ -91,16 +81,13 @@ private static void FilterShortStops( int minTime, List startIndexes, List finishIndexes - ) - { + ) { if (minTime < 0) throw new ArgumentException("minTime must be non-negative."); - for (int i = startIndexes.Count - 1; i >= 0; i--) - { + for (int i = startIndexes.Count - 1; i >= 0; i--) { double duration = time[finishIndexes[i]] - time[startIndexes[i]]; - if (duration < (ulong)minTime) - { + if (duration < (ulong)minTime) { startIndexes.RemoveAt(i); finishIndexes.RemoveAt(i); } @@ -119,8 +106,7 @@ private static void ConvertStopsIndexesToTimeValues( //(double start, double finish)[] time_values, double[] startTimes, double[] finishTimes - ) - { + ) { if (indexesArraySize < 0) throw new ArgumentException("indexesArraySize cannot be negative."); if ( @@ -136,11 +122,10 @@ double[] finishTimes "Start and finish indexes arrays must have the same length as indexesArraySize." ); - for (int i = 0; i < indexesArraySize; i++) - { + for (int i = 0; i < indexesArraySize; i++) { startTimes[i] = time[startIndexes[i]]; finishTimes[i] = time[finishIndexes[i]]; } } } -} \ No newline at end of file +} diff --git a/T2dMath/DefineGoodQualityRecordingIntervals.cs b/T2dMath/DefineGoodQualityRecordingIntervals.cs index 45b82f2..b4435a8 100644 --- a/T2dMath/DefineGoodQualityRecordingIntervals.cs +++ b/T2dMath/DefineGoodQualityRecordingIntervals.cs @@ -1,21 +1,18 @@ -using System; - -namespace T2dMath -{ - public partial class LogMath - { - //Определение хороших интервалов записи - public void DefineGoodQualityRecordingIntervalsByRotor( - double[] time, - double[] depth, - double[] spd, - (double start, double finish)[] stop_inteval, - (double start, double finish)[] rotor_interval, - double max_spd, - out double[] quality_depth - ) - { - throw new NotImplementedException(); - } - } -} +using System; + +namespace T2dMath { + public partial class LogMath { + //Определение хороших интервалов записи + public void DefineGoodQualityRecordingIntervalsByRotor( + double[] time, + double[] depth, + double[] spd, + (double start, double finish)[] stop_inteval, + (double start, double finish)[] rotor_interval, + double max_spd, + out double[] quality_depth + ) { + throw new NotImplementedException(); + } + } +} diff --git a/T2dMath/DepthGridNoStopsAvg.cs b/T2dMath/DepthGridNoStopsAvg.cs index 6c960cc..af942ca 100644 --- a/T2dMath/DepthGridNoStopsAvg.cs +++ b/T2dMath/DepthGridNoStopsAvg.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -namespace T2dMath -{ +namespace T2dMath { using AlmazTime = double; - public partial class LogMath - { + public partial class LogMath { /* @param[in] n количество отсчетов @param[in] depth глубина привязанная ко времени [n] @@ -24,8 +22,7 @@ public static int BuildLogCurveDepthGridNoStopsAvg( int ngrid, double[] depthgrid, double[] outdata - ) - { + ) { const double eps = 0.001; // Минимально допустимое расстояние между точками int m = 0; // Размерность монотонной функции int codeError = 0; // Код возвращаемой ошибки @@ -33,10 +30,8 @@ double[] outdata double[] depthMonoton = new double[n]; double[] valueMonoton = new double[n]; - try - { - if (IsNullValueInArray(n, depth) || IsNullValueInArray(n, data)) - { + try { + if (IsNullValueInArray(n, depth) || IsNullValueInArray(n, data)) { throw new Exception("5"); } @@ -70,8 +65,7 @@ out int fin outdata ); } - catch (Exception ex) - { + catch (Exception ex) { Console.WriteLine($"Error at line 66: {ex.Message}"); Console.WriteLine( $"Array sizes - depth: {depth.Length}, time: {time.Length}, data: {data.Length}, depthGrid: {depthgrid.Length}, outData: {outdata.Length}" @@ -84,32 +78,26 @@ out int fin return codeError; } - private static bool IsNullValueInArray(int n, double[] value) - { - for (int i = 0; i < n; i++) - { + private static bool IsNullValueInArray(int n, double[] value) { + for (int i = 0; i < n; i++) { if (double.IsNaN(value[i])) return true; } return false; } - private static double MinInArray(int n, double[] array) - { + private static double MinInArray(int n, double[] array) { double min = double.MaxValue; - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { if (array[i] < min) min = array[i]; } return min; } - private static double MaxInArray(int n, double[] array) - { + private static double MaxInArray(int n, double[] array) { double max = double.MinValue; - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { if (array[i] > max) max = array[i]; } @@ -126,39 +114,32 @@ private static void Hod( double minDepth, double maxDepth, double eps - ) - { + ) { List> tmpVector = new List>(n); double fDirection = depth[n - 1] - depth[0]; int count = 0; - for (int i = 0; i < n; i++) - { - if (i == 0 || fDirection * (depth[i] - depth[i - 1]) > 0) - { + for (int i = 0; i < n; i++) { + if (i == 0 || fDirection * (depth[i] - depth[i - 1]) > 0) { tmpVector.Add(new Tuple(depth[i], value[i])); count++; } } - if (fDirection > 0) - { + if (fDirection > 0) { tmpVector.Sort((x, y) => x.Item1.CompareTo(y.Item1)); // Сортировка по возрастанию } - else - { + else { tmpVector.Sort((x, y) => y.Item1.CompareTo(x.Item1)); // Сортировка по убыванию } m = 0; int iIndex = 0; - while (iIndex < count) - { + while (iIndex < count) { double firstI = tmpVector[iIndex].Item1; - if (minDepth <= firstI && firstI <= maxDepth) - { + if (minDepth <= firstI && firstI <= maxDepth) { int j = iIndex + 1; while (j < count && Math.Abs(tmpVector[j].Item1 - firstI) < eps) j++; @@ -167,8 +148,7 @@ double eps double d = 0, v = 0; - for (int k = iIndex; k < j; k++) - { + for (int k = iIndex; k < j; k++) { d += tmpVector[k].Item1; v += tmpVector[k].Item2; } @@ -179,8 +159,7 @@ double eps iIndex += length; } - else - { + else { iIndex++; } } @@ -193,8 +172,7 @@ private static void IntervalIntersectionDoublesVlasov( double[] intervalOfDoubles, out int idxStartCat, out int idxStopCat - ) - { + ) { idxStartCat = -1; idxStopCat = -1; @@ -223,21 +201,17 @@ out int idxStopCat idxStopCat = (intervalOfDoubles[0] <= intervalOfDoubles[m - 1]) ? m - 1 : 0; } - if (idxStopCat == -1 || idxStartCat == -1) - { + if (idxStopCat == -1 || idxStartCat == -1) { // Если один из концов не определен, пробегаем по массиву и ищем границы - for (int i = 0; i < m - 1; i++) - { + for (int i = 0; i < m - 1; i++) { double curMin = Math.Min(intervalOfDoubles[i], intervalOfDoubles[i + 1]); double curMax = Math.Max(intervalOfDoubles[i], intervalOfDoubles[i + 1]); - if (idxStartCat == -1 && curMin <= borderStart && borderStart <= curMax) - { + if (idxStartCat == -1 && curMin <= borderStart && borderStart <= curMax) { idxStartCat = (intervalOfDoubles[i] > borderStart) ? i : i + 1; } - if (idxStopCat == -1 && curMin <= borderFinish && borderFinish <= curMax) - { + if (idxStopCat == -1 && curMin <= borderFinish && borderFinish <= curMax) { idxStopCat = (intervalOfDoubles[i] < borderFinish) ? i : i + 1; } } @@ -257,11 +231,9 @@ private static int GridDepthTransformateAvgFast( double[] grid1, double[] value2, double[] resvalue - ) - { + ) { int cur_pos = 0; - for (int i = 0; i < m; i++) - { + for (int i = 0; i < m; i++) { GetInterval(m, grid2, i, out double start, out double finish); int count = 0; double sum = 0; @@ -274,8 +246,7 @@ double[] resvalue ) ) && cur_pos < n - 1 - ) - { + ) { cur_pos++; } @@ -286,8 +257,7 @@ double[] resvalue Math.Min(start, finish) <= grid1[cur_pos] && grid1[cur_pos] < Math.Max(start, finish) ) - ) - { + ) { count++; sum += value2[cur_pos]; cur_pos++; @@ -303,17 +273,14 @@ private static void GetInterval( int idx, out double start, out double finish - ) - { - if (idx == 0) - { + ) { + if (idx == 0) { double len = (grid[1] - grid[0]) / 2; start = grid[0] - len; finish = grid[0] + len; return; } - if (idx == n - 1) - { + if (idx == n - 1) { double len = (grid[n - 1] - grid[n - 2]) / 2; start = grid[n - 1] - len; finish = grid[n - 1] + len; diff --git a/T2dMath/SimulationHoistBlockMovement.cs b/T2dMath/SimulationHoistBlockMovement.cs index f8480d0..fad6e7d 100644 --- a/T2dMath/SimulationHoistBlockMovement.cs +++ b/T2dMath/SimulationHoistBlockMovement.cs @@ -1,13 +1,10 @@ -using System; - -namespace T2dMath -{ - public partial class LogMath - { - // https://www.actio.ru/rekomendacii-po-ustanovke-i-ispolzovaniyu-kanatov.html - public void SimulationHoistBlockMovement() - { - throw new NotImplementedException(); - } - } -} +using System; + +namespace T2dMath { + public partial class LogMath { + // https://www.actio.ru/rekomendacii-po-ustanovke-i-ispolzovaniyu-kanatov.html + public void SimulationHoistBlockMovement() { + throw new NotImplementedException(); + } + } +} diff --git a/T2dMath/T2DMathCalculateDepth.cs b/T2dMath/T2DMathCalculateDepth.cs index a0d5133..485ad3d 100644 --- a/T2dMath/T2DMathCalculateDepth.cs +++ b/T2dMath/T2DMathCalculateDepth.cs @@ -1,973 +1,874 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace T2dMath -{ - public partial class LogMath - { - public static int SetAbsoluteDepthByPromerVlasov( - int n, - double[] time, - double[] depth, - double[] njoint, - int nstops, - //double[] stopsStart, - //double[] stopsFinish, - (double start, double finish)[] time_intervals, - int npipe, - double[] lengthPipe, - int maxFullPipe, - double lengthDevice, - double epsZaboy, - double[] kgrip, - double[] kmove, - out int numStartPipe, - out int numStopPipe, - double[] deformationPipe) - { - // Преобразование временных меток в индексы стоянок - //var idxStart = new int[nstops]; - //var idxFinish = new int[nstops]; - int nstopsIdx = nstops; - (int idxStart, int idxFinish)[] idxs_intervals = ConvertTimeValuesToStopsIndexes(time, time_intervals); - //ConvertTimeValuesToStopsIndexes(n, ref nstopsIdx, time, idxStart, idxFinish, stopsStart, stopsFinish); - - // Вычисляем глубины по промеру труб - var promerStopDepth = new double[npipe + 1]; - CalculateStopDepthPromer(npipe, lengthPipe, lengthDevice, promerStopDepth); - double defaultDepth = promerStopDepth.Last(); - - // Обработка случая без стоянок - if (nstopsIdx == 0) - { - for (int i = 0; i < n; i++) depth[i] = defaultDepth; - for (int i = 0; i < n; i++) njoint[i] = -127; - numStartPipe = 0; - numStopPipe = npipe - 1; - for (int i = 0; i < npipe; i++) deformationPipe[i] = -1; - return 0; - } - - // Вычисляем глубины стоянок (среднее) - var oldStopDepth = new double[nstopsIdx]; - CalculateStopDepth(nstopsIdx, depth, idxs_intervals, oldStopDepth); - - // Определяем индексы труб на стоянках - var idxPipeStop = new int[nstopsIdx]; - CalculateIdxPipe(depth, nstopsIdx, idxs_intervals, oldStopDepth, maxFullPipe, epsZaboy, idxPipeStop); - - // Проверяем корректность индексов труб - for (int i = 0; i < nstopsIdx; i++) - { - if (idxPipeStop[i] < 0) - { - numStartPipe = numStopPipe = 0; - return 57; - } - } - - // Инициализация njoint - for (int i = 0; i < n; i++) njoint[i] = -127; - int curIdx = 0; - for (int i = 0; i < n; i++) - { - while (curIdx < nstopsIdx && /*idxFinish[curIdx]*/idxs_intervals[curIdx].idxFinish < i) curIdx++; - if (curIdx >= nstopsIdx) break; - njoint[i] = idxPipeStop[curIdx]; - } - - // Выставляем глубину промера на стоянках - for (int i = 0; i < nstopsIdx; i++) - for (int j = /*idxStart[i]*/idxs_intervals[i].idxStart; j <= idxs_intervals[i].idxFinish; j++) - depth[j] = promerStopDepth[idxPipeStop[i]]; - - // Обработка до первой и после последней стоянки - double firstDepth = promerStopDepth[idxPipeStop[0]]; - for (int i = 0; i < idxs_intervals[0].idxStart; i++) depth[i] = firstDepth; - double lastDepth = promerStopDepth[idxPipeStop[nstopsIdx - 1]]; - for (int i = idxs_intervals[nstopsIdx - 1].idxFinish + 1; i < n; i++) depth[i] = lastDepth; - - // Интерполяция между стоянками - for (int i = 0; i < nstopsIdx - 1; i++) - { - if (idxPipeStop[i] != idxPipeStop[i + 1]) - { - CalculateAffineCoefficient( - oldStopDepth[i], oldStopDepth[i + 1], - promerStopDepth[idxPipeStop[i]], promerStopDepth[idxPipeStop[i + 1]], - out double A, out double B, - out kgrip[i], out kmove[i]); - //TransformVectorAphine(idxStart[i + 1] - idxFinish[i] - 1, depth, idxFinish[i] + 1, A, B); - TransformVectorAphine(idxs_intervals[i + 1].idxStart - idxs_intervals[i].idxFinish - 1, - depth, - idxs_intervals[i].idxFinish + 1, - A, - B); - } - else - { - double move = promerStopDepth[idxPipeStop[i + 1]] - oldStopDepth[i + 1]; - for (int j = idxs_intervals[i].idxFinish + 1; j < idxs_intervals[i + 1].idxStart; j++) depth[j] += move; - kgrip[i] = 1; - kmove[i] = move; - } - } - - // Вычисляем деформации труб - var resDelta = Enumerable.Repeat(-1.0, npipe).ToArray(); - int correctIndex = idxPipeStop[0] > idxPipeStop[nstopsIdx - 1] ? 1 : 0; - for (int i = 1; i < nstopsIdx; i++) - { - int numPipe = idxPipeStop[i - correctIndex]; - if (numPipe - 1 >= 0) - { - double val = resDelta[numPipe - 1] < 0 - ? kgrip[i - 1] - : MaxDeformation(resDelta[numPipe - 1], kgrip[i]); - resDelta[numPipe - 1] = val; - } - } - - int l = 0; - while (l < npipe && resDelta[l] < 0) l++; - - int r = npipe - 1; - while (r >= 0 && resDelta[r] < 0) r--; - - numStartPipe = l; - numStopPipe = r; - - Array.Copy(resDelta, deformationPipe, npipe); - - // Диапазон используемых труб - //numStartPipe = Array.FindIndex(resDelta, d => d >= 0); - //numStopPipe = Array.FindLastIndex(resDelta, d => d >= 0); - //Array.Copy(resDelta, deformationPipe, npipe); - - return 0; - } - - public static int DefineIdxZaboyDepth(double[] depth, out double avgDepth) - { - if (depth == null || depth.Length == 0) - { - avgDepth = 0; - return -1; - } - int idx = 0; - double sum = depth[0]; - for (int i = 1; i < depth.Length; i++) - { - sum += depth[i]; - if (depth[i] > depth[idx]) - idx = i; - } - avgDepth = sum / depth.Length; - return idx; - } - - /// - /// Корректирует интервалы стоянок, добавляя фиксы по забою. - /// - public static int CorrectStopsFixZaboy( - int n, - double[] time, - double[] depth, - int nstops, - //double[] stopsStart, - //double[] stopsFinish, - (double start, double finish)[] time_intervals, - double epsZaboy, - double length_tool, - double[] length_drill_pipe, - out int nstopsNew, - out (double start, double finish)[] time_intervals_new) - { - // 1. Индексы исходных стоянок - //int[] idxStart = new int[nstops]; - //int[] idxFinish = new int[nstops]; - int tmpStops = nstops; - //ConvertTimeValuesToStopsIndexes(n, ref tmpStops, time, idxStart, idxFinish, stopsStart, stopsFinish); - (int idxStart, int idxFinish)[] idxs_intervals = ConvertTimeValuesToStopsIndexes(time, time_intervals); - - // Нормализация глубин на основе реального масштаба инструмента + труб - /*double expectedMax = length_tool + (length_drill_pipe?.Sum() ?? 0); - double maxDepth = depth.Max(); - - if (Math.Abs(maxDepth - expectedMax) > 1.0) - { - double shift = maxDepth - expectedMax; - for (int i = 0; i < depth.Length; i++) - depth[i] -= shift; - }*/ - - // 2. Глубины стоянок - double[] stopDepth = new double[nstops]; - CalculateStopDepth(nstops, depth, idxs_intervals, stopDepth); - - // 3. Индексы труб с maxFullPipe = 0 (забой) - int[] idxPipeStop = new int[nstops]; - CalculateIdxPipe(depth, nstops, idxs_intervals, stopDepth, maxNumPipe: 0, epsZaboy, idxPipeStop); - - // 4. Индексы стоянок на забое (где idxPipeStop == 0) - var zaboyStops = new List(); - for (int i = 0; i < nstops; i++) - if (idxPipeStop[i] == 0) - zaboyStops.Add(i); - int nZaboy = zaboyStops.Count; - - // 5. Подготовка к новым стоянкам - var idxStartRes = new List(); - var idxFinishRes = new List(); - int cursor = 0; - double curDepth = 0; - - // Обход интервалов: начало, промежутки, хвост - for (int k = -1; k < nZaboy + 1; k++) - { - int startInt, stopInt; - if (k == -1) - { - startInt = 0; - stopInt = idxs_intervals[zaboyStops[0]].idxStart; - curDepth = stopDepth[zaboyStops[0]]; - } - else if (k == nZaboy) - { - // Добавляем оставшиеся исходные стоянки - for (int i = cursor; i < nstops; i++) - { - idxStartRes.Add(idxs_intervals[i].idxStart); - idxFinishRes.Add(idxs_intervals[i].idxFinish); - } - startInt = idxs_intervals[zaboyStops[nZaboy - 1]].idxFinish + 1; - stopInt = n; - curDepth = stopDepth[zaboyStops[nZaboy - 1]]; - } - else - { - int iZ = zaboyStops[k]; - // Копируем стоянки до текущей забойной - for (int i = cursor; i <= iZ; i++) - { - idxStartRes.Add(idxs_intervals[i].idxStart); - idxFinishRes.Add(idxs_intervals[i].idxFinish); - } - cursor = iZ + 1; - curDepth = stopDepth[iZ]; - startInt = idxs_intervals[iZ].idxFinish; - stopInt = idxs_intervals[iZ].idxStart; - if (k + 1 < nZaboy && zaboyStops[k] + 1 == zaboyStops[k + 1]) - stopInt = idxs_intervals[zaboyStops[k + 1]].idxStart; - } - - if (startInt < stopInt) - { - int len = stopInt - startInt; - var segment = depth.Skip(startInt).Take(len).ToArray(); - int idx = DefineIdxZaboyDepth(segment, out double avgSeg) + startInt; - if (avgSeg > curDepth) - { - idxStartRes.Add(idx); - idxFinishRes.Add(idx); - } - } - } - - // 6. Конвертация индексов в времена - nstopsNew = idxStartRes.Count; - //stopsStartNew = new double[nstopsNew]; - //stopsFinishNew = new double[nstopsNew]; - time_intervals_new = new (double start, double finish)[nstopsNew]; - time_intervals_new = ConvertStopsIndexesToTimeValues(nstopsNew, idxs_intervals, time); - //ConvertStopsIndexesToTimeValues(nstopsNew, idxStartRes.ToArray(), idxFinishRes.ToArray(), time, stopsStartNew, stopsFinishNew); - - return 0; - } - - - // Вспомогательные методы: - - /*private static void ConvertStopsIndexesToTimeValues( - int count, - int[] idxStart, - int[] idxFinish, - double[] time, - double[] stopsStart, - double[] stopsFinish) - { - for (int i = 0; i < count; i++) - { - stopsStart[i] = time[idxStart[i]]; - stopsFinish[i] = time[idxFinish[i]]; - } - }*/ - - public static (double Start, double Finish)[] ConvertStopsIndexesToTimeValues( - int count, - (int idxStart, int idxFinish)[] idxsIntervals, - double[] time) - { - (double Start, double Finish)[] newTimeVals = new (double Start, double Finish)[count]; - for (int i = 0; i < count; i++) - { - newTimeVals[i] = (time[idxsIntervals[i].idxStart], time[idxsIntervals[i].idxFinish]); - } - return newTimeVals; - } - - - - public static (int idx_start, int idx_end)[] ConvertTimeValuesToStopsIndexes( - double[] time, (double time_start, double time_end)[] time_intervals) - { - if (time == null || time.Length == 0 || time_intervals == null) - return Array.Empty<(int, int)>(); - - var adjustedIntervals = new List<(double start, double end)>(); - double t0 = time[0]; - double tLast = time[^1]; - - foreach (var interval in time_intervals) - { - if (interval.time_end <= t0 || interval.time_start >= tLast) - continue; - - double adjustedStart = Math.Max(interval.time_start, t0); - double adjustedEnd = Math.Min(interval.time_end, tLast); - adjustedIntervals.Add((adjustedStart, adjustedEnd)); - } - - return adjustedIntervals - .Select(interval => ( - Array.FindIndex(time, t => t >= interval.start), - Array.FindIndex(time, t => t >= interval.end) - )) - .ToArray(); - } - - public static void ConvertTimeValuesToStopsIndexes( - int timeCount, ref int nstopsIdx, double[] time, - int[] idxStart, int[] idxFinish, - double[] stopsStart, double[] stopsFinish) - { - var starts = new System.Collections.Generic.List(); - var finishes = new System.Collections.Generic.List(); - double t0 = time[0], tLast = time[timeCount - 1]; - for (int i = 0; i < nstopsIdx; i++) - { - double s = stopsStart[i], f = stopsFinish[i]; - if (s <= t0 && f > t0) { starts.Add(t0); finishes.Add(f); } - else if (s < tLast && f >= tLast) { starts.Add(s); finishes.Add(tLast); } - else if (s > t0 && f < tLast) { starts.Add(s); finishes.Add(f); } - } - nstopsIdx = starts.Count; - for (int i = 0; i < nstopsIdx; i++) - { - idxStart[i] = Array.FindIndex(time, x => x >= starts[i]); - idxFinish[i] = Array.FindIndex(time, x => x >= finishes[i]); - } - } - - private static void CalculateStopDepth( - int nstops, double[] depth, - (int idxStart, int idxFinish)[] idxs_intervals, - double[] stopDepth) - { - for (int i = 0; i < nstops; i++) - { - int start = idxs_intervals[i].idxStart; - int end = idxs_intervals[i].idxFinish; - int length = end - start + 1; - - stopDepth[i] = depth - .Skip(start) - .Take(length) - .Average(); - } - } - - private static void CalculateStopDepthPromer( - int npipe, double[] lengthPipe, - double lengthDevice, double[] promerStopDepth) - { - promerStopDepth[0] = lengthDevice; - for (int i = 0; i < npipe; i++) - promerStopDepth[i + 1] = promerStopDepth[i] + lengthPipe[i]; - } - - private static void CalculateIdxPipe( - double[] depth, - int nstops, - (int idxStart, int idxFinish)[] idxs_intervals, - double[] stopDepth, - int maxNumPipe, - double epsZaboy, - int[] idxPipeStop) - { - int n = depth.Length; - - // 1. Находим индекс самой глубокой стоянки - int idxLastFullLengthStop = 0; - for (int i = 1; i < nstops; i++) - { - if (stopDepth[i] > stopDepth[idxLastFullLengthStop]) - idxLastFullLengthStop = i; - } - - // 2. Вычисляем тенденции для интервалов - double[] tendency = new double[nstops + 1]; - for (int i = 0; i <= nstops; i++) - { - if (i == 0) - { - tendency[i] = idxs_intervals[0].idxStart != 0 - ? stopDepth[0] - depth[0] - : 0; - } - else if (i == nstops) - { - tendency[i] = idxs_intervals[nstops - 1].idxFinish != n - 1 - ? depth[n - 1] - stopDepth[nstops - 1] - : 0; - } - else - { - tendency[i] = stopDepth[i] - stopDepth[i - 1]; - } - } - - // 3. Определяем действия (+1/-1/0) - int[] action = new int[nstops + 1]; - for (int i = 0; i <= nstops; i++) - { - if (tendency[i] < -epsZaboy) - { - action[i] = -1; - continue; - } - if (tendency[i] > epsZaboy) - { - action[i] = +1; - continue; - } - action[i] = 0; - } - - // 4. Заполняем индексы труб - idxPipeStop[idxLastFullLengthStop] = maxNumPipe; - - // Двигаемся влево от самой глубокой стоянки - for (int i = idxLastFullLengthStop - 1; i >= 0; i--) - { - idxPipeStop[i] = idxPipeStop[i + 1] - action[i + 1]; - } - - // Двигаемся вправо от самой глубокой стоянки - for (int i = idxLastFullLengthStop + 1; i < nstops; i++) - { - idxPipeStop[i] = idxPipeStop[i - 1] + action[i]; - } - } - - public static void CalculateAffineCoefficient( - double x_start_old, double x_finish_old, - double x_start_new, double x_finish_new, - out double A, out double B, - out double kgrip, out double kmove) - { - kgrip = (x_finish_new - x_start_new) / (x_finish_old - x_start_old); - A = kgrip; - kmove = x_start_new - x_start_old * A; - B = kmove; - } - - private static void TransformVectorAphine( - int length, double[] depth, - int startIndex, double A, double B) - { - for (int i = 0; i < length; i++) - depth[startIndex + i] = A * depth[startIndex + i] + B; - } - - private static double MaxDeformation(double d1, double d2) - { - return Math.Abs(d1 - 1) > Math.Abs(d2 - 1) ? d1 : d2; - } - - // Перевод стоянок в кривую - public static double[] StopsToCurve(int mstop, (double tStart, double tStop)[] stops_intervals, double[] time) - { - int n = time.Length; - var curve = Enumerable.Repeat(-1.0, n).ToArray(); - int countPipe = 0; - - for (int j = 0; j < mstop; j++) - { - for (int i = 0; i < n; i++) - { - if (stops_intervals[j].tStart <= time[i] && time[i] <= stops_intervals[j].tStop) - curve[i] = countPipe; - } - countPipe++; - } - - return curve; - } - - // Вычисление стоянок по весу на основе анализа спектра - /// - /// Вычисление стоянок по весу на основе анализа спектра - /// - /// - /// - /// ворота в милисекундах - /// критерий на основе которго вычисляют стоянки - /// - public static (double start, double end)[] CalculateStopsStatisticVlasov( - double[] time, double[] weight, int gate, double criteria) - { - int n = time.Length; - if (n < 3) - return Array.Empty<(double, double)>(); - - // Префиксная сумма для быстрого среднего - double[] prefixSum = new double[n + 1]; - for (int i = 0; i < n; i++) - prefixSum[i + 1] = prefixSum[i] + weight[i]; - - double AverageFast(int l, int r) - { - if (l > r) return 0; - return (prefixSum[r + 1] - prefixSum[l]) / (r - l + 1); - } - - // Градиентная функция - double[] fw = new double[n]; - for (int i = 0; i < n; i++) - { - GetGateInterval(time, i, -gate, out int idxL1, out int idxR1); - double avLeft = AverageFast(idxL1, idxR1); - - GetGateInterval(time, i, gate, out int idxL2, out int idxR2); - double avRight = AverageFast(idxL2, idxR2); - - fw[i] = avRight - avLeft; - } - - // Экстремумы - List extrem = new() { 0 }; - for (int i = 1; i < n - 1; i++) - { - double ldf = fw[i] - fw[i - 1]; - double rdf = fw[i + 1] - fw[i]; - if (ldf * rdf <= 0 && Math.Abs(fw[i]) >= criteria) - extrem.Add(i); - } - extrem.Add(n - 1); - - // Средние по интервалам - int segCount = extrem.Count - 1; - double[] avgWeight = new double[segCount]; - for (int i = 0; i < segCount; i++) - avgWeight[i] = AverageFast(extrem[i], extrem[i + 1]); - - // Классификация - double border = AutoDefineCriteria(avgWeight); - bool[] fStops = avgWeight.Select(w => w < border).ToArray(); - - // Построение интервалов - List<(double, double)> stops = new(); - bool open = false; - for (int i = 0; i < segCount; i++) - { - if (fStops[i] && !open) - { - stops.Add((time[extrem[i]], 0)); - open = true; - } - if (!fStops[i] && open) - { - int idx = stops.Count - 1; - stops[idx] = (stops[idx].Item1, time[extrem[i]]); - open = false; - } - } - if (open) - { - int idx = stops.Count - 1; - stops[idx] = (stops[idx].Item1, time[extrem.Last()]); - } - - return stops.ToArray(); - } - - public static (double start, double end)[] CalculateStopsStatisticVlasovAuto( - double[] time, double[] weight) - { - int n = time.Length; - if (n < 3) return Array.Empty<(double, double)>(); - - // 1. Вычисляем шаг дискретизации dt (медиана разностей) - double[] diffs = new double[n - 1]; - for (int i = 0; i < n - 1; i++) - diffs[i] = time[i + 1] - time[i]; - Array.Sort(diffs); - double dt = diffs[diffs.Length / 2]; - - // 2. Подбираем gate, чтобы окно было ~30 точек - int pointsWindow = 500; - int gate = (int)Math.Round(dt * pointsWindow); - - // 3. Строим префиксную сумму для быстрого среднего - double[] prefix = new double[n + 1]; - for (int i = 0; i < n; i++) - prefix[i + 1] = prefix[i] + weight[i]; - double Avg(int l, int r) => l > r ? 0 : (prefix[r + 1] - prefix[l]) / (r - l + 1); - - // 4. Считаем градиент fw без порога - double[] fw = new double[n]; - for (int i = 0; i < n; i++) - { - GetGateInterval(time, i, -gate, out int l1, out int r1); - GetGateInterval(time, i, gate, out int l2, out int r2); - fw[i] = Avg(l2, r2) - Avg(l1, r1); - } - - // 5. Авто-критерий: 30% квантиль абсолютных fw - var absFw = fw.Select(Math.Abs).OrderBy(v => v).ToArray(); - double criteria = absFw[(int)(absFw.Length * 0.3)]; - - // 6. Далее копируем логику из оригинальной функции: - // поиск экстремумов, средних на сегментах, классификация и построение интервалов. - var extrem = new List { 0 }; - for (int i = 1; i < n - 1; i++) - { - double ldf = fw[i] - fw[i - 1]; - double rdf = fw[i + 1] - fw[i]; - if (ldf * rdf <= 0 && Math.Abs(fw[i]) >= criteria) - extrem.Add(i); - } - extrem.Add(n - 1); - - int segCount = extrem.Count - 1; - bool[] fStops = new bool[segCount]; - for (int i = 0; i < segCount; i++) - { - double avgW = Avg(extrem[i], extrem[i + 1]); - fStops[i] = avgW < criteria; - } - - var stops = new List<(double, double)>(); - bool open = false; - for (int i = 0; i < segCount; i++) - { - if (fStops[i] && !open) - { - stops.Add((time[extrem[i]], 0)); - open = true; - } - if (!fStops[i] && open) - { - int idx = stops.Count - 1; - stops[idx] = (stops[idx].Item1, time[extrem[i]]); - open = false; - } - } - if (open) - { - int idx = stops.Count - 1; - stops[idx] = (stops[idx].Item1, time[extrem.Last()]); - } - - return stops.ToArray(); - } - - - public static (double start, double end)[] FilterMoveNoOperation( - double[] time, double[] pos, - (double start, double end)[] stopIntervals) - { - // Критерий, сколько процентов от макс перемещения во время хода - const double CRITERIA_FOR_NO_MOVE = 0.06; //сейчас 6% - int nstops = stopIntervals.Length; - if (nstops == 0) - return Array.Empty<(double, double)>(); - - // Конвертация временных интервалов в индексы для удобства работы с массивами - var stopIdxIntervals = ConvertTimeValuesToStopsIndexes(time, stopIntervals); - - // Ищем максимальное перемещение между соседними стоянками - double maxMove = 0; - for (int i = 0; i < nstops - 1; i++) - { - int idxEndCurrent = stopIdxIntervals[i].idx_end; - int idxStartNext = stopIdxIntervals[i + 1].idx_start; - - if (idxEndCurrent >= pos.Length || idxStartNext >= pos.Length) - continue; // защитная проверка - - double mv = Math.Abs(pos[idxStartNext] - pos[idxEndCurrent]); - if (mv > maxMove) - maxMove = mv; - } - - double threshold = maxMove * CRITERIA_FOR_NO_MOVE; - - var result = new List<(double, double)>(); - bool open = false; - - for (int i = 0; i < nstops - 1; i++) - { - int idxEndCurrent = stopIdxIntervals[i].idx_end; - int idxStartNext = stopIdxIntervals[i + 1].idx_start; - - if (idxEndCurrent >= pos.Length || idxStartNext >= pos.Length) - continue; - - double mv = Math.Abs(pos[idxStartNext] - pos[idxEndCurrent]); - - if (mv < threshold) - { - // Если движение между соседними стоянками маленькое — объединяем интервалы - if (!open) - { - open = true; - result.Add(stopIntervals[i]); - } - } - else if (open) - { - // Закрываем открытый интервал при существенном движении - open = false; - } - else - { - // Если движение большое и нет открытого интервала — добавляем текущий отдельно - result.Add(stopIntervals[i]); - } - } - - // Обработка последнего интервала - if (open) - { - // Если открыт объединённый интервал — добавляем последний интервал из исходных стоянок - result.Add(stopIntervals.Last()); - } - else if (nstops > 0) - { - // Если интервал не открыт, добавляем последний интервал из исходных стоянок - // (иначе он может потеряться) - result.Add(stopIntervals.Last()); - } - - return result.ToArray(); - } - - - // Коррекция границ остановок по весу - public static (double start, double end)[] CorrectStartAndStopMove( - double[] time, double[] pos, double[] weight, - (double start, double end)[] stopIntervals) - { - int nstops = stopIntervals.Length; - (int idxStart, int idxEnd)[] stopIdxIntervals = new (int idxStart, int idxEnd)[nstops]; - //var starts = stopIntervals.Select(t => t.start).ToArray(); - //var ends = stopIntervals.Select(t => t.end).ToArray(); - //convert_time_values_to_stops_indexes(time.Length, ref nstops, time, starts, ends, - // out int[] idxStart, out int[] idxStop, out _); - stopIdxIntervals = ConvertTimeValuesToStopsIndexes(time, stopIntervals); - - for (int i = 0; i < nstops - 1; i++) - { - int startCurrent = stopIdxIntervals[i].idxStart; - int endCurrent = stopIdxIntervals[i].idxEnd; - int startNext = stopIdxIntervals[i + 1].idxStart; - - int len = startNext - startCurrent + 1; - if (len < 5) continue; - - // Берём сегмент весов между концом текущей стоянки и началом следующей - // Чтобы не выйти за границы, убедимся что Take не выйдет за length массива - int takeLen = Math.Min(len, weight.Length - endCurrent); - if (takeLen <= 0) continue; - - var segment = weight.Skip(endCurrent).Take(takeLen).OrderBy(w => w).ToArray(); - if (segment.Length == 0) continue; - - double border = segment[(int)(0.3 * segment.Length)]; - - // Определяем монотонный интервал на позиции конца текущего стопа - DefineMonotoneInterval(time.Length, pos, endCurrent, out int sIdx, out int eIdx); - - // Корректируем sIdx в границах [sIdx, eIdx] пока вес ниже порога - while (sIdx < eIdx && weight[sIdx] < border * 0.9) - sIdx++; - - // Проверяем условие изменения позиции для решения о коррекции границ - double denom = pos[startNext] - pos[endCurrent]; - if (Math.Abs(denom) < 1e-8) // избегаем деления на 0 - continue; - - double ratio = (pos[eIdx] - pos[sIdx]) / denom; - - if (Math.Abs(ratio - 1) < 0.1) - { - stopIdxIntervals[i].idxEnd = sIdx; - stopIdxIntervals[i + 1].idxStart = eIdx; - } - } - - - //convert_stops_indexes_to_time_values(nstops, idxStart, idxStop, time, - // out double[] newStarts, out double[] newEnds); - (double start, double end)[] newTimes = new (double start, double end)[stopIntervals.Length]; - newTimes = ConvertStopsIndexesToTimeValues(nstops, stopIdxIntervals, time); - return newTimes; //newStarts.Zip(newEnds, (s, e) => (s, e)).ToArray(); - } - - // Вычисление глубины по разрезным данным - public static double[] CalculateDepthOnRazrezData( - double[] time, double[] blockMovement, - (double start, double end)[] stopIntervals) - { - int n = time.Length; - double[] depth = new double[n]; - int curStop = 0; - double depthCum = 0; - - for (int i = 0; i < n - 1; i++) - { - bool inStop = false; - - // Проверка, что curStop в пределах массива - if (curStop < stopIntervals.Length) - { - var stop = stopIntervals[curStop]; - inStop = (time[i] >= stop.start && time[i] <= stop.end); - } - - if (!inStop) - { - depthCum -= blockMovement[i + 1] - blockMovement[i]; - } - - depth[i + 1] = depthCum; - - // Обновляем curStop, если текущий момент времени вышел за текущий интервал - if (curStop < stopIntervals.Length - 1 && time[i] >= stopIntervals[curStop].end) - { - curStop++; - } - } - - return depth; - } - - - // Вспомогательные функции - private static void GetGateInterval(double[] time, int idx, int gate, out int idxStart, out int idxFinish) - { - if (gate == 0) - { - idxStart = idxFinish = idx; - return; - } - - double target = time[idx] + gate; - - if (gate < 0) - { - idxFinish = idx; - idxStart = idx; - for (int i = idx - 1; i >= 0; i--) - { - if (time[i] < target) - { - idxStart = i + 1; - break; - } - } - } - else - { - idxStart = idx; - idxFinish = idx; - for (int i = idx + 1; i < time.Length; i++) - { - if (time[i] > target) - { - idxFinish = i - 1; - break; - } - } - } - - // Защита от выхода за границы - idxStart = Math.Max(0, Math.Min(idxStart, time.Length - 1)); - idxFinish = Math.Max(0, Math.Min(idxFinish, time.Length - 1)); - - if (idxFinish < idxStart) - Console.WriteLine($"[WARN] Empty window at i={idx}, gate={gate}, t={time[idx]}: start={idxStart}, finish={idxFinish}"); - } - - - private static double Average(double[] arr, int start, int end) - { - int len = end - start + 1; - if (len <= 0) - { - Console.WriteLine($"[WARN] Average called with empty range: start={start}, end={end}"); - return 0; - } - double sum = 0; - for (int i = start; i <= end; i++) sum += arr[i]; - return sum / len; - } - - private static double AutoDefineCriteria(double[] values) - { - return values.Average(); - } - - // Реализация DefineMonotoneInterval на C# - private static void DefineMonotoneInterval( - int n, double[] pos, int idx, - //out (int idxStart, int idxFinish)[] idxs_intervals, - out int idxStart, out int idxFinish) - { - const double EPS = 1e-6; - double weightLeft = 0; - int i; - for (i = idx; i >= 0; i--) - { - double curWeight = pos[i] - pos[idx]; - if (Math.Abs(curWeight) - Math.Abs(weightLeft) > -EPS && curWeight * weightLeft > -EPS) - weightLeft = curWeight; - else - break; - } - idxStart = i + 1; - - double weightRight = 0; - for (i = idx; i < n; i++) - { - double curWeight = pos[idx] - pos[i]; - if (Math.Abs(curWeight) - Math.Abs(weightRight) > -EPS && curWeight * weightRight > -EPS) - weightRight = curWeight; - else - break; - } - idxFinish = i - 1; - - if (weightRight * weightLeft <= 0) - { - idxStart = idx; - idxFinish = idx; - } - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace T2dMath { + public partial class LogMath { + public static int SetAbsoluteDepthByPromerVlasov( + int n, + double[] time, + double[] depth, + double[] njoint, + int nstops, + //double[] stopsStart, + //double[] stopsFinish, + (double start, double finish)[] time_intervals, + int npipe, + double[] lengthPipe, + int maxFullPipe, + double lengthDevice, + double epsZaboy, + double[] kgrip, + double[] kmove, + out int numStartPipe, + out int numStopPipe, + double[] deformationPipe) { + // Преобразование временных меток в индексы стоянок + //var idxStart = new int[nstops]; + //var idxFinish = new int[nstops]; + int nstopsIdx = nstops; + (int idxStart, int idxFinish)[] idxs_intervals = ConvertTimeValuesToStopsIndexes(time, time_intervals); + //ConvertTimeValuesToStopsIndexes(n, ref nstopsIdx, time, idxStart, idxFinish, stopsStart, stopsFinish); + + // Вычисляем глубины по промеру труб + var promerStopDepth = new double[npipe + 1]; + CalculateStopDepthPromer(npipe, lengthPipe, lengthDevice, promerStopDepth); + double defaultDepth = promerStopDepth.Last(); + + // Обработка случая без стоянок + if (nstopsIdx == 0) { + for (int i = 0; i < n; i++) depth[i] = defaultDepth; + for (int i = 0; i < n; i++) njoint[i] = -127; + numStartPipe = 0; + numStopPipe = npipe - 1; + for (int i = 0; i < npipe; i++) deformationPipe[i] = -1; + return 0; + } + + // Вычисляем глубины стоянок (среднее) + var oldStopDepth = new double[nstopsIdx]; + CalculateStopDepth(nstopsIdx, depth, idxs_intervals, oldStopDepth); + + // Определяем индексы труб на стоянках + var idxPipeStop = new int[nstopsIdx]; + CalculateIdxPipe(depth, nstopsIdx, idxs_intervals, oldStopDepth, maxFullPipe, epsZaboy, idxPipeStop); + + // Проверяем корректность индексов труб + for (int i = 0; i < nstopsIdx; i++) { + if (idxPipeStop[i] < 0) { + numStartPipe = numStopPipe = 0; + return 57; + } + } + + // Инициализация njoint + for (int i = 0; i < n; i++) njoint[i] = -127; + int curIdx = 0; + for (int i = 0; i < n; i++) { + while (curIdx < nstopsIdx && /*idxFinish[curIdx]*/idxs_intervals[curIdx].idxFinish < i) curIdx++; + if (curIdx >= nstopsIdx) break; + njoint[i] = idxPipeStop[curIdx]; + } + + // Выставляем глубину промера на стоянках + for (int i = 0; i < nstopsIdx; i++) + for (int j = /*idxStart[i]*/idxs_intervals[i].idxStart; j <= idxs_intervals[i].idxFinish; j++) + depth[j] = promerStopDepth[idxPipeStop[i]]; + + // Обработка до первой и после последней стоянки + double firstDepth = promerStopDepth[idxPipeStop[0]]; + for (int i = 0; i < idxs_intervals[0].idxStart; i++) depth[i] = firstDepth; + double lastDepth = promerStopDepth[idxPipeStop[nstopsIdx - 1]]; + for (int i = idxs_intervals[nstopsIdx - 1].idxFinish + 1; i < n; i++) depth[i] = lastDepth; + + // Интерполяция между стоянками + for (int i = 0; i < nstopsIdx - 1; i++) { + if (idxPipeStop[i] != idxPipeStop[i + 1]) { + CalculateAffineCoefficient( + oldStopDepth[i], oldStopDepth[i + 1], + promerStopDepth[idxPipeStop[i]], promerStopDepth[idxPipeStop[i + 1]], + out double A, out double B, + out kgrip[i], out kmove[i]); + //TransformVectorAphine(idxStart[i + 1] - idxFinish[i] - 1, depth, idxFinish[i] + 1, A, B); + TransformVectorAphine(idxs_intervals[i + 1].idxStart - idxs_intervals[i].idxFinish - 1, + depth, + idxs_intervals[i].idxFinish + 1, + A, + B); + } + else { + double move = promerStopDepth[idxPipeStop[i + 1]] - oldStopDepth[i + 1]; + for (int j = idxs_intervals[i].idxFinish + 1; j < idxs_intervals[i + 1].idxStart; j++) depth[j] += move; + kgrip[i] = 1; + kmove[i] = move; + } + } + + // Вычисляем деформации труб + var resDelta = Enumerable.Repeat(-1.0, npipe).ToArray(); + int correctIndex = idxPipeStop[0] > idxPipeStop[nstopsIdx - 1] ? 1 : 0; + for (int i = 1; i < nstopsIdx; i++) { + int numPipe = idxPipeStop[i - correctIndex]; + if (numPipe - 1 >= 0) { + double val = resDelta[numPipe - 1] < 0 + ? kgrip[i - 1] + : MaxDeformation(resDelta[numPipe - 1], kgrip[i]); + resDelta[numPipe - 1] = val; + } + } + + int l = 0; + while (l < npipe && resDelta[l] < 0) l++; + + int r = npipe - 1; + while (r >= 0 && resDelta[r] < 0) r--; + + numStartPipe = l; + numStopPipe = r; + + Array.Copy(resDelta, deformationPipe, npipe); + + // Диапазон используемых труб + //numStartPipe = Array.FindIndex(resDelta, d => d >= 0); + //numStopPipe = Array.FindLastIndex(resDelta, d => d >= 0); + //Array.Copy(resDelta, deformationPipe, npipe); + + return 0; + } + + public static int DefineIdxZaboyDepth(double[] depth, out double avgDepth) { + if (depth == null || depth.Length == 0) { + avgDepth = 0; + return -1; + } + int idx = 0; + double sum = depth[0]; + for (int i = 1; i < depth.Length; i++) { + sum += depth[i]; + if (depth[i] > depth[idx]) + idx = i; + } + avgDepth = sum / depth.Length; + return idx; + } + + /// + /// Корректирует интервалы стоянок, добавляя фиксы по забою. + /// + public static int CorrectStopsFixZaboy( + int n, + double[] time, + double[] depth, + int nstops, + //double[] stopsStart, + //double[] stopsFinish, + (double start, double finish)[] time_intervals, + double epsZaboy, + double length_tool, + double[] length_drill_pipe, + out int nstopsNew, + out (double start, double finish)[] time_intervals_new) { + // 1. Индексы исходных стоянок + //int[] idxStart = new int[nstops]; + //int[] idxFinish = new int[nstops]; + int tmpStops = nstops; + //ConvertTimeValuesToStopsIndexes(n, ref tmpStops, time, idxStart, idxFinish, stopsStart, stopsFinish); + (int idxStart, int idxFinish)[] idxs_intervals = ConvertTimeValuesToStopsIndexes(time, time_intervals); + + // Нормализация глубин на основе реального масштаба инструмента + труб + /*double expectedMax = length_tool + (length_drill_pipe?.Sum() ?? 0); + double maxDepth = depth.Max(); + + if (Math.Abs(maxDepth - expectedMax) > 1.0) + { + double shift = maxDepth - expectedMax; + for (int i = 0; i < depth.Length; i++) + depth[i] -= shift; + }*/ + + // 2. Глубины стоянок + double[] stopDepth = new double[nstops]; + CalculateStopDepth(nstops, depth, idxs_intervals, stopDepth); + + // 3. Индексы труб с maxFullPipe = 0 (забой) + int[] idxPipeStop = new int[nstops]; + CalculateIdxPipe(depth, nstops, idxs_intervals, stopDepth, maxNumPipe: 0, epsZaboy, idxPipeStop); + + // 4. Индексы стоянок на забое (где idxPipeStop == 0) + var zaboyStops = new List(); + for (int i = 0; i < nstops; i++) + if (idxPipeStop[i] == 0) + zaboyStops.Add(i); + int nZaboy = zaboyStops.Count; + + // 5. Подготовка к новым стоянкам + var idxStartRes = new List(); + var idxFinishRes = new List(); + int cursor = 0; + double curDepth = 0; + + // Обход интервалов: начало, промежутки, хвост + for (int k = -1; k < nZaboy + 1; k++) { + int startInt, stopInt; + if (k == -1) { + startInt = 0; + stopInt = idxs_intervals[zaboyStops[0]].idxStart; + curDepth = stopDepth[zaboyStops[0]]; + } + else if (k == nZaboy) { + // Добавляем оставшиеся исходные стоянки + for (int i = cursor; i < nstops; i++) { + idxStartRes.Add(idxs_intervals[i].idxStart); + idxFinishRes.Add(idxs_intervals[i].idxFinish); + } + startInt = idxs_intervals[zaboyStops[nZaboy - 1]].idxFinish + 1; + stopInt = n; + curDepth = stopDepth[zaboyStops[nZaboy - 1]]; + } + else { + int iZ = zaboyStops[k]; + // Копируем стоянки до текущей забойной + for (int i = cursor; i <= iZ; i++) { + idxStartRes.Add(idxs_intervals[i].idxStart); + idxFinishRes.Add(idxs_intervals[i].idxFinish); + } + cursor = iZ + 1; + curDepth = stopDepth[iZ]; + startInt = idxs_intervals[iZ].idxFinish; + stopInt = idxs_intervals[iZ].idxStart; + if (k + 1 < nZaboy && zaboyStops[k] + 1 == zaboyStops[k + 1]) + stopInt = idxs_intervals[zaboyStops[k + 1]].idxStart; + } + + if (startInt < stopInt) { + int len = stopInt - startInt; + var segment = depth.Skip(startInt).Take(len).ToArray(); + int idx = DefineIdxZaboyDepth(segment, out double avgSeg) + startInt; + if (avgSeg > curDepth) { + idxStartRes.Add(idx); + idxFinishRes.Add(idx); + } + } + } + + // 6. Конвертация индексов в времена + nstopsNew = idxStartRes.Count; + //stopsStartNew = new double[nstopsNew]; + //stopsFinishNew = new double[nstopsNew]; + time_intervals_new = new (double start, double finish)[nstopsNew]; + time_intervals_new = ConvertStopsIndexesToTimeValues(nstopsNew, idxs_intervals, time); + //ConvertStopsIndexesToTimeValues(nstopsNew, idxStartRes.ToArray(), idxFinishRes.ToArray(), time, stopsStartNew, stopsFinishNew); + + return 0; + } + + + // Вспомогательные методы: + + /*private static void ConvertStopsIndexesToTimeValues( + int count, + int[] idxStart, + int[] idxFinish, + double[] time, + double[] stopsStart, + double[] stopsFinish) + { + for (int i = 0; i < count; i++) + { + stopsStart[i] = time[idxStart[i]]; + stopsFinish[i] = time[idxFinish[i]]; + } + }*/ + + public static (double Start, double Finish)[] ConvertStopsIndexesToTimeValues( + int count, + (int idxStart, int idxFinish)[] idxsIntervals, + double[] time) { + (double Start, double Finish)[] newTimeVals = new (double Start, double Finish)[count]; + for (int i = 0; i < count; i++) { + newTimeVals[i] = (time[idxsIntervals[i].idxStart], time[idxsIntervals[i].idxFinish]); + } + return newTimeVals; + } + + + + public static (int idx_start, int idx_end)[] ConvertTimeValuesToStopsIndexes( + double[] time, (double time_start, double time_end)[] time_intervals) { + if (time == null || time.Length == 0 || time_intervals == null) + return Array.Empty<(int, int)>(); + + var adjustedIntervals = new List<(double start, double end)>(); + double t0 = time[0]; + double tLast = time[^1]; + + foreach (var interval in time_intervals) { + if (interval.time_end <= t0 || interval.time_start >= tLast) + continue; + + double adjustedStart = Math.Max(interval.time_start, t0); + double adjustedEnd = Math.Min(interval.time_end, tLast); + adjustedIntervals.Add((adjustedStart, adjustedEnd)); + } + + return adjustedIntervals + .Select(interval => ( + Array.FindIndex(time, t => t >= interval.start), + Array.FindIndex(time, t => t >= interval.end) + )) + .ToArray(); + } + + public static void ConvertTimeValuesToStopsIndexes( + int timeCount, ref int nstopsIdx, double[] time, + int[] idxStart, int[] idxFinish, + double[] stopsStart, double[] stopsFinish) { + var starts = new System.Collections.Generic.List(); + var finishes = new System.Collections.Generic.List(); + double t0 = time[0], tLast = time[timeCount - 1]; + for (int i = 0; i < nstopsIdx; i++) { + double s = stopsStart[i], f = stopsFinish[i]; + if (s <= t0 && f > t0) { starts.Add(t0); finishes.Add(f); } + else if (s < tLast && f >= tLast) { starts.Add(s); finishes.Add(tLast); } + else if (s > t0 && f < tLast) { starts.Add(s); finishes.Add(f); } + } + nstopsIdx = starts.Count; + for (int i = 0; i < nstopsIdx; i++) { + idxStart[i] = Array.FindIndex(time, x => x >= starts[i]); + idxFinish[i] = Array.FindIndex(time, x => x >= finishes[i]); + } + } + + private static void CalculateStopDepth( + int nstops, double[] depth, + (int idxStart, int idxFinish)[] idxs_intervals, + double[] stopDepth) { + for (int i = 0; i < nstops; i++) { + int start = idxs_intervals[i].idxStart; + int end = idxs_intervals[i].idxFinish; + int length = end - start + 1; + + stopDepth[i] = depth + .Skip(start) + .Take(length) + .Average(); + } + } + + private static void CalculateStopDepthPromer( + int npipe, double[] lengthPipe, + double lengthDevice, double[] promerStopDepth) { + promerStopDepth[0] = lengthDevice; + for (int i = 0; i < npipe; i++) + promerStopDepth[i + 1] = promerStopDepth[i] + lengthPipe[i]; + } + + private static void CalculateIdxPipe( + double[] depth, + int nstops, + (int idxStart, int idxFinish)[] idxs_intervals, + double[] stopDepth, + int maxNumPipe, + double epsZaboy, + int[] idxPipeStop) { + int n = depth.Length; + + // 1. Находим индекс самой глубокой стоянки + int idxLastFullLengthStop = 0; + for (int i = 1; i < nstops; i++) { + if (stopDepth[i] > stopDepth[idxLastFullLengthStop]) + idxLastFullLengthStop = i; + } + + // 2. Вычисляем тенденции для интервалов + double[] tendency = new double[nstops + 1]; + for (int i = 0; i <= nstops; i++) { + if (i == 0) { + tendency[i] = idxs_intervals[0].idxStart != 0 + ? stopDepth[0] - depth[0] + : 0; + } + else if (i == nstops) { + tendency[i] = idxs_intervals[nstops - 1].idxFinish != n - 1 + ? depth[n - 1] - stopDepth[nstops - 1] + : 0; + } + else { + tendency[i] = stopDepth[i] - stopDepth[i - 1]; + } + } + + // 3. Определяем действия (+1/-1/0) + int[] action = new int[nstops + 1]; + for (int i = 0; i <= nstops; i++) { + if (tendency[i] < -epsZaboy) { + action[i] = -1; + continue; + } + if (tendency[i] > epsZaboy) { + action[i] = +1; + continue; + } + action[i] = 0; + } + + // 4. Заполняем индексы труб + idxPipeStop[idxLastFullLengthStop] = maxNumPipe; + + // Двигаемся влево от самой глубокой стоянки + for (int i = idxLastFullLengthStop - 1; i >= 0; i--) { + idxPipeStop[i] = idxPipeStop[i + 1] - action[i + 1]; + } + + // Двигаемся вправо от самой глубокой стоянки + for (int i = idxLastFullLengthStop + 1; i < nstops; i++) { + idxPipeStop[i] = idxPipeStop[i - 1] + action[i]; + } + } + + public static void CalculateAffineCoefficient( + double x_start_old, double x_finish_old, + double x_start_new, double x_finish_new, + out double A, out double B, + out double kgrip, out double kmove) { + kgrip = (x_finish_new - x_start_new) / (x_finish_old - x_start_old); + A = kgrip; + kmove = x_start_new - x_start_old * A; + B = kmove; + } + + private static void TransformVectorAphine( + int length, double[] depth, + int startIndex, double A, double B) { + for (int i = 0; i < length; i++) + depth[startIndex + i] = A * depth[startIndex + i] + B; + } + + private static double MaxDeformation(double d1, double d2) { + return Math.Abs(d1 - 1) > Math.Abs(d2 - 1) ? d1 : d2; + } + + // Перевод стоянок в кривую + public static double[] StopsToCurve(int mstop, (double tStart, double tStop)[] stops_intervals, double[] time) { + int n = time.Length; + var curve = Enumerable.Repeat(-1.0, n).ToArray(); + int countPipe = 0; + + for (int j = 0; j < mstop; j++) { + for (int i = 0; i < n; i++) { + if (stops_intervals[j].tStart <= time[i] && time[i] <= stops_intervals[j].tStop) + curve[i] = countPipe; + } + countPipe++; + } + + return curve; + } + + // Вычисление стоянок по весу на основе анализа спектра + /// + /// Вычисление стоянок по весу на основе анализа спектра + /// + /// + /// + /// ворота в милисекундах + /// критерий на основе которго вычисляют стоянки + /// + public static (double start, double end)[] CalculateStopsStatisticVlasov( + double[] time, double[] weight, int gate, double criteria) { + int n = time.Length; + if (n < 3) + return Array.Empty<(double, double)>(); + + // Префиксная сумма для быстрого среднего + double[] prefixSum = new double[n + 1]; + for (int i = 0; i < n; i++) + prefixSum[i + 1] = prefixSum[i] + weight[i]; + + double AverageFast(int l, int r) { + if (l > r) return 0; + return (prefixSum[r + 1] - prefixSum[l]) / (r - l + 1); + } + + // Градиентная функция + double[] fw = new double[n]; + for (int i = 0; i < n; i++) { + GetGateInterval(time, i, -gate, out int idxL1, out int idxR1); + double avLeft = AverageFast(idxL1, idxR1); + + GetGateInterval(time, i, gate, out int idxL2, out int idxR2); + double avRight = AverageFast(idxL2, idxR2); + + fw[i] = avRight - avLeft; + } + + // Экстремумы + List extrem = new() { 0 }; + for (int i = 1; i < n - 1; i++) { + double ldf = fw[i] - fw[i - 1]; + double rdf = fw[i + 1] - fw[i]; + if (ldf * rdf <= 0 && Math.Abs(fw[i]) >= criteria) + extrem.Add(i); + } + extrem.Add(n - 1); + + // Средние по интервалам + int segCount = extrem.Count - 1; + double[] avgWeight = new double[segCount]; + for (int i = 0; i < segCount; i++) + avgWeight[i] = AverageFast(extrem[i], extrem[i + 1]); + + // Классификация + double border = AutoDefineCriteria(avgWeight); + bool[] fStops = avgWeight.Select(w => w < border).ToArray(); + + // Построение интервалов + List<(double, double)> stops = new(); + bool open = false; + for (int i = 0; i < segCount; i++) { + if (fStops[i] && !open) { + stops.Add((time[extrem[i]], 0)); + open = true; + } + if (!fStops[i] && open) { + int idx = stops.Count - 1; + stops[idx] = (stops[idx].Item1, time[extrem[i]]); + open = false; + } + } + if (open) { + int idx = stops.Count - 1; + stops[idx] = (stops[idx].Item1, time[extrem.Last()]); + } + + return stops.ToArray(); + } + + public static (double start, double end)[] CalculateStopsStatisticVlasovAuto( + double[] time, double[] weight) { + int n = time.Length; + if (n < 3) return Array.Empty<(double, double)>(); + + // 1. Вычисляем шаг дискретизации dt (медиана разностей) + double[] diffs = new double[n - 1]; + for (int i = 0; i < n - 1; i++) + diffs[i] = time[i + 1] - time[i]; + Array.Sort(diffs); + double dt = diffs[diffs.Length / 2]; + + // 2. Подбираем gate, чтобы окно было ~30 точек + int pointsWindow = 500; + int gate = (int)Math.Round(dt * pointsWindow); + + // 3. Строим префиксную сумму для быстрого среднего + double[] prefix = new double[n + 1]; + for (int i = 0; i < n; i++) + prefix[i + 1] = prefix[i] + weight[i]; + double Avg(int l, int r) => l > r ? 0 : (prefix[r + 1] - prefix[l]) / (r - l + 1); + + // 4. Считаем градиент fw без порога + double[] fw = new double[n]; + for (int i = 0; i < n; i++) { + GetGateInterval(time, i, -gate, out int l1, out int r1); + GetGateInterval(time, i, gate, out int l2, out int r2); + fw[i] = Avg(l2, r2) - Avg(l1, r1); + } + + // 5. Авто-критерий: 30% квантиль абсолютных fw + var absFw = fw.Select(Math.Abs).OrderBy(v => v).ToArray(); + double criteria = absFw[(int)(absFw.Length * 0.3)]; + + // 6. Далее копируем логику из оригинальной функции: + // поиск экстремумов, средних на сегментах, классификация и построение интервалов. + var extrem = new List { 0 }; + for (int i = 1; i < n - 1; i++) { + double ldf = fw[i] - fw[i - 1]; + double rdf = fw[i + 1] - fw[i]; + if (ldf * rdf <= 0 && Math.Abs(fw[i]) >= criteria) + extrem.Add(i); + } + extrem.Add(n - 1); + + int segCount = extrem.Count - 1; + bool[] fStops = new bool[segCount]; + for (int i = 0; i < segCount; i++) { + double avgW = Avg(extrem[i], extrem[i + 1]); + fStops[i] = avgW < criteria; + } + + var stops = new List<(double, double)>(); + bool open = false; + for (int i = 0; i < segCount; i++) { + if (fStops[i] && !open) { + stops.Add((time[extrem[i]], 0)); + open = true; + } + if (!fStops[i] && open) { + int idx = stops.Count - 1; + stops[idx] = (stops[idx].Item1, time[extrem[i]]); + open = false; + } + } + if (open) { + int idx = stops.Count - 1; + stops[idx] = (stops[idx].Item1, time[extrem.Last()]); + } + + return stops.ToArray(); + } + + + public static (double start, double end)[] FilterMoveNoOperation( + double[] time, double[] pos, + (double start, double end)[] stopIntervals) { + // Критерий, сколько процентов от макс перемещения во время хода + const double CRITERIA_FOR_NO_MOVE = 0.06; //сейчас 6% + int nstops = stopIntervals.Length; + if (nstops == 0) + return Array.Empty<(double, double)>(); + + // Конвертация временных интервалов в индексы для удобства работы с массивами + var stopIdxIntervals = ConvertTimeValuesToStopsIndexes(time, stopIntervals); + + // Ищем максимальное перемещение между соседними стоянками + double maxMove = 0; + for (int i = 0; i < nstops - 1; i++) { + int idxEndCurrent = stopIdxIntervals[i].idx_end; + int idxStartNext = stopIdxIntervals[i + 1].idx_start; + + if (idxEndCurrent >= pos.Length || idxStartNext >= pos.Length) + continue; // защитная проверка + + double mv = Math.Abs(pos[idxStartNext] - pos[idxEndCurrent]); + if (mv > maxMove) + maxMove = mv; + } + + double threshold = maxMove * CRITERIA_FOR_NO_MOVE; + + var result = new List<(double, double)>(); + bool open = false; + + for (int i = 0; i < nstops - 1; i++) { + int idxEndCurrent = stopIdxIntervals[i].idx_end; + int idxStartNext = stopIdxIntervals[i + 1].idx_start; + + if (idxEndCurrent >= pos.Length || idxStartNext >= pos.Length) + continue; + + double mv = Math.Abs(pos[idxStartNext] - pos[idxEndCurrent]); + + if (mv < threshold) { + // Если движение между соседними стоянками маленькое — объединяем интервалы + if (!open) { + open = true; + result.Add(stopIntervals[i]); + } + } + else if (open) { + // Закрываем открытый интервал при существенном движении + open = false; + } + else { + // Если движение большое и нет открытого интервала — добавляем текущий отдельно + result.Add(stopIntervals[i]); + } + } + + // Обработка последнего интервала + if (open) { + // Если открыт объединённый интервал — добавляем последний интервал из исходных стоянок + result.Add(stopIntervals.Last()); + } + else if (nstops > 0) { + // Если интервал не открыт, добавляем последний интервал из исходных стоянок + // (иначе он может потеряться) + result.Add(stopIntervals.Last()); + } + + return result.ToArray(); + } + + + // Коррекция границ остановок по весу + public static (double start, double end)[] CorrectStartAndStopMove( + double[] time, double[] pos, double[] weight, + (double start, double end)[] stopIntervals) { + int nstops = stopIntervals.Length; + (int idxStart, int idxEnd)[] stopIdxIntervals = new (int idxStart, int idxEnd)[nstops]; + //var starts = stopIntervals.Select(t => t.start).ToArray(); + //var ends = stopIntervals.Select(t => t.end).ToArray(); + //convert_time_values_to_stops_indexes(time.Length, ref nstops, time, starts, ends, + // out int[] idxStart, out int[] idxStop, out _); + stopIdxIntervals = ConvertTimeValuesToStopsIndexes(time, stopIntervals); + + for (int i = 0; i < nstops - 1; i++) { + int startCurrent = stopIdxIntervals[i].idxStart; + int endCurrent = stopIdxIntervals[i].idxEnd; + int startNext = stopIdxIntervals[i + 1].idxStart; + + int len = startNext - startCurrent + 1; + if (len < 5) continue; + + // Берём сегмент весов между концом текущей стоянки и началом следующей + // Чтобы не выйти за границы, убедимся что Take не выйдет за length массива + int takeLen = Math.Min(len, weight.Length - endCurrent); + if (takeLen <= 0) continue; + + var segment = weight.Skip(endCurrent).Take(takeLen).OrderBy(w => w).ToArray(); + if (segment.Length == 0) continue; + + double border = segment[(int)(0.3 * segment.Length)]; + + // Определяем монотонный интервал на позиции конца текущего стопа + DefineMonotoneInterval(time.Length, pos, endCurrent, out int sIdx, out int eIdx); + + // Корректируем sIdx в границах [sIdx, eIdx] пока вес ниже порога + while (sIdx < eIdx && weight[sIdx] < border * 0.9) + sIdx++; + + // Проверяем условие изменения позиции для решения о коррекции границ + double denom = pos[startNext] - pos[endCurrent]; + if (Math.Abs(denom) < 1e-8) // избегаем деления на 0 + continue; + + double ratio = (pos[eIdx] - pos[sIdx]) / denom; + + if (Math.Abs(ratio - 1) < 0.1) { + stopIdxIntervals[i].idxEnd = sIdx; + stopIdxIntervals[i + 1].idxStart = eIdx; + } + } + + + //convert_stops_indexes_to_time_values(nstops, idxStart, idxStop, time, + // out double[] newStarts, out double[] newEnds); + (double start, double end)[] newTimes = new (double start, double end)[stopIntervals.Length]; + newTimes = ConvertStopsIndexesToTimeValues(nstops, stopIdxIntervals, time); + return newTimes; //newStarts.Zip(newEnds, (s, e) => (s, e)).ToArray(); + } + + // Вычисление глубины по разрезным данным + public static double[] CalculateDepthOnRazrezData( + double[] time, double[] blockMovement, + (double start, double end)[] stopIntervals) { + int n = time.Length; + double[] depth = new double[n]; + int curStop = 0; + double depthCum = 0; + + for (int i = 0; i < n - 1; i++) { + bool inStop = false; + + // Проверка, что curStop в пределах массива + if (curStop < stopIntervals.Length) { + var stop = stopIntervals[curStop]; + inStop = (time[i] >= stop.start && time[i] <= stop.end); + } + + if (!inStop) { + depthCum -= blockMovement[i + 1] - blockMovement[i]; + } + + depth[i + 1] = depthCum; + + // Обновляем curStop, если текущий момент времени вышел за текущий интервал + if (curStop < stopIntervals.Length - 1 && time[i] >= stopIntervals[curStop].end) { + curStop++; + } + } + + return depth; + } + + + // Вспомогательные функции + private static void GetGateInterval(double[] time, int idx, int gate, out int idxStart, out int idxFinish) { + if (gate == 0) { + idxStart = idxFinish = idx; + return; + } + + double target = time[idx] + gate; + + if (gate < 0) { + idxFinish = idx; + idxStart = idx; + for (int i = idx - 1; i >= 0; i--) { + if (time[i] < target) { + idxStart = i + 1; + break; + } + } + } + else { + idxStart = idx; + idxFinish = idx; + for (int i = idx + 1; i < time.Length; i++) { + if (time[i] > target) { + idxFinish = i - 1; + break; + } + } + } + + // Защита от выхода за границы + idxStart = Math.Max(0, Math.Min(idxStart, time.Length - 1)); + idxFinish = Math.Max(0, Math.Min(idxFinish, time.Length - 1)); + + if (idxFinish < idxStart) + Console.WriteLine($"[WARN] Empty window at i={idx}, gate={gate}, t={time[idx]}: start={idxStart}, finish={idxFinish}"); + } + + + private static double Average(double[] arr, int start, int end) { + int len = end - start + 1; + if (len <= 0) { + Console.WriteLine($"[WARN] Average called with empty range: start={start}, end={end}"); + return 0; + } + double sum = 0; + for (int i = start; i <= end; i++) sum += arr[i]; + return sum / len; + } + + private static double AutoDefineCriteria(double[] values) { + return values.Average(); + } + + // Реализация DefineMonotoneInterval на C# + private static void DefineMonotoneInterval( + int n, double[] pos, int idx, + //out (int idxStart, int idxFinish)[] idxs_intervals, + out int idxStart, out int idxFinish) { + const double EPS = 1e-6; + double weightLeft = 0; + int i; + for (i = idx; i >= 0; i--) { + double curWeight = pos[i] - pos[idx]; + if (Math.Abs(curWeight) - Math.Abs(weightLeft) > -EPS && curWeight * weightLeft > -EPS) + weightLeft = curWeight; + else + break; + } + idxStart = i + 1; + + double weightRight = 0; + for (i = idx; i < n; i++) { + double curWeight = pos[idx] - pos[i]; + if (Math.Abs(curWeight) - Math.Abs(weightRight) > -EPS && curWeight * weightRight > -EPS) + weightRight = curWeight; + else + break; + } + idxFinish = i - 1; + + if (weightRight * weightLeft <= 0) { + idxStart = idx; + idxFinish = idx; + } + } + } +} diff --git a/T2dMath/T2dMath.cs b/T2dMath/T2dMath.cs index 2148aad..e984068 100644 --- a/T2dMath/T2dMath.cs +++ b/T2dMath/T2dMath.cs @@ -1,745 +1,667 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace T2dMath -{ - public partial class LogMath - { - //! Вырезание кривой по шаблону - /*! - Вызов происходит после функции анализа спуска-подьема - @param[in] n количество отсчетов в шаблоне - @param[in] time_template шаблон в который должна попасть кривая [n] - @param[in] m количество отсчетов в кривой - @param[in] time_curve временная сетка кривой [m] - @param[out] action результат анализа(действие), 0 - кривая полностью лежит в шаблоне (отрезать не требуется), - 1 - кривую необходимо отрезать (выходит за границы шаблона), - 2 - кривая не пересекается с шаблоном ДОБАВИТЬ ENUM - @param[out] idx_start_cat начало в кривой отрезания, если кривые не пересекаются -1 - @param[out] idx_stop_cat конец в кривой отрезания, если кривые не пересекаются -1 - @return 0 в случае успеха, иначе код ошибки - */ - //extern "C" AMEXPORT int IntervalsIntersection(double time_template_start, double time_template_finish, int m, double* time_curve,int *action,int *idx_start_cat,int *idx_stop_cat); - public enum ActionIntersection - { - NoIntersection, - PartialIntersection, - FullIntersection, - } - - /// - /// Определяет тип пересечения временной кривой с заданным шаблоном. - /// - /// Интервал шаблона времени (start, end). - /// Массив отсчётов кривой времени. - /// Результат пересечения: статус (нет, частичное, полное) и индексы начала и конца пересечения. - /// - public static void IntervalsIntersection( - (double start, double end) time_template_interval, - double[] time_curve, - out (LogMath.ActionIntersection status, int idx_start, int idx_end) intersection - ) - { - intersection = (ActionIntersection.NoIntersection, -1, -1); - - if (time_curve == null || time_curve.Length == 0) - { - throw new ArgumentException("time_curve cannot be null or empty."); - } - - int m = time_curve.Length; - double time_template_start = time_template_interval.start; - double time_template_finish = time_template_interval.end; - - // Кривая полностью находится внутри интервала - if (time_curve[0] >= time_template_start && time_curve[^1] <= time_template_finish) - { - intersection = (ActionIntersection.FullIntersection, 0, m - 1); - return; - } - - // Кривая начинается внутри и заканчивается снаружи - if ( - time_template_start <= time_curve[0] - && time_curve[0] <= time_template_finish - && time_curve[^1] > time_template_finish - ) - { - intersection.idx_start = 0; - } - - // Кривая начинается снаружи и заканчивается внутри - if (time_curve[0] <= time_template_start && time_curve[^1] <= time_template_finish) - { - intersection.idx_end = m - 1; - } - //TODO необходимо вставить логарифмический поиск - // Проверка на наличие пересечений во внутренних точках - for (int i = 1; i < m; i++) - { - if (time_curve[i] >= time_template_start && time_curve[i - 1] < time_template_start) - { - intersection.idx_start = i; - } - if ( - time_curve[i] > time_template_finish - && time_curve[i - 1] <= time_template_finish - ) - { - intersection.idx_end = i - 1; - } - } - - // Обработка граничных случаев для пересечений в одной точке - if (intersection.idx_start == -1 && intersection.idx_end != -1) - { - intersection.idx_start = intersection.idx_end; - } - - if (intersection.idx_end == -1 && intersection.idx_start != -1) - { - intersection.idx_end = intersection.idx_start; - } - - // Определение типа перекрестка - if (intersection.idx_start != -1 && intersection.idx_end != -1) - { - intersection.status = ActionIntersection.PartialIntersection; - } - } - - /// - /// Фильтрация кривой убирает нулевые значения из кривой - /// - /// Массив времени, изменяется через ссылку. - /// Массив значений, изменяется через ссылку. - // - public static void FilterCurveNotNull(ref double[] timeMPP, ref double[] valueMPP) - { - int count = 0; - for (int i = 0; i < timeMPP.Length; i++) - { - if (!double.IsNaN(valueMPP[i])) - { - valueMPP[count] = valueMPP[i]; - timeMPP[count] = timeMPP[i]; - count++; - } - } - // Уменьшаем размер - Array.Resize(ref timeMPP, count); - Array.Resize(ref valueMPP, count); - } - - /* - Сопоставляет времени timeMPP значения глубины (пересчитывая с сетки timeSurface) и выкидывает нулевые значения (видимо глубин) - @param[in] n количество отсчетов - @param[in] m количество отсчетов - @param[in] timeMPP временные отсчеты снятые с МПП [n] - @param[in] timeSurface временные отсчеты снятые наземным оборудованием, измеряющим перемещения талевого блока [m] - @param[in] depthSurface отсчеты глубины, снятые наземным оборудованием, измеряющим перемещения талевого блока [m] - @param[in] nstops количество остановок - @param[in] start индекс начала остановок [nstop] - @param[in] finish индекс окончания остановок [nstop] - @param[in] measurepoint точка записи - @param[out] depthMPP глубина, увязанная со временем - @return 0 в случае успеха, иначе код ошибки - - extern "C" AMEXPORT int MatchDepthToTimeNoStops( - int n, - int m, - AlmazTime *timeMPP, - AlmazTime *timeSurface, - double *depthSurface, - double *depthMPP, - double measurepoint - );*/ - /// - /// Интерполирует значения глубин в соответствии с временными отметками на основе опорной сетки. - /// - /// Массив временных отсчётов для МПП. - /// Временная сетка наземного оборудования. - /// Глубины, соответствующие timeSurface. - /// Результирующий массив глубин по сетке timeMPP. - public static void InterpolateDepths( - double[] timeMPP, - double[] timeSurface, - double[] depthSurface, - double[] depthMPP - ) - { - int idx_mpp = 0; - - for (int i = 0; i < timeSurface.Length - 1; i++) - { - while ( - idx_mpp < timeMPP.Length - && timeMPP[idx_mpp] >= timeSurface[i] - && timeMPP[idx_mpp] <= timeSurface[i + 1] - ) - { - // Линейная интерполяция глубины для точки timeMPP[mppIndex] - // Используем формулу: depth = d1 + (fraction * (d2 - d1)) - double t1 = timeSurface[i]; - double t2 = timeSurface[i + 1]; - double d1 = depthSurface[i]; - double d2 = depthSurface[i + 1]; - double fraction = (timeMPP[idx_mpp] - t1) / (t2 - t1); - - depthMPP[idx_mpp] = d1 + fraction * (d2 - d1); - - idx_mpp++; - } - - if (idx_mpp >= timeMPP.Length) - { - break; - } - } - - // Для точек timeMPP, которые не попали ни в один интервал, задаём значение NaN - for (int i = idx_mpp; i < depthMPP.Length; i++) - { - depthMPP[i] = double.NaN; - } - } - - /// - /// Сопоставляет глубины по времени с учётом поправки на measurepoint, используя интерполяцию. - /// - /// Массив времён МПП. - /// Массив времён наземного оборудования. - /// Глубины по наземной сетке. - /// Поправка на точку измерения. - /// Результирующий массив глубин МПП. - public static void MatchDepthToLine( - double[] timeMPP, - double[] timeSurface, - double[] depthSurface, - double measurepoint, - out double[] depthMPP - ) - { - if ( - timeMPP == null - || timeSurface == null - || depthSurface == null - || depthSurface.Length == 0 - ) - { - throw new ArgumentException("Input arrays cannot be null or empty."); - } - - if (timeSurface.Length != depthSurface.Length) - { - throw new ArgumentException( - "timeSurface and depthSurface arrays must have the same length." - ); - } - - depthMPP = new double[timeMPP.Length]; - - if (timeMPP[0] < timeSurface[0] || timeMPP[^1] > timeSurface[^1]) - { - throw new InvalidOperationException( - "timeMPP is outside the bounds of timeSurface." - ); - } - - double[] shiftedDepthSurface = new double[depthSurface.Length]; - for (int i = 0; i < depthSurface.Length; i++) - { - shiftedDepthSurface[i] = depthSurface[i] - measurepoint; - } - - InterpolateDepths(timeMPP, timeSurface, shiftedDepthSurface, depthMPP); - } - - // Построение монотонной функции по глубине (Умный способ) - /* - @param[in] n количество отсчетовthrow new ArgumentException("Input arrays cannot be null or empty."); - @param[in] depth глубина привязанная ко времени [n] - @param[in] time время записи [n] - @param[in] data данные измерений (кривая) [n] (nullable) - @param[in,out] n_monoton количество точек в выходной монотонной сетке глубин, входное значение должно быть равно n, - выходное значение количество монотонных элементов - @param[out] depth_monoton сетка глубин (неравномерная) [ngrid] - @param[out] value_monoton выходные данные [ngrid] - - extern "C" AMEXPORT int BuildMonotoneLogSmart(int n, double *depth, AlmazTime *time, double *data,int *n_monoton, double *depth_monoton,double *value_monoton); - */ - - /// - /// Построение монотонной функции по глубине - /// - /// Глубина привязанная ко времени. - /// Время записи. - /// Данные измерений (nullable). - /// Выходная сетка глубин (неравномерная). - /// Выходной массив соответствующих значений. - public static void BuildMonotoneLogSmart( - double[] depth, - double[] time, - double[] data, - out double[] depth_monoton, - out double[] value_monoton - ) - { - const double eps = 1e-7; - if (depth == null || data == null || depth.Length != data.Length || depth.Length == 0) - { - throw new ArgumentException( - "Invalid input arrays. Ensure depth and data are non-null and of the same length." - ); - } - - int n = depth.Length; - double sign = Math.Sign(depth[^1] - depth[0]); // +1 - спуск, -1 - подъем - double max = depth[0]; - - var depthMonotonList = new List(depth.Length) { depth[0] }; - var valueMonotonList = new List(data.Length) { data[0] }; - - for (int i = 1; i < n; i++) - { - if ((depth[i] - max) * sign > -eps) - { - max = depth[i]; - depthMonotonList.Add(depth[i]); - valueMonotonList.Add(data[i]); - } - } - - depth_monoton = depthMonotonList.ToArray(); - value_monoton = valueMonotonList.ToArray(); - } - - //! Интерполирование и усреднение в окресности точки - /* - @param[in] n количество точек в монотоной функции - @param[in] depth_monoton сетка глубин (неравномерная) [n] - @param[in] value_monoton выходные данные [n] - @param[in] ngrid количество точек в выходной сетке глубин - @param[in] depthgrid сетка глубин [ngrid] - @param[out] outdata выходные данные [ngrid] - - extern "C" AMEXPORT int LogInterpolateAvg(int n, double *depth_monoton, double *value_monoton,int ngrid,double *depthgrid,double *outdata); - */ - /// - /// Выдает интервал окресности точки grid[idx]=[start,finish) - /// - /// Сетка глубин. - /// Индекс текущей точки. - /// Начало интервала. - /// Конец интервала. - public static void GetInterval(double[] grid, int idx, out double start, out double finish) - { - if (idx < 0 || idx >= grid.Length) - { - start = double.NaN; - finish = double.NaN; - return; - } - if (grid.Length == 0) - { - start = double.NaN; - finish = double.NaN; - return; - } - if (grid.Length == 1) - { - start = grid[0]; - finish = grid[0]; - return; - } - if (idx == 0) - { - double len = (grid[1] - grid[0]) / 2; - start = grid[0] - len; - finish = grid[0] + len; - } - else if (idx == grid.Length - 1) - { - double len = (grid[^1] - grid[^2]) / 2; - start = grid[^1] - len; - finish = grid[^1] + len; - } - else - { - start = grid[idx] - (grid[idx] - grid[idx - 1]) / 2; - finish = grid[idx] + (grid[idx + 1] - grid[idx]) / 2; - } - } - - /// - /// Заменяет одиночные NaN значениями, интерполированными по соседним точкам. - /// - /// Массив значений кривой. - public static void FilterOnePointNullValue(double[] curve) - { - for (int i = 1; i < curve.Length - 1; i++) - { - if ( - double.IsNaN(curve[i]) - && !double.IsNaN(curve[i - 1]) - && !double.IsNaN(curve[i + 1]) - ) - { - curve[i] = (curve[i - 1] + curve[i + 1]) / 2; - } - } - } - - /// - /// Находит индексы начала и конца интервала пересечения в сетке глубин. - /// - /// Начало интервала. - /// Конец интервала. - /// Сетка глубин. - /// Результирующий индекс начала пересечения. - /// Результирующий индекс конца пересечения. - public static void IntervalIntersectionDoublesVlasov( - double interval_start, - double interval_end, - double[] depthgrid, - out int idx_start, - out int idx_end - ) - { - idx_start = -1; - idx_end = -1; - - double borderStart = Math.Min(interval_start, interval_end); - double borderFinish = Math.Max(interval_start, interval_end); - - double intervalMin = Math.Min(depthgrid[0], depthgrid[^1]); - double intervalMax = Math.Max(depthgrid[0], depthgrid[^1]); - - if (borderStart > intervalMax || borderFinish < intervalMin) - { - //TODO add exception - return; - } - - if (borderStart <= intervalMin) - { - idx_start = depthgrid[0] <= depthgrid[^1] ? 0 : depthgrid.Length - 1; - } - - if (intervalMax <= borderFinish) - { - idx_end = depthgrid[0] <= depthgrid[^1] ? depthgrid.Length - 1 : 0; - } - - if (idx_start == -1 || idx_end == -1) - { - for (int i = 0; i < depthgrid.Length - 1; i++) - { - double curMin = Math.Min(depthgrid[i], depthgrid[i + 1]); - double curMax = Math.Max(depthgrid[i], depthgrid[i + 1]); - - if (idx_start == -1 && curMin <= borderStart && borderStart <= curMax) - { - idx_start = depthgrid[i] > borderStart ? i : i + 1; - } - - if (idx_end == -1 && curMin <= borderFinish && borderFinish <= curMax) - { - // Неправильное присваивание, заменено на - // idx_end = depthgrid[i] < borderFinish ? i : i + 1; - idx_end = depthgrid[i + 1] <= borderFinish ? i + 1 : i; - } - } - } - - if (idx_start > idx_end) - { - (idx_start, idx_end) = (idx_end, idx_start); - } - } - - /// - /// Осреднение значений по заданной сетке глубин, используя монотонную кривую. - /// - /// Сетка глубин для усреднения. - /// Монотонная глубинная сетка. - /// Значения, соответствующие depth_monoton. - /// Результат осреднения для каждой точки depthgrid. - public static void GridDepthTransformateAvgFast( - double[] depthgrid, - double[] depth_monoton, - double[] value_monoton, - double[] outdata - ) - { - int cur_pos = 0; - bool isIncreasingDepth = depth_monoton[0] < depth_monoton[^1]; - - for (int i = 0; i < depthgrid.Length; i++) - { - GetInterval(depthgrid, i, out double start, out double finish); - int count = 0; - double sum = 0; - var start_point = Math.Min(start, finish); - var finish_point = Math.Max(start, finish); - // Пропускаем точки вне диапазона - while ( - cur_pos < depth_monoton.Length - && ( - (finish_point < depth_monoton[cur_pos] && !isIncreasingDepth) - || (start_point > depth_monoton[cur_pos] && isIncreasingDepth) - ) - ) - { - cur_pos++; - } - - // Подсчитываем среднее значение в пределах интервала - int start_pos = cur_pos; - - while ( - cur_pos < depth_monoton.Length - && ( - Math.Min(start, finish) <= depth_monoton[cur_pos] - && depth_monoton[cur_pos] < Math.Max(start, finish) - ) - ) - { - count++; - sum += value_monoton[cur_pos]; - cur_pos++; - } - - outdata[i] = count == 0 ? double.NaN : sum / count; - - if (count > 0) - { - cur_pos = start_pos; - } - } - } - - /// - /// Данные только спускаются вниз, в случае если - /// - /// - /// - /// - /// - public static void GridDepthTransformateAvgFastOnlyDown( - double[] depthgrid, - double[] depth_monoton, - double[] value_monoton, - out double[] outdata - ) - { - outdata = new double[depthgrid.Length]; - Array.Fill(outdata, double.NaN); - int cur_pos = 0; - - for (int i = 0; i < depthgrid.Length; i++) - { - GetInterval(depthgrid, i, out double start_depth, out double end_depth); - int count = 0; - double sum = 0; - // Пропускаем точки вне диапазона - while (cur_pos < depth_monoton.Length && start_depth > depth_monoton[cur_pos]) - { - cur_pos++; - } - - // Подсчитываем среднее значение в пределах интервала - - while (cur_pos < depth_monoton.Length && depth_monoton[cur_pos] < end_depth) - { - count++; - sum += value_monoton[cur_pos]; - cur_pos++; - } - if (count > 0) - outdata[i] = sum / count; - } - } - - /// - /// Интерполирование и усреднение в окресности точки. - /// - /// Сетка глубин (неравномерная). - /// Выходные данные. - /// Сетка глубин. - /// Выходные данные. - public static void LogInterpolateAvg( - double[] depth_monoton, - double[] value_monoton, - double[] depthgrid, - out double[] outdata - ) - { - outdata = new double[depthgrid.Length]; - - if ( - depth_monoton == null - || value_monoton == null - || depthgrid == null - || depth_monoton.Length == 0 - || value_monoton.Length == 0 - || depthgrid.Length == 0 - || depth_monoton.Length != value_monoton.Length - ) - { - Array.Fill(outdata, double.NaN); - return; - } - - try - { - IntervalIntersectionDoublesVlasov( - depth_monoton[0], - depth_monoton[^1], - depthgrid, - out int idx_start, - out int idx_end - ); - - if (idx_start > idx_end || idx_start < 0 || idx_end < 0) - { - Array.Fill(outdata, double.NaN); - throw new InvalidOperationException("Invalid interval intersection."); - } - GridDepthTransformateAvgFast(depthgrid, depth_monoton, value_monoton, outdata); - FilterOnePointNullValue(outdata); - } - catch (Exception ex) - { - Array.Fill(outdata, double.NaN); - Console.WriteLine($"Error during interpolation: {ex.Message}"); - } - } - - //! Анализ записанного лога определение простой алгоритм определения,спуск или подьем - /*! - // стоянки пополам - @param[in] n количесво временных отсчетов - @param[in] time временные отсчеты [n] - @param[in] depth глубина по скважине [n] - @param[out] action 1 - спуск инструмента, 2 - подьем инструмента[m] - @return 0 в случае успеха, иначе код ошибки - 1 определить действие невозможно - - extern "C" AMEXPORT int AnalysisActionSimpleVlasov(int n, AlmazTime *time, double *depth, int *action); - */ - public enum ToolDirectionMoving - { - Up, // запись сигналов на подъеме - Down, // запись сигналов на спуске - } - - /// - /// Анализ записанного лога определение простой алгоритм определения,спуск или подьем. - /// - /// Временные отсчеты. - /// Глубина по скважине. - /// - public static ToolDirectionMoving AnalysisActionSimpleVlasov(double[] time, double[] depth) - { - if (depth[0] < depth[^1]) - return ToolDirectionMoving.Down; - return ToolDirectionMoving.Up; - } - - /// - /// Преобразует массив временных меток в интервалы остановок. - /// - /// Тип временной метки. - /// Массив временных точек. - /// Массив значений. - /// Массив кортежей с началом и концом каждой остановки. - public static (T start, T finish)[] CurveToStops(T[] time, double[] stps) - where T : IComparable - { - var stops = new List<(T start, T finish)>(); - if (time.Length != 0) - { - bool isStop = false; - double eps = 0.0000001; - T start = time[0]; - T stop = time[0]; - int cnt = 0; - for (int i = 0; i < stps.Length; i++) - { - if (stps[i] > eps) - { - if (!isStop) - { - start = time[i]; - cnt++; - isStop = true; - } - if (i == (stps.Length - 1)) - { - stop = time[i]; - cnt++; - isStop = false; - } - } - else - { - if (isStop) - { - stop = time[i]; - cnt++; - isStop = false; - } - } - if (cnt == 2) - { - stops.Add((start, stop)); - cnt = 0; - } - } - } - return stops.ToArray(); - } - - /// - /// Построение равномерной сетки с заданным шагом. С учётом направления изменения глубины. - /// - /// Изменение глубины нижней точки КНБК. - /// Шаг равномерной генерируемой сетки. - /// Длина компоновки. - /// Равномерная сетка с учётом движения. - public static double[] BuildGrid(double[] depth, double step, double toolLength) - { - if (depth.Length == 0) - { - return Array.Empty(); - } - - int sign = Math.Sign(depth[^1] - depth[0]); - if (sign == 0) - { - double res = Math.Floor((depth[0] + step / 2.0) / step) * step; - return new double[] { res }; - } - - double min = depth.AsParallel().Where(d => !double.IsNaN(d)).Min(); - double max = depth.AsParallel().Where(d => !double.IsNaN(d)).Max(); - - double adjustedMin = Math.Ceiling((min - toolLength - step / 2.0) / step) * step; - double adjustedMax = Math.Floor((max + step / 2.0) / step) * step; - - int n = (int)Math.Ceiling((adjustedMax - adjustedMin) / step) + 1; - - step = sign * step; - var start = sign > 0 ? adjustedMin : adjustedMax; - - double[] grid = Enumerable - .Range(0, n) - .AsParallel() - .Select(i => start + i * step) - .ToArray(); - return grid; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace T2dMath { + public partial class LogMath { + //! Вырезание кривой по шаблону + /*! + Вызов происходит после функции анализа спуска-подьема + @param[in] n количество отсчетов в шаблоне + @param[in] time_template шаблон в который должна попасть кривая [n] + @param[in] m количество отсчетов в кривой + @param[in] time_curve временная сетка кривой [m] + @param[out] action результат анализа(действие), 0 - кривая полностью лежит в шаблоне (отрезать не требуется), + 1 - кривую необходимо отрезать (выходит за границы шаблона), + 2 - кривая не пересекается с шаблоном ДОБАВИТЬ ENUM + @param[out] idx_start_cat начало в кривой отрезания, если кривые не пересекаются -1 + @param[out] idx_stop_cat конец в кривой отрезания, если кривые не пересекаются -1 + @return 0 в случае успеха, иначе код ошибки + */ + //extern "C" AMEXPORT int IntervalsIntersection(double time_template_start, double time_template_finish, int m, double* time_curve,int *action,int *idx_start_cat,int *idx_stop_cat); + public enum ActionIntersection { + NoIntersection, + PartialIntersection, + FullIntersection, + } + + /// + /// Определяет тип пересечения временной кривой с заданным шаблоном. + /// + /// Интервал шаблона времени (start, end). + /// Массив отсчётов кривой времени. + /// Результат пересечения: статус (нет, частичное, полное) и индексы начала и конца пересечения. + /// + public static void IntervalsIntersection( + (double start, double end) time_template_interval, + double[] time_curve, + out (LogMath.ActionIntersection status, int idx_start, int idx_end) intersection + ) { + intersection = (ActionIntersection.NoIntersection, -1, -1); + + if (time_curve == null || time_curve.Length == 0) { + throw new ArgumentException("time_curve cannot be null or empty."); + } + + int m = time_curve.Length; + double time_template_start = time_template_interval.start; + double time_template_finish = time_template_interval.end; + + // Кривая полностью находится внутри интервала + if (time_curve[0] >= time_template_start && time_curve[^1] <= time_template_finish) { + intersection = (ActionIntersection.FullIntersection, 0, m - 1); + return; + } + + // Кривая начинается внутри и заканчивается снаружи + if ( + time_template_start <= time_curve[0] + && time_curve[0] <= time_template_finish + && time_curve[^1] > time_template_finish + ) { + intersection.idx_start = 0; + } + + // Кривая начинается снаружи и заканчивается внутри + if (time_curve[0] <= time_template_start && time_curve[^1] <= time_template_finish) { + intersection.idx_end = m - 1; + } + //TODO необходимо вставить логарифмический поиск + // Проверка на наличие пересечений во внутренних точках + for (int i = 1; i < m; i++) { + if (time_curve[i] >= time_template_start && time_curve[i - 1] < time_template_start) { + intersection.idx_start = i; + } + if ( + time_curve[i] > time_template_finish + && time_curve[i - 1] <= time_template_finish + ) { + intersection.idx_end = i - 1; + } + } + + // Обработка граничных случаев для пересечений в одной точке + if (intersection.idx_start == -1 && intersection.idx_end != -1) { + intersection.idx_start = intersection.idx_end; + } + + if (intersection.idx_end == -1 && intersection.idx_start != -1) { + intersection.idx_end = intersection.idx_start; + } + + // Определение типа перекрестка + if (intersection.idx_start != -1 && intersection.idx_end != -1) { + intersection.status = ActionIntersection.PartialIntersection; + } + } + + /// + /// Фильтрация кривой убирает нулевые значения из кривой + /// + /// Массив времени, изменяется через ссылку. + /// Массив значений, изменяется через ссылку. + // + public static void FilterCurveNotNull(ref double[] timeMPP, ref double[] valueMPP) { + int count = 0; + for (int i = 0; i < timeMPP.Length; i++) { + if (!double.IsNaN(valueMPP[i])) { + valueMPP[count] = valueMPP[i]; + timeMPP[count] = timeMPP[i]; + count++; + } + } + // Уменьшаем размер + Array.Resize(ref timeMPP, count); + Array.Resize(ref valueMPP, count); + } + + /* + Сопоставляет времени timeMPP значения глубины (пересчитывая с сетки timeSurface) и выкидывает нулевые значения (видимо глубин) + @param[in] n количество отсчетов + @param[in] m количество отсчетов + @param[in] timeMPP временные отсчеты снятые с МПП [n] + @param[in] timeSurface временные отсчеты снятые наземным оборудованием, измеряющим перемещения талевого блока [m] + @param[in] depthSurface отсчеты глубины, снятые наземным оборудованием, измеряющим перемещения талевого блока [m] + @param[in] nstops количество остановок + @param[in] start индекс начала остановок [nstop] + @param[in] finish индекс окончания остановок [nstop] + @param[in] measurepoint точка записи + @param[out] depthMPP глубина, увязанная со временем + @return 0 в случае успеха, иначе код ошибки + + extern "C" AMEXPORT int MatchDepthToTimeNoStops( + int n, + int m, + AlmazTime *timeMPP, + AlmazTime *timeSurface, + double *depthSurface, + double *depthMPP, + double measurepoint + );*/ + /// + /// Интерполирует значения глубин в соответствии с временными отметками на основе опорной сетки. + /// + /// Массив временных отсчётов для МПП. + /// Временная сетка наземного оборудования. + /// Глубины, соответствующие timeSurface. + /// Результирующий массив глубин по сетке timeMPP. + public static void InterpolateDepths( + double[] timeMPP, + double[] timeSurface, + double[] depthSurface, + double[] depthMPP + ) { + int idx_mpp = 0; + + for (int i = 0; i < timeSurface.Length - 1; i++) { + while ( + idx_mpp < timeMPP.Length + && timeMPP[idx_mpp] >= timeSurface[i] + && timeMPP[idx_mpp] <= timeSurface[i + 1] + ) { + // Линейная интерполяция глубины для точки timeMPP[mppIndex] + // Используем формулу: depth = d1 + (fraction * (d2 - d1)) + double t1 = timeSurface[i]; + double t2 = timeSurface[i + 1]; + double d1 = depthSurface[i]; + double d2 = depthSurface[i + 1]; + double fraction = (timeMPP[idx_mpp] - t1) / (t2 - t1); + + depthMPP[idx_mpp] = d1 + fraction * (d2 - d1); + + idx_mpp++; + } + + if (idx_mpp >= timeMPP.Length) { + break; + } + } + + // Для точек timeMPP, которые не попали ни в один интервал, задаём значение NaN + for (int i = idx_mpp; i < depthMPP.Length; i++) { + depthMPP[i] = double.NaN; + } + } + + /// + /// Сопоставляет глубины по времени с учётом поправки на measurepoint, используя интерполяцию. + /// + /// Массив времён МПП. + /// Массив времён наземного оборудования. + /// Глубины по наземной сетке. + /// Поправка на точку измерения. + /// Результирующий массив глубин МПП. + public static void MatchDepthToLine( + double[] timeMPP, + double[] timeSurface, + double[] depthSurface, + double measurepoint, + out double[] depthMPP + ) { + if ( + timeMPP == null + || timeSurface == null + || depthSurface == null + || depthSurface.Length == 0 + ) { + throw new ArgumentException("Input arrays cannot be null or empty."); + } + + if (timeSurface.Length != depthSurface.Length) { + throw new ArgumentException( + "timeSurface and depthSurface arrays must have the same length." + ); + } + + depthMPP = new double[timeMPP.Length]; + + if (timeMPP[0] < timeSurface[0] || timeMPP[^1] > timeSurface[^1]) { + throw new InvalidOperationException( + "timeMPP is outside the bounds of timeSurface." + ); + } + + double[] shiftedDepthSurface = new double[depthSurface.Length]; + for (int i = 0; i < depthSurface.Length; i++) { + shiftedDepthSurface[i] = depthSurface[i] - measurepoint; + } + + InterpolateDepths(timeMPP, timeSurface, shiftedDepthSurface, depthMPP); + } + + // Построение монотонной функции по глубине (Умный способ) + /* + @param[in] n количество отсчетовthrow new ArgumentException("Input arrays cannot be null or empty."); + @param[in] depth глубина привязанная ко времени [n] + @param[in] time время записи [n] + @param[in] data данные измерений (кривая) [n] (nullable) + @param[in,out] n_monoton количество точек в выходной монотонной сетке глубин, входное значение должно быть равно n, + выходное значение количество монотонных элементов + @param[out] depth_monoton сетка глубин (неравномерная) [ngrid] + @param[out] value_monoton выходные данные [ngrid] + + extern "C" AMEXPORT int BuildMonotoneLogSmart(int n, double *depth, AlmazTime *time, double *data,int *n_monoton, double *depth_monoton,double *value_monoton); + */ + + /// + /// Построение монотонной функции по глубине + /// + /// Глубина привязанная ко времени. + /// Время записи. + /// Данные измерений (nullable). + /// Выходная сетка глубин (неравномерная). + /// Выходной массив соответствующих значений. + public static void BuildMonotoneLogSmart( + double[] depth, + double[] time, + double[] data, + out double[] depth_monoton, + out double[] value_monoton + ) { + const double eps = 1e-7; + if (depth == null || data == null || depth.Length != data.Length || depth.Length == 0) { + throw new ArgumentException( + "Invalid input arrays. Ensure depth and data are non-null and of the same length." + ); + } + + int n = depth.Length; + double sign = Math.Sign(depth[^1] - depth[0]); // +1 - спуск, -1 - подъем + double max = depth[0]; + + var depthMonotonList = new List(depth.Length) { depth[0] }; + var valueMonotonList = new List(data.Length) { data[0] }; + + for (int i = 1; i < n; i++) { + if ((depth[i] - max) * sign > -eps) { + max = depth[i]; + depthMonotonList.Add(depth[i]); + valueMonotonList.Add(data[i]); + } + } + + depth_monoton = depthMonotonList.ToArray(); + value_monoton = valueMonotonList.ToArray(); + } + + //! Интерполирование и усреднение в окресности точки + /* + @param[in] n количество точек в монотоной функции + @param[in] depth_monoton сетка глубин (неравномерная) [n] + @param[in] value_monoton выходные данные [n] + @param[in] ngrid количество точек в выходной сетке глубин + @param[in] depthgrid сетка глубин [ngrid] + @param[out] outdata выходные данные [ngrid] + + extern "C" AMEXPORT int LogInterpolateAvg(int n, double *depth_monoton, double *value_monoton,int ngrid,double *depthgrid,double *outdata); + */ + /// + /// Выдает интервал окресности точки grid[idx]=[start,finish) + /// + /// Сетка глубин. + /// Индекс текущей точки. + /// Начало интервала. + /// Конец интервала. + public static void GetInterval(double[] grid, int idx, out double start, out double finish) { + if (idx < 0 || idx >= grid.Length) { + start = double.NaN; + finish = double.NaN; + return; + } + if (grid.Length == 0) { + start = double.NaN; + finish = double.NaN; + return; + } + if (grid.Length == 1) { + start = grid[0]; + finish = grid[0]; + return; + } + if (idx == 0) { + double len = (grid[1] - grid[0]) / 2; + start = grid[0] - len; + finish = grid[0] + len; + } + else if (idx == grid.Length - 1) { + double len = (grid[^1] - grid[^2]) / 2; + start = grid[^1] - len; + finish = grid[^1] + len; + } + else { + start = grid[idx] - (grid[idx] - grid[idx - 1]) / 2; + finish = grid[idx] + (grid[idx + 1] - grid[idx]) / 2; + } + } + + /// + /// Заменяет одиночные NaN значениями, интерполированными по соседним точкам. + /// + /// Массив значений кривой. + public static void FilterOnePointNullValue(double[] curve) { + for (int i = 1; i < curve.Length - 1; i++) { + if ( + double.IsNaN(curve[i]) + && !double.IsNaN(curve[i - 1]) + && !double.IsNaN(curve[i + 1]) + ) { + curve[i] = (curve[i - 1] + curve[i + 1]) / 2; + } + } + } + + /// + /// Находит индексы начала и конца интервала пересечения в сетке глубин. + /// + /// Начало интервала. + /// Конец интервала. + /// Сетка глубин. + /// Результирующий индекс начала пересечения. + /// Результирующий индекс конца пересечения. + public static void IntervalIntersectionDoublesVlasov( + double interval_start, + double interval_end, + double[] depthgrid, + out int idx_start, + out int idx_end + ) { + idx_start = -1; + idx_end = -1; + + double borderStart = Math.Min(interval_start, interval_end); + double borderFinish = Math.Max(interval_start, interval_end); + + double intervalMin = Math.Min(depthgrid[0], depthgrid[^1]); + double intervalMax = Math.Max(depthgrid[0], depthgrid[^1]); + + if (borderStart > intervalMax || borderFinish < intervalMin) { + //TODO add exception + return; + } + + if (borderStart <= intervalMin) { + idx_start = depthgrid[0] <= depthgrid[^1] ? 0 : depthgrid.Length - 1; + } + + if (intervalMax <= borderFinish) { + idx_end = depthgrid[0] <= depthgrid[^1] ? depthgrid.Length - 1 : 0; + } + + if (idx_start == -1 || idx_end == -1) { + for (int i = 0; i < depthgrid.Length - 1; i++) { + double curMin = Math.Min(depthgrid[i], depthgrid[i + 1]); + double curMax = Math.Max(depthgrid[i], depthgrid[i + 1]); + + if (idx_start == -1 && curMin <= borderStart && borderStart <= curMax) { + idx_start = depthgrid[i] > borderStart ? i : i + 1; + } + + if (idx_end == -1 && curMin <= borderFinish && borderFinish <= curMax) { + // Неправильное присваивание, заменено на + // idx_end = depthgrid[i] < borderFinish ? i : i + 1; + idx_end = depthgrid[i + 1] <= borderFinish ? i + 1 : i; + } + } + } + + if (idx_start > idx_end) { + (idx_start, idx_end) = (idx_end, idx_start); + } + } + + /// + /// Осреднение значений по заданной сетке глубин, используя монотонную кривую. + /// + /// Сетка глубин для усреднения. + /// Монотонная глубинная сетка. + /// Значения, соответствующие depth_monoton. + /// Результат осреднения для каждой точки depthgrid. + public static void GridDepthTransformateAvgFast( + double[] depthgrid, + double[] depth_monoton, + double[] value_monoton, + double[] outdata + ) { + int cur_pos = 0; + bool isIncreasingDepth = depth_monoton[0] < depth_monoton[^1]; + + for (int i = 0; i < depthgrid.Length; i++) { + GetInterval(depthgrid, i, out double start, out double finish); + int count = 0; + double sum = 0; + var start_point = Math.Min(start, finish); + var finish_point = Math.Max(start, finish); + // Пропускаем точки вне диапазона + while ( + cur_pos < depth_monoton.Length + && ( + (finish_point < depth_monoton[cur_pos] && !isIncreasingDepth) + || (start_point > depth_monoton[cur_pos] && isIncreasingDepth) + ) + ) { + cur_pos++; + } + + // Подсчитываем среднее значение в пределах интервала + int start_pos = cur_pos; + + while ( + cur_pos < depth_monoton.Length + && ( + Math.Min(start, finish) <= depth_monoton[cur_pos] + && depth_monoton[cur_pos] < Math.Max(start, finish) + ) + ) { + count++; + sum += value_monoton[cur_pos]; + cur_pos++; + } + + outdata[i] = count == 0 ? double.NaN : sum / count; + + if (count > 0) { + cur_pos = start_pos; + } + } + } + + /// + /// Данные только спускаются вниз, в случае если + /// + /// + /// + /// + /// + public static void GridDepthTransformateAvgFastOnlyDown( + double[] depthgrid, + double[] depth_monoton, + double[] value_monoton, + out double[] outdata + ) { + outdata = new double[depthgrid.Length]; + Array.Fill(outdata, double.NaN); + int cur_pos = 0; + + for (int i = 0; i < depthgrid.Length; i++) { + GetInterval(depthgrid, i, out double start_depth, out double end_depth); + int count = 0; + double sum = 0; + // Пропускаем точки вне диапазона + while (cur_pos < depth_monoton.Length && start_depth > depth_monoton[cur_pos]) { + cur_pos++; + } + + // Подсчитываем среднее значение в пределах интервала + + while (cur_pos < depth_monoton.Length && depth_monoton[cur_pos] < end_depth) { + count++; + sum += value_monoton[cur_pos]; + cur_pos++; + } + if (count > 0) + outdata[i] = sum / count; + } + } + + /// + /// Интерполирование и усреднение в окресности точки. + /// + /// Сетка глубин (неравномерная). + /// Выходные данные. + /// Сетка глубин. + /// Выходные данные. + public static void LogInterpolateAvg( + double[] depth_monoton, + double[] value_monoton, + double[] depthgrid, + out double[] outdata + ) { + outdata = new double[depthgrid.Length]; + + if ( + depth_monoton == null + || value_monoton == null + || depthgrid == null + || depth_monoton.Length == 0 + || value_monoton.Length == 0 + || depthgrid.Length == 0 + || depth_monoton.Length != value_monoton.Length + ) { + Array.Fill(outdata, double.NaN); + return; + } + + try { + IntervalIntersectionDoublesVlasov( + depth_monoton[0], + depth_monoton[^1], + depthgrid, + out int idx_start, + out int idx_end + ); + + if (idx_start > idx_end || idx_start < 0 || idx_end < 0) { + Array.Fill(outdata, double.NaN); + throw new InvalidOperationException("Invalid interval intersection."); + } + GridDepthTransformateAvgFast(depthgrid, depth_monoton, value_monoton, outdata); + FilterOnePointNullValue(outdata); + } + catch (Exception ex) { + Array.Fill(outdata, double.NaN); + Console.WriteLine($"Error during interpolation: {ex.Message}"); + } + } + + //! Анализ записанного лога определение простой алгоритм определения,спуск или подьем + /*! + // стоянки пополам + @param[in] n количесво временных отсчетов + @param[in] time временные отсчеты [n] + @param[in] depth глубина по скважине [n] + @param[out] action 1 - спуск инструмента, 2 - подьем инструмента[m] + @return 0 в случае успеха, иначе код ошибки + 1 определить действие невозможно + + extern "C" AMEXPORT int AnalysisActionSimpleVlasov(int n, AlmazTime *time, double *depth, int *action); + */ + public enum ToolDirectionMoving { + Up, // запись сигналов на подъеме + Down, // запись сигналов на спуске + } + + /// + /// Анализ записанного лога определение простой алгоритм определения,спуск или подьем. + /// + /// Временные отсчеты. + /// Глубина по скважине. + /// + public static ToolDirectionMoving AnalysisActionSimpleVlasov(double[] time, double[] depth) { + if (depth[0] < depth[^1]) + return ToolDirectionMoving.Down; + return ToolDirectionMoving.Up; + } + + /// + /// Преобразует массив временных меток в интервалы остановок. + /// + /// Тип временной метки. + /// Массив временных точек. + /// Массив значений. + /// Массив кортежей с началом и концом каждой остановки. + public static (T start, T finish)[] CurveToStops(T[] time, double[] stps) + where T : IComparable { + var stops = new List<(T start, T finish)>(); + if (time.Length != 0) { + bool isStop = false; + double eps = 0.0000001; + T start = time[0]; + T stop = time[0]; + int cnt = 0; + for (int i = 0; i < stps.Length; i++) { + if (stps[i] > eps) { + if (!isStop) { + start = time[i]; + cnt++; + isStop = true; + } + if (i == (stps.Length - 1)) { + stop = time[i]; + cnt++; + isStop = false; + } + } + else { + if (isStop) { + stop = time[i]; + cnt++; + isStop = false; + } + } + if (cnt == 2) { + stops.Add((start, stop)); + cnt = 0; + } + } + } + return stops.ToArray(); + } + + /// + /// Построение равномерной сетки с заданным шагом. С учётом направления изменения глубины. + /// + /// Изменение глубины нижней точки КНБК. + /// Шаг равномерной генерируемой сетки. + /// Длина компоновки. + /// Равномерная сетка с учётом движения. + public static double[] BuildGrid(double[] depth, double step, double toolLength) { + if (depth.Length == 0) { + return Array.Empty(); + } + + int sign = Math.Sign(depth[^1] - depth[0]); + if (sign == 0) { + double res = Math.Floor((depth[0] + step / 2.0) / step) * step; + return new double[] { res }; + } + + double min = depth.AsParallel().Where(d => !double.IsNaN(d)).Min(); + double max = depth.AsParallel().Where(d => !double.IsNaN(d)).Max(); + + double adjustedMin = Math.Ceiling((min - toolLength - step / 2.0) / step) * step; + double adjustedMax = Math.Floor((max + step / 2.0) / step) * step; + + int n = (int)Math.Ceiling((adjustedMax - adjustedMin) / step) + 1; + + step = sign * step; + var start = sign > 0 ? adjustedMin : adjustedMax; + + double[] grid = Enumerable + .Range(0, n) + .AsParallel() + .Select(i => start + i * step) + .ToArray(); + return grid; + } + } +} From 9ef9cbfa3da1fc9bc389110d1eaeb660e1510676 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 06:18:18 +0000 Subject: [PATCH 5/7] Auto-format code --- .../CalculateDepthTestNew.cs | 13 +- T2dMath.Pipeline/Time2DepthLogging.cs | 271 +++++++++--------- 2 files changed, 139 insertions(+), 145 deletions(-) diff --git a/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs b/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs index cce28dc..75cc150 100644 --- a/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs +++ b/T2dMath.Pipeline.Test/CalculateDepthTestNew.cs @@ -1,14 +1,11 @@ -using System; -using Xunit; +using System; using T2dMath.Pipeline; +using Xunit; -namespace T2dMath.Tests -{ - public class PipelinesTests - { +namespace T2dMath.Tests { + public class PipelinesTests { [Fact] - public void CalculateDepth_CalculatesDepthWithFixZaboy() - { + public void CalculateDepth_CalculatesDepthWithFixZaboy() { // arrange double[] time_surface = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; double[] block_height = new double[] { 0, 0.1, 0.2, 0.21, 0.2, 0.19, 0.2, 0.21, 0.2, 0.19 }; diff --git a/T2dMath.Pipeline/Time2DepthLogging.cs b/T2dMath.Pipeline/Time2DepthLogging.cs index 86c19e4..5bb0b2e 100644 --- a/T2dMath.Pipeline/Time2DepthLogging.cs +++ b/T2dMath.Pipeline/Time2DepthLogging.cs @@ -1,137 +1,134 @@ -<<<<<<< HEAD -using System.Collections.Generic; -using System.Linq; -using T2dMath.ILogAlgorithm; - -namespace T2dMath.Pipeline -{ - using AlgCurveInfo = ( - string name_curve, - ILogTransformation alg, - string[] name_curves, - string[] name_params - ); - using CurveInfoTransformation = ( - string name_curve, - double offset, - double time_delay, - bool use_only_rotor, - IGridTransformation alg_avg, - string[] name_params_avg - ); - - public partial class Pipelines - { - //depth_monoton, value_monoton, depthGrid, param_alg - public delegate double[] AlgorithmAvarage( - double[] depth_monotone, - double[] value_monoton, - double[] depthGrid, - object[] param - ); - - /// - /// Преобразует входные кривые, заданные во временной шкале, в кривые по глубине. - /// Учитываются остановки, вращения, параметры трансформации и шаг глубинной сетки. - /// - /// Массив временных отметок, соответствующих измерениям инструмента. - /// Словарь временных кривых: ключ — имя кривой, значение — массив значений. - /// Параметры расчета: могут включать фильтры, коэффициенты и т.д. - /// Список кривых, подлежащих расчету во временной области. - /// Описание преобразований для выходных кривых. - /// Временные метки, соответствующие известным глубинам. - /// Глубинные значения, соответствующие времени в . - /// Признаки остановок инструмента (0.0 или меньше - нет, больше 0.0 - есть). - /// Признаки включенного ротора (0.0 — выключен, 1.0 — включен). - /// Список кривых, подлежащих расчету в глубинной области. - /// Шаг равномерной глубинной сетки. Должен быть > 0.Add commentMore actions - /// Результирующая глубинная сетка, равномерная по . - /// Результирующие кривые по глубине: ключ — имя кривой, значение — массив значений. - /// (Необязательно) Колбэк для отслеживания прогресса выполнения. - /// (Необязательно) Колбэк для вывода вспомогательной информации. - /// Выбрасывается при некорректном шаге или несогласованных массивах. - /// Если не найдена требуемая входная кривая. - /// Если не удалось выполнить трансформацию или интерполяцию. - - public static void Time2DepthLogging( - double[] time_tool, - IDictionary input_curves, - Dictionary input_parameters, - AlgCurveInfo[] calculate_time_curve, - CurveInfoTransformation[] curve_out_param, - double[] time_surface, - double[] depth, - double[] f_stop, - double[] f_rotor, - AlgCurveInfo[] calculate_depth_curve, - double step_grid, - out double[] depth_out, - out Dictionary curve_out, - ProgressCallback progress = null, - InfoCallback info = null - ) - { - IEnumerable t = new double[5]; - var r = t.ElementAt(0); - depth_out = null; - curve_out = null; - } - } -} -======= -using System.Collections.Generic; -using System.Linq; -using T2dMath.ILogAlgorithm; - -namespace T2dMath.Pipeline { - using AlgCurveInfo = ( - string name_curve, - ILogTransformation alg, - string[] name_curves, - string[] name_params - ); - using CurveInfoTransformation = ( - string name_curve, - double offset, - double time_delay, - bool use_only_rotor, - IGridTransformation alg_avg, - string[] name_params_avg - ); - - public partial class Pipelines { - //depth_monoton, value_monoton, depthGrid, param_alg - public delegate double[] AlgorithmAvarage( - double[] depth_monotone, - double[] value_monoton, - double[] depthGrid, - object[] param - ); - - // - - public static void Time2DepthLogging( - double[] time_tool, - IDictionary input_curves, - Dictionary input_parameters, - AlgCurveInfo[] calculate_time_curve, - CurveInfoTransformation[] curve_out_param, - double[] time_surface, - double[] depth, - double[] f_stop, - double[] f_rotor, - AlgCurveInfo[] calculate_depth_curve, - double step_grid, - out double[] depth_out, - out Dictionary curve_out, - ProgressCallback progress = null, - InfoCallback info = null - ) { - IEnumerable t = new double[5]; - var r = t.ElementAt(0); - depth_out = null; - curve_out = null; - } - } -} ->>>>>>> 62659233a362a58abee892789f6088ba177713d3 +<<<<<<< HEAD +using System.Collections.Generic; +using System.Linq; +using T2dMath.ILogAlgorithm; + +namespace T2dMath.Pipeline { + using AlgCurveInfo = ( + string name_curve, + ILogTransformation alg, + string[] name_curves, + string[] name_params + ); + using CurveInfoTransformation = ( + string name_curve, + double offset, + double time_delay, + bool use_only_rotor, + IGridTransformation alg_avg, + string[] name_params_avg + ); + + public partial class Pipelines { + //depth_monoton, value_monoton, depthGrid, param_alg + public delegate double[] AlgorithmAvarage( + double[] depth_monotone, + double[] value_monoton, + double[] depthGrid, + object[] param + ); + + /// + /// Преобразует входные кривые, заданные во временной шкале, в кривые по глубине. + /// Учитываются остановки, вращения, параметры трансформации и шаг глубинной сетки. + /// + /// Массив временных отметок, соответствующих измерениям инструмента. + /// Словарь временных кривых: ключ — имя кривой, значение — массив значений. + /// Параметры расчета: могут включать фильтры, коэффициенты и т.д. + /// Список кривых, подлежащих расчету во временной области. + /// Описание преобразований для выходных кривых. + /// Временные метки, соответствующие известным глубинам. + /// Глубинные значения, соответствующие времени в . + /// Признаки остановок инструмента (0.0 или меньше - нет, больше 0.0 - есть). + /// Признаки включенного ротора (0.0 — выключен, 1.0 — включен). + /// Список кривых, подлежащих расчету в глубинной области. + /// Шаг равномерной глубинной сетки. Должен быть > 0.Add commentMore actions + /// Результирующая глубинная сетка, равномерная по . + /// Результирующие кривые по глубине: ключ — имя кривой, значение — массив значений. + /// (Необязательно) Колбэк для отслеживания прогресса выполнения. + /// (Необязательно) Колбэк для вывода вспомогательной информации. + /// Выбрасывается при некорректном шаге или несогласованных массивах. + /// Если не найдена требуемая входная кривая. + /// Если не удалось выполнить трансформацию или интерполяцию. + + public static void Time2DepthLogging( + double[] time_tool, + IDictionary input_curves, + Dictionary input_parameters, + AlgCurveInfo[] calculate_time_curve, + CurveInfoTransformation[] curve_out_param, + double[] time_surface, + double[] depth, + double[] f_stop, + double[] f_rotor, + AlgCurveInfo[] calculate_depth_curve, + double step_grid, + out double[] depth_out, + out Dictionary curve_out, + ProgressCallback progress = null, + InfoCallback info = null + ) { + IEnumerable t = new double[5]; + var r = t.ElementAt(0); + depth_out = null; + curve_out = null; + } + } +} +======= +using System.Collections.Generic; +using System.Linq; +using T2dMath.ILogAlgorithm; + +namespace T2dMath.Pipeline { + using AlgCurveInfo = ( + string name_curve, + ILogTransformation alg, + string[] name_curves, + string[] name_params + ); + using CurveInfoTransformation = ( + string name_curve, + double offset, + double time_delay, + bool use_only_rotor, + IGridTransformation alg_avg, + string[] name_params_avg + ); + + public partial class Pipelines { + //depth_monoton, value_monoton, depthGrid, param_alg + public delegate double[] AlgorithmAvarage( + double[] depth_monotone, + double[] value_monoton, + double[] depthGrid, + object[] param + ); + + // + + public static void Time2DepthLogging( + double[] time_tool, + IDictionary input_curves, + Dictionary input_parameters, + AlgCurveInfo[] calculate_time_curve, + CurveInfoTransformation[] curve_out_param, + double[] time_surface, + double[] depth, + double[] f_stop, + double[] f_rotor, + AlgCurveInfo[] calculate_depth_curve, + double step_grid, + out double[] depth_out, + out Dictionary curve_out, + ProgressCallback progress = null, + InfoCallback info = null + ) { + IEnumerable t = new double[5]; + var r = t.ElementAt(0); + depth_out = null; + curve_out = null; + } + } +} +>>>>>>> 62659233a362a58abee892789f6088ba177713d3 From aeebd174076140223915ec951cf32a17446403f1 Mon Sep 17 00:00:00 2001 From: vlad dalevich Date: Thu, 3 Jul 2025 15:40:08 +0700 Subject: [PATCH 6/7] fast fix --- T2dMath.Pipeline/Time2DepthLogging.cs | 60 +-------------------------- 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/T2dMath.Pipeline/Time2DepthLogging.cs b/T2dMath.Pipeline/Time2DepthLogging.cs index 5bb0b2e..752b907 100644 --- a/T2dMath.Pipeline/Time2DepthLogging.cs +++ b/T2dMath.Pipeline/Time2DepthLogging.cs @@ -1,4 +1,3 @@ -<<<<<<< HEAD using System.Collections.Generic; using System.Linq; using T2dMath.ILogAlgorithm; @@ -74,61 +73,4 @@ public static void Time2DepthLogging( curve_out = null; } } -} -======= -using System.Collections.Generic; -using System.Linq; -using T2dMath.ILogAlgorithm; - -namespace T2dMath.Pipeline { - using AlgCurveInfo = ( - string name_curve, - ILogTransformation alg, - string[] name_curves, - string[] name_params - ); - using CurveInfoTransformation = ( - string name_curve, - double offset, - double time_delay, - bool use_only_rotor, - IGridTransformation alg_avg, - string[] name_params_avg - ); - - public partial class Pipelines { - //depth_monoton, value_monoton, depthGrid, param_alg - public delegate double[] AlgorithmAvarage( - double[] depth_monotone, - double[] value_monoton, - double[] depthGrid, - object[] param - ); - - // - - public static void Time2DepthLogging( - double[] time_tool, - IDictionary input_curves, - Dictionary input_parameters, - AlgCurveInfo[] calculate_time_curve, - CurveInfoTransformation[] curve_out_param, - double[] time_surface, - double[] depth, - double[] f_stop, - double[] f_rotor, - AlgCurveInfo[] calculate_depth_curve, - double step_grid, - out double[] depth_out, - out Dictionary curve_out, - ProgressCallback progress = null, - InfoCallback info = null - ) { - IEnumerable t = new double[5]; - var r = t.ElementAt(0); - depth_out = null; - curve_out = null; - } - } -} ->>>>>>> 62659233a362a58abee892789f6088ba177713d3 +} \ No newline at end of file From 8e4ee7c0c62303fb74e5c5174f80ede61e6d5e9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 08:43:22 +0000 Subject: [PATCH 7/7] Auto-format code --- T2dMath.Pipeline/Time2DepthLogging.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/T2dMath.Pipeline/Time2DepthLogging.cs b/T2dMath.Pipeline/Time2DepthLogging.cs index 752b907..c1a5216 100644 --- a/T2dMath.Pipeline/Time2DepthLogging.cs +++ b/T2dMath.Pipeline/Time2DepthLogging.cs @@ -73,4 +73,4 @@ public static void Time2DepthLogging( curve_out = null; } } -} \ No newline at end of file +}