Skip to content

Commit

Permalink
- PlantSimulator.SimulateSingle that uses PlantSimulator.Simulate.
Browse files Browse the repository at this point in the history
  • Loading branch information
Steinar Elgsæter committed Dec 18, 2024
1 parent ff9db76 commit 2ec1aa8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 6 deletions.
15 changes: 15 additions & 0 deletions Dynamic/PlantSimulator/PlantSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,21 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName,
return SimulateSingleInternalCore(inputData, singleModelName, false, out simData);
}

/// <summary>
/// Simulate a single model to get the output including any additive inputs.
/// </summary>
/// <param name="inputData"></param>
/// <param name="model"></param>
/// <param name="simData"></param>
/// <returns></returns>
public static bool SimulateSingle(TimeSeriesDataSet inputData, ISimulatableModel model, out TimeSeriesDataSet simData)
{
var plant = new PlantSimulator(new List<ISimulatableModel> { model });

return plant.Simulate(inputData, out simData );
}


/// <summary>
/// Simulates a single model for a unit dataset and adds the output to unitData.Y_meas of the unitData, optionally with noise
/// </summary>
Expand Down
13 changes: 9 additions & 4 deletions TimeSeriesAnalysis.Tests/Tests/PlantSimulatorSISOTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ public void BasicPID_SetpointStep_RunsAndConverges(bool delayPidOutputOneSample)
[TestCase]
public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDisturbanceEstToWork()
{
//var pidCopy = pidModel1.Clone();
double newSetpoint = 51;
int N = 100;
var plantSim = new PlantSimulator(
Expand All @@ -492,13 +493,17 @@ public void BasicPID_CompareSimulateAndSimulateSingle_MustGiveSameResultForDistu

var newSet = new TimeSeriesDataSet();
newSet.AddSet(inputData);
// var outputSignalName = SignalNamer.GetSignalName(processModel1.GetID(), SignalType.Output_Y);
// newSet.Add(outputSignalName, simData.GetValues(outputSignalName));
newSet.AddSet(simData);
newSet.SetTimeStamps(inputData.GetTimeStamps().ToList());

// var isOk2 = PlantSimulator.SimulateSingle(newSet, pidModel1, out var simData2);
var isOK2 = plantSim.SimulateSingle(newSet, pidModel1.ID,out TimeSeriesDataSet simData2);
// slight deviation between SimulateSingle and Simulate regardless of which version is used:
//v1
// var isOK2 = plantSim.SimulateSingle(newSet, pidModel1.ID,out TimeSeriesDataSet simData2);
//v2
// pidCopy.SetInputIDs(pidModel1.GetModelInputIDs());
// var isOk2 = PlantSimulator.SimulateSingle(newSet, pidCopy, out var simData2);
//v3
var isOk2 = PlantSimulator.SimulateSingle(newSet, pidModel1, out var simData2);

if (true)
{
Expand Down
27 changes: 25 additions & 2 deletions docs/articles/processsimulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,36 @@ Each model has a number of inputs that travel through the model, each with an id
In addition, models support adding signals directly to the output. This is a feature intended for modeling disturbances.
The IDs of such signals are referred to as *additive input IDs*.

### Estimating disturbances and simulating their effects using PlantSimulator.SimulateSingle()
### Estimating disturbances and simulating their effects

If the ``inputData`` given to the PlantSimulator includes measured pid-outputs ``u`` and process outputs ``y`` of feedback loops,
then PlantSimulator uses that
``y_meas = y_proc+d``
``y_meas[k] = y_proc[k-1]+d[k]``
calculate the disturbance vector ``d`` and this disturbance is then simulated as the ``driving force`` of closed loop dynamics.

Thus when the process model is known and inputs to the model are given, the method ``PlantSimulator.SimulateSingle()`` is used to
simulate the disturbance vector as part of the initialization of ``PlantSimulator.Simulate()``

### Closed loops : simulation order and disturbance

In a closed loop the simulation order will be

- *PidModel* reads ``y_meas[k]`` and ``y_setpoint[k]`` and calculates ``u_pid[k]``
- the process model (usally a *UnitModel*) gives ``u_pid[k]`` and any other inputs ``u[k]`` and outputs an internal state ``y_proc[k]``
- In the next iteration y_meas[k+1] = y_proc[k] + d[k+1]

This is by convention, and is how the simulator avoids both reading to ymeas[k] and writing to ymeas[k] in the same iteration.

This above means that implicitly all closed-loop processes in a manner of speaking have a time delay of 1.

It is somewhat challenging to define this behavior so that it is consistent when a model is for instance open-loop tested, then a pid-control is applied.
A static process by definition has ``y[k] = f(u[k])`` but in a closed loop that could mean that the measurement y[k] that is used to determine u_pid[k]
is again overwritten by the output of astatic process.

It coudl also be possible to deal with this by stating that PID-controllers by convention deal with the signals of the previous step, but so that
u_pid[k] = f(y_set[k], y_meas[k-1]), but this does not match real-world industrial data, it introduces a lag. It may be that in some cases pid-controllers
are implemented like this, but often the analysis is done on down-sampled data, and in that case ``u_pid[k]`` appears simultanous to changes in ``y_meas[k]``

**Implicitly the above also defines how to interpret the disturbance d[k].** To be extremely precise with how this is defined is important, as the PlantSimulator is
used internally to back-calculte disturbances as is described in the above section, and how the distrubance is calcualted will again be important as both single simulations and co-simulations
are used by ClosedLoopUnitIdentifier to identify the process model including possibly time constants and time-delays.

0 comments on commit 2ec1aa8

Please sign in to comment.