diff --git a/Dynamic/PlantSimulator/PlantSimulator.cs b/Dynamic/PlantSimulator/PlantSimulator.cs index 5aafc58..0d33c79 100644 --- a/Dynamic/PlantSimulator/PlantSimulator.cs +++ b/Dynamic/PlantSimulator/PlantSimulator.cs @@ -304,8 +304,10 @@ private Dictionary DeterminePidControlledOutputs() modelThatCreatesOutputID = otherModel.Value.GetID(); } } - // if(modelThatCreatesOutputID != null) - ret.Add(model.Value.GetModelInputIDs().ElementAt((int)PidModelInputsIdx.Y_meas), modelThatCreatesOutputID); + var key = model.Value.GetModelInputIDs().ElementAt((int)PidModelInputsIdx.Y_meas); + // note that in the case of a select, the key may already be present in the dictionary + if (!ret.ContainsKey(key)) + ret.Add(key, modelThatCreatesOutputID); } } return ret; diff --git a/TimeSeriesAnalysis.Tests/Test/PlantSimulations/AdvancedControlExamples.cs b/TimeSeriesAnalysis.Tests/Test/PlantSimulations/AdvancedControlExamples.cs index 1f4947e..8849487 100644 --- a/TimeSeriesAnalysis.Tests/Test/PlantSimulations/AdvancedControlExamples.cs +++ b/TimeSeriesAnalysis.Tests/Test/PlantSimulations/AdvancedControlExamples.cs @@ -25,62 +25,54 @@ class AdvancedControlExamples [Test] public void CascadeControl() { - // Shared.DisablePlots(); // Shared.EnablePlots(); ProcessControl pc = new ProcessControl(); var dataSet = pc.CascadeControl(); Shared.DisablePlots(); - // Shared.EnablePlots(); + // Shared.DisablePlots(); } [Test] public void FeedForwardControl_Part1() { - // Shared.EnablePlots(); // Shared.EnablePlots(); ProcessControl pc = new ProcessControl(); var dataSet = pc.FeedForward_Part1(); // Shared.DisablePlots(); Assert.IsTrue(dataSet.GetValue("Process1-Output_Y", 599) -60< 0.01); - - // Shared.EnablePlots(); } [Test] public void FeedForwardControl_Part2() { - Shared.DisablePlots(); - + // Shared.EnablePlots(); ProcessControl pc = new ProcessControl(); var dataSet = pc.FeedForward_Part2(); - - Shared.EnablePlots(); + // Shared.DisablePlots(); } [Test] public void GainScheduling() { - Shared.DisablePlots(); - + // Shared.EnablePlots(); ProcessControl pc = new ProcessControl(); var dataSet = pc.GainScheduling(); - - Shared.EnablePlots(); + // Shared.DisablePlots(); } [Test] public void MinSelect() { - Shared.DisablePlots(); - ProcessControl pc = new ProcessControl(); - var dataSet = pc.MinSelect(); - // dataSet.SetT0(new DateTime(2021,1,1)); - // var isOk = dataSet.ToCSV(@"C:\Appl\source\TimeSeriesAnalysis\minSelect_large.csv"); - // Assert.IsTrue(isOk); - // var dataSet2 = new TimeSeriesDataSet(@"C:\Appl\source\TimeSeriesAnalysis\minSelect.csv"); Shared.EnablePlots(); + var dataSet = pc.MinSelect(); + Shared.DisablePlots(); + // dataSet.SetT0(new DateTime(2021,1,1)); + // var isOk = dataSet.ToCSV(@"C:\Appl\source\TimeSeriesAnalysis\minSelect_large.csv"); + // Assert.IsTrue(isOk); + // var dataSet2 = new TimeSeriesDataSet(@"C:\Appl\source\TimeSeriesAnalysis\minSelect.csv"); + } } } diff --git a/TimeSeriesAnalysis.Tests/Test/PlantSimulations/LargerSystemSimulations.cs b/TimeSeriesAnalysis.Tests/Test/PlantSimulations/LargerSystemSimulations.cs index 5cde022..84945ba 100644 --- a/TimeSeriesAnalysis.Tests/Test/PlantSimulations/LargerSystemSimulations.cs +++ b/TimeSeriesAnalysis.Tests/Test/PlantSimulations/LargerSystemSimulations.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using NUnit.Framework.Interfaces; +using System.Diagnostics; using System.Runtime.ConstrainedExecution; using TimeSeriesAnalysis.Dynamic; using TimeSeriesAnalysis.Utility; @@ -93,7 +94,7 @@ public void MinSelectWithPID_RunsAndConverges() var plantSim = new PlantSimulator( new List { processModel1, processModel2, minSelect1, pidModel1 }); var inputData = new TimeSeriesDataSet(); - inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Step(N/4, N, 0, 1)); + inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Step(N/4, N, 5.5, 6)); inputData.Add(plantSim.AddExternalSignal(processModel1, SignalType.External_U, (int)INDEX.SECOND), TimeSeriesCreator.Step(N*3/4, N, 0, 1)); inputData.Add(plantSim.AddExternalSignal(processModel2, SignalType.External_U, (int)INDEX.FIRST), @@ -108,13 +109,35 @@ public void MinSelectWithPID_RunsAndConverges() plantSim.ConnectModels(processModel1, minSelect1, (int)INDEX.FIRST); plantSim.ConnectModels(processModel2, minSelect1, (int)INDEX.SECOND); - var isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData); + + if (false) + { + Shared.EnablePlots(); + Plot.FromList(new List + { + simData.GetValues(processModel1.GetID(),SignalType.Output_Y), + simData.GetValues(processModel2.GetID(),SignalType.Output_Y), + inputData.GetValues(pidModel1.GetID(),SignalType.Setpoint_Yset), + inputData.GetValues(processModel1.GetID(),SignalType.External_U,(int)INDEX.SECOND), + inputData.GetValues(processModel2.GetID(),SignalType.External_U,(int)INDEX.FIRST), + inputData.GetValues(processModel2.GetID(),SignalType.External_U,(int)INDEX.SECOND), + simData.GetValues(pidModel1.GetID(),SignalType.PID_U), + simData.GetValues(minSelect1.GetID(),SignalType.SelectorOut), + }, + new List { "y1=y1", "y1=y2", "y1=y1_set","y3=y1_u1","y3=y2_u1","y3=y1_u1", + "y3=u_pid", "y1=y_select" }, timeBase_s, "MinSelectWithPID"); + Shared.DisablePlots(); + } + + + Assert.IsTrue(isOk); PsTest.CommonAsserts(inputData, simData, plantSim); double[] simY = simData.GetValues(minSelect1.GetID(), SignalType.SelectorOut); - SerializeHelper.Serialize("MinSelectWithPID", plantSim, inputData, simData); + + //SerializeHelper.Serialize("MinSelectWithPID", plantSim, inputData, simData); //Assert.IsTrue(Math.Abs(simY[0] - (6.5)) < 0.01); //Assert.IsTrue(Math.Abs(simY.Last() - (6.5)) < 0.01); diff --git a/docs/articles/processsimulator.md b/docs/articles/processsimulator.md index bbed61d..13930e0 100644 --- a/docs/articles/processsimulator.md +++ b/docs/articles/processsimulator.md @@ -93,8 +93,18 @@ are used by ClosedLoopUnitIdentifier to identify the process model including pos +### Computational loops other than PID-feedback loops +The PlantSimulator can deal with computational loops other than PID-feedback loops. These are initalized to steady-state by co-simulating the loop for a number of iterations until the outputs hopefully settle on a steady value. + + + +### PlantSimulatorHelper + +The class PlantSimulatorHelper gives some convenience methods that make it easier to do common types of simulations: +- +``SimulateSingle()`` is useful for quickly simulating any ``ISimulateableModel``. +- methods that allow calling the PlantSimulator with data in ``UnitData`` datasets rather than the more general ``TimeSeriesDataSet``. +- methods that return a PlantSimulator object with a standard feedback loop. -### Computational loops other than PID-feedback loops -The PlantSimulator can deal with computational loops other than PID-feedback loops. These are initalized to steady-state by co-simulating the loop for a number of iterations until the outputs hopefully settle on a steady value. \ No newline at end of file