diff --git a/Dynamic/Identification/ClosedLoopUnitIdentifier.cs b/Dynamic/Identification/ClosedLoopUnitIdentifier.cs index 0100c46..0fd83ea 100644 --- a/Dynamic/Identification/ClosedLoopUnitIdentifier.cs +++ b/Dynamic/Identification/ClosedLoopUnitIdentifier.cs @@ -459,7 +459,7 @@ private static double EstimateDisturbanceLF(UnitDataSet dataSet, UnitModel unitM { unitParams.LinearGains = new double[] { Kp }; umInternal.SetModelParameters(unitParams); - (var isOk, var y_proc) = PlantSimulator.SimulateSingle(dataSet, umInternal, false); + (var isOk, var y_proc) = PlantSimulator.SimulateSingle(dataSet, umInternal); var d_LF = vec.Multiply(vec.Subtract(y_proc, y_proc[0]), -1); var d_est1 = vec.Add(d_HF, d_LF); var d_est2 = vec.Subtract(dataSet.Y_meas, y_proc); diff --git a/Dynamic/Identification/DisturbanceCalculator.cs b/Dynamic/Identification/DisturbanceCalculator.cs index 127b091..986f36e 100644 --- a/Dynamic/Identification/DisturbanceCalculator.cs +++ b/Dynamic/Identification/DisturbanceCalculator.cs @@ -289,7 +289,7 @@ public static DisturbanceIdResult CalculateDisturbanceVector(UnitDataSet unitDat // non-disturbance related changes in the dataset producing "unitDataSet_adjusted" var unitDataSet_adjusted = RemoveSetpointAndOtherInputChangeEffectsFromDataSet(unitDataSet, unitModel, pidInputIdx, pidParams); unitDataSet_adjusted.D = null; - (bool isOk, double[] y_proc) = PlantSimulator.SimulateSingle(unitDataSet_adjusted, unitModel, false); + (bool isOk, double[] y_proc) = PlantSimulator.SimulateSingle(unitDataSet_adjusted, unitModel); if (y_proc == null) { diff --git a/Dynamic/Identification/FitScoreCalculator.cs b/Dynamic/Identification/FitScoreCalculator.cs index c537fa1..5cd6454 100644 --- a/Dynamic/Identification/FitScoreCalculator.cs +++ b/Dynamic/Identification/FitScoreCalculator.cs @@ -95,7 +95,10 @@ static public double GetPlantWideSimulated(PlantSimulator plantSimObj, TimeSerie { if (!modelObj.GetAdditiveInputIDs()[0].StartsWith(disturbanceSignalPrefix)) { - measY = inputData.GetValues(outputName); + if (inputData.ContainsSignal(outputName)) + { + measY = inputData.GetValues(outputName); + } } } else if (inputData.ContainsSignal(outputName)) diff --git a/Dynamic/Identification/GainSchedIdentifier.cs b/Dynamic/Identification/GainSchedIdentifier.cs index 4e56507..e19eebc 100644 --- a/Dynamic/Identification/GainSchedIdentifier.cs +++ b/Dynamic/Identification/GainSchedIdentifier.cs @@ -388,7 +388,7 @@ static private (GainSchedParameters, int) ChooseBestModelFromFittingInfo( if (simulateAndAddToYsimInDataSet) { var bestModel = new GainSchedModel(BestGainSchedParams); - PlantSimulator.SimulateSingle(dataSet, bestModel, true); + PlantSimulator.SimulateSingleToYsim(dataSet, bestModel); } return (BestGainSchedParams, bestModelIdx); } @@ -512,7 +512,7 @@ private static bool DetermineOperatingPointAndSimulate(ref GainSchedParameters g } gsParams.MoveOperatingPointUWithoutChangingModel(desiredOpU); - (var isOk, var y_sim) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel,false); + (var isOk, var y_sim) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel); if (isOk) { @@ -523,7 +523,7 @@ private static bool DetermineOperatingPointAndSimulate(ref GainSchedParameters g if (estBias.HasValue) { gsParams.IncreaseOperatingPointY(estBias.Value); - (var isOk2, var y_sim2) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel, false); + (var isOk2, var y_sim2) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel); dataSet.Y_sim = y_sim2; if (gsParams.Fitting == null) gsParams.Fitting = new FittingInfo(); @@ -587,7 +587,7 @@ private static void EstimateTimeDelay(ref GainSchedParameters gsParams, ref Unit copiedGsParams.TimeConstant_s = vec.Subtract(gsParams.TimeConstant_s, timedelay_s); var gsIdentModel = new GainSchedModel(copiedGsParams, "ident_model"); - (var isOk, var y_sim) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel, false); + (var isOk, var y_sim) = PlantSimulator.SimulateSingle(dataSet, gsIdentModel); if (isOk) { diff --git a/Dynamic/Identification/UnitIdentifier.cs b/Dynamic/Identification/UnitIdentifier.cs index 399ba45..c78d353 100644 --- a/Dynamic/Identification/UnitIdentifier.cs +++ b/Dynamic/Identification/UnitIdentifier.cs @@ -118,9 +118,7 @@ public static UnitModel IdentifyLinearDiff(ref UnitDataSet dataSet, FittingSpecs if (model.modelParameters.Fitting.WasAbleToIdentify) { - PlantSimulator.SimulateSingle(dataSet, model, true); - //var simulator = new UnitSimulator(model); - //simulator.Simulate(ref dataSet, default, true);// overwrite any y_sim + PlantSimulator.SimulateSingleToYsim(dataSet, model); model.SetFittedDataSet(dataSet); } return model; @@ -451,10 +449,9 @@ private static UnitModel Identify_Internal(ref UnitDataSet dataSet, FittingSpecs // simulate if (modelParameters.Fitting.WasAbleToIdentify) { - PlantSimulator.SimulateSingle(dataSet, model, true);// overwrite any y_sim + PlantSimulator.SimulateSingleToYsim(dataSet, model); model.SetFittedDataSet(dataSet); } - return model; } diff --git a/Dynamic/PlantSimulator/PlantSimulator.cs b/Dynamic/PlantSimulator/PlantSimulator.cs index fffd24f..dae08bf 100644 --- a/Dynamic/PlantSimulator/PlantSimulator.cs +++ b/Dynamic/PlantSimulator/PlantSimulator.cs @@ -83,7 +83,7 @@ public Comment(string author, DateTime date, string comment, double plantScore = public class PlantSimulator { - private const bool doDestBasedONYsimOfLastTimestep = false; //TODO: was false, but want to refactor so that "true" works. + private const bool doDestBasedONYsimOfLastTimestep = true; /// /// User-friendly name that may include white spaces. @@ -123,91 +123,8 @@ public class PlantSimulator /// public double PlantFitScore; - /// - /// Returns a unit data set for a given UnitModel. - /// - /// - /// - /// - public UnitDataSet GetUnitDataSetForProcess(TimeSeriesDataSet inputData, UnitModel unitModel) - { - UnitDataSet dataset = new UnitDataSet(); - dataset.U = new double[inputData.GetLength().Value, 1]; - - dataset.Times = inputData.GetTimeStamps(); - var inputIDs = unitModel.GetModelInputIDs(); - var outputID = unitModel.GetOutputID(); - dataset.Y_meas = inputData.GetValues(outputID); - for (int inputIDidx = 0; inputIDidx < inputIDs.Length; inputIDidx++) - { - var inputID = inputIDs[inputIDidx]; - var curCol = inputData.GetValues(inputID); - dataset.U.WriteColumn(inputIDidx, curCol); - } - return dataset; - } - /// - /// Returns a "unitDataSet" for the given pidModel in the plant. - /// This function only works when the unit model connected to the pidModel only has a single input. - /// - /// - /// - /// - public UnitDataSet GetUnitDataSetForPID(TimeSeriesDataSet inputData,PidModel pidModel) - { - var unitModID = connections.GetUnitModelControlledByPID(pidModel.GetID(),modelDict); - string[] modelInputIDs = null; - if (unitModID != null) - { - modelInputIDs = modelDict[unitModID].GetModelInputIDs(); - } - UnitDataSet dataset = new UnitDataSet(); - - if (modelInputIDs != null) - { - dataset.U = new double[inputData.GetLength().Value, modelInputIDs.Length]; - for (int modelInputIdx = 0; modelInputIdx < modelInputIDs.Length; modelInputIdx++) - { - var inputID = modelInputIDs[modelInputIdx]; - dataset.U.WriteColumn(modelInputIdx, inputData.GetValues(inputID)); - } - } - else - { - dataset.U = new double[inputData.GetLength().Value, 1]; - dataset.U.WriteColumn(0, inputData.GetValues(pidModel.GetOutputID())); - } - - dataset.Times = inputData.GetTimeStamps(); - var inputIDs = pidModel.GetModelInputIDs(); - - for (int inputIDidx=0; inputIDidx /// Constructor /// @@ -458,6 +375,91 @@ public static (PlantSimulator, TimeSeriesDataSet) CreateFeedbackLoopWithEstimate } + /// + /// Returns a unit data set for a given UnitModel. + /// + /// + /// + /// + public UnitDataSet GetUnitDataSetForProcess(TimeSeriesDataSet inputData, UnitModel unitModel) + { + UnitDataSet dataset = new UnitDataSet(); + dataset.U = new double[inputData.GetLength().Value, 1]; + + dataset.Times = inputData.GetTimeStamps(); + var inputIDs = unitModel.GetModelInputIDs(); + var outputID = unitModel.GetOutputID(); + dataset.Y_meas = inputData.GetValues(outputID); + for (int inputIDidx = 0; inputIDidx < inputIDs.Length; inputIDidx++) + { + var inputID = inputIDs[inputIDidx]; + var curCol = inputData.GetValues(inputID); + dataset.U.WriteColumn(inputIDidx, curCol); + } + return dataset; + } + + + /// + /// Returns a "unitDataSet" for the given pidModel in the plant. + /// This function only works when the unit model connected to the pidModel only has a single input. + /// + /// + /// + /// + public UnitDataSet GetUnitDataSetForPID(TimeSeriesDataSet inputData, PidModel pidModel) + { + var unitModID = connections.GetUnitModelControlledByPID(pidModel.GetID(), modelDict); + string[] modelInputIDs = null; + if (unitModID != null) + { + modelInputIDs = modelDict[unitModID].GetModelInputIDs(); + } + UnitDataSet dataset = new UnitDataSet(); + + if (modelInputIDs != null) + { + dataset.U = new double[inputData.GetLength().Value, modelInputIDs.Length]; + for (int modelInputIdx = 0; modelInputIdx < modelInputIDs.Length; modelInputIdx++) + { + var inputID = modelInputIDs[modelInputIdx]; + dataset.U.WriteColumn(modelInputIdx, inputData.GetValues(inputID)); + } + } + else + { + dataset.U = new double[inputData.GetLength().Value, 1]; + dataset.U.WriteColumn(0, inputData.GetValues(pidModel.GetOutputID())); + } + + dataset.Times = inputData.GetTimeStamps(); + var inputIDs = pidModel.GetModelInputIDs(); + + for (int inputIDidx = 0; inputIDidx < inputIDs.Length; inputIDidx++) + { + var inputID = inputIDs[inputIDidx]; + + if (inputIDidx == (int)PidModelInputsIdx.Y_setpoint) + { + dataset.Y_setpoint = inputData.GetValues(inputID); + } + else if (inputIDidx == (int)PidModelInputsIdx.Y_meas) + { + dataset.Y_meas = inputData.GetValues(inputID); + } + //todo: feedforward? + /*else if (type == SignalType.Output_Y_sim) + { + dataset.U.WriteColumn(1, inputData.GetValues(inputID)); + } + else + { + throw new Exception("unexepcted signal type"); + }*/ + } + return dataset; + } + /// /// Get a TimeSeriesDataSet of all external signals of model. /// @@ -486,6 +488,92 @@ public Dictionary GetModels() } + /// + /// + /// + /// + /// + /// + /// + /// + private double[] GetValuesFromEitherDatasetInternal(string[] inputIDs, int timeIndex, + TimeSeriesDataSet dataSet1, TimeSeriesDataSet dataSet2) + { + double[] retVals = new double[inputIDs.Length]; + + int index = 0; + foreach (var inputId in inputIDs) + { + double? retVal = null; + if (dataSet1.ContainsSignal(inputId)) + { + retVal = dataSet1.GetValue(inputId, timeIndex); + } + else if (dataSet2.ContainsSignal(inputId)) + { + retVal = dataSet2.GetValue(inputId, timeIndex); + } + if (!retVal.HasValue) + { + retVals[index] = Double.NaN; + } + else + { + retVals[index] = retVal.Value; + } + + index++; + } + return retVals; + } + + /// + /// Gets data for a given model from either of two datasets (usally the inputdata and possibly simulated data. + /// This method also has a special treatment of PID-inputs, + /// + /// the model that the data is for() + /// + /// + /// + /// + /// + public double[] GetValuesFromEitherDataset(ISimulatableModel model, string[] inputIDs, int timeIndex, + TimeSeriesDataSet dataSet1, TimeSeriesDataSet dataSet2) + { + if (model.GetProcessModelType() == ModelType.PID && timeIndex > 0) + { + int lookBackIndex = 1; + double[] lookBackValues = GetValuesFromEitherDatasetInternal(inputIDs, timeIndex - lookBackIndex, dataSet1, dataSet2); ; + double[] currentValues = GetValuesFromEitherDatasetInternal(inputIDs, timeIndex, dataSet1, dataSet2); + + // "use values from current data point when available, but fall back on using values from the previous sample if need be" + // for instance, always use the most current setpoint value, but if no disturbance vector is given, then use the y_proc simulated from the last iteration. + double[] retValues = new double[currentValues.Length]; + retValues = lookBackValues; + + // adding in the below code seems to remove the issue with there being a one sample wait time before the effect of a setpoint + // is seen on the output, but causes there to be small deviation between what the PlantSimulator.SimulateSingle and PlantSimulator.Simulate + // seem to return for a PID-loop in the test BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDisturbanceEstToWork + /* + for (int i = 0; i < currentValues.Length; i++) + { + if (Double.IsNaN(currentValues[i])) + { + retValues[i] = lookBackValues[i]; + } + else + { + retValues[i] = currentValues[i]; + } + }*/ + return retValues; + } + else + { + return GetValuesFromEitherDatasetInternal(inputIDs, timeIndex, dataSet1, dataSet2); + } + } + /// @@ -495,9 +583,9 @@ public Dictionary GetModels() /// /// /// - public bool SimulateSingleInternal(TimeSeriesDataSet inputData, string singleModelName, out TimeSeriesDataSet simData) + public bool SimulateSingleWithoutAdditive(TimeSeriesDataSet inputData, string singleModelName, out TimeSeriesDataSet simData) { - return SimulateSingle(inputData,singleModelName,true, out simData); + return SimulateSingleInternalCore(inputData,singleModelName,true, out simData); } /// @@ -509,11 +597,9 @@ public bool SimulateSingleInternal(TimeSeriesDataSet inputData, string singleMod /// public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, out TimeSeriesDataSet simData) { - return SimulateSingle(inputData, singleModelName, false, out simData); + return SimulateSingleInternalCore(inputData, singleModelName, false, out simData); } - - /// /// Simulates a single model for a unit dataset and adds the output to unitData.Y_meas of the unitData, optionally with noise /// @@ -522,23 +608,48 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, /// the amplitude of noise to be added to Y_meas /// a seed value of the randm noise(specify so that tests are repeatable) /// - public static (bool, double[]) SimulateSingleToYmeas(UnitDataSet unitData, ISimulatableModel model, double noiseAmplitude = 0, + public static bool SimulateSingleToYmeas(UnitDataSet unitData, ISimulatableModel model, double noiseAmplitude = 0, int noiseSeed= 123) { - return SimulateSingle(unitData, model, true, noiseAmplitude, true, noiseSeed); + (bool isOk, double[] y_proc) = SimulateSingleUnitDataWrapper(unitData, model); + + if (noiseAmplitude > 0) + { + // use a specific seed here, to avoid potential issues with "random unit tests" and not-repeatable + // errors. + Random rand = new Random(noiseSeed); + for (int k = 0; k < y_proc.Count(); k++) + { + y_proc[k] += (rand.NextDouble() - 0.5) * 2 * noiseAmplitude; + } + } + unitData.Y_meas = y_proc; + return isOk; } /// - /// Simulates a single model given a unit data set, optionally writing the simulation to unitData.Y_sim + /// Simulates a single model for a unit dataset and adds the output to unitData.Y_meas of the unitData, optionally with noise + /// + /// the dataset to be simualted over, and where the Y_meas is updated with result + /// the model to be simulated + /// + public static (bool, double[]) SimulateSingleToYsim(UnitDataSet unitData, ISimulatableModel model) + { + (bool isOk, double[] y_proc) = SimulateSingleUnitDataWrapper(unitData, model); + unitData.Y_sim = y_proc; + return (isOk, y_proc); + } + + + /// + /// Simulates a single model given a unit data set /// /// /// - /// /// - public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model, bool addSimToUnitData) + public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model) { - - return SimulateSingle(unitData, model, false, 0, addSimToUnitData, 0); + return SimulateSingleUnitDataWrapper(unitData, model); } /// @@ -551,14 +662,8 @@ public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl /// /// contains a unit data set that must have U filled, Y_sim will be written here /// model to simulate - /// if set to true, the simulated result is written to unitData.Y_meas instead of Y_sim - /// if writing to Ymeas, it is possible to add noise of the given amplitude to signal - /// if true, the Y_sim of unitData has the simulation result written two i - /// the seed value of the noise to be added /// a tuple, first aa true if able to simulate, otherwise false, second is the simulated time-series "y_proc" without any additive - static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model, bool writeToYmeas= false, - double noiseAmplitude=0, - bool addSimToUnitData=false, int seedNr=123) + static private (bool, double[]) SimulateSingleUnitDataWrapper(UnitDataSet unitData, ISimulatableModel model ) { const string defaultOutputName = "output"; var inputData = new TimeSeriesDataSet(); @@ -586,7 +691,7 @@ static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl } var sim = new PlantSimulator(new List { modelCopy }); - var isOk = sim.SimulateSingle(inputData, singleModelName, false, out var simData); + var isOk = sim.SimulateSingleInternalCore(inputData, singleModelName, false, out var simData); if(!isOk) return (false, null); @@ -604,60 +709,27 @@ static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl { y_proc = y_sim; } - - - // if the input included a "additive distubance" signal,there will be a internal process output - /* double[] y_proc = simData.GetValues(singleModelName); - double[] y_sim = y_proc; - - if (y_proc == null) - { - y_proc = simData.GetValues(defaultOutputName); - y_sim = simData.GetValues(singleModelName, SignalType.Output_Y); - }*/ - if (noiseAmplitude > 0) - { - // use a specific seed here, to avoid potential issues with "random unit tests" and not-repeatable - // errors. - Random rand = new Random(seedNr); - for (int k = 0; k < y_sim.Count(); k++) - { - y_sim[k] += (rand.NextDouble() - 0.5) * 2 * noiseAmplitude; - } - } - - if (addSimToUnitData) - { - if (writeToYmeas) - { - unitData.Y_meas = y_sim; - } - else - { - unitData.Y_sim = y_sim; - } - } return (isOk, y_proc); } - /// /// Simulate a single model(any ISimulatable model), using inputData as inputs, /// /// If the model is a unitModel and the inputData inludes both the measured y and measured u, the /// simData will include an estimate of the additive disturbance. /// - /// + /// + /// All other SimulateSingle() methods in this class should be convenience wrapper that ultimately call this method. + /// /// /// /// /// /// /// - public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, + private bool SimulateSingleInternalCore(TimeSeriesDataSet inputData, string singleModelName, bool doCalcYwithoutAdditiveTerms, out TimeSeriesDataSet simData) { - if (!modelDict.ContainsKey(singleModelName)) { simData = null; @@ -709,7 +781,6 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, model.WarmStart(inputVals, outputVals[0]); } // main loop - var timeBase_s = inputData.GetTimeBase(); ; for (timeIdx = 0; timeIdx < N; timeIdx++) @@ -912,100 +983,13 @@ public bool Simulate (TimeSeriesDataSet inputData, out TimeSeriesDataSet simData } } simData.SetTimeStamps(inputDataMinimal.GetTimeStamps().ToList()); - PlantFitScore = FitScoreCalculator.GetPlantWideSimulated(this, inputData, simData); + PlantFitScore = FitScoreCalculator.GetPlantWideSimulated(this, inputData, simData); return true; } - /// - /// - /// - /// - /// - /// - /// - /// - private double[] GetValuesFromEitherDatasetInternal(string[] inputIDs, int timeIndex, - TimeSeriesDataSet dataSet1, TimeSeriesDataSet dataSet2) - { - double[] retVals = new double[inputIDs.Length]; - - int index = 0; - foreach (var inputId in inputIDs) - { - double? retVal=null; - if (dataSet1.ContainsSignal(inputId)) - { - retVal = dataSet1.GetValue(inputId, timeIndex); - } - else if (dataSet2.ContainsSignal(inputId)) - { - retVal= dataSet2.GetValue(inputId, timeIndex); - } - if (!retVal.HasValue) - { - retVals[index] = Double.NaN; - } - else - { - retVals[index] = retVal.Value; - } - - index++; - } - return retVals; - } - - /// - /// Gets data for a given model from either of two datasets (usally the inputdata and possibly simulated data. - /// This method also has a special treatment of PID-inputs, - /// - /// the model that the data is for() - /// - /// - /// - /// - /// - public double[] GetValuesFromEitherDataset(ISimulatableModel model, string[] inputIDs, int timeIndex, - TimeSeriesDataSet dataSet1, TimeSeriesDataSet dataSet2) - { - if (model.GetProcessModelType() == ModelType.PID && timeIndex>0) - { - - int lookBackIndex = 1; - double[] lookBackValues = GetValuesFromEitherDatasetInternal(inputIDs, timeIndex - lookBackIndex, dataSet1, dataSet2); ; - double[] currentValues = GetValuesFromEitherDatasetInternal(inputIDs, timeIndex, dataSet1, dataSet2); - - // "use values from current data point when available, but fall back on using values from the previous sample if need be" - // for instance, always use the most current setpoint value, but if no disturbance vector is given, then use the y_proc simulated from the last iteration. - double[] retValues = new double[currentValues.Length]; - retValues = lookBackValues; - - // adding in the below code seems to remove the issue with there being a one sample wait time before the effect of a setpoint - // is seen on the output, but causes there to be small deviation between what the PlantSimulator.SimulateSingle and PlantSimulator.Simulate - // seem to return for a PID-loop in the test BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDisturbanceEstToWork - /* - for (int i = 0; i < currentValues.Length; i++) - { - if (Double.IsNaN(currentValues[i])) - { - retValues[i] = lookBackValues[i]; - } - else - { - retValues[i] = currentValues[i]; - } - }*/ - return retValues; - } - else - { - return GetValuesFromEitherDatasetInternal(inputIDs, timeIndex, dataSet1, dataSet2); - } - } - /// diff --git a/Dynamic/PlantSimulator/PlantSimulatorInitalizer.cs b/Dynamic/PlantSimulator/PlantSimulatorInitalizer.cs index 3b4aa3c..612a430 100644 --- a/Dynamic/PlantSimulator/PlantSimulatorInitalizer.cs +++ b/Dynamic/PlantSimulator/PlantSimulatorInitalizer.cs @@ -215,7 +215,7 @@ private bool InitComputationalLoops(Dictionary> compLoopDic } else { - Shared.GetParserObj().AddError("PlantSimulatorInitializer: computational loop init failed"); + // Shared.GetParserObj().AddError("PlantSimulatorInitializer: computational loop init failed"); return false; } modelIdx++; @@ -265,7 +265,7 @@ private bool EstimateDisturbances(ref TimeSeriesDataSet inputData, ref TimeSerie continue; var processId = upstreamModels.First(); - var isOK = simulator.SimulateSingleInternal(inputData, processId, + var isOK = simulator.SimulateSingleWithoutAdditive(inputData, processId, out TimeSeriesDataSet singleSimDataSetWithDisturbance); if (isOK) { diff --git a/TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs b/TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs index d676bc5..c621e55 100644 --- a/TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs +++ b/TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs @@ -68,7 +68,7 @@ public void NonlinearUnitModel() var refData = new UnitDataSet(); refData.U = U; refData.CreateTimeStamps(timeBase_s); - PlantSimulator.SimulateSingle(refData, refModel,true); + PlantSimulator.SimulateSingleToYsim(refData, refModel); // simulate the nonlinear model UnitParameters designParameters = new UnitParameters diff --git a/TimeSeriesAnalysis.Tests/Tests/DisturbanceCalculatorTests.cs b/TimeSeriesAnalysis.Tests/Tests/DisturbanceCalculatorTests.cs new file mode 100644 index 0000000..fdfb518 --- /dev/null +++ b/TimeSeriesAnalysis.Tests/Tests/DisturbanceCalculatorTests.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +using NUnit.Framework; +using TimeSeriesAnalysis.Dynamic; +using TimeSeriesAnalysis; +using TimeSeriesAnalysis.Utility; + +namespace TimeSeriesAnalysis.Test.DisturbanceID +{ + /// + /// In these tests, the UnitModel is given, and the aim is to verify that for a known model the distubance estimator is able + /// to arrive at the correct disturbance time-series. + /// + [TestFixture] + class DisturbanceCalculatorTests + { + UnitParameters staticModelParameters = new UnitParameters + { + TimeConstant_s = 0, + LinearGains = new double[] { 1.5 }, + TimeDelay_s = 0, + Bias = 5 + }; + + UnitParameters dynamicModelParameters = new UnitParameters + { + TimeConstant_s = 10, + LinearGains = new double[] { 1.5 }, + TimeDelay_s = 5, + Bias = 5 + }; + + PidParameters pidParameters1 = new PidParameters() + { + Kp = 0.2, + Ti_s = 20 + }; + + int timeBase_s = 1; + int N = 300; + DateTime t0 = new DateTime(2010,1,1); + + + [SetUp] + public void SetUp() + { + Shared.GetParserObj().EnableDebugOutput(); + } + + public static void CommonPlotAndAsserts(UnitDataSet pidDataSet, double[] d_est, double[] trueDisturbance, double tolPrc =0) + { + Vec vec = new Vec(); + double distTrueAmplitude = vec.Max(vec.Abs(trueDisturbance)); + + Assert.IsTrue(d_est != null); + string caseId = TestContext.CurrentContext.Test.Name.Replace("(", "_"). + Replace(")", "_").Replace(",", "_") + "y"; + if (false) + { + Shared.EnablePlots(); + Plot.FromList(new List{ pidDataSet.Y_meas, pidDataSet.Y_setpoint, + pidDataSet.U.GetColumn(0), + d_est, trueDisturbance }, + new List { "y1=y meas", "y1=y set", "y2=u(right)", "y3=est disturbance", "y3=true disturbance" }, + pidDataSet.GetTimeBase(), caseId); + Shared.DisablePlots(); + } + var errorPrc = vec.Mean(vec.Abs(vec.Subtract(trueDisturbance, d_est))) / vec.Mean(vec.Abs(trueDisturbance)); + + Console.WriteLine("disturbance error prc:" + errorPrc.Value.ToString("F5")); + + Assert.IsTrue(errorPrc< tolPrc/100, "true disturbance and actual disturbance too far apart"); + } + + [TestCase(-5, 0.01)] + [TestCase(5, 0.01)] + public void Static_StepDisturbance_EstimatesOk(double stepAmplitude, double tolPrc) + { + var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); + GenericDisturbanceTest(new UnitModel(staticModelParameters, "StaticProcess"), trueDisturbance, tolPrc); + } + + [TestCase(-5, 0.01)] + [TestCase(5, 0.01)] + public void Static_SinusDisturbance_EstimatesOk(double stepAmplitude,double tolPrc) + { + var sinusPeriod = timeBase_s * 15; + var trueDisturbance = TimeSeriesCreator.Sinus(stepAmplitude, sinusPeriod, timeBase_s,N ); + GenericDisturbanceTest(new UnitModel(staticModelParameters, "StaticProcess"), trueDisturbance, tolPrc); + } + + [TestCase(-5,0.01)] + [TestCase(5, 0.01)] + public void Dynamic_StepDisturbance_EstimatesOk(double stepAmplitude,double tolPrc) + { + var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); + GenericDisturbanceTest(new UnitModel(dynamicModelParameters, "DynamicProcess"), trueDisturbance, tolPrc); + } + + + + public void GenericDisturbanceTest (UnitModel processModel, double[] trueDisturbance, double tolPrc ) + { + bool doAssertResult = true; + // create synthetic dataset + var pidModel1 = new PidModel(pidParameters1, "PID1"); + var plantSim = new PlantSimulator( + new List { pidModel1, processModel }); + plantSim.ConnectModels(processModel, pidModel1); + plantSim.ConnectModels(pidModel1, processModel); + var inputData = new TimeSeriesDataSet(); + + inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Constant(50, N)); + inputData.Add(plantSim.AddExternalSignal(processModel, SignalType.Disturbance_D), trueDisturbance); + inputData.CreateTimestamps(timeBase_s); + var isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData); + Assert.IsTrue(isOk); + Assert.IsTrue(simData.ContainsSignal(processModel.GetID()),"simulated dataset should include internal process model output (pre-disturbance)"); + var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simData), pidModel1); + var result = DisturbanceCalculator.CalculateDisturbanceVector(pidDataSet, processModel); + + + if (doAssertResult) + { + CommonPlotAndAsserts(pidDataSet, result.d_est, trueDisturbance, tolPrc); + } + } + + + + + } +} diff --git a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs index b7274b9..73ff02c 100644 --- a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs @@ -458,7 +458,7 @@ public void TwoGainsConstTc_RampChange_Both_AllParamsEstOk(string solver, double var unitData = new UnitDataSet(); unitData.SetU(input); unitData.CreateTimeStamps(timeBase_s); - (bool isOk, double[] y_meas)= PlantSimulator.SimulateSingleToYmeas(unitData,trueModel, noise_abs); + bool isOk = PlantSimulator.SimulateSingleToYmeas(unitData,trueModel, noise_abs); GainSchedModel idModel = new GainSchedModel(); if (solver == "Identify") @@ -481,7 +481,7 @@ public void TwoGainsConstTc_RampChange_Both_AllParamsEstOk(string solver, double { Shared.EnablePlots(); Plot.FromList(new List { - y_meas, + unitData.Y_meas, unitData.Y_sim, unitData.U.GetColumn(0), }, @@ -606,13 +606,13 @@ public void ChangeOperatingPoint_YsimUnchanged(double origOpU, double origOpY) // make the bias nonzero to test that the operating point estimation works. GainSchedModel trueModel = new GainSchedModel(trueParams, "true"); - PlantSimulator.SimulateSingle(unitData, trueModel,true); + PlantSimulator.SimulateSingleToYsim(unitData, trueModel); var alteredParams = new GainSchedParameters(trueModel.GetModelParameters()); var alteredIdModel = new GainSchedModel(alteredParams,"altered"); alteredIdModel.GetModelParameters().MoveOperatingPointUWithoutChangingModel(3); - (bool isOk3, double[] simY2) = PlantSimulator.SimulateSingle(unitData, alteredIdModel, false); + (bool isOk3, double[] simY2) = PlantSimulator.SimulateSingle(unitData, alteredIdModel); // plot bool doPlot = false; @@ -756,11 +756,11 @@ public void TwoGainsConstTc_StepChange_Both_TCAndThresholdFoundOk(double TimeCon gsFittingSpecs.uTimeConstantThresholds = trueParams.TimeConstantThresholds; idModel = GainSchedIdentifier.IdentifyForGivenThresholds(unitData, gsFittingSpecs); } - (bool isOk2, double[] simY2) = PlantSimulator.SimulateSingle(unitData, idModel, false); + (bool isOk2, double[] simY2) = PlantSimulator.SimulateSingle(unitData, idModel); var alteredIdModel = (GainSchedModel)idModel.Clone("altered"); alteredIdModel.GetModelParameters().MoveOperatingPointUWithoutChangingModel(3); - (bool isOk3, double[] simY3) = PlantSimulator.SimulateSingle(unitData, alteredIdModel, false); + (bool isOk3, double[] simY3) = PlantSimulator.SimulateSingle(unitData, alteredIdModel); // plot bool doPlot = false; diff --git a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs index 553837a..70996ad 100644 --- a/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs @@ -5,7 +5,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; - +using Accord.Statistics.Models.Regression.Fitting; using NUnit.Framework; using TimeSeriesAnalysis.Dynamic; @@ -479,13 +479,14 @@ public void BasicPID_SetpointStep_RunsAndConverges(bool delayPidOutputOneSample) public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDisturbanceEstToWork() { double newSetpoint = 51; + int N = 100; var plantSim = new PlantSimulator( new List { pidModel1, processModel1 }); plantSim.ConnectModels(processModel1, pidModel1); plantSim.ConnectModels(pidModel1, processModel1); var inputData = new TimeSeriesDataSet(); inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), - TimeSeriesCreator.Step(N / 4, N, Ysetpoint, newSetpoint)); + TimeSeriesCreator.Step(1, N, Ysetpoint, newSetpoint)); inputData.CreateTimestamps(timeBase_s); bool isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData); @@ -496,7 +497,7 @@ public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDistu newSet.AddSet(simData); newSet.SetTimeStamps(inputData.GetTimeStamps().ToList()); - // var isOk2 = PlantSimulator.SimulateSingle(newSet, pidModel1, out var simData2); + // var isOk2 = PlantSimulator.SimulateSingle(newSet, pidModel1, out var simData2); var isOK2 = plantSim.SimulateSingle(newSet, pidModel1.ID,out TimeSeriesDataSet simData2); if (true) @@ -507,7 +508,7 @@ public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDistu simData.GetValues(pidModel1.GetID(),SignalType.PID_U), simData2.GetValues(pidModel1.GetID(),SignalType.PID_U), inputData.GetValues(pidModel1.GetID(),SignalType.Setpoint_Yset)}, - new List { "y1=processOutSimulate", "y3=upidSimulate", "y3=upidSimulateSingle", "y2=setpoint" }, + new List { "y1=processOutSimulate", "y3=upidSim", "y3=upidSimSingle", "y2=setpoint" }, timeBase_s, TestContext.CurrentContext.Test.Name); Shared.DisablePlots(); } @@ -516,8 +517,15 @@ public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDistu Assert.IsTrue(isOk); Assert.IsTrue(firstYsimE < 0.01, "System should start in steady-state"); - Assert.IsTrue(lastYsimE < 0.01, "PID should bring system to setpoint after disturbance"); BasicPIDCommonTests(simData,pidModel1); + + var vec = new Vec(); + var v1 = simData.GetValues(pidModel1.GetID(), SignalType.PID_U); + var v2 = simData2.GetValues(pidModel1.GetID(), SignalType.PID_U); + var errorPrc = vec.Mean(vec.Abs(vec.Subtract(v1, v2) )) / vec.Mean(vec.Abs(v2)); + Console.WriteLine("error prc:" + errorPrc.Value.ToString("F5")); + Assert.IsTrue(errorPrc < 0.001 / 100, "true disturbance and actual disturbance too far apart"); + } [TestCase] diff --git a/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSingleTests.cs similarity index 65% rename from TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs rename to TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSingleTests.cs index b57eec1..7594549 100644 --- a/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSingleTests.cs @@ -1,23 +1,17 @@ -using System; +using NUnit.Framework; +using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; - -using NUnit.Framework; +using System.Threading.Tasks; using TimeSeriesAnalysis.Dynamic; -using TimeSeriesAnalysis; using TimeSeriesAnalysis.Utility; namespace TimeSeriesAnalysis.Test.DisturbanceID { - /// - /// In these tests, the UnitModel is given, and the aim is to verify that for a known model the distubance estimator is able - /// to arrive at the correct disturbance time-series. - /// - [TestFixture] - class FindDisturbanceWhenProcessModelIsGiven + internal class PlantSimulatorSingleTests { + UnitParameters staticModelParameters = new UnitParameters { TimeConstant_s = 0, @@ -42,70 +36,10 @@ class FindDisturbanceWhenProcessModelIsGiven int timeBase_s = 1; int N = 300; - DateTime t0 = new DateTime(2010,1,1); - - - [SetUp] - public void SetUp() - { - Shared.GetParserObj().EnableDebugOutput(); - } - - public void CommonPlotAndAsserts(UnitDataSet pidDataSet, double[] d_est, double[] trueDisturbance) - { - Vec vec = new Vec(); - double distTrueAmplitude = vec.Max(vec.Abs(trueDisturbance)); - - Assert.IsTrue(d_est != null); - string caseId = TestContext.CurrentContext.Test.Name.Replace("(", "_"). - Replace(")", "_").Replace(",", "_") + "y"; - if (false) - { - Shared.EnablePlots(); - Plot.FromList(new List{ pidDataSet.Y_meas, pidDataSet.Y_setpoint, - pidDataSet.U.GetColumn(0), - d_est, trueDisturbance }, - new List { "y1=y meas", "y1=y set", "y2=u(right)", "y3=est disturbance", "y3=true disturbance" }, - pidDataSet.GetTimeBase(), caseId); - Shared.DisablePlots(); - } - Assert.IsTrue(vec.Mean(vec.Abs(vec.Subtract(trueDisturbance, d_est))) < distTrueAmplitude / 10,"true disturbance and actual disturbance too far apart"); - } - - [TestCase(-5)] - [TestCase(5)] - public void Static_StepDisturbance_EstimatesOk(double stepAmplitude) - { - var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); - GenericDisturbanceTest(new UnitModel(staticModelParameters, "StaticProcess"), trueDisturbance); - } - - [TestCase(-5)] - [TestCase(5)] - public void Static_SinusDisturbance_EstimatesOk(double stepAmplitude) - { - var sinusPeriod = timeBase_s * 15; - var trueDisturbance = TimeSeriesCreator.Sinus(stepAmplitude, sinusPeriod, timeBase_s,N ); - GenericDisturbanceTest(new UnitModel(staticModelParameters, "StaticProcess"), trueDisturbance); - } - - [TestCase(-5)] - [TestCase(5)] - public void Dynamic_StepDisturbance_EstimatesOk(double stepAmplitude) - { - var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); - GenericDisturbanceTest(new UnitModel(dynamicModelParameters, "DynamicProcess"), trueDisturbance); - } - [TestCase(-4)] - [TestCase(4)] - public void PlantSimulatorSingle_StepDisturbance_EstimatesOk(double stepAmplitude) - { - var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); - DisturbanceTestUsingPlantSimulateSingle(new UnitModel(dynamicModelParameters, "PlantSim_d"), trueDisturbance); - } + DateTime t0 = new DateTime(2010, 1, 1); // an extension of the above test to use the more general PlantSimulator.Simulate, rather than the PlantSimulator.SimulateSingle - [TestCase(4)] + [TestCase(4),Explicit] public void PlantSimulator_StepDisturbance_EstimatesOk(double stepAmplitude) { var locModelParameters = new UnitParameters @@ -120,48 +54,29 @@ public void PlantSimulator_StepDisturbance_EstimatesOk(double stepAmplitude) DisturbanceTestUsingPlantSimulator(locModelParameters, trueDisturbance); } - [TestCase(4,1)] - [TestCase(4,-1)] - public void PlantSimulator_StepDisturbanceANDSetPointStep_EstimatesOk(double disturbanceStepAmplitude,double setpointAmplitude) + [TestCase(4, 1),Explicit] + [TestCase(4, -1)] + public void PlantSimulator_StepDisturbanceANDSetPointStep_EstimatesOk(double disturbanceStepAmplitude, double setpointAmplitude) { - // Shared.EnablePlots(); + // Shared.EnablePlots(); var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, disturbanceStepAmplitude); - var setpoint = TimeSeriesCreator.Step(50, N, 50, 50+setpointAmplitude); + var setpoint = TimeSeriesCreator.Step(50, N, 50, 50 + setpointAmplitude); DisturbanceTestUsingPlantSimulator(dynamicModelParameters, trueDisturbance, true, setpoint); //Shared.DisablePlots(); } - - public void GenericDisturbanceTest (UnitModel processModel, double[] trueDisturbance, - bool doAssertResult=true) + [TestCase(-4), Explicit] + [TestCase(4)] + public void PlantSimulatorSingle_StepDisturbance_EstimatesOk(double stepAmplitude) { - // create synthetic dataset - var pidModel1 = new PidModel(pidParameters1, "PID1"); - var plantSim = new PlantSimulator( - new List { pidModel1, processModel }); - plantSim.ConnectModels(processModel, pidModel1); - plantSim.ConnectModels(pidModel1, processModel); - var inputData = new TimeSeriesDataSet(); - - inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Constant(50, N)); - inputData.Add(plantSim.AddExternalSignal(processModel, SignalType.Disturbance_D), trueDisturbance); - inputData.CreateTimestamps(timeBase_s); - var isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData); - Assert.IsTrue(isOk); - Assert.IsTrue(simData.ContainsSignal(processModel.GetID()),"simulated dataset should include internal process model output (pre-disturbance)"); - var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simData), pidModel1); - var result = DisturbanceCalculator.CalculateDisturbanceVector(pidDataSet, processModel); - - - if (doAssertResult) - { - CommonPlotAndAsserts(pidDataSet, result.d_est, trueDisturbance); - } + var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude); + DisturbanceTestUsingPlantSimulateSingle(new UnitModel(dynamicModelParameters, "PlantSim_d"), trueDisturbance); } + public void DisturbanceTestUsingPlantSimulator(UnitParameters unitParams, double[] trueDisturbance, - bool doAssertResult = true, double[] externalYset=null) + bool doAssertResult = true, double[] externalYset = null) { TimeSeriesDataSet referenceSimDataSet; TimeSeriesDataSet referenceInputDataSet; @@ -185,9 +100,9 @@ public void DisturbanceTestUsingPlantSimulator(UnitParameters unitParams, double referenceInputDataSet.Add(refYsetSignal, externalYset); referenceInputDataSet.Add(refDistSignal, trueDisturbance); referenceInputDataSet.CreateTimestamps(timeBase_s); - + var simOk = plantSim.Simulate(referenceInputDataSet, out referenceSimDataSet); - Assert.IsTrue(simOk,"simulating reference case failed!"); + Assert.IsTrue(simOk, "simulating reference case failed!"); } // 2.create plant model without disturbance, and try to to find the disturbance signal { @@ -204,9 +119,9 @@ public void DisturbanceTestUsingPlantSimulator(UnitParameters unitParams, double // signals can really be named anything, but important for this to work that the names are the same // in the model objects and in the inputData object - var ysetSignal = SignalNamer.GetSignalName(pidModel1.GetID(), SignalType.Setpoint_Yset); + var ysetSignal = SignalNamer.GetSignalName(pidModel1.GetID(), SignalType.Setpoint_Yset); plantSim.AddAndConnectExternalSignal(pidModel1, ysetSignal, SignalType.Setpoint_Yset); - var ymeasSignal = processModel3.GetOutputID() ; + var ymeasSignal = processModel3.GetOutputID(); plantSim.AddAndConnectExternalSignal(processModel3, ymeasSignal, SignalType.Output_Y); var uMeasSignal = SignalNamer.GetSignalName(pidModel1.GetID(), SignalType.PID_U); ; plantSim.AddAndConnectExternalSignal(pidModel1, uMeasSignal, SignalType.PID_U); @@ -221,7 +136,7 @@ public void DisturbanceTestUsingPlantSimulator(UnitParameters unitParams, double inputData.Add(uMeasSignal, referenceSimDataSet.GetValues("PID1", SignalType.PID_U)); inputData.Add(ymeasSignal, referenceSimDataSet.GetValues("Proc1", SignalType.Output_Y)); ///////////////// - Assert.IsTrue(inputData.GetSignalNames().Count() == 3,"configuration errors" );//sanity check for configuration errors + Assert.IsTrue(inputData.GetSignalNames().Count() == 3, "configuration errors");//sanity check for configuration errors inputData.CreateTimestamps(timeBase_s); ////////////////////////////////// @@ -231,10 +146,10 @@ public void DisturbanceTestUsingPlantSimulator(UnitParameters unitParams, double if (doAssertResult) { var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simDataSetWithDisturbance), pidModel1); - CommonPlotAndAsserts(pidDataSet, simDataSetWithDisturbance.GetValues(distSignal), + DisturbanceCalculatorTests.CommonPlotAndAsserts(pidDataSet, simDataSetWithDisturbance.GetValues(distSignal), trueDisturbance); } - Assert.IsTrue(isOK, "simulate dataset with the disturbane FAILED!!"); + Assert.IsTrue(isOK, "simulate dataset with the disturbance FAILED!!"); Assert.IsTrue(plantSim.PlantFitScore == 100, "expect plant fit score 100"); Assert.IsTrue(simDataSetWithDisturbance.ContainsSignal(distSignal), "simulated dataset does not contain the disturbance signal"); } @@ -246,7 +161,7 @@ public void DisturbanceTestUsingPlantSimulateSingle(UnitModel processModel, doub TimeSeriesDataSet referenceSimDataSet; TimeSeriesDataSet referenceInputDataSet; // 1 .create synthetic dataset - where disturbance is specified - { + { var pidModel1 = new PidModel(pidParameters1, "PID1"); var plantSim = new PlantSimulator( new List { pidModel1, processModel }); @@ -281,10 +196,10 @@ public void DisturbanceTestUsingPlantSimulateSingle(UnitModel processModel, doub referenceSimDataSet.GetValues(processModel.ID, SignalType.Output_Y)); ///////////////// inputData.CreateTimestamps(timeBase_s); - var isOK = plantSim.SimulateSingle(inputData, processModel.ID, + var isOK = plantSim.SimulateSingle(inputData, processModel.ID, out TimeSeriesDataSet simDataSetWithDisturbance); - // var isOK = PlantSimulator.SimulateSingle(inputData, processModel, - // out TimeSeriesDataSet simDataSetWithDisturbance); + // var isOK = PlantSimulator.SimulateSingle(inputData, processModel, + // out TimeSeriesDataSet simDataSetWithDisturbance); Assert.IsTrue(isOK); Assert.IsTrue(simDataSetWithDisturbance.ContainsSignal(SignalNamer.EstDisturbance(processModel))); @@ -293,13 +208,11 @@ public void DisturbanceTestUsingPlantSimulateSingle(UnitModel processModel, doub if (doAssertResult) { var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simDataSetWithDisturbance), pidModel1); - CommonPlotAndAsserts(pidDataSet, simDataSetWithDisturbance.GetValues(SignalNamer.EstDisturbance(processModel)), + DisturbanceCalculatorTests.CommonPlotAndAsserts(pidDataSet, simDataSetWithDisturbance.GetValues(SignalNamer.EstDisturbance(processModel)), trueDisturbance); } } } - - } } diff --git a/TimeSeriesAnalysis.Tests/Tests/SysIDUnitTests.cs b/TimeSeriesAnalysis.Tests/Tests/SysIDUnitTests.cs index 128cf7b..4efb72a 100644 --- a/TimeSeriesAnalysis.Tests/Tests/SysIDUnitTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/SysIDUnitTests.cs @@ -37,7 +37,7 @@ public void UnitSimulate_TimeDelay(int timeDelay_s) dataSet.U = Array2D.CreateFromList(new List { u1 }); dataSet.CreateTimeStamps(timeBase_s); - (bool isOk,double[] y_sim) = PlantSimulator.SimulateSingle(dataSet, model,false); + (bool isOk,double[] y_sim) = PlantSimulator.SimulateSingle(dataSet, model); // plot bool doPlot = false; if (doPlot)