diff --git a/.gitignore b/.gitignore index acecd4d..cbf87d0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ TestResult.xml profiler/ docs/_site/ docs/api/ +TimeSeriesAnalysis.ClosedTests/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..67c2240 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "FSharp.suggestGitignore": false +} \ No newline at end of file diff --git a/Dynamic/Identification/FitScoreCalculator.cs b/Dynamic/Identification/FitScoreCalculator.cs index 15c1d77..c537fa1 100644 --- a/Dynamic/Identification/FitScoreCalculator.cs +++ b/Dynamic/Identification/FitScoreCalculator.cs @@ -55,7 +55,7 @@ public static double Calc(double[] meas, double[] sim) static public double GetPlantWideSimulated(PlantSimulator plantSimObj, TimeSeriesDataSet inputData, TimeSeriesDataSet simData) { - // const string disturbanceSignalPrefix = "_D"; + const string disturbanceSignalPrefix = "_D"; List fitScores = new List(); foreach (var modelName in plantSimObj.modelDict.Keys) { @@ -68,17 +68,13 @@ static public double GetPlantWideSimulated(PlantSimulator plantSimObj, TimeSerie if (outputName == "" || outputName == null) continue; - - // - // Step 1: get the output signals of individual models in the measured dataset - // - if (plantSimObj.modelDict[modelName].GetProcessModelType() == ModelType.PID) { if (inputData.ContainsSignal(outputName)) { measY = inputData.GetValues(outputName); } + } else //if (plantSimObj.modelDict[modelName].GetProcessModelType() == ModelType.SubProcess) { @@ -88,29 +84,25 @@ static public double GetPlantWideSimulated(PlantSimulator plantSimObj, TimeSerie { measY = inputData.GetValues(outputIdentName); } - /* else if (simData.ContainsSignal(outputIdentName)) + else if (simData.ContainsSignal(outputIdentName)) { measY = simData.GetValues(outputIdentName); - }*/ + } } // add in fit of process output, but only if "additive output signal" is not a // locally identified "_D_" signal, as then output matches 100% always (any model error is put into disturbance signal as well) - /* else if (modelObj.GetAdditiveInputIDs() != null) + else if (modelObj.GetAdditiveInputIDs() != null) { if (!modelObj.GetAdditiveInputIDs()[0].StartsWith(disturbanceSignalPrefix)) { measY = inputData.GetValues(outputName); } - }*/ + } else if (inputData.ContainsSignal(outputName)) { measY = inputData.GetValues(outputName); } } - - // - // Step 2: get the corresponding signal from the simulated dataset - // if (simData.ContainsSignal(outputName)) { simY = simData.GetValues(outputName); diff --git a/Dynamic/Identification/GainSchedFittingSpecs.cs b/Dynamic/Identification/GainSchedFittingSpecs.cs index c65f12c..2f12cbb 100644 --- a/Dynamic/Identification/GainSchedFittingSpecs.cs +++ b/Dynamic/Identification/GainSchedFittingSpecs.cs @@ -29,14 +29,5 @@ public GainSchedFittingSpecs() /// Default is 0, i.e. first input is used for gain-scheduling. /// public int uGainScheduledInputIndex = 0; - - /// - /// If set to false, the model starts in the same value as the tuning set starts in, - /// if set to true, the model passes through the mean of the dataset. - /// - public bool DoSetOperatingPointToDatasetMean = false; - - - } } diff --git a/Dynamic/Identification/GainSchedIdentifier.cs b/Dynamic/Identification/GainSchedIdentifier.cs index 98c6f32..4e56507 100644 --- a/Dynamic/Identification/GainSchedIdentifier.cs +++ b/Dynamic/Identification/GainSchedIdentifier.cs @@ -329,7 +329,7 @@ public static GainSchedModel IdentifyForGivenThresholds(UnitDataSet dataSet, Gai if (idParams.Fitting.WasAbleToIdentify) { // simulate the model and determine the optimal bias term: - DetermineOperatingPointAndSimulate(ref idParams, ref dataSet, gsFittingSpecs.DoSetOperatingPointToDatasetMean); + DetermineOperatingPointAndSimulate(ref idParams, ref dataSet); if (doTimeDelayEstimation) EstimateTimeDelay(ref idParams, ref dataSet); @@ -493,12 +493,12 @@ static private (GainSchedParameters, int) ChooseBestModelFromSimulationList(List /// /// the gain-scheduled parameters that are to be updated with an operating point. /// tuning dataset to be updated with Y_sim - /// set the operating point to be the centre of the tuning set, if set to false models is set to match the start of the dataset - /// /// true if able to estiamte bias, otherwise false - private static bool DetermineOperatingPointAndSimulate(ref GainSchedParameters gsParams, ref UnitDataSet dataSet, bool doMeanU) + private static bool DetermineOperatingPointAndSimulate(ref GainSchedParameters gsParams, ref UnitDataSet dataSet) { // if set to true, then the operating point is set to the average U in the dataset, otherwise it is set to equal the start + const bool doMeanU = false; // can be true or false, makes little difference? + var gsIdentModel = new GainSchedModel(gsParams, "ident_model"); var vec = new Vec(dataSet.BadDataID); @@ -508,7 +508,7 @@ private static bool DetermineOperatingPointAndSimulate(ref GainSchedParameters g var val = vec.Mean(vec.GetValues(dataSet.U.GetColumn(gsParams.GainSchedParameterIndex), dataSet.IndicesToIgnore)); if (val.HasValue && doMeanU) { - desiredOpU = val.Value; //dataSet.U.GetColumn(gsParams.GainSchedParameterIndex).First(); + desiredOpU = dataSet.U.GetColumn(gsParams.GainSchedParameterIndex).First(); } gsParams.MoveOperatingPointUWithoutChangingModel(desiredOpU); @@ -768,7 +768,7 @@ private static GainSchedParameters EvaluateMultipleTimeConstantsForGivenGainThre { curGainSchedParams_separateTc.TimeConstant_s = curTimeConstants; curGainSchedParams_separateTc.TimeConstantThresholds = new double[] { candidateTcThresholds[i] }; - DetermineOperatingPointAndSimulate(ref curGainSchedParams_separateTc, ref DS_separateTc,false); + DetermineOperatingPointAndSimulate(ref curGainSchedParams_separateTc, ref DS_separateTc); candModelsStep2.Add(new GainSchedParameters(curGainSchedParams_separateTc)); // candYsimStep2.Add((double[])DS_separateTc.Y_sim.Clone()); } @@ -776,9 +776,27 @@ private static GainSchedParameters EvaluateMultipleTimeConstantsForGivenGainThre { curGainSchedParams_separateTc.TimeConstant_s = new double[] { curTimeConstants.First() }; curGainSchedParams_separateTc.TimeConstantThresholds = null; - DetermineOperatingPointAndSimulate(ref curGainSchedParams_separateTc, ref DS_separateTc,false); + DetermineOperatingPointAndSimulate(ref curGainSchedParams_separateTc, ref DS_separateTc); candModelsStep2.Add(new GainSchedParameters(curGainSchedParams_separateTc)); } +/* + { + var curGainSchedParams_commonTc1 = new GainSchedParameters(curGainSchedParams_separateTc); + curGainSchedParams_commonTc1.TimeConstant_s = new double[] { curTimeConstants[0] }; + curGainSchedParams_commonTc1.TimeConstantThresholds = null; + DetermineOperatingPointAndSimulate(ref curGainSchedParams_commonTc1, ref DS_commonTc1); + candModelsStep2.Add(new GainSchedParameters(curGainSchedParams_commonTc1)); + // candYsimStep2.Add((double[])DS_commonTc1.Y_sim.Clone()); + } + { + var curGainSchedParams_commonTc2 = new GainSchedParameters(curGainSchedParams_separateTc); + curGainSchedParams_commonTc2.TimeConstant_s = new double[] { curTimeConstants[1] }; + curGainSchedParams_commonTc2.TimeConstantThresholds = null; + DetermineOperatingPointAndSimulate(ref curGainSchedParams_commonTc2, ref DS_commonTc2); + candModelsStep2.Add(new GainSchedParameters(curGainSchedParams_commonTc2)); + // candYsimStep2.Add((double[])DS_commonTc2.Y_sim.Clone()); + } +*/ bool doDebugPlot = false; if (doDebugPlot) diff --git a/Dynamic/Identification/UnitIdentifier.cs b/Dynamic/Identification/UnitIdentifier.cs index dfd57da..54e02cb 100644 --- a/Dynamic/Identification/UnitIdentifier.cs +++ b/Dynamic/Identification/UnitIdentifier.cs @@ -18,39 +18,39 @@ namespace TimeSeriesAnalysis.Dynamic { /// /// Identifier of the "Default" process model - a dynamic process model with time-constant, time-delay, - /// linear process gain and optional (nonlinear)curvature process gains. + /// linear process gain and optional (nonlinear) curvature process gains. /// /// This model class is sufficent for real-world linear or weakly nonlinear dynamic systems, yet also introduces the fewest possible - /// parameters to describe the system in an attempt to avoiding over-fitting/over-parametrization + /// parameters to describe the system in an attempt to avoid over-fitting/over-parameterization. /// /// - /// The "default" process model is identified using a linear-in-parameters paramterization(paramters a,b,c), so that it can be solved by linear regression - /// and identification should thus be both fast and stable. The issue with the parametriation(a,b,c) is that the meaning of each paramter is less - /// inutitive, for instance the time constant depends on a, but linear gain depends on both a and b, while curvature depends on a and c. + /// The "default" process model is identified using a linear-in-parameters parameterization (parameters a,b,c), so that it can be solved by linear regression + /// and identification should thus be both fast and stable. The issue with the parameterization (a,b,c) is that the meaning of each parameter is less + /// intuitive, for instance the time constant depends on a, but linear gain depends on both a and b, while curvature depends on a and c. /// Looking at the unceratinty of each parameter to determine if the model should be dynamic or static or what the uncertainty of the time constant is, - /// is very hard, and this observation motivates re-paramtrizing the model after identification. + /// is very hard, and this observation motivates re-parameterizing the model after identification. /// /// - /// When assessing and simulating the model, parmaters are converted into more intuitive paramters "time constant", "linear gains" and "curvature gain" - /// which are a different parametrization. The UnitIdentifier, UnitModel and UnitParamters classes handle this transition seamlessly to the user. - /// Uncertainty is expressed in terms of this more intuitive parametrization, to allow for a more intuitive assessment of the parameters. + /// When assessing and simulating the model, parameters are converted into the more intuitive parameters "time constant", "linear gains" and "curvature gain" + /// which are a different parameterization. The UnitIdentifier, UnitModel and UnitParameters classes handle this transition seamlessly to the user. + /// Uncertainty is expressed in terms of this more intuitive parameterization, to allow for a more intuitive assessment of the parameters. /// /// - /// Another advantage of the paramterization, is that the model internally separates betwen stedy-state and transient state, you can at any instance + /// Another advantage of the parameterization is that the model internally separates between steady-state and transient state. You can at any instance /// "turn off" dynamics and request the steady-state model output for the current input. This is useful if you have transient data that you want to - /// analyze in the steady-state, as you can then fit the model to all available data-points without having to select what data points you beleive are at + /// analyze in the steady-state, as you can then fit the model to all available data-points without having to select what data points you believe are at /// steady state, then you can disable dynamic terms to do a static analysis of the dynamic model. /// /// - /// Time-delay is an integer parameter, and finding the time-delay alongside continous paramters + /// Time-delay is an integer parameter, and finding the time-delay alongside continuous parameters /// turns the identification problem into a linear mixed-integer problem. - /// The time delay identification is done by splitting the time-delay estimation from continous parameter + /// The time delay identification is done by splitting the time-delay estimation from continuous parameter /// identification, turning the solver into a sequential optimization solver. - /// This logic to re-run estimation for multiple time-delays and selecting the best estiamte of time delay - /// is deferred to + /// This logic to re-run estimation for multiple time-delays and selecting the best estimate of time delay + /// is deferred to . /// /// - /// Since the aim is to identify transients/dynamics, the regression is done on model differences rather than absolute values + /// Since the aim is to identify transients/dynamics, the regression is done on model differences rather than absolute values. /// /// @@ -67,7 +67,7 @@ public static class UnitIdentifier /// /// The dataset containing the ymeas and U that is to be fitted against, /// a new y_sim is also added - /// optional fitting specs object for tuning data + /// optional fitting specs object for tuning data /// (default:true) if set to false, time delay estimation is disabled (can drastically speeed up identification) /// the identified model parameters and some information about the fit public static UnitModel Identify(ref UnitDataSet dataSet, diff --git a/Dynamic/Interfaces/ISimulateableModel.cs b/Dynamic/Interfaces/ISimulateableModel.cs index 50fe5ac..489e7c7 100644 --- a/Dynamic/Interfaces/ISimulateableModel.cs +++ b/Dynamic/Interfaces/ISimulateableModel.cs @@ -16,17 +16,17 @@ namespace TimeSeriesAnalysis.Dynamic public interface ISimulatableModel { /// - /// Iterate the process model one timestep forward + /// Iterate the process model one timestep forward. /// /// a 2d array of inputs, one row for each time step, or null if model is autonomous /// the time in seconds between the data samples of the inputs /// is a special reserverd value of inputs U that is to be treated as NaN - /// First value: the value of the state x of the process model at the new time step(be aware that if a disturbance is defined, - /// they need ot be added to states to get y_sim), if the model has additive outputs, the second state is the "internal output" upstream of those. + /// First value: the value of the state x of the process model at the new time step (be aware that if a disturbance is defined, + /// it needs to be added to the states to get y_sim), if the model has additive outputs, the second state is the "internal output" upstream of those. double[] Iterate(double[] inputsU, double timeBase_s,double badDataID=-9999); /// - /// If possible, set the internal state of the model so that the given inputs give the given output + /// If possible, set the internal state of the model so that the given inputs give the given output. /// /// /// @@ -35,70 +35,70 @@ public interface ISimulatableModel /// /// Calculates the value u0 of u that at steady-state will give the output value y0. - /// This method is used when starting a method at steady-state (assumes that disturbance is zero!) + /// This method is used when starting a method at steady-state (assumes that disturbance is zero!). /// /// value of x for which to find matching u0 - /// index of input(only applicable if multiple inputs) + /// index of input (only applicable if multiple inputs) /// for multi-input systems, all values except one must be given to calculate the steady-state u0 /// double? GetSteadyStateInput(double x0, int inputIdx=0,double[] givenInputValues=null); /// - /// Get the steady state value of the model output + /// Get the steady state value of the model output. /// /// vector of inputs for which the steady state is to be calculated /// is a special reserverd value of inputs U that is to be treated as NaN - /// the steady-state value, if it is not possible to calculate, a null is returned + /// the steady-state value, if it is not possible to calculate, null is returned. double? GetSteadyStateOutput(double[] u0, double badDataID = -9999); /// - /// Returns the type of process model + /// Returns the type of process model. /// /// ModelType GetProcessModelType(); /// - /// Return the inputIDs that are "internal" i.e. related to the model and internal state x, but not "additive" + /// Return the inputIDs that are "internal" i.e. related to the model and internal state x, but not "additive". /// /// string[] GetModelInputIDs(); /// - /// Get both additive and model input IDs + /// Get both additive and model input IDs. /// /// string[] GetBothKindsOfInputIDs(); /// - /// Get additive input IDs + /// Get additive input IDs. /// /// string[] GetAdditiveInputIDs(); /// - /// Get the output ID + /// Get the output ID. /// /// string GetOutputID(); /// - /// Get the ID of the "OutputIdent" signal + /// Get the ID of the "OutputIdent" signal. /// /// string GetOutputIdentID(); /// - /// Set the output ID + /// Set the output ID. /// /// void SetOutputID(string outputID); /// - /// Set the input IDs + /// Set the input IDs. /// /// /// @@ -106,35 +106,35 @@ public interface ISimulatableModel bool SetInputIDs(string[] manipulatedVariablesU_stringIDs, int? index=null); /// - /// Add an additive signal to the output + /// Add an additive signal to the output. /// /// void AddSignalToOutput(string additiveInputID); /// - /// Get the length of the input vector + /// Get the length of the input vector. /// /// int GetLengthOfInputVector(); /// - /// An unique name of the process model + /// An unique name of the process model. /// /// string GetID(); /// - /// Get the type of the output signal + /// Get the type of the output signal. /// /// SignalType GetOutputSignalType(); /// - /// Returns true if the paramters are specified + /// Returns true if the parameters are specified . /// - /// string explaining why return is false, if applicable + /// string explaining why return is false, if applicable. bool IsModelSimulatable(out string explanationStr); diff --git a/Dynamic/Interfaces/ModelBaseClass.cs b/Dynamic/Interfaces/ModelBaseClass.cs index 22513c9..69c1f49 100644 --- a/Dynamic/Interfaces/ModelBaseClass.cs +++ b/Dynamic/Interfaces/ModelBaseClass.cs @@ -7,29 +7,29 @@ namespace TimeSeriesAnalysis.Dynamic { /// - /// Abstract base class that contains common functionality across all models which are to implement + /// Abstract base class that contains common functionality across all models which are to be implemented. /// /// public abstract class ModelBaseClass { /// - /// A unique ID string that is used to identify the model uniquely in a PlantSimulation + /// A unique ID string that is used to identify the model uniquely in a PlantSimulation. /// public string ID { get; set; } = "not_named"; /// - /// Unique signal IDs that are mapped to the non-additive model inputs + /// Unique signal IDs that are mapped to the non-additive model inputs. /// public string[] ModelInputIDs; /// - /// Unique signal IDs that are added to the output of the model(typically a disturbance) + /// Unique signal IDs that are added to the output of the model (typically a disturbance). /// public List additiveInputIDs; /// - /// Unique signal ID that defines the name of output signal of the model + /// Unique signal ID that defines the name of output signal of the model. /// public string outputID=null; @@ -40,33 +40,33 @@ public abstract class ModelBaseClass public string outputIdentID = null; /// - /// The type of the model + /// The type of the model. /// public ModelType processModelType = ModelType.UnTyped; /// - /// Optional comment + /// Optional comment. /// public string comment; /// - /// Optional visual position of model when displayed as graph: x-axis position(origo is top left) + /// Optional visual position of model when displayed as graph: x-axis position (origo is top left). /// public double? x; /// - /// Optional visual position of model when displayed as graph: y-axis position(origo is top left) + /// Optional visual position of model when displayed as graph: y-axis position (origo is top left). /// public double? y; /// - /// Optional color string when model is displayed as a graph + /// Optional color string when model is displayed as a graph. /// public string color; /// - /// Get the ID of the model + /// Get the ID of the model. /// /// public string GetID() @@ -75,7 +75,7 @@ public string GetID() } /// - /// Set ID of the model + /// Set the ID of the model. /// /// public void SetID(string ID) @@ -86,7 +86,7 @@ public void SetID(string ID) /// - /// Set the type of the process model + /// Set the type of the process model. /// /// public void SetProcessModelType(ModelType newType) @@ -95,7 +95,7 @@ public void SetProcessModelType(ModelType newType) } /// - /// Get the type of the process model + /// Get the type of the process model. /// /// public ModelType GetProcessModelType() @@ -104,8 +104,8 @@ public ModelType GetProcessModelType() } /// - /// Set the stringIDs of the one or more manipulated variables U that enter model. - /// This method may append/lengthen the inputIDs + /// Set the stringIDs of one or more of the manipulated variables U that enter model. + /// This method may append/lengthen the inputIDs. /// /// /// if non-null, this is the index of the element in U to set @@ -161,7 +161,7 @@ public bool SetInputIDs(string[] U_stringIDs, int? idx = null) } /// - /// Add an additive signal to the output + /// Add an additive signal to the output. /// /// ID of signal to add public void AddSignalToOutput(string additiveInputID) @@ -181,7 +181,7 @@ public void AddSignalToOutput(string additiveInputID) } /// - /// Get the type of the process model + /// Get the type of the process model. /// /// public string[] GetModelInputIDs() @@ -190,7 +190,7 @@ public string[] GetModelInputIDs() } /// - /// Get the DIs of any additive inputs that are included in model + /// Get the IDs of any additive inputs that are included in model. /// /// returns null if no additive inputs are defined. public string[] GetAdditiveInputIDs() @@ -203,7 +203,7 @@ public string[] GetAdditiveInputIDs() } /// - /// Gets IDS both of model inputs and additive model outputs + /// Get the IDs of both the model inputs and the additive model inputs. /// /// public string[] GetBothKindsOfInputIDs() @@ -222,7 +222,7 @@ public string[] GetBothKindsOfInputIDs() /// - /// Set the ID of the output + /// Set the ID of the output. /// /// public void SetOutputID(string outputID) @@ -231,9 +231,9 @@ public void SetOutputID(string outputID) } /// - /// returns the output ID + /// Return the output ID. /// - /// may return null if output is not set + /// may return null if output is not set. public virtual string GetOutputID() { if (this.outputID == null) @@ -244,9 +244,9 @@ public virtual string GetOutputID() /// - /// returns the ID of the signal the output is identified against + /// Return the ID of the signal the output is identified against. /// - /// may return null if output is not set + /// may return null if output is not set. public virtual string GetOutputIdentID() { return this.outputIdentID; @@ -255,7 +255,7 @@ public virtual string GetOutputIdentID() /// - /// Get the length of the output vector + /// Get the length of the output vector. /// /// virtual public int GetLengthOfInputVector() @@ -265,7 +265,7 @@ virtual public int GetLengthOfInputVector() } /// - /// Get the type of the output signal + /// Get the type of the output signal. /// /// public abstract SignalType GetOutputSignalType(); diff --git a/Dynamic/PlantSimulator/PlantSimulator.cs b/Dynamic/PlantSimulator/PlantSimulator.cs index 2bea48c..77c6cbe 100644 --- a/Dynamic/PlantSimulator/PlantSimulator.cs +++ b/Dynamic/PlantSimulator/PlantSimulator.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Net.Http.Headers; using System.Reflection; @@ -23,16 +22,16 @@ namespace TimeSeriesAnalysis.Dynamic { /// - /// Class that holds comments added to models + /// Class that holds comments added to models. /// public class Comment { /// - /// Author of comment + /// Author of comment. /// public string author; /// - /// Date of comment + /// Date of comment. /// public DateTime date; /// @@ -45,7 +44,7 @@ public class Comment public double plantScore; /// - /// Comment constructor + /// Comment constructor. /// /// /// @@ -61,19 +60,19 @@ public Comment(string author, DateTime date, string comment, double plantScore = } /// - /// Simulates larger "plant-models" that is built up connected sub-models, - /// that each implement ISimulatableModel + /// Simulates larger "plant-models" that is built up of connected sub-models + /// that each implement ISimulatableModel. /// /// To set up a simulation, first connect models, and then add external input signals. - /// This class handles information about which model is connected to which, and handles callig sub-models in the + /// This class handles information about which model is connected to which, and handles calling sub-models in the /// correct order with the correct input signals. /// /// - /// By default, the model attempts to start in steady-state, intalization handled by ProcessSimulatorInitalizer - /// (this requires no user interaction) + /// By default, the model attempts to start in steady-state, intalization handled by ProcessSimulatorInitializer + /// (this requires no user interaction). /// /// - /// The building blocks of plant models are PIDModel, DefaultProcessModel and Select + /// The building blocks of plant models are UnitModel, PidModel and Select /// /// /// @@ -83,42 +82,127 @@ public Comment(string author, DateTime date, string comment, double plantScore = public class PlantSimulator { /// - /// User-friendly name that may include white spaces + /// User-friendly name that may include white spaces. /// public String plantName { get; set; } /// - /// A short user-friendly description of what the plant is and does + /// A short user-friendly description of what the plant is and does. /// public String plantDescription { get; set; } /// - /// A list of comments that user may have added to track changes made over time + /// A list of comments that the user may have added to track changes made over time. /// public List comments; /// - /// The date of when the model was last saved + /// The date of when the model was last saved. /// public DateTime date { get; set; } /// - /// Dictionary of all unit models in the plant simulator (must implement ISimlatableModel) + /// Dictionary of all unit models in the plant simulator (must implement ISimulatableModel). /// public Dictionary modelDict; /// - /// List of all external signal IDs + /// List of all external signal IDs. /// public List externalInputSignalIDs; /// - /// The connection parser object + /// The connection parser object. /// public ConnectionParser connections; /// /// The fitScore of the plant the last time it was saved. /// - public double PlantFitScore = double.NaN; + 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 @@ -126,7 +210,7 @@ public class PlantSimulator /// A list of process models, each implementing ISimulatableModel /// optional name of plant, used when serializing /// optional description of plant - public PlantSimulator(List processModelList, string plantName = "", string plantDescription = "") + public PlantSimulator(List processModelList, string plantName="", string plantDescription="") { externalInputSignalIDs = new List(); this.comments = new List(); @@ -153,14 +237,14 @@ public PlantSimulator(List processModelList, string plantName } /// - /// Add an external signal. Preferred implementation, as signal can have any ID without naming convention. + /// Add an external signal. Preferred implementation, as the signal can have any ID without naming convention. /// /// /// /// /// - /// returns signalID or null if something went wrong - public string AddAndConnectExternalSignal(ISimulatableModel model, string signalID, SignalType type, int index = 0) + /// signalID or null if something went wrong + public string AddAndConnectExternalSignal(ISimulatableModel model,string signalID, SignalType type, int index = 0) { ModelType modelType = model.GetProcessModelType(); externalInputSignalIDs.Add(signalID); @@ -206,7 +290,7 @@ public string AddAndConnectExternalSignal(ISimulatableModel model, string signal } else { - Shared.GetParserObj().AddError("PlantSimulator.AddSignal was unable to add signal '" + signalID + "'"); + Shared.GetParserObj().AddError("PlantSimulator.AddSignal was unable to add signal '"+ signalID+"'" ); return null; } @@ -214,8 +298,8 @@ public string AddAndConnectExternalSignal(ISimulatableModel model, string signal /// - /// Informs the PlantSimulator that a specific sub-model has a specifc signal at its input, - /// (use for unit testing only, using a naming convention to name signal) + /// Informs the PlantSimulator that a specific sub-model has a specific signal at its input + /// (use for unit testing only, using a naming convention to name signal). /// /// /// @@ -224,11 +308,11 @@ public string AddAndConnectExternalSignal(ISimulatableModel model, string signal public string AddExternalSignal(ISimulatableModel model, SignalType type, int index = 0) { string signalID = SignalNamer.GetSignalName(model.GetID(), type, index); - return AddAndConnectExternalSignal(model, signalID, type, index); + return AddAndConnectExternalSignal(model,signalID,type,index); } /// - /// Connect an existing signal with a given signalID to a new model + /// Connect an existing signal with a given signalID to a new model. /// /// /// @@ -241,32 +325,32 @@ public bool ConnectSignalToInput(string signalID, ISimulatableModel model, int i } /// - /// Add a disturbance model to the output a given model + /// Add a disturbance model to the output a given model. /// /// /// /// - public bool ConnectModelToOutput(ISimulatableModel disturbanceModel, ISimulatableModel model) + public bool ConnectModelToOutput(ISimulatableModel disturbanceModel, ISimulatableModel model ) { model.AddSignalToOutput(disturbanceModel.GetOutputID()); return true; } /// - /// Connect the output of the upstream model to the input of the downstream model + /// Connect the output of the upstream model to the input of the downstream model. /// /// the upstream model, meaning the model whose output will be connected /// the downstream model, meaning the model whose input will be connected /// input index of the downstream model to connect to (default is first input) - /// returns the signal id if all is ok, otherwise null. - public string ConnectModels(ISimulatableModel upstreamModel, ISimulatableModel downstreamModel, int? inputIndex = null) + /// returns the signal id if all is ok, otherwise null. + public string ConnectModels(ISimulatableModel upstreamModel, ISimulatableModel downstreamModel, int? inputIndex=null) { ModelType upstreamType = upstreamModel.GetProcessModelType(); ModelType downstreamType = downstreamModel.GetProcessModelType(); string outputId = upstreamModel.GetOutputID(); int nInputs = downstreamModel.GetLengthOfInputVector(); - if (nInputs == 1 && inputIndex == 0) + if (nInputs == 1 && inputIndex ==0) { downstreamModel.SetInputIDs(new string[] { outputId }); } @@ -299,7 +383,7 @@ public string ConnectModels(ISimulatableModel upstreamModel, ISimulatableModel d return false; } }*/ - else + else { var isOk = downstreamModel.SetInputIDs(new string[] { outputId }, inputIndex); if (!isOk) @@ -313,16 +397,7 @@ public string ConnectModels(ISimulatableModel upstreamModel, ISimulatableModel d } /// - /// Get ConnenectionParser object - /// - /// - public ConnectionParser GetConnections() - { - return connections; - } - - /// - /// Get a TimeSeriesDataSet of all external signals of model + /// Get a TimeSeriesDataSet of all external signals of model. /// /// public string[] GetExternalSignalIDs() @@ -330,146 +405,29 @@ public string[] GetExternalSignalIDs() return externalInputSignalIDs.ToArray(); } - - /// - /// Get dictionary of all models - /// - /// - public Dictionary GetModels() - { - return modelDict; - } - - /// - /// Returns a unit data set for a given unitModel. + /// Get ConnenectionParser object. /// - /// - /// /// - public UnitDataSet GetUnitDataSetForProcess(TimeSeriesDataSet inputData, UnitModel unitModel) + public ConnectionParser GetConnections() { - 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; + return connections; } - /// - /// 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. + /// Get a dictionary of all models. /// - /// - /// /// - public UnitDataSet GetUnitDataSetForPID(TimeSeriesDataSet inputData, PidModel pidModel) + public Dictionary GetModels() { - 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; + return modelDict; } - /// - /// - /// - /// - /// - /// - /// - /// - private double[] GetValuesFromEitherDataset(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; - } - /// - /// Simulate single model to get the internal "x" unmeasured output that excludes any additive outputs(like disturbances) + /// Simulate a single model to get the internal "x" unmeasured output that excludes any additive outputs (like disturbances). /// /// /// @@ -477,11 +435,11 @@ private double[] GetValuesFromEitherDataset(string[] inputIDs, int timeIndex, /// public bool SimulateSingleInternal(TimeSeriesDataSet inputData, string singleModelName, out TimeSeriesDataSet simData) { - return SimulateSingle(inputData, singleModelName, true, out simData); + return SimulateSingle(inputData,singleModelName,true, out simData); } /// - /// Simulate single model to get the output including any additive inputs + /// Simulate a single model to get the output including any additive inputs. /// /// /// @@ -501,7 +459,7 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, /// 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, - int noiseSeed = 123) + int noiseSeed= 123) { return SimulateSingle(unitData, model, true, noiseAmplitude, true, noiseSeed); } @@ -513,7 +471,7 @@ public static (bool, double[]) SimulateSingleToYmeas(UnitDataSet unitData, ISimu /// /// /// - public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model, bool addSimToUnitData) + public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model, bool addSimToUnitData) { return SimulateSingle(unitData, model, false, 0, addSimToUnitData, 0); @@ -529,9 +487,9 @@ public static (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatable /// 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 - 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[]) SimulateSingle(UnitDataSet unitData, ISimulatableModel model,bool writeToYmeas= false, + double noiseAmplitude=0, + bool addSimToUnitData=false, int seedNr=123) { var inputData = new TimeSeriesDataSet(); var singleModelName = "SimulateSingle"; @@ -545,7 +503,7 @@ static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl } var uNames = new List(); - for (int colIdx = 0; colIdx < unitData.U.GetNColumns(); colIdx++) + for (int colIdx = 0; colIdx< unitData.U.GetNColumns(); colIdx++) { var uName = "U" + colIdx; inputData.Add(uName, unitData.U.GetColumn(colIdx)); @@ -556,9 +514,9 @@ static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl modelCopy.SetOutputID("output"); PlantSimulator sim = new PlantSimulator(new List { modelCopy }); - // var simData = new TimeSeriesDataSet(); + // var simData = new TimeSeriesDataSet(); var isOk = sim.SimulateSingle(inputData, singleModelName, false, out var simData); - if (!isOk) + if(!isOk) return (false, null); double[] y_sim = simData.GetValues(singleModelName, SignalType.Output_Y); if (noiseAmplitude > 0) @@ -598,7 +556,7 @@ static private (bool, double[]) SimulateSingle(UnitDataSet unitData, ISimulatabl /// /// /// - public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, + public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, bool doCalcYwithoutAdditiveTerms, out TimeSeriesDataSet simData) { if (!modelDict.ContainsKey(singleModelName)) @@ -674,7 +632,7 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, return false; } } - var isOk = simData.AddDataPoint(nameOfSimulatedSignal, timeIdx, outputVal[0]); + var isOk = simData.AddDataPoint(nameOfSimulatedSignal, timeIdx, outputVal[0]); if (!isOk) { return false; @@ -683,8 +641,8 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, if (inputData.GetTimeStamps() != null) simData.SetTimeStamps(inputData.GetTimeStamps().ToList()); else - { - //? + { + //? } // disturbance estimation if (modelDict[singleModelName].GetProcessModelType() == ModelType.SubProcess && doEstimateDisturbance) @@ -706,12 +664,12 @@ public bool SimulateSingle(TimeSeriesDataSet inputData, string singleModelName, return true; } /// - /// Perform a dynamic simulation of the model provided, given the specified connections and external signals + /// Perform a dynamic simulation of the model provided, given the specified connections and external signals. /// /// the external signals for the simulation(also, determines the simulation time span and timebase) /// the simulated data set to be outputted(excluding the external signals) /// - public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) + public bool Simulate (TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) { var timeBase_s = inputData.GetTimeBase(); ; @@ -726,15 +684,15 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) { if (!modelDict.ElementAt(i).Value.IsModelSimulatable(out string explStr)) { - Shared.GetParserObj().AddError("PlantSimulator could not run, model " + - modelDict.ElementAt(i).Key + " lacks all required inputs to be simulatable:" + + Shared.GetParserObj().AddError("PlantSimulator could not run, model "+ + modelDict.ElementAt(i).Key + " lacks all required inputs to be simulatable:"+ explStr); simData = null; return false; } } - (var orderedSimulatorIDs, var compLoopDict) = connections.InitAndDetermineCalculationOrderOfModels(modelDict); + (var orderedSimulatorIDs,var compLoopDict) = connections.InitAndDetermineCalculationOrderOfModels(modelDict); simData = new TimeSeriesDataSet(); // initalize the new time-series to be created in simData. @@ -743,6 +701,7 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) var inputDataMinimal = new TimeSeriesDataSet(inputData); var didInit = init.ToSteadyStateAndEstimateDisturbances(ref inputDataMinimal, ref simData, compLoopDict); + if (!didInit) { Shared.GetParserObj().AddError("PlantSimulator failed to initalize."); @@ -757,14 +716,15 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) string[] inputIDs = model.GetBothKindsOfInputIDs(); if (inputIDs == null) { - Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Model \"" + model.GetID() + + Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Model \""+ model.GetID() + "\" has null inputIDs."); return false; } + double[] inputVals = GetValuesFromEitherDataset(inputIDs, timeIdx, simData, inputDataMinimal); - string outputID = model.GetOutputID(); - if (outputID == null) + string outputID = model.GetOutputID(); + if (outputID==null) { Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Model \"" + model.GetID() + "\" has null outputID."); @@ -796,12 +756,14 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) { var model = modelDict[orderedSimulatorIDs.ElementAt(modelIdx)]; string[] inputIDs = model.GetBothKindsOfInputIDs(); - int inputDataLookBackIdx = 0; + int inputDataLookBackIdx = 0; if (model.GetProcessModelType() == ModelType.PID && timeIdx > 0) { inputDataLookBackIdx = 1;//if set to zero, model fails(requires changing model order). } + double[] inputVals = GetValuesFromEitherDataset(inputIDs, lastGoodTimeIndex - inputDataLookBackIdx, simData, inputDataMinimal); + if (inputVals == null) { Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Model \"" + model.GetID() + @@ -809,18 +771,18 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) return false; } double[] outputVal = model.Iterate(inputVals, timeBase_s); - bool isOk = simData.AddDataPoint(model.GetOutputID(), timeIdx, outputVal[0]); + bool isOk = simData.AddDataPoint(model.GetOutputID(),timeIdx,outputVal[0]); if (!isOk) { - Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Unable to add data point for \"" + Shared.GetParserObj().AddError("PlantSimulator.Simulate() failed. Unable to add data point for \"" + model.GetOutputID() + "\", indicating an error in initalization. "); return false; } if (outputVal.Length > 1) { - if (timeIdx == 0) + if (timeIdx ==0) { - simData.InitNewSignal(model.GetID(), outputVal[1], N.Value); + simData.InitNewSignal(model.GetID(), outputVal[1],N.Value); } bool isOk2 = simData.AddDataPoint(model.GetID(), timeIdx, outputVal[1]); if (!isOk2) @@ -830,20 +792,59 @@ public bool Simulate(TimeSeriesDataSet inputData, out TimeSeriesDataSet simData) } simData.SetTimeStamps(inputDataMinimal.GetTimeStamps().ToList()); PlantFitScore = FitScoreCalculator.GetPlantWideSimulated(this, inputData, simData); + return true; } + /// + /// + /// + /// + /// + /// + /// + /// + private double[] GetValuesFromEitherDataset(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; + } /// - /// Creates a JSON text string serialization of this object + /// Creates a JSON text string serialization of this object. /// /// public string SerializeTxt() { var settings = new JsonSerializerSettings(); - settings.TypeNameHandling = TypeNameHandling.Auto; + settings.TypeNameHandling = TypeNameHandling.Auto; settings.Formatting = Formatting.Indented; // models outputs that are not connected to anyting are "null" @@ -859,20 +860,20 @@ public string SerializeTxt() } /// - /// Creates a file JSON representation of this object + /// Creates a JSON file representation of this object. /// - /// the desired file name and plant name(can be null, in which case the filename should be given in the path argument) + /// the desired file name and plant name (can be null, in which case the filename should be given in the path argument) /// create file in the given path - public bool Serialize(string newPlantName = null, string path = null) + public bool Serialize(string newPlantName = null, string path= null) { string fileName = ""; if (path != null) { fileName = path; if (!fileName.EndsWith(@"\")) - fileName += @"\"; + fileName += @"\"; } - if (newPlantName != null) + if (newPlantName!=null) { fileName += newPlantName; } @@ -893,57 +894,6 @@ public bool Serialize(string newPlantName = null, string path = null) return fileWriter.Close(); } - /// - /// Writes the plant information in a human-friendly format - /// - /// - public override string ToString() - { - var writeCulture = new CultureInfo("en-US"); - var numberFormat = (System.Globalization.NumberFormatInfo)writeCulture.NumberFormat.Clone(); - numberFormat.NumberDecimalSeparator = "."; - - int sDigits = 3; - - StringBuilder sb = new StringBuilder(); - sb.AppendLine(this.GetType().ToString()); - sb.AppendLine("-------------------------"); - sb.AppendLine("Name: " + plantName); - sb.AppendLine("Description: " + plantDescription); - sb.AppendLine("Date: " + date); - sb.AppendLine(""); - - foreach (var model in modelDict) - { - if (model.Value.GetOutputIdentID()== null) - sb.AppendLine("model:" + model.Key + " with output: " + model.Value.GetOutputID()); - else - sb.AppendLine("model:" + model.Key + " with output: " + model.Value.GetOutputID() +" ident.against:" + model.Value.GetOutputIdentID()); - - } - foreach (var connection in connections.connections) - { - sb.AppendLine("connection:" + connection.Item1 + " to " + connection.Item2); - } - foreach (var signal in externalInputSignalIDs) - { - sb.AppendLine("external signals:" + signal); - } - - sb.AppendLine(""); - - if (double.IsNaN(PlantFitScore)) - { - sb.AppendLine("Plant Fit Score: not avaiable, most likely because inputData did not contain the measured outputs required to calculate it."); - } - else - sb.AppendLine("Plant Fit Score: " + SignificantDigits.Format(PlantFitScore, sDigits)); - - return sb.ToString(); - - } - - } } diff --git a/Dynamic/SimulatableModels/GainSchedParameters.cs b/Dynamic/SimulatableModels/GainSchedParameters.cs index 35e3c8c..f861ba0 100644 --- a/Dynamic/SimulatableModels/GainSchedParameters.cs +++ b/Dynamic/SimulatableModels/GainSchedParameters.cs @@ -185,7 +185,7 @@ public void MoveOperatingPointUWithoutChangingModel(double newOperatingPointU) /// public void SetOperatingPoint(double newOperatingPointU, double newOperatingPointY) { - OperatingPoint_U = newOperatingPointU; + OperatingPoint_Y = newOperatingPointU; OperatingPoint_Y = newOperatingPointY; } diff --git a/Dynamic/SimulatableModels/UnitModel.cs b/Dynamic/SimulatableModels/UnitModel.cs index c7c01cd..082e6ec 100644 --- a/Dynamic/SimulatableModels/UnitModel.cs +++ b/Dynamic/SimulatableModels/UnitModel.cs @@ -19,25 +19,25 @@ namespace TimeSeriesAnalysis.Dynamic /// /// This is a model that can be either dynamic or static, have one or multiple inputs /// and can be either linear in inputs or have inputs nonlinearity described by a - /// second-order polynominal. Dynamics can be either 1.order time-constant, time-delay or both. - /// The model also supports "additive" signals added to its output(intended for modeling disturbances.) + /// second-order polynominal. Dynamics can be either 1. order time-constant, time-delay or both. + /// The model also supports "additive" signals added to its output (intended for modeling disturbances). /// /// - /// The model is designed to lend itself well to identificaiton from industrial time-series - /// datasets, and is supported by the accompanying identificaiton method . + /// The model is designed to lend itself well to identification from industrial time-series + /// datasets, and is supported by the accompanying identification method . /// /// /// This model is also intended to be co-simulated with by to study /// process control feedback loops. /// /// - /// It is assumed that for most unit processes in industrial process control systems can be described + /// It is assumed that most unit processes in industrial process control systems can be described /// sufficiently by this model, and thus that larger plants can be modeled by connecting unit models /// based on this model structure. /// /// /// It would be possible to extend this model to also describe second-order dynamics along the same principles by - /// the intorduction of one additional paramters in future work. + /// the introduction of one additional parameter in future work. /// /// /// See also: @@ -45,7 +45,7 @@ namespace TimeSeriesAnalysis.Dynamic public class UnitModel : ModelBaseClass, ISimulatableModel { /// - /// The paramters of the UnitModel + /// The parameters of the UnitModel. /// public UnitParameters modelParameters; @@ -67,10 +67,12 @@ public class UnitModel : ModelBaseClass, ISimulatableModel private List TimeDelayEstWarnings { get; } + public UnitModel(){} + /// /// Constructor /// - /// model paramter object + /// model parameter object /// a unique string that identifies this model in larger process models [JsonConstructor] public UnitModel(UnitParameters modelParameters, string ID="not_named") @@ -80,7 +82,7 @@ public UnitModel(UnitParameters modelParameters, string ID="not_named") InitSim(modelParameters); } /// - /// Initalizer of model that for the given dataSet also creates the resulting y_sim + /// Initalizer of model that for the given dataSet also creates the resulting y_sim. /// /// /// @@ -91,8 +93,12 @@ public UnitModel(UnitParameters modelParameters, UnitDataSet dataSet,string ID = this.ID = ID; InitSim(modelParameters); } + public string test() + { + return "test"; + } /// - /// Answers if the model can be simulated with the inputs provided + /// Answers if the model can be simulated with the inputs provided. /// /// a string that explains why the model cannot be simulated if that is the case /// @@ -101,7 +107,7 @@ public bool IsModelSimulatable(out string explainStr) explainStr = ""; if (modelParameters == null) { - explainStr = "modelParamters is null"; + explainStr = "modelParameters is null"; return false; } /* if (modelParameters.LinearGains == null) @@ -133,10 +139,10 @@ public bool IsModelSimulatable(out string explainStr) /// - /// Initalize the process model with a sampling time + /// Initalize the process model with a sampling time. /// - /// model paramters object - private void InitSim(UnitParameters modelParameters) + /// model parameters object + public void InitSim(UnitParameters modelParameters) { this.isFirstIteration = true; this.modelParameters = modelParameters; @@ -164,7 +170,7 @@ private void InitSim(UnitParameters modelParameters) /// - /// Store the fitted dataset + /// Store the fitted dataset. /// /// public void SetFittedDataSet(UnitDataSet dataset) @@ -183,7 +189,7 @@ public UnitDataSet GetFittedDataSet() /// - /// Returns the number of external inputs U of the model. Note that this model may have an disturbance signal + /// Returns the number of external inputs U of the model. Note that this model may have a disturbance signal /// added to the output in addition to the other signals. /// /// @@ -208,16 +214,16 @@ override public int GetLengthOfInputVector() } /// - /// Get the objet of model paramters contained in the model + /// Get the object of model parameters contained in the model. /// - /// Model paramter object + /// Model parameter object public UnitParameters GetModelParameters() { return modelParameters; } /// - /// Update the paramter object of the model + /// Update the parameter object of the model. /// /// public void SetModelParameters(UnitParameters parameters) @@ -226,7 +232,7 @@ public void SetModelParameters(UnitParameters parameters) } /// - /// Calcuate the steady-state input if the output and all-but-one input are known + /// Calcuate the steady-state input if the output and all-but-one input are known. /// /// /// This method has no concept of disturbances, so a nonzero disturbance at time zero may throw it off. @@ -332,11 +338,11 @@ public void SetModelParameters(UnitParameters parameters) } /// - /// Determine the process-gain(linear) contribution to the outputof a particular index for a particular value + /// Determine the process-gain (linear) contribution to the output of a particular index for a particular value. /// /// the index of the input /// the value of the input - /// contribution to the output y, excluding bias and curvature contributions + /// contribution to the output y, excluding bias and curvature contributions. private double CalculateLinearProcessGainTerm(int inputIndex, double u) { double processGainTerm = 0; @@ -352,7 +358,7 @@ private double CalculateLinearProcessGainTerm(int inputIndex, double u) } /// - /// Determine the curvature term contribution(c*(u-u0)^2/unorm) to the output from a particular input for a particular value + /// Determine the curvature term contribution (c*(u-u0)^2/unorm) to the output from a particular input for a particular value. /// /// the index of the input /// the value of the input @@ -394,7 +400,7 @@ private double CalculateCurvatureProcessGainTerm(int inputIndex, double u) } /// - /// Calculates the state x_ss excluding transients (y_ss = x_ss+bias) + /// Calculates the state x_ss excluding transients (y_ss = x_ss+bias). /// /// /// @@ -428,7 +434,7 @@ private double CalculateSteadyStateWithoutAdditive(double[] inputs, double badVa } /// - /// Get the steady state output y for a given input(including additive terms) + /// Get the steady state output y for a given input (including additive terms). /// /// vector of input values /// @@ -451,7 +457,7 @@ private double CalculateSteadyStateWithoutAdditive(double[] inputs, double badVa } /// - /// Get the type of output signal + /// Get the type of output signal. /// /// override public SignalType GetOutputSignalType() @@ -463,14 +469,14 @@ override public SignalType GetOutputSignalType() /// - /// Iterates the process model state one time step, based on the inputs given + /// Iterates the process model state one time step, based on the inputs given. /// /// vector of inputs U. Optionally the output disturbance D can be added as the last value. /// the time in seconds between samples /// value in U that is to be treated as NaN - /// the updated process model state(x) - the output without any output noise or disturbance. - /// NaN is returned if model was not able to be identfied, or if no good values U values yet have been given. - /// If some data points in U inputsU are NaN or equal to badValueIndicator, the last good value is returned + /// the updated process model state (x) - the output without any output noise or disturbance. + /// NaN is returned if model was not able to be identfied, or if no good U values have been given yet. + /// If some data points in U inputs are NaN or equal to badValueIndicator, the last good value is returned /// public double[] Iterate(double[] inputs, double timeBase_s,double badValueIndicator=-9999) { @@ -568,7 +574,7 @@ public bool IsModelStatic() /// - /// Sovel quadratic equation "a x^2 + b*x +c =0" (second order of polynomial equation in a single variable x) + /// Solve quadratic equation "a x^2 + b*x +c =0" (second order polynomial equation in a single variable x) /// x = [ -b +/- sqrt(b^2 - 4ac) ] / 2a /// /// @@ -789,7 +795,7 @@ override public string ToString() /// - /// Warm-starting + /// Warm-starting. /// /// not used, leave as null /// not used, leave as null diff --git a/Dynamic/SimulatableModels/UnitParameters.cs b/Dynamic/SimulatableModels/UnitParameters.cs index 1c8e6cf..c5666b5 100644 --- a/Dynamic/SimulatableModels/UnitParameters.cs +++ b/Dynamic/SimulatableModels/UnitParameters.cs @@ -6,7 +6,7 @@ namespace TimeSeriesAnalysis.Dynamic { /// - /// Parameters data class of the + /// Parameters data class of the . /// public class UnitParameters : ModelParametersBaseClass { @@ -15,6 +15,7 @@ public class UnitParameters : ModelParametersBaseClass /// public double[] LinearGains { get; set; } = null; + /// /// An array of 95% uncertatinty in the linear gains (u-u0)) /// @@ -49,6 +50,8 @@ public class UnitParameters : ModelParametersBaseClass /// public double TimeDelay_s { get; set; } = 0; + + /// /// Damping (second-order) values between ~0.3-0.99 will cause step response with a single visibl overshoot. ) /// Set to zero to disable damping. @@ -68,13 +71,13 @@ public class UnitParameters : ModelParametersBaseClass /// /// The working point of the model, the value of each U around which the model is localized. - /// If value is nullc> then no U0 is used in the model + /// If value is nullc> then no U0 is used in the model. /// public double[] U0 { get; set; } = null; /// /// A "normal range" of U that is used in the nonlinear curvature term ((u-u0)/Unorm)^2. - /// If value is nullc> then no Unorm is used in the model + /// If value is nullc> then no UNorm is used in the model. /// public double[] UNorm { get; set; } = null; @@ -98,7 +101,7 @@ public class UnitParameters : ModelParametersBaseClass internal List TimeDelayEstimationWarnings; /// - /// Default constructor + /// Default constructor. /// public UnitParameters() { @@ -120,7 +123,7 @@ public int GetNumInputs() } /// - /// Creates a deep-copy of the object + /// Creates a deep-copy of the object. /// /// public UnitParameters CreateCopy() @@ -158,7 +161,7 @@ public UnitParameters CreateCopy() } /// - /// Return the "total combined" process gain for a given index at u=u0, a combination of lineargain and curvature gain + /// Return the "total combined" process gain for a given index at u=u0, a combination of linear gain and curvature gain. /// /// Note that for nonlinear processes, the process gain is given by a combination of /// the linear and curvature terms of the model : dy/du(u=u0) @@ -203,7 +206,7 @@ public double GetTotalCombinedProcessGain(int inputIdx) } /// - /// Return the process gain uncertatinty for a given input index at u=u0 + /// Return the process gain uncertatinty for a given input index at u=u0. /// /// Note that for nonlinear processes, the process gain is given by a combination of /// the linear and curvature terms of the model : dy/du(u=u0) @@ -239,9 +242,9 @@ public double GetTotalCombinedProcessGainUncertainty(int inputIdx) /// - /// Get all process gains (including both linear and any nonlinear terms) + /// Get all process gains (including both linear and any nonlinear terms). /// - /// may return null if no process gains given + /// may return null if no process gains given. public double[] GetProcessGains() { var list = new List(); @@ -261,7 +264,7 @@ public double[] GetProcessGains() } /// - /// Get all the process gain uncertainties + /// Get all the process gain uncertainties. /// /// public double[] GetProcessGainUncertainties() @@ -276,7 +279,7 @@ public double[] GetProcessGainUncertainties() /// - /// Adds a identifiation warning to the object + /// Adds an identification warning to the object. /// /// public void AddWarning(UnitdentWarnings warning) @@ -286,7 +289,7 @@ public void AddWarning(UnitdentWarnings warning) } /// - /// Get the list of all warnings given during identification of the model + /// Get the list of all warnings given during identification of the model. /// /// public List GetWarningList() diff --git a/TimeSeriesAnalysis.Tests/Examples/GettingStarted.cs b/TimeSeriesAnalysis.Tests/Examples/GettingStarted.cs index c11b15f..1eb1fc3 100644 --- a/TimeSeriesAnalysis.Tests/Examples/GettingStarted.cs +++ b/TimeSeriesAnalysis.Tests/Examples/GettingStarted.cs @@ -150,7 +150,7 @@ public void Ex4_system_identification() Assert.IsTrue(isOK); - // create a "unit data set" and try to estimate the paramters of processModel from the data alone + // create a "unit data set" and try to estimate the parameters of processModel from the data alone var unitDataSet = new UnitDataSet(); unitDataSet.U = Array2D.CreateFromList(new List { u1, u2 }); unitDataSet.CreateTimeStamps(timeBase_s); diff --git a/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs b/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs index 561897e..af93ebe 100644 --- a/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/DisturbanceEstimatorTests.cs @@ -140,7 +140,7 @@ public void GenericDisturbanceTest (UnitModel processModel, double[] trueDistur var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simData), pidModel1); var result = DisturbanceIdentifier.EstimateDisturbance(pidDataSet, processModel); - Console.WriteLine(plantSim.ToString()); + // Console.WriteLine(result.); if (doAssertResult) { @@ -218,9 +218,6 @@ public void DisturbanceTestUsingPlantSimulator(UnitModel processModel, double[] ////////////////////////////////// Assert.IsTrue(isOK); Assert.IsTrue(simDataSetWithDisturbance.ContainsSignal(distSignal)); - // - Console.WriteLine(plantSim.ToString()); - if (doAssertResult) { var pidDataSet = plantSim.GetUnitDataSetForPID(inputData.Combine(simDataSetWithDisturbance), pidModel1); diff --git a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs index 49e35ea..b7274b9 100644 --- a/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs +++ b/TimeSeriesAnalysis.Tests/Tests/GainSchedIdentifyTests.cs @@ -405,12 +405,13 @@ public void NonzeroOperatingPointU_Both_EstimatesStillOk(double uOperatingPoint, } else { - GainSchedFittingSpecs gsFittingSpecs = new GainSchedFittingSpecs { uGainThresholds = new double[] { gainSchedThreshold },DoSetOperatingPointToDatasetMean =false }; + GainSchedFittingSpecs gsFittingSpecs = new GainSchedFittingSpecs { uGainThresholds = new double[] { gainSchedThreshold } }; idModel = GainSchedIdentifier.IdentifyForGivenThresholds(unitData, gsFittingSpecs); } // plot - if (false) + bool doPlot = false; + if (doPlot) { Shared.EnablePlots(); Plot.FromList(new List { diff --git a/TimeSeriesAnalysis/Array2DExtensionMethods.cs b/TimeSeriesAnalysis/Array2DExtensionMethods.cs index 89a19f1..d6b9568 100644 --- a/TimeSeriesAnalysis/Array2DExtensionMethods.cs +++ b/TimeSeriesAnalysis/Array2DExtensionMethods.cs @@ -16,7 +16,7 @@ namespace TimeSeriesAnalysis public static class Array2DExtensionMethods { /// - /// overwrites the columin in matrix with the new column newColumnValues + /// overwrites the column in matrix with the new column newColumnValues /// static public double[,] WriteColumn(this double[,] matrix, int colIdx, double[] newColumnValues) { @@ -28,7 +28,7 @@ public static class Array2DExtensionMethods } /// - /// returns the column of a 2d-array of strings corresponding to columnIndex(starts at zero) + /// returns the column of a 2d-array of strings corresponding to columnIndex (starts at zero) /// static public string[] GetColumn(this string[,] matrix, int columnIndex) @@ -37,7 +37,7 @@ static public string[] GetColumn(this string[,] matrix, int columnIndex) } /// - /// returns the column of a 2d-array of doubles corresponding to columnIndex(starts at zero) + /// returns the column of a 2d-array of doubles corresponding to columnIndex (starts at zero) /// static public double[] GetColumn(this double[,] matrix, int columnIndex) { @@ -45,7 +45,7 @@ static public double[] GetColumn(this double[,] matrix, int columnIndex) } /// - /// returns the row of a 2d-array of strings corresponding to columnIndex(starts at zero) + /// returns the row of a 2d-array of strings corresponding to rowNumber (starts at zero) /// static public string[] GetRow(this string[,] matrix, int rowNumber) { @@ -53,7 +53,7 @@ static public string[] GetRow(this string[,] matrix, int rowNumber) } /// - /// returns the row of a 2d-array of doubles corresponding to columnIndex(starts at zero) + /// returns the row of a 2d-array of doubles corresponding to rowNumber (starts at zero) /// static public double[] GetRow(this double[,] matrix, int rowNumber) diff --git a/TimeSeriesAnalysis/Vec.cs b/TimeSeriesAnalysis/Vec.cs index e56623e..afc3f6f 100644 --- a/TimeSeriesAnalysis/Vec.cs +++ b/TimeSeriesAnalysis/Vec.cs @@ -16,7 +16,7 @@ namespace TimeSeriesAnalysis { /// - /// Utility functions and operations for treating arrays as mathetmatical vectors. + /// Utility functions and operations for treating arrays as mathematical vectors. /// /// This class considers doubles, methods that require comparisons cannot be easily ported to generic "Vec"/> /// @@ -31,7 +31,7 @@ public class Vec /// /// inputs values matching this value are treated as "NaN" /// and are excluded from all calculations - /// value to return in elementwise calculations to indiate Nan output + /// value to return in elementwise calculations to indiate NaN output public Vec(double nanValue = -9999, double valuteToReturnElementIsNaN = Double.NaN) { this.nanValue = nanValue; @@ -41,9 +41,9 @@ public Vec(double nanValue = -9999, double valuteToReturnElementIsNaN = Double.N // Methods should be sorted alphabetically - /// - /// returns an array where each value is the absolute value of array1 - /// + /// + /// Returns an array where each value is the absolute value of array1. + /// public double[] Abs(double[] array1) { if (array1 == null) @@ -60,9 +60,9 @@ public double[] Abs(double[] array1) return retVal; } - /// - /// returns an array which is the elementwise addition of array1 and array2 - /// + /// + /// Returns an array which is the elementwise addition of array1 and array2. + /// public double[] Add(double[] array1, double[] array2) { @@ -82,9 +82,9 @@ public double[] Add(double[] array1, double[] array2) } - /// - /// elementwise addition of val2 to array1 - /// + /// + /// Elementwise addition of val2 to array1. + /// public double[] Add(double[] array1, double val2) { if (array1 == null) @@ -102,9 +102,9 @@ public double[] Add(double[] array1, double val2) - /// - /// Returns true f array contains a "-9999" or NaN indicating missing data - /// + /// + /// Returns true if array contains a "-9999" or NaN indicating missing data. + /// public bool ContainsBadData(double[] x) { bool doesContainBadData = false; @@ -118,9 +118,9 @@ public bool ContainsBadData(double[] x) return doesContainBadData; } - /// - /// returns the co-variance of two arrays(interpreted as "vectors") - /// + /// + /// Returns the co-variance of two arrays (interpreted as "vectors"). + /// public double Cov(double[] array1, double[] array2, bool doNormalize = false) { double retVal = 0; @@ -152,7 +152,7 @@ public double Cov(double[] array1, double[] array2, bool doNormalize = false) } /// - /// de-serializes a single vector/array (written by serialize) + /// De-serializes a single vector/array (written by serialize). /// static public double[] Deserialize(string fileName) { @@ -180,7 +180,7 @@ static public double[] Deserialize(string fileName) /// /// Return an array of the differences between the neighboring items in array - /// but ignores indices in the array that are in "indicesToIgnore" + /// but ignores indices in the array that are in "indicesToIgnore". /// /// /// @@ -204,12 +204,12 @@ public double[] Diff(double[] vec,List indicesToIgnore=null) } /// - /// Divides an vector by a scalar value + /// Divides a vector by a scalar value. /// /// /// - /// an vector of values representing the array didived by a scalar. - /// In case of NaN inputs or divide-by-zero NaN elements are returned. + /// a vector of values representing the array divided by a scalar. + /// In case of NaN inputs or divide-by-zero, NaN elements are returned. public double[] Div(double[] vector, double scalar) { double[] outArray = new double[vector.Length]; @@ -228,12 +228,12 @@ public double[] Div(double[] vector, double scalar) } /// - /// Divides two vectors of equal length + /// Divides two vectors of equal length. /// /// /// - /// an vector of values representing the array didived by a scalar. - /// In case of NaN inputs or divide-by-zero NaN elements are returned + /// a vector of values representing the array divided by a scalar. + /// In case of NaN inputs or divide-by-zero, NaN elements are returned. public double[] Div(double[] vector1, double[] vector2) { int N = Math.Min(vector1.Length, vector2.Length); @@ -275,7 +275,7 @@ public static bool Equal(double[] arr1, double[] arr2) /// - /// Returns all the values of vec, except for those corresponding with indices in indicesToIngore + /// Returns all the values of vec, except for those corresponding with indices in "indicesToIngore". /// /// /// @@ -296,8 +296,8 @@ public double[] GetValues(double[] vec, List indicesToIgnore) } /// - /// return the indices of elements in the array that have certain relation to value given type (bigger,smaller,equal etc.) - /// Also capable of finding NaN values + /// Return the indices of elements in the array that have a certain relation to value given type (bigger, smaller, equal, etc.). + /// Also capable of finding NaN values. /// /// /// @@ -387,16 +387,16 @@ void Add(int index) /// - /// Gets the gradient of a time-series + /// Gets the gradient of a time-series. /// /// Works by running a regression with time as the "X" variable /// /// - /// values for which the graident is sought - /// dates corrsponding to the values - /// in what unit of time (given in seconds)the gradient shall be persented + /// values for which the gradient is sought + /// dates corresponding to the values + /// in what unit of time (given in seconds)the gradient shall be presented /// optional array of indices that are to be ignored during regression - /// the gradient will be the "Gain" of the returned object (in units per second by default) + /// the gradient will be the "Gain" of the returned object (in units per second by default). public static RegressionResults GetGradient(double[] values, DateTime[] dates, int sampleTime_sec = 1,int[] indicesToIgnore=null) { var vec = new Vec(); @@ -414,9 +414,9 @@ public static RegressionResults GetGradient(double[] values, DateTime[] dates, i - /// - /// Returns true if all elements in array are the specific value - /// + /// + /// Returns true if all elements in the array are the specific value. + /// public static bool IsAllValue(double[] array, double value = 0) { int count = 0; @@ -434,9 +434,9 @@ public static bool IsAllValue(double[] array, double value = 0) } } - /// - /// Returns true if all elements in array are "-9999" or Double.NaN, or is null - /// + /// + /// Returns true if all elements in the array are "-9999" or Double.NaN, or is null. + /// public bool IsAllNaN(double[] array) { if (array == null) @@ -459,9 +459,9 @@ public bool IsAllNaN(double[] array) } - /// - /// All checks for NaN will test both for Double.IsNan and if value== a specific "nan" value (-9999) - /// + /// + /// All checks for NaN will test both for Double.IsNan and if value == {a specific "NaN" value (like "-9999")}. + /// private bool IsNaN(double value) { if (double.IsNaN(value) || value == nanValue) @@ -470,9 +470,9 @@ private bool IsNaN(double value) return false; } - /// - /// Returns maximum value of two array as new array - /// + /// + /// Returns the maximum value of two arrays as a new array. + /// public double[] Max(double[] array1, double[] array2) { double[] retVal = new double[array1.Length]; @@ -487,9 +487,9 @@ public double[] Max(double[] array1, double[] array2) return retVal; } - /// - /// Returns maximum value of array and index of maximum value - /// + /// + /// Returns the maximum value of the array and the index of the maximum value. + /// public double Max(double[] array, out int ind) { ind = 0; @@ -508,9 +508,9 @@ public double Max(double[] array, out int ind) return maxVal; } - /// - /// Returns maximum value of array, ignoring the given indices - /// + /// + /// Returns the maximum value of the array, ignoring the given indices. + /// public double Max(double[] array, List indicesToIgnore) { double maxVal = double.MinValue; @@ -530,9 +530,9 @@ public double Max(double[] array, List indicesToIgnore) return maxVal; } - /// - /// Returns element-wise maximum of array element and value - /// + /// + /// Returns the element-wise maximum of the array element and value. + /// public double[] Max(double[] array, double value) { double[] retArray = new double[array.Length]; @@ -553,16 +553,16 @@ public double[] Max(double[] array, double value) return retArray; } - /// - /// Returns maximum value of array - /// + /// + /// Returns the maximum value of the array. + /// public double Max(double[] array) { return Max(array, out _); } /// - /// Minimum value of array + /// Returns the minimum value of the array. /// /// /// @@ -570,9 +570,9 @@ public double Min(double[] array) { return Min(array, out _); } - /// - /// Returns minimum value of array, ignoring certain indices - /// + /// + /// Returns the minimum value of the array, ignoring certain indices. + /// public double Min(double[] array, List indicesToIgnore) { @@ -593,9 +593,9 @@ public double Min(double[] array, List indicesToIgnore) return minVal; } - /// - /// Returns minimum value of array and index of maximum value - /// + /// + /// Returns the minimum value of the array and the index of the maximum value. + /// public double Min(double[] array, out int ind) { ind = 0; @@ -615,9 +615,9 @@ public double Min(double[] array, out int ind) } - /// - /// Returns minimum value of two array as new array - /// + /// + /// Returns the minimum value of the two arrays as new array. + /// static public double[] Min(double[] array1, double[] array2) { double[] retVal = new double[array1.Length]; @@ -632,9 +632,9 @@ static public double[] Min(double[] array1, double[] array2) return retVal; } - /// - /// Returns element-wise minimum of array element and value - /// + /// + /// Returns the element-wise minimum of the array elements and value. + /// public double[] Min(double[] array, double value) { double[] retArray = new double[array.Length]; @@ -656,9 +656,9 @@ public double[] Min(double[] array, double value) } - /// - /// elementwise multipliation of val2 to array1 - /// + /// + /// Elementwise multiplication of val2 to array1. + /// public double[] Multiply(double[] array1, double val2) { if (array1 == null) @@ -675,9 +675,9 @@ public double[] Multiply(double[] array1, double val2) return retVal; } - /// - /// elementwise multiplication of array1 and array2, assuming they are same size - /// + /// + /// Elementwise multiplication of array1 and array2, assuming they are same size. + /// public double[] Multiply(double[] array1, double[] array2) { @@ -701,9 +701,9 @@ public double[] Multiply(double[] array1, double[] array2) return retVal; } - /// - /// returns the mean value of array1 - /// + /// + /// Returns the mean value of array1. + /// public double? Mean(double[] array1) { if (array1 == null) @@ -724,9 +724,9 @@ public double[] Multiply(double[] array1, double[] array2) - /// - /// Calculates the power of an array - /// + /// + /// Calculates the power of the array. + /// public double[] Pow(double[] array, double factor) { double[] ret = new double[array.Length]; @@ -740,9 +740,9 @@ public double[] Pow(double[] array, double factor) return ret; } - /// - /// Returns range of an array, the difference between minimum and maximum - /// + /// + /// Returns the range of an array; the difference between minimum and maximum element values. + /// public double Range(double[] array) { double range = Max(array) - Min(array); @@ -753,13 +753,13 @@ public double Range(double[] array) /// - /// Create a vector of random numbers + /// Create a vector of random numbers. /// /// the number of samples of the returned array /// lower end of random number range /// higher end of random number range /// optionally, give in a seed number, this makes random sequence repeatable - /// an array of size N of random numbers between minValue and maxValue + /// an array of size N of random numbers between minValue and maxValue. public static double[] Rand(int N, double minValue = 0, double maxValue = 1,int? seed=null) { Random rand;//= null; @@ -775,18 +775,18 @@ public static double[] Rand(int N, double minValue = 0, double maxValue = 1,int? double[] ret = new double[N]; for (int i = 0; i < N; i++) { - ret[i] = rand.NextDouble() * (maxValue - minValue) + minValue; ;//NextDouble by itself return a valube btw 0 and 1 + ret[i] = rand.NextDouble() * (maxValue - minValue) + minValue; ;//NextDouble by itself return a value btw 0 and 1 } return ret; } /// - /// Robust linear regression + /// Robust linear regression. /// - /// vector of responve variable values (to be modelled) - /// 2D matrix of of mainpulated values/independent values/regressors used to explain Y + /// vector of response variable values (to be modelled) + /// 2D matrix of manipulated values/independent values/regressors used to explain Y /// (optional) a list of the indices of values in Y to ignore in regression. By default it is null - /// an object of the RegressionResult class with the paramters, as well as + /// an object of the RegressionResult class with the parameters, as well as /// some statistics on the fit and uncertainty thereof. public RegressionResults Regress(double[] Y, double[,] X, int[] yIndToIgnore = null) @@ -795,7 +795,7 @@ public RegressionResults Regress(double[] Y, double[,] X, int[] yIndToIgnore = n } /// - /// Regression that does not attempt to regualarize inputs toward zero + /// Regression that does not attempt to regualarize inputs toward zero. /// /// /// @@ -807,18 +807,18 @@ public RegressionResults RegressUnRegularized(double[] Y, double[][] X, int[] yI } /// - /// Robust linear regression, regularizd - /// To avoid paramters taking on exteremly high values in the case of little excitation in the inputs, - /// two mitigating actions are implemented by the solver, to be "robust" - /// - a "robust" Signular Value Decomposition(SVD)-based solver is used - /// - a regularization term is added to the objective function that will bring paramters to zero if (Y,X) does not contain - /// any information to force the parameter away from zero + /// Robust linear regression, regularized. + /// To avoid parameters taking on extremely high values in the case of a little excitation in the inputs, + /// two mitigating actions are implemented by the solver to be "robust": + /// - a "robust" Singular Value Decomposition (SVD) -based solver is used. + /// - a regularization term is added to the objective function that will bring parameters to zero if (Y,X) does not contain + /// any information to force the parameter away from zero. /// - /// vector of outptu variable values to be modelled - /// jagged 2D matrix of of mainpulated values/independent values/regressors used to explain Y + /// vector of output variable values to be modelled + /// jagged 2D matrix of manipulated values/independent values/regressors used to explain Y /// (optional) a list of the indices of values in Y to ignore in regression. By default it is null /// (optional) only the indices in this list are to be regularized to zero - /// an object of the RegressionResult class with the paramters, as well as + /// an object of the RegressionResult class with the parameters, as well as /// some statistics on the fit and uncertainty thereof. public RegressionResults RegressRegularized(double[] Y, double[][] X, int[] yIndToIgnore = null, List XindicesToRegularize = null) { @@ -827,19 +827,19 @@ public RegressionResults RegressRegularized(double[] Y, double[][] X, int[] yInd /// - /// Robust linear regression - /// To avoid paramters taking on exteremly high values in the case of little excitation in the inputs, - /// two mitigating actions are implemented by the solver, to be "robust" - /// - a "robust" Singular Value Decomposition(SVD)-based solver is used - /// - a regularization term is added to the objective function that will bring paramters to zero if (Y,X) does not contain - /// any information to force the parameter away from zero + /// Robust linear regression. + /// To avoid parameters taking on exteremly high values in the case of a little excitation in the inputs, + /// two mitigating actions are implemented by the solver to be "robust": + /// - a "robust" Singular Value Decomposition (SVD) -based solver is used. + /// - a regularization term is added to the objective function that will bring parameters to zero if (Y,X) does not contain + /// any information to force the parameter away from zero. /// - /// vector of outptu variable values to be modelled - /// jagged 2D matrix of of mainpulated values/independent values/regressors used to explain Y + /// vector of output variable values to be modelled + /// jagged 2D matrix of manipulated values/independent values/regressors used to explain Y /// (optional) a list of the indices of values in Y to ignore in regression. By default it is null /// (optional) only the indices in this list are to be regularized to zero - /// (optional) adds a minor "regularization" term to objective that will pull values toward zero if corresponding input is not excited - /// an object of the RegressionResult class with the paramters, as well as + /// (optional) adds a minor "regularization" term to objective that will pull values toward zero if corresponding input is not excited + /// an object of the RegressionResult class with the parameters, as well as /// some statistics on the fit and uncertainty thereof. private RegressionResults Regress(double[] Y, double[][] X, int[] yIndToIgnore=null, List XindicesToRegularize=null, bool doNormalizationToZero = true) { @@ -1068,10 +1068,10 @@ private RegressionResults Regress(double[] Y, double[][] X, int[] yIndToIgnore=n /// /// Replace certain values in an array with a new value. /// - /// the array to be replaces - /// list of all the indices of all data points in array to be replaced - /// the new value to use in place of old values. - /// A copy of the original array with the values repalced as specified + /// the array to be replaced + /// list of all the indices of all data points in the array to be replaced + /// the new value to use in place of old values + /// a copy of the original array with the values replaced as specified. public static double[] ReplaceIndWithValue(double[] array, List indList, double valueToReplaceWith) { @@ -1093,7 +1093,7 @@ public static double[] ReplaceIndWithValue(double[] array, List indList, } /// - /// Replace values below a threshold in an array with a new value + /// Replace values below a certain threshold in an array with a new value. /// /// /// @@ -1106,7 +1106,7 @@ public static double[] ReplaceValuesAbove(double[] array, double threshold, doub } /// - /// Replace all values above a certain threshold in array with a new value + /// Replace all values above a certain threshold in array with a new value. /// /// /// @@ -1119,7 +1119,7 @@ public static double[] ReplaceValuesBelow(double[] array, double threshold, doub } /// - /// Replace values above a higher threshold or below a lower threshold with a new value + /// Replace values above a higher threshold or below a lower threshold with a new value. /// /// /// @@ -1132,7 +1132,7 @@ public static double[] ReplaceValuesAboveOrBelow(double[] array, double lowerThr } /// - /// R-squared + /// R-squared. /// R-squared (R2) is a statistical measure that represents the proportion of the variance for a dependent /// variable that's explained by an independent variable or variables in a regression model. /// Whereas correlation explains the strength of the relationship between an independent and @@ -1142,10 +1142,10 @@ public static double[] ReplaceValuesAboveOrBelow(double[] array, double lowerThr /// /// first vector /// second vector - /// optionally: indices to be ignored(for instance bad values) - /// set the offset beteen vector1 and vector2 (for difference equations, ymod is offset by -1 from ymeas).-1 is default. + /// optionally: indices to be ignored (for instance bad values) + /// set the offset beteen vector1 and vector2 (for difference equations, ymod is offset by -1 from ymeas). -1 is default. /// R2 squared, a value between -1 and 1. If an error occured, - /// Double.PositiveInfinity is returned + /// Double.PositiveInfinity is returned. /// public double RSquared(double[] vector1, double[] vector2, List indToIgnoreExt = null, int ymodOffset = -1) { @@ -1187,10 +1187,10 @@ public double RSquared(double[] vector1, double[] vector2, List indToIgnore } - /// - /// smooths the array without phase-shifting by using both past and future values - /// (i.e. so called non-causal smoothing) - /// + /// + /// Smooths the array without phase-shifting by using both past and future values + /// (i.e. so called non-causal smoothing). + /// public double[] NonCausalSmooth(double[] array1,int kernel=1) { if (array1 == null) @@ -1229,9 +1229,9 @@ public double[] NonCausalSmooth(double[] array1,int kernel=1) - /// - /// elementwise subtraction of array1 and array2, assuming they are same size - /// + /// + /// Elementwise subtraction of array1 and array2, assuming they are same size. + /// public double[] Subtract(double[] array1, double[] array2) { @@ -1249,9 +1249,9 @@ public double[] Subtract(double[] array1, double[] array2) } return retVal; } - /// - /// elementwise subtraction of val2 from array1 - /// + /// + /// Elementwise subtraction of val2 from array1. + /// public double[] Subtract(double[] array1, double val2) { if (array1 == null) @@ -1269,9 +1269,9 @@ public double[] Subtract(double[] array1, double val2) - /// - /// returns the sum of array1 - /// + /// + /// Returns the elementwise sum of array1. + /// public double? Sum(double[] array1) { if (array1 == null) @@ -1286,9 +1286,9 @@ public double[] Subtract(double[] array1, double val2) return retVal; } - /// - /// The sum of absolute errors (|a1-a2|) between array1 and array2 - /// + /// + /// The sum of absolute errors (|a1-a2|) between array1 and array2. + /// public double SumOfAbsErr(double[] array1, double[] array2, int indexOffset = -1) { int nGoodValues = 0; @@ -1312,12 +1312,12 @@ public double SumOfAbsErr(double[] array1, double[] array2, int indexOffset = -1 /// - /// The sum of square errors (a1-a2)^2 between array1 and array2. + /// The sum of square errors (a1-a2)^2 between array1 and array2. /// /// /// /// - /// if true, the result is normalized by the number of good values + /// if true, the result is normalized by the number of good values /// optionally a list of indices of array1 to ignore /// public double SumOfSquareErr(double[] array1, double[] array2, int ymodOffset = -1, @@ -1355,10 +1355,10 @@ public double SumOfSquareErr(double[] array1, double[] array2, int ymodOffset = return ret; } - /// - /// sum of square error of the vector compared to a constant. by defautl the return value is normalized by dividing by, - /// this normalization can be turned off - /// + /// + /// Sum of the square errors of the vector compared to a constant. by default the return value is normalized by dividing by the number of elements; + /// this normalization can be turned off. + /// public static double SumOfSquareErr(double[] vec, double constant, bool doNormalization = true) { double ret = 0; @@ -1372,27 +1372,27 @@ public static double SumOfSquareErr(double[] vec, double constant, bool doNormal return ret; } - /// - /// sum of square error of the vector compared to itself - /// + /// + /// Sum of the square errors of the vector compared to itself + /// public double SelfSumOfSquareErr(double[] vec) { return SumOfSquareErr(Vec.SubArray(vec, 1), Vec.SubArray(vec, 0, vec.Length - 2), 0); } - /// - /// sum of absolute error of the vector compared to itself - /// + /// + /// Sum of the absolute errors of the vector compared to itself. + /// public double SelfSumOfAbsErr(double[] vec) { return SumOfAbsErr(Vec.SubArray(vec, 1), Vec.SubArray(vec, 0, vec.Length - 2), 0); } /// - /// serializes a single vector/array to a file for persistent storage to a human-readable text format - /// Vector data can then be retreived by companion method Deserialize + /// Serializes a single vector/array to a file for persistent storage to a human-readable text format. + /// Vector data can then be retreived by companion method Deserialize. /// - /// vector to be written to afile + /// vector to be written to a file /// the file name (or path) of the file to which the vector is to serialized to /// static public bool Serialize(double[] vector, string fileName) @@ -1409,7 +1409,7 @@ static public bool Serialize(double[] vector, string fileName) } /// - /// Create a compact string of vector with a certain number of significant digits and a chosen divider + /// Create a compact string of vector with a certain number of significant digits and a chosen divider. /// /// /// @@ -1440,9 +1440,9 @@ static public bool Serialize(double[] vector, string fileName) return sb.ToString(); } - /// - /// returns the variance of the array (always apositive number) - /// + /// + /// Returns the variance of the array (always a positive number). + /// public double Var(double[] array1, bool doNormalize = false) { double retVal = 0; diff --git a/docs/articles/index.md b/docs/articles/index.md index efa91f3..463ef7c 100644 --- a/docs/articles/index.md +++ b/docs/articles/index.md @@ -16,11 +16,11 @@ ![TimeSeriesAnalysis mindmap](./images/tsa_mindmap.png) -**An open-source library of methods to identify,simulate and control industrial process plants, +**An open-source library of methods to identify, simulate and control industrial process plants, built on .NET Standard 2.0.** The most significant contributions of the library are: -1. am (open-loop) **unit model and -identification algorithm** that can describe both stationary and dynamic, linear and weakly nonlinear processes, and an algorithm to automatically choose the best model for a given input. +1. an (open-loop) **unit model and -identification algorithm** that can describe both stationary and dynamic, linear and weakly nonlinear processes, and an algorithm to automatically choose the best model for a given input. 2. a **closed-loop identification algorithm** that can estimate processes and disturbances from closed loop time-series. 3. a **PID-controller identification algorithm** that can estimate gain and integral time by observing the output of a controller., 4. a **state-of-the-art advanced industrial PID-controller model** implemented as a *unit-model*, and @@ -69,7 +69,7 @@ Grey-box models have two very interesting properties: Grey-box modeling may have an advantage when fitting to data that has some, but less than the ideal amount of information, which is a typical scenario for "as-is" real-world industrial process data. In such a scenario, it is possible to fill in gaps in the model -using a priori knowledge since parameters have a phyiscal interpretation. +using a priori knowledge since parameters have a physical interpretation. ### Advanced analytics diff --git a/docs/articles/plotting.md b/docs/articles/plotting.md index 16ca95b..71a206a 100644 --- a/docs/articles/plotting.md +++ b/docs/articles/plotting.md @@ -4,7 +4,7 @@ > [!Note] > This library is written to support test-driven development(TDD) of algorithms based on time-series, > and to support the TDD workflow, it supports time-series plotting. This feature is extremely useful to -> visualize the results of of unit tests or acceptance tests, and this helps enormously with debugging. +> visualize the results of unit tests or acceptance tests, and this helps enormously with debugging. > The unit tests of this repository, give an example of how you can use these plotting capabilities for your own testing. Plotting features supported diff --git a/docs/articles/python.md b/docs/articles/python.md index 36d656f..8a2377e 100644 --- a/docs/articles/python.md +++ b/docs/articles/python.md @@ -3,18 +3,18 @@ Loading and using the TimeSeriesAnalysis .NET class library in Python is possible by utilizing the [Python.NET](https://github.com/pythonnet/pythonnet) package. ``pythonnet`` enables calling .NET code in Python, and allows Python code to interact with the .NET Common Language Runtime (CLR). > [!Note] -> The current version of Python.NET is supported for Python 2.7 and for versions <= 3.8. +> Make sure that you use a python version compatible with a recent release of Python.NET. ### Python setup In order to set up a working configuration in Windows, follow the steps below: -1. Install a compatible version of [Python](https://www.python.org/downloads/windows/) (>= 3.5, <= 3.8). -2. In your project directory (e.g. "C:\Appl\myProject"), create a new virtual environment and specify the desired Python version (e.g. 3.8): +1. Install a compatible version of [Python](https://www.python.org/downloads/windows/). +2. In your project directory (e.g. "C:\Appl\myProject"), create a new virtual environment and specify the desired Python version (e.g. 3.12): ```console # In project directory "C:\Appl\myProject" -> python3.8 -m venv venv +> python3.12 -m venv venv ``` 3. Activate the virtual environment: diff --git a/readme.md b/readme.md index 117cfcf..c7d1a30 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ # TimeSeriesAnalysis : Data-driven dynamic modeling and simulation ## Overview -This library that deals with developing *time-series models and simulators* from *time-series data*. +This library deals with developing *time-series models and simulators* from *time-series data*. The methods in this library are primarily designed to describe time-series of physical, real-world systems for an industrial setting.