diff --git a/Dynamic/SimulatableModels/GainSchedModel.cs b/Dynamic/SimulatableModels/GainSchedModel.cs index 97f692df..84d2a6ec 100644 --- a/Dynamic/SimulatableModels/GainSchedModel.cs +++ b/Dynamic/SimulatableModels/GainSchedModel.cs @@ -241,6 +241,7 @@ public void SetModelParameters(GainSchedParameters parameters) else*/ { double x_otherInputs = modelParameters.Bias; + double gainSched = givenInputs[modelParameters.GainSchedParameterIndex]; //nb! input may include a disturbance! if (givenInputs != null) { @@ -250,7 +251,7 @@ public void SetModelParameters(GainSchedParameters parameters) continue; if (i < GetModelInputIDs().Length)//model inputs { - x_otherInputs += CalculateLinearProcessGainTerm(i, givenInputs[i]); + x_otherInputs += CalculateLinearProcessGainTerm(i, givenInputs[i], gainSched); } else // additive inputs { @@ -279,19 +280,50 @@ public void SetModelParameters(GainSchedParameters parameters) /// /// the index of the input /// the value of the input + /// the value of scheduling input /// contribution to the output y, excluding bias and curvature contributions - private double CalculateLinearProcessGainTerm(int inputIndex, double u) + private double CalculateLinearProcessGainTerm(int inputIndex, double u, double u_GainSched) { double processGainTerm = 0; + int gainSchedModelIdx = 0; + for (int idx = 0; idx < modelParameters.LinearGainThresholds.Length; idx++) + { + if (idx == 0) + { + if (u_GainSched < modelParameters.LinearGainThresholds[idx]) + { + gainSchedModelIdx = idx; + break;// jump out of for-loop + } + else + { + gainSchedModelIdx = idx+1; + } + } + else if (idx == modelParameters.LinearGainThresholds.Length - 1) + { + if (u_GainSched > modelParameters.LinearGainThresholds[idx]) + { + gainSchedModelIdx = idx; + } + } + else + { + if (u_GainSched > modelParameters.LinearGainThresholds[idx-1] && + u_GainSched < modelParameters.LinearGainThresholds[idx]) + { + gainSchedModelIdx = idx; + } + } + } + if (modelParameters.U0 != null) { - //TODO: use correct linear gain - processGainTerm += modelParameters.LinearGains.First()[inputIndex] * (u- modelParameters.U0[inputIndex]); + processGainTerm += modelParameters.LinearGains.ElementAt(gainSchedModelIdx)[inputIndex] * (u- modelParameters.U0[inputIndex]); } else { - //TODO: use correct linear gain - processGainTerm += modelParameters.LinearGains.First()[inputIndex] * u; + processGainTerm += modelParameters.LinearGains.ElementAt(gainSchedModelIdx)[inputIndex] * u; } return processGainTerm; } @@ -309,6 +341,8 @@ private double CalculateStaticStateWithoutAdditive(double[] inputs, double badVa double x_static = modelParameters.Bias; // inputs U may include a disturbance as the last entry + double gainSched = inputs[modelParameters.GainSchedParameterIndex]; + for (int curInput = 0; curInput < Math.Min(inputs.Length, GetLengthOfInputVector()); curInput++) { if (curInput + 1 <= modelParameters.GetNumInputs()) @@ -326,7 +360,7 @@ private double CalculateStaticStateWithoutAdditive(double[] inputs, double badVa } lastGoodValuesOfInputs[curInput] = inputs[curInput]; } - x_static += CalculateLinearProcessGainTerm(curInput, curUvalue); + x_static += CalculateLinearProcessGainTerm(curInput, curUvalue, gainSched); } } return x_static; diff --git a/Dynamic/SimulatableModels/GainSchedParameters.cs b/Dynamic/SimulatableModels/GainSchedParameters.cs index cac33a42..447ae9ab 100644 --- a/Dynamic/SimulatableModels/GainSchedParameters.cs +++ b/Dynamic/SimulatableModels/GainSchedParameters.cs @@ -27,7 +27,6 @@ public class GainSchedParameters : ModelParametersBaseClass public FittingSpecs FittingSpecs = new FittingSpecs(); - /// /// A time constant in seconds, the time a 1. order linear system requires to do 63% of a step response. /// Set to zero to turn off time constant in model. @@ -35,12 +34,23 @@ public class GainSchedParameters : ModelParametersBaseClass public double[] TimeConstant_s { get; set; } =null; + + /// + /// The index of the parameter among the ModelInputIdx + /// + public int GainSchedParameterIndex = 0; + + + /// + /// Thresholds for when to use which gains + /// + public double[] TimeConstantThresholds { get; set; } = null; + /// /// The uncertinty of the time constant estimate /// public double[] TimeConstantUnc_s { get; set; } = null; - /// /// The time delay in seconds.This number needs to be a multiple of the sampling rate. /// Set to zero to turn of time delay in model. @@ -51,6 +61,11 @@ public class GainSchedParameters : ModelParametersBaseClass /// public List LinearGains { get; set; } = null; + /// + /// Threshold for when to use different gains + /// + public double[] LinearGainThresholds { get; set; } = null; + /// /// An array of 95% uncertatinty in the linear gains (u-u0)) /// diff --git a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorMISOTests.cs b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorMISOTests.cs index a6debc32..4b6aa390 100644 --- a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorMISOTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorMISOTests.cs @@ -127,8 +127,10 @@ public void SetUp() gainSchedParameters1 = new GainSchedParameters { - TimeConstant_s = new double[] { 10 }, + TimeConstant_s = new double[] { 10,20 }, + TimeConstantThresholds = new double[] { 2}, LinearGains = new List { new double[] { 5 }, new double[] { 10 } }, + LinearGainThresholds = new double[] { 2 }, TimeDelay_s = 0, Bias = 0 }; @@ -223,18 +225,21 @@ public void GainSched_Single_RunsAndConverges() var isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData); Assert.IsTrue(isOk); SISOTests.CommonAsserts(inputData, simData, plantSim); - double[] simY = simData.GetValues(processModel1.GetID(), SignalType.Output_Y); + double[] simY = simData.GetValues(gainSched1.GetID(), SignalType.Output_Y); - Assert.IsTrue(Math.Abs(simY[0] - (1 * 50 + 0.5 * 50 + 5)) < 0.01); - Assert.IsTrue(Math.Abs(simY.Last() - (1 * 55 + 0.5 * 45 + 5)) < 0.01); + Assert.IsTrue(Math.Abs(simY[N/3-2] - 5) < 0.2,"first step should have a gain of 5"); + Assert.IsTrue(Math.Abs(simY.Last() - 30) < 0.2, "third step should have a gain of 10"); + // Assert.IsTrue(Math.Abs(simY.Last() - (1 * 55 + 0.5 * 45 + 5)) < 0.01); - /*Plot.FromList(new List { - simData.GetValues(processModel1.GetID(),SignalType.Output_Y_sim), - simData.GetValues(processModel1.GetID(),SignalType.External_U,0), - simData.GetValues(processModel1.GetID(),SignalType.External_U,1) + //y[k] = a y[k-1] + b u[k] + Shared.EnablePlots(); + Plot.FromList(new List { + simData.GetValues(gainSched1.GetID(),SignalType.Output_Y), + inputData.GetValues(gainSched1.GetID(),SignalType.External_U,0), }, - new List { "y1=y_sim1", "y3=u1","y3=u2" }, - timeBase_s, "UnitTest_SingleMISO");*/ + new List { "y1=y_sim1", "y3=u1" }, + timeBase_s, "GainSched_Single"); + Shared.DisablePlots(); } diff --git a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs index 2b76d659..d3fd24c4 100644 --- a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs @@ -236,7 +236,7 @@ public static void CommonAsserts(TimeSeriesDataSet inputData,TimeSeriesDataSet s Assert.IsTrue(firstTwoValuesDiff < 0.01, "system should start up in steady-state"); Assert.IsTrue(lastTwoValuesDiff < 0.01, "system should end up in stedy-state"); } - Assert.AreEqual(simData.GetLength(), simData.GetTimeStamps().Count(), "number of timestamps shoudl match number of data points in sim"); + Assert.AreEqual(simData.GetLength(), simData.GetTimeStamps().Count(), "number of timestamps should match number of data points in sim"); Assert.AreEqual(simData.GetTimeStamps().Last(), inputData.GetTimeStamps().Last(),"datasets should end at same timestamp"); /* foreach (var modelKeyValuePair in plant.GetModels())