block CombiTimeTable "Table look-up with respect to time and linear/periodic extrapolation methods (data from matrix/file)"
import Modelica.Blocks.Tables.Internal;
extends Modelica.Blocks.Interfaces.MO(final nout = max([size(columns, 1); size(offset, 1)]));
parameter Boolean tableOnFile = false "= true, if table is defined on file or in function usertab"
annotation (Dialog(group = "Table data definition"));
parameter Real table[:,:] = fill(0, 0, 2) "Table matrix (time = first column; e.g., table=[0, 0; 1, 1; 2, 4])"
annotation (Dialog(
group = "Table data definition",
enable = not tableOnFile));
parameter String tableName = "NoName" "Table name on file or in function usertab (see docu)"
annotation (Dialog(
group = "Table data definition",
enable = tableOnFile));
parameter String fileName = "NoName" "File where matrix is stored"
annotation (Dialog(
group = "Table data definition",
enable = tableOnFile,
loadSelector(
filter = "Text files (*.txt);;MATLAB MAT-files (*.mat)",
caption = "Open file in which table is present")));
parameter Boolean verboseRead = true "= true, if info message that file is loading is to be printed"
annotation (Dialog(
group = "Table data definition",
enable = tableOnFile));
parameter Integer columns[:] = 2:size(table, 2) "Columns of table to be interpolated"
annotation (Dialog(
group = "Table data interpretation",
groupImage = "modelica://Modelica/Resources/Images/Blocks/Sources/CombiTimeTable.png"));
parameter Modelica.Blocks.Types.Smoothness smoothness = Modelica.Blocks.Types.Smoothness.LinearSegments "Smoothness of table interpolation"
annotation (Dialog(group = "Table data interpretation"));
parameter Modelica.Blocks.Types.Extrapolation extrapolation = Modelica.Blocks.Types.Extrapolation.LastTwoPoints "Extrapolation of data outside the definition range"
annotation (Dialog(group = "Table data interpretation"));
parameter Modelica.SIunits.Time timeScale(min = Modelica.Constants.eps) = 1 "Time scale of first table column"
annotation (
Dialog(group = "Table data interpretation"),
Evaluate = true);
parameter Real offset[:] = {0} "Offsets of output signals"
annotation (Dialog(group = "Table data interpretation"));
parameter Modelica.SIunits.Time startTime = 0 "Output = offset for time < startTime"
annotation (Dialog(group = "Table data interpretation"));
parameter Modelica.SIunits.Time shiftTime = startTime "Shift time of first table column"
annotation (Dialog(group = "Table data interpretation"));
parameter Modelica.Blocks.Types.TimeEvents timeEvents = Modelica.Blocks.Types.TimeEvents.Always "Time event handling of table interpolation"
annotation (Dialog(
group = "Table data interpretation",
enable = smoothness == Modelica.Blocks.Types.Smoothness.LinearSegments));
parameter Boolean verboseExtrapolation = false "= true, if warning messages are to be printed if time is outside the table definition range"
annotation (Dialog(
group = "Table data interpretation",
enable = extrapolation == Modelica.Blocks.Types.Extrapolation.LastTwoPoints or extrapolation == Modelica.Blocks.Types.Extrapolation.HoldLastPoint));
final parameter Modelica.SIunits.Time t_min = t_minScaled * timeScale "Minimum abscissa value defined in table";
final parameter Modelica.SIunits.Time t_max = t_maxScaled * timeScale "Maximum abscissa value defined in table";
final parameter Real t_minScaled = Internal.getTimeTableTmin(tableID) "Minimum (scaled) abscissa value defined in table";
final parameter Real t_maxScaled = Internal.getTimeTableTmax(tableID) "Maximum (scaled) abscissa value defined in table";
protected
final parameter Real p_offset[nout] = if size(offset, 1) == 1 then ones(nout) * offset[1] else offset "Offsets of output signals";
parameter Modelica.Blocks.Types.ExternalCombiTimeTable tableID = Modelica.Blocks.Types.ExternalCombiTimeTable(if tableOnFile then tableName else "NoName", if tableOnFile and fileName <> "NoName" and not Modelica.Utilities.Strings.isEmpty(fileName) then fileName else "NoName", table, startTime / timeScale, columns, smoothness, extrapolation, shiftTime / timeScale, if smoothness == Modelica.Blocks.Types.Smoothness.LinearSegments then timeEvents else if smoothness == Modelica.Blocks.Types.Smoothness.ConstantSegments then Modelica.Blocks.Types.TimeEvents.Always else Modelica.Blocks.Types.TimeEvents.NoTimeEvents, if tableOnFile then verboseRead else false) "External table object";
discrete Modelica.SIunits.Time nextTimeEvent(start = 0, fixed = true) "Next time event instant";
discrete Real nextTimeEventScaled(start = 0, fixed = true) "Next scaled time event instant";
Real timeScaled "Scaled time";
function readTableData = Modelica.Blocks.Tables.Internal.readTimeTableData "Read table data from text or MATLAB MAT-file";
equation
if verboseExtrapolation and (extrapolation == Modelica.Blocks.Types.Extrapolation.LastTwoPoints or extrapolation == Modelica.Blocks.Types.Extrapolation.HoldLastPoint) then
assert(noEvent(t_min <= time), "\nExtrapolation warning: Time (=" + String(time) + ") must be greater or equal\nthan the minimum abscissa value t_min (=" + String(t_min) + ") defined in the table.\n", level = AssertionLevel.warning);
assert(noEvent(time <= t_max), "\nExtrapolation warning: Time (=" + String(time) + ") must be less or equal\nthan the maximum abscissa value t_max (=" + String(t_max) + ") defined in the table.\n", level = AssertionLevel.warning);
end if;
if tableOnFile then
assert(tableName <> "NoName", "tableOnFile = true and no table name given");
else
assert(0 < size(table, 1) and 0 < size(table, 2), "tableOnFile = false and parameter table is an empty matrix");
end if;
if smoothness == Modelica.Blocks.Types.Smoothness.ConstantSegments then
for i in 1:nout loop
y[i] = p_offset[i] + Internal.getTimeTableValueNoDer(tableID, i, timeScaled, nextTimeEventScaled, pre(nextTimeEventScaled));
end for;
else
for i in 1:nout loop
y[i] = p_offset[i] + Internal.getTimeTableValue(tableID, i, timeScaled, nextTimeEventScaled, pre(nextTimeEventScaled));
end for;
end if;
when {pre(nextTimeEvent) <= time, initial()} then
nextTimeEventScaled = Internal.getNextTimeEvent(tableID, timeScaled);
nextTimeEvent = if nextTimeEventScaled < Modelica.Constants.inf then nextTimeEventScaled * timeScale else Modelica.Constants.inf;
end when;
timeScaled = time / timeScale;
annotation (
Documentation(
info = "<html>\n<p>\nThis block generates an output signal y[:] by <strong>constant</strong>,\n<strong>linear</strong> or <strong>cubic Hermite spline interpolation</strong>\nin a table. The time points and function values are stored in a matrix\n<strong>table[i,j]</strong>, where the first column table[:,1] contains the\ntime points and the other columns contain the data to be interpolated.\n</p>\n\n<p>\n<img src=\"modelica://Modelica/Resources/Images/Blocks/Sources/CombiTimeTable.png\"\n alt=\"CombiTimeTable.png\">\n</p>\n\n<p>\nVia parameter <strong>columns</strong> it can be defined which columns of the\ntable are interpolated. If, e.g., columns={2,4}, it is assumed that\n2 output signals are present and that the first output is computed\nby interpolation of column 2 and the second output is computed\nby interpolation of column 4 of the table matrix.\nThe table interpolation has the following properties:\n</p>\n<ul>\n<li>The interpolation interval is found by a binary search where the interval used in the\n last call is used as start interval.</li>\n<li>The time points need to be <strong>strictly increasing</strong> for cubic Hermite\n spline interpolation, otherwise <strong>monotonically increasing</strong>.</li>\n<li><strong>Discontinuities</strong> are allowed for (constant or) linear interpolation,\n by providing the same time point twice in the table.</li>\n<li>Via parameter <strong>smoothness</strong> it is defined how the data is interpolated:\n<pre>\n smoothness = 1: Linear interpolation\n = 2: Akima interpolation: Smooth interpolation by cubic Hermite\n splines such that der(y) is continuous, also if extrapolated.\n = 3: Constant segments\n = 4: Fritsch-Butland interpolation: Smooth interpolation by cubic\n Hermite splines such that y preserves the monotonicity and\n der(y) is continuous, also if extrapolated.\n = 5: Steffen interpolation: Smooth interpolation by cubic Hermite\n splines such that y preserves the monotonicity and der(y)\n is continuous, also if extrapolated.\n</pre></li>\n<li>Values <strong>outside</strong> of the table range, are computed by\n extrapolation according to the setting of parameter <strong>extrapolation</strong>:\n<pre>\n extrapolation = 1: Hold the first or last value of the table,\n if outside of the table scope.\n = 2: Extrapolate by using the derivative at the first/last table\n points if outside of the table scope.\n (If smoothness is LinearSegments or ConstantSegments\n this means to extrapolate linearly through the first/last\n two table points.).\n = 3: Periodically repeat the table data (periodical function).\n = 4: No extrapolation, i.e. extrapolation triggers an error\n</pre></li>\n<li>If the table has only <strong>one row</strong>, no interpolation is performed and\n the table values of this row are just returned.</li>\n<li>Via parameters <strong>shiftTime</strong> and <strong>offset</strong> the curve defined\n by the table can be shifted both in time and in the ordinate value.\n The time instants stored in the table are therefore <strong>relative</strong>\n to <strong>shiftTime</strong>.</li>\n<li>If time < startTime, no interpolation is performed and the offset\n is used as ordinate value for all outputs.</li>\n<li>The table is implemented in a numerically sound way by\n generating <strong>time events</strong> at interval boundaries, in case of\n interpolation by linear segments.\n This generates continuously differentiable values for the integrator.\n Via parameter <strong>timeEvents</strong> it is defined how the time events are generated:\n<pre>\n timeEvents = 1: Always generate time events at interval boundaries\n = 2: Generate time events at discontinuities (defined by duplicated sample points)\n = 3: No time events at interval boundaries\n</pre>\n For interpolation by constant segments time events are always generated at interval boundaries.\n For smooth interpolation by cubic Hermite splines no time events are generated at interval boundaries.</li>\n<li>Via parameter <strong>timeScale</strong> the first column of the table array can\n be scaled, e.g., if the table array is given in hours (instead of seconds)\n <strong>timeScale</strong> shall be set to 3600.</li>\n<li>For special applications it is sometimes needed to know the minimum\n and maximum time instant defined in the table as a parameter. For this\n reason parameters <strong>t_min</strong>/<strong>t_minScaled</strong> and\n <strong>t_max</strong>/<strong>t_maxScaled</strong> are provided and can be\n accessed from the outside of the table object. Whereas <strong>t_min</strong> and\n <strong>t_max</strong> define the scaled abscissa values (using parameter\n <strong>timeScale</strong>) in SIunits.Time, <strong>t_minScaled</strong> and\n <strong>t_maxScaled</strong> define the unitless original abscissa values of\n the table.</li>\n</ul>\n<p>\nExample:\n</p>\n<pre>\n table = [0, 0;\n 1, 0;\n 1, 1;\n 2, 4;\n 3, 9;\n 4, 16];\n extrapolation = 2 (default), timeEvents = 2\nIf, e.g., time = 1.0, the output y = 0.0 (before event), 1.0 (after event)\n e.g., time = 1.5, the output y = 2.5,\n e.g., time = 2.0, the output y = 4.0,\n e.g., time = 5.0, the output y = 23.0 (i.e., extrapolation via last 2 points).\n</pre>\n<p>\nThe table matrix can be defined in the following ways:\n</p>\n<ol>\n<li>Explicitly supplied as <strong>parameter matrix</strong> \"table\",\n and the other parameters have the following values:\n<pre>\n tableName is \"NoName\" or has only blanks,\n fileName is \"NoName\" or has only blanks.\n</pre></li>\n<li><strong>Read</strong> from a <strong>file</strong> \"fileName\" where the matrix is stored as\n \"tableName\". Both text and MATLAB MAT-file format is possible.\n (The text format is described below).\n The MAT-file format comes in four different versions: v4, v6, v7 and v7.3.\n The library supports at least v4, v6 and v7 whereas v7.3 is optional.\n It is most convenient to generate the MAT-file from FreeMat or MATLAB®\n by command\n<pre>\n save tables.mat tab1 tab2 tab3\n</pre>\n or Scilab by command\n<pre>\n savematfile tables.mat tab1 tab2 tab3\n</pre>\n when the three tables tab1, tab2, tab3 should be used from the model.<br>\n Note, a fileName can be defined as URI by using the helper function\n <a href=\"modelica://Modelica.Utilities.Files.loadResource\">loadResource</a>.</li>\n<li>Statically stored in function \"usertab\" in file \"usertab.c\".\n The matrix is identified by \"tableName\". Parameter\n fileName = \"NoName\" or has only blanks. Row-wise storage is always to be\n preferred as otherwise the table is reallocated and transposed.</li>\n</ol>\n<p>\nWhen the constant \"NO_FILE_SYSTEM\" is defined, all file I/O related parts of the\nsource code are removed by the C-preprocessor, such that no access to files takes place.\n</p>\n<p>\nIf tables are read from a text file, the file needs to have the\nfollowing structure (\"-----\" is not part of the file content):\n</p>\n<pre>\n-----------------------------------------------------\n#1\ndouble tab1(6,2) # comment line\n 0 0\n 1 0\n 1 1\n 2 4\n 3 9\n 4 16\ndouble tab2(6,2) # another comment line\n 0 0\n 2 0\n 2 2\n 4 8\n 6 18\n 8 32\n-----------------------------------------------------\n</pre>\n<p>\nNote, that the first two characters in the file need to be\n\"#1\" (a line comment defining the version number of the file format).\nAfterwards, the corresponding matrix has to be declared\nwith type (= \"double\" or \"float\"), name and actual dimensions.\nFinally, in successive rows of the file, the elements of the matrix\nhave to be given. The elements have to be provided as a sequence of\nnumbers in row-wise order (therefore a matrix row can span several\nlines in the file and need not start at the beginning of a line).\nNumbers have to be given according to C syntax (such as 2.3, -2, +2.e4).\nNumber separators are spaces, tab (\\t), comma (,), or semicolon (;).\nSeveral matrices may be defined one after another. Line comments start\nwith the hash symbol (#) and can appear everywhere.\nText files should either be ASCII or UTF-8 encoded, where UTF-8 encoded strings are only allowed in line comments and an optional UTF-8 BOM at the start of the text file is ignored.\nOther characters, like trailing non comments, are not allowed in the file.\n</p>\n<p>\nMATLAB is a registered trademark of The MathWorks, Inc.\n</p>\n</html>",
revisions = "<html>\n<p><strong>Release Notes:</strong></p>\n<ul>\n<li><em>April 09, 2013</em>\n by Thomas Beutlich:<br>\n Implemented as external object.</li>\n<li><em>March 31, 2001</em>\n by <a href=\"http://www.robotic.dlr.de/Martin.Otter/\">Martin Otter</a>:<br>\n Used CombiTableTime as a basis and added the\n arguments <strong>extrapolation, columns, startTime</strong>.\n This allows periodic function definitions.</li>\n</ul>\n</html>"),
Icon(
coordinateSystem(
preserveAspectRatio = true,
extent = {
{-100, -100},
{100, 100}}),
graphics = {
Polygon(
lineColor = {192, 192, 192},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid,
points = {
{-80, 90},
{-88, 68},
{-72, 68},
{-80, 90}}),
Line(
points = {
{-80, 68},
{-80, -80}},
color = {192, 192, 192}),
Line(
points = {
{-90, -70},
{82, -70}},
color = {192, 192, 192}),
Polygon(
lineColor = {192, 192, 192},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid,
points = {
{90, -70},
{68, -62},
{68, -78},
{90, -70}}),
Rectangle(
lineColor = {255, 255, 255},
fillColor = {255, 215, 136},
fillPattern = FillPattern.Solid,
extent = {
{-48, -50},
{2, 70}}),
Line(points = {
{-48, -50},
{-48, 70},
{52, 70},
{52, -50},
{-48, -50},
{-48, -20},
{52, -20},
{52, 10},
{-48, 10},
{-48, 40},
{52, 40},
{52, 70},
{2, 70},
{2, -51}})}),
Diagram(
coordinateSystem(
preserveAspectRatio = true,
extent = {
{-100, -100},
{100, 100}}),
graphics = {
Polygon(
points = {
{-80, 90},
{-88, 68},
{-72, 68},
{-80, 90}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Line(
points = {
{-80, 68},
{-80, -80}},
color = {95, 95, 95}),
Line(
points = {
{-90, -70},
{82, -70}},
color = {95, 95, 95}),
Polygon(
points = {
{90, -70},
{68, -62},
{68, -78},
{90, -70}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Rectangle(
extent = {
{-20, 90},
{20, -30}},
lineColor = {255, 255, 255},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid),
Line(points = {
{-20, -30},
{-20, 90},
{80, 90},
{80, -30},
{-20, -30},
{-20, 0},
{80, 0},
{80, 30},
{-20, 30},
{-20, 60},
{80, 60},
{80, 90},
{20, 90},
{20, -30}}),
Text(
extent = {
{-71, -42},
{-32, -54}},
textString = "offset"),
Polygon(
points = {
{-31, -30},
{-33, -40},
{-28, -40},
{-31, -30}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Polygon(
points = {
{-31, -70},
{-34, -60},
{-29, -60},
{-31, -70},
{-31, -70}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Line(
points = {
{-31, -31},
{-31, -70}},
color = {95, 95, 95}),
Line(
points = {
{-20, -30},
{-20, -70}},
color = {95, 95, 95}),
Text(
extent = {
{-42, -74},
{6, -84}},
textString = "startTime"),
Line(
points = {
{-20, -30},
{-80, -30}},
color = {95, 95, 95}),
Text(
extent = {
{-73, 93},
{-44, 74}},
textString = "y"),
Text(
extent = {
{66, -81},
{92, -92}},
textString = "time"),
Text(
extent = {
{-19, 83},
{20, 68}},
textString = "time"),
Text(
extent = {
{21, 82},
{50, 68}},
textString = "y[1]"),
Line(points = {
{50, 90},
{50, -30}}),
Line(
points = {
{80, 0},
{100, 0}},
color = {0, 0, 255}),
Text(
extent = {
{34, -30},
{71, -42}},
textString = "columns",
lineColor = {0, 0, 255}),
Text(
extent = {
{51, 82},
{80, 68}},
textString = "y[2]")}));
end CombiTimeTable;