From b866e2509b24fbc227e47ed8d529f50da4d47e4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Steinar=20Elgs=C3=A6ter?= <stelg@equinor.com>
Date: Thu, 1 Feb 2024 14:16:00 +0100
Subject: [PATCH] -added test and gain sched identify method to attempt to
 estiamate a large number of gains when thresholds are given.

---
 Dynamic/Identification/GainSchedIdentifier.cs | 46 ++++++++++++++-
 .../Tests/GainSchedIdentifyTests.cs           | 58 ++++++++++++++++---
 2 files changed, 94 insertions(+), 10 deletions(-)

diff --git a/Dynamic/Identification/GainSchedIdentifier.cs b/Dynamic/Identification/GainSchedIdentifier.cs
index c44f1134..0bc01283 100644
--- a/Dynamic/Identification/GainSchedIdentifier.cs
+++ b/Dynamic/Identification/GainSchedIdentifier.cs
@@ -112,7 +112,48 @@ static public GainSchedParameters Identify(UnitDataSet dataSet, GainSchedFitting
         /// <returns></returns>
         public static GainSchedParameters IdentifyGainsForGivenThresholds(UnitDataSet dataSet, GainSchedFittingSpecs gsFittingSpecs)
         {
-            return null;
+            var vec = new Vec();
+            GainSchedParameters ret = new GainSchedParameters();
+            ret.GainSchedParameterIndex = gsFittingSpecs.uGainScheduledInputIndex;
+            ret.LinearGainThresholds = gsFittingSpecs.uGainThresholds;
+
+            const int WIDTH = 1; // 
+
+            int number_of_inputs = dataSet.U.GetLength(1);
+            double gsVarMinU = vec.Min(Array2D<double>.GetColumn(dataSet.U, ret.GainSchedParameterIndex));
+            double gsVarMaxU = 0;
+            var linearGains = new List<double[]>();
+            for (int curGainIdx = 0; curGainIdx < ret.LinearGainThresholds.Count()+1; curGainIdx++)
+            {
+                double[] uMinFit = new double[number_of_inputs];
+                double[] uMaxFit = new double[number_of_inputs];
+
+                if (curGainIdx < ret.LinearGainThresholds.Count()- WIDTH)
+                    gsVarMaxU = ret.LinearGainThresholds[curGainIdx+ WIDTH];//NB! +1
+                else
+                    gsVarMaxU = vec.Max(Array2D<double>.GetColumn(dataSet.U, ret.GainSchedParameterIndex));
+                for (int idx = 0; idx < number_of_inputs; idx++)
+                {
+                    if (idx == ret.GainSchedParameterIndex)
+                    {
+                        uMinFit[idx] = gsVarMinU;
+                        uMaxFit[idx] = gsVarMaxU;
+                    }
+                    else
+                    {
+                        uMinFit[idx] = double.NaN;
+                        uMaxFit[idx] = double.NaN;
+                    }
+                }
+                var idResults = IdentifySingleGainForGivenThresholds(ref dataSet, uMinFit, uMaxFit);
+                if (curGainIdx>-1+ WIDTH)
+                    gsVarMinU = ret.LinearGainThresholds[curGainIdx- WIDTH];
+               //  if (idResults.Item1 != null)
+                linearGains.Add(idResults.Item1);
+            }
+            // final gain:above the highest threshold
+            ret.LinearGains = linearGains;
+            return ret;
         }
 
 
@@ -203,7 +244,8 @@ private static (double[], double) IdentifySingleGainForGivenThresholds(ref UnitD
             fittingSpecs.U_min_fit = u_min_fit;
             fittingSpecs.U_max_fit = u_max_fit;
             fittingSpecs.u0 = new double[] { u_min_fit[0] + (u_max_fit[0] - u_min_fit[0]) / 2 };
-            var unitModel = UnitIdentifier.IdentifyLinear(ref dataSet, fittingSpecs, false); ;
+            //     var unitModel = UnitIdentifier.IdentifyLinear(ref dataSet, fittingSpecs, false); ;
+            var unitModel = UnitIdentifier.IdentifyLinearAndStatic(ref dataSet, fittingSpecs, false); ;
             var unitParams = unitModel.GetModelParameters();
 
             return (unitParams.LinearGains, unitParams.TimeConstant_s);
diff --git a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs
index fc75d3ee..086ac17e 100644
--- a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs
+++ b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs
@@ -15,6 +15,48 @@ public class GainSchedIdentifyTests
         int timeBase_s = 1;
         const double TimeConstantAllowedDev_s = 0.5;
 
+        [Test]
+        public void GainEstOnly_CorrectTresholdsGiven_CorrectGainsReturned()
+        {
+            int N = 100;
+
+            var gainSched_tenThresholds_singleInput = new GainSchedParameters
+            {
+                TimeConstant_s = null,
+                TimeConstantThresholds = null,
+                LinearGains = new List<double[]> { new double[] { 0 }, new double[] { 1 }, new double[] { 2 }, new double[] { 3 }, new double[] { 4 }, new double[] { 5 },
+                    new double[] { 6 }, new double[] { 7 }, new double[] { 8 }, new double[] { 9 }, new double[] { 10 } },
+                LinearGainThresholds = new double[] { 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5 },
+                TimeDelay_s = 0,
+                Bias = 5,
+                GainSchedParameterIndex = 0
+            };
+
+            var refModel = new GainSchedModel(gainSched_tenThresholds_singleInput);
+            var gsFittingSpecs= new GainSchedFittingSpecs();
+            gsFittingSpecs.uGainThresholds = refModel.GetModelParameters().LinearGainThresholds;
+
+            var plantSim = new PlantSimulator(new List<ISimulatableModel> { refModel });
+            var inputData = new TimeSeriesDataSet();
+            var input = TimeSeriesCreator.ThreeSteps(N / 4, N * 2 / 4, N * 3 / 4, N, 0, 1, 2, 3).
+                 Concat(TimeSeriesCreator.ThreeSteps(N / 4, N * 2 / 4, N * 3 / 4, N, 4, 5, 6, 7)).
+                 Concat(TimeSeriesCreator.ThreeSteps(N / 4, N * 2 / 4, N * 3 / 4, N, 8, 9, 10, 11)).ToArray();
+            inputData.Add(plantSim.AddExternalSignal(refModel, SignalType.External_U, (int)INDEX.FIRST), input);
+            inputData.CreateTimestamps(timeBase_s);
+
+            // Act
+            var isSimulatable = plantSim.Simulate(inputData, out TimeSeriesDataSet simData);
+            Assert.IsTrue(isSimulatable);
+            var dataSet = new UnitDataSet();
+            dataSet.Y_meas = simData.GetValues(refModel.ID, SignalType.Output_Y);
+            dataSet.U = Array2D<double>.CreateFromList(new List<double[]> { inputData.GetValues(refModel.ID,SignalType.External_U)});
+
+            GainSchedIdentifier.IdentifyGainsForGivenThresholds(dataSet, gsFittingSpecs);
+
+        }
+
+
+
         /*
         [TestCase()]
         public void ReturnsParametersWithNumberOfLinearGainsNotExceeding2()
@@ -82,7 +124,7 @@ public void ReturnsParametersWithNumberOfLinearGainsNotExceeding2()
         }
         */
         [TestCase()]
-        public void GainEstimationOnly_GainsNotLargerThanTheBiggestPossibleGain()
+        public void GainAndThreshold_GainsNotLargerThanTheBiggestPossibleGain()
         {
             int N = 500;
 
@@ -161,7 +203,7 @@ public void GainEstimationOnly_GainsNotLargerThanTheBiggestPossibleGain()
         [TestCase(5, 2.5)]
        [TestCase(6, 3.0)]*/
         [TestCase(7, 4.0)]
-        public void ThresholdEstimation_LinearGainThresholdAtReasonablePlace(int ver, double gain_sched_threshold)
+        public void GainAndThreshold_LinearGainThresholdAtReasonablePlace(int ver, double gain_sched_threshold)
         {
             int N = 300;
             // Arrange
@@ -231,11 +273,10 @@ public void ThresholdEstimation_LinearGainThresholdAtReasonablePlace(int ver, do
         [TestCase(1, 35)]
         [TestCase(38, 40)]
         [TestCase(40, 20)]*/
-        public void GainEstimationOnly_TwoGains_TimeConstantsAndThresholdFoundOk(double TimeConstant1_s, double TimeConstant2_s)
+        public void GainAndThreshold_TwoGains_TimeConstantsAndThresholdFoundOk(double TimeConstant1_s, double TimeConstant2_s)
         {
             int N = 300;
 
-
             // Arrange
             var unitData = new UnitDataSet("test"); 
             double[] u1 = TimeSeriesCreator.ThreeSteps(N/5, N/3, N/2, N, -2, -1, 0, 1);
@@ -288,9 +329,6 @@ public void GainEstimationOnly_TwoGains_TimeConstantsAndThresholdFoundOk(double
             }
 
 
-
-
-
 /*            Shared.EnablePlots();
             Plot.FromList(new List<double[]> {
                     simY1,
@@ -309,7 +347,7 @@ public void GainEstimationOnly_TwoGains_TimeConstantsAndThresholdFoundOk(double
         [TestCase(5, 3.5)]
         [TestCase(6, 4.0)]*/
      //   [TestCase(7, 4.5)]
-        public void ThresholdEstimation_ThresholdsWithinUminAndUmax(int ver, double gain_sched_threshold)
+        public void GainAndThreshold_ThresholdsWithinUminAndUmax(int ver, double gain_sched_threshold)
         {
             int N = 250;
             // Arrange
@@ -449,5 +487,9 @@ public void GainSchedIdentify_AllTimeConstantsArePositive(int ver, double gain_s
           //  Shared.DisablePlots();
         }
         */
+
+
+
+
     }
 }