Lookup Table file
Lookup tables are common in numerical simulations, allowing discrete points of empirical data or data calculated offline to be interpolated or extrapolated and used during a simulation.
Multics is capable of reading data from lookup tables of arbitrary dimensions. This page discusses the format of files that store data which can be used to populate such lookup tables.
Definitions
Independent and Dependent Variables
In general, an dependent variable refers to a quantity whose value is determined by one or more independent variables. In Multics lookup tables, the independent variables are the values that the user knows, and the dependent variable is the quantity that is looked up as a function of the independent variables’ values.
An arbitrary number of independent variables are allowed per lookup table in Multics, but only one dependent variable is allowed.
Dimension
The dimension of a lookup table refers to the number of independent variables that need to be specified to retrieve a unique value (the dependent variable) from the lookup table.
File Format
The details of the content of lookup table files and examples will be provided later. The format can be confusing at first, so if you’re struggling to understand it, skip ahead to later sections.
Suppose that we want to store a lookup table with N
dimensions. In general,
a lookup table file has the following format:
[DIM_SIZE_1] [DIM_SIZE_2] ... [DIM_SIZE_N]
[DIM_1_UNIT]: [DIM_1_MIN] [DIM_1_STEP] [DIM_1_APPROX_METHOD]
[DIM_2_UNIT]: [DIM_2_MIN] [DIM_2_STEP] [DIM_2_APPROX_METHOD]
...
[DIM_N_UNIT]: [DIM_N_MIN] [DIM_N_STEP] [DIM_N_APPROX_METHOD]
[DEPENDENT_VAR_UNIT_1]: [DEPENDENT_VAR_DATA_1] ...
...
[DEPENDENT_VAR_UNIT_M]: [DEPENDENT_VAR_DATA_M] ...
Section 1: Lookup Table Dimensions
[DIM_SIZE_1] [DIM_SIZE_2] ... [DIM_SIZE_N]
The first section of the lookup table file defines the number of dimensions N
(independent variables) in the lookup table, as well as the number of points
in each dimension.
The size of each dimension refers to the number of values of the independent variable at which the dependent variable is explicitly defined.
The sizes DIM_SIZE_1
, DIM_SIZE_2
, …, DIM_SIZE_N
of each of the
N
dimensions should be listed as whitespace-separated integers on a
single line.
Section 2: Independent Variable Spacing and Approximation Method
[DIM_1_UNIT]: [DIM_1_MIN] [DIM_1_STEP] [DIM_1_APPROX_METHOD]
[DIM_2_UNIT]: [DIM_2_MIN] [DIM_2_STEP] [DIM_2_APPROX_METHOD]
...
[DIM_N_UNIT]: [DIM_N_MIN] [DIM_N_STEP] [DIM_N_APPROX_METHOD]
The next section of the lookup table file provides the points (values of each independent variable) at which the dependent variable is explicitly defined, as well as the method for interpolating and/or extrapolating values along each dimension of the lookup table. All values should be whitespace-separated.
There are several important pieces of information defined for each independent
variable dimension. First, the independent variable must be assigned a unit,
specified by DIM_1_UNIT
, DIM_2_UNIT
, …, DIM_N_UNIT
for each of
the N
independent variables. A list of valid units can be found on the
Units page.
Next, the independent variable spacing is defined similar to a combination of NumPy’s
arange and
linspace
functions. The minimum value of each independent variable is given by DIM_1_MIN
,
DIM_2_MIN
, …, DIM_N_MIN
, and the corresponding step (the difference between
adjacent values of the independent variable) is given by DIM_1_STEP
,
DIM_2_STEP
, …, DIM_N_STEP
. Multics requires a constant “step size”
between values of all independent variables. Notice that these parameters,
combined with DIM_SIZE_1
, DIM_SIZE_2
, …, DIM_SIZE_N
from Section 1,
allow the independent variable values to be fully specified.
Finally, the last value provided on each line (DIM_1_APPROX_METHOD
,
DIM_2_APPROX_METHOD
, …, DIM_N_APPROX_METHOD
) is the approximation
method used to interpolate and extrapolate values along the given dimension in
the lookup table. The following approximation methods are valid:
|
Description |
---|---|
0 (nearest neighbor) |
Nearest neighbor interpolation and extrapolation |
1 (linear) |
Linear interpolation and extrapolation |
2 (saturation) |
Linear interpolation; no extrapolation (\(f(x) = f(x_{min})\) for \(x \le x_{min}\) and \(f(x) = f(x_{max})\) for \(x \ge x_{max}\)) |
3 (periodic) |
Linear interpolation; to extrapolate, it is assumed that the dependent
variable is periodic with period \(x_{max} - x_{min} + \Delta x\), where
\(\Delta x\) represents |
Warning
If using the periodic approximation method (3), notice that you should not include both endpoints in the lookup table (otherwise, the period would be \(x_{max} - x_{min}\), not \(x_{max} - x_{min} + \Delta x\)).
For example, suppose your independent variable is a cycle that repeats every rotation (\(360^\circ\)) and you are defining values in your lookup table every \(1^\circ\). In this case, your lookup table file should include data for the following angles: \(0^\circ, 1^\circ, 2^\circ, ..., 358^\circ, 359^\circ\).
Section 3: Dependent Variable Data
[DEPENDENT_VAR_UNIT_1]: [DEPENDENT_VAR_DATA_1] ...
...
[DEPENDENT_VAR_UNIT_M]: [DEPENDENT_VAR_DATA_M] ...
The final section of a lookup table file contains the values of the dependent variable. The values of the dependent variable for every possible combination of the independent variables must be specified.
Each line must begin with a unit DEPENDENT_VAR_UNIT_1
, …, DEPENDENT_VAR_UNIT_M
specifying the units in which the data on that line are stored.
The order in which the dependent variable values are given can be thought of as resulting from fixing the independent variables “from right to left.” To be exact:
Consider the order in which the independent variables are specified on the first line of the lookup table file (which is the same order they are listed in Section 2 of the file). First fix all dimensions
2
,3
, …,N
with their minimum values.On each row (after the unit), provide dependent variable values
DEPENDENT_VAR_DATA_1
for every value of the dimension 1 as whitespace-separated numbers; that is, each row of this section should haveDIM_SIZE_1
values.Then, increment dimension 2 and add subsequent rows (
DIM_SIZE_1
rows total) for each value of the second independent variable.Then, work “rightward” and increment dimension 3, and add rows for each value of dimension 2, and repeat for all
DIM_SIZE_3
values of dimension 3.Repeat this process until all
N
dimensions have been incremented.
For an example of how to implement this order, refer to the 3D lookup table example below.
Examples
1D Lookup Table
One potential application of a 1D lookup table is to approximate a function of one
variable. For instance, suppose we want to create a Multics lookup table that
approximates \(f(x) = x^2\) at \(x = 4, 5, 6, 7, 8, 9\). For this example,
we’ll assume that \(x\) has units of mm
and \(f(x)\) has units of kg
.
In this case, we have one dimension, and the independent variable is defined at 6
values. Therefore, DIM_SIZE_1 = 6
, DIM_1_MIN = 4
, and DIM_1_STEP = 1
.
Assuming that we want to use linear interpolation and “saturate” the dependent
variable at the boundary values, DIM_1_APPROX_METHOD = 2
. Finally, the values
of \(f(x)\) for \(x = 4, 5, 6, 7, 8, 9\) are \(16, 25, 36, 49, 64, 81\),
respectively.
Combining all these results, the final lookup table file would be (the numbers at the beginning of each line are line numbers, not part of the file):
16
2mm: 4 1 2
3kg: 16 25 36 49 64 81
Sample code to read this file is shown below:
1#include <iostream>
2
3#include "Multics/multics.h"
4
5int main()
6{
7 // Multics setup
8 CLargs args; IODict dictInOut(args);
9
10 // Define lookup table
11 LookupTable lookup_table_1D(dictInOut, "lookup_table_1D.txt");
12
13 // Display a few sample values read from the lookup table
14 std::cout
15 << "x = 0 | f(x) = " << lookup_table_1D.read({0}) << "\n"
16 << "x = 0.004 | f(x) = " << lookup_table_1D.read({0.004}) << "\n"
17 << "x = 0.0065 | f(x) = " << lookup_table_1D.read({0.0065})
18 << std::endl;
19
20 return 0;
21}
Running this code produces the following output:
x = 0 | f(x) = 16
x = 0.004 | f(x) = 16
x = 0.0065 | f(x) = 42.5
There are several important observations from this example:
The inputs to the
LookupTable::read()
method were in meters, and the output is returned in kilogramsThe output at \(x = 0\) was “saturated” at 16, the value provided at the minimum value of the independent variable.
The output at \(x = 0.0065\) was was interpolated with linear interpolation
2D Lookup Table
Next, consider a fluid power application: a hydraulic pump efficiency map. For this example, we’ll assume that the efficiency is based on two parameters: the pump speed and pressure. A hypothetical (and very unrealistic) sample efficiency map is shown below.
200 rpm |
300 rpm |
400 rpm |
500 rpm |
|
---|---|---|---|---|
50 bar |
0.85 |
0.86 |
0.87 |
0.88 |
75 bar |
0.89 |
0.90 |
0.91 |
0.92 |
100 bar |
0.93 |
0.94 |
0.95 |
0.96 |
To construct the Multics lookup table file, first notice that there are two independent variables: speed and pressure.
There are four speeds defined, each spaced at \(100\ rpm\) intervals beginning at
\(200\ rpm\). Thus, DIM_SIZE_1 = 4
, DIM_1_MIN = 200
, and DIM_1_STEP = 100
.
Assuming that we want to use linear interpolation and “saturate” the efficiency at
the maximum and minimum values defined in the lookup table, we would set
DIM_1_APPROX_METHOD = 2
.
The second independent variable is pressure. There are three pressure levels defined,
beginning at \(50\ bar\) and spaced at \(25\ bar\) intervals. Thus,
DIM_SIZE_2 = 3
, DIM_2_MIN = 50
, and DIM_2_STEP = 25
. To use the same
methods for interpolation/extrapolation as speed, we could set DIM_2_APPROX_METHOD = 2
.
The complete lookup table file would be (the numbers at the beginning of each line are line numbers, not part of the file):
14 3
2rev/min: 200 100 2
3bar: 50 25 2
4-: 0.85 0.86 0.87 0.88
5-: 0.89 0.90 0.91 0.92
6-: 0.93 0.94 0.95 0.96
Sample code to read this file is shown below:
1#include <cmath>
2#include <iostream>
3
4#include "Multics/multics.h"
5
6double bar2PaAbs(double x) {
7 return 1e5 * (x + 1.01325);
8}
9
10double rpm2radsec(double x) {
11 return x * M_PI / 30;
12}
13
14int main()
15{
16 // Multics setup
17 CLargs args; IODict dictInOut(args);
18
19 // Define lookup table
20 LookupTable lookup_table_2D(dictInOut, "lookup_table_2D.txt");
21
22 // Display a few sample values read from the lookup table
23 std::cout
24 << "x = 300 rpm, y = 50 bar | f(x,y) = "
25 << lookup_table_2D.read({rpm2radsec(300), bar2PaAbs(50)}) << "\n"
26 << "x = 400 rpm, y = 75 bar | f(x,y) = "
27 << lookup_table_2D.read({rpm2radsec(400), bar2PaAbs(75)}) << "\n"
28 << "x = 325 rpm, y = 43 bar | f(x,y) = "
29 << lookup_table_2D.read({rpm2radsec(325), bar2PaAbs(43)}) << "\n"
30 << std::endl;
31
32 return 0;
33}
Running this code produces the following output:
x = 300 rpm, y = 50 bar | f(x,y) = 0.86
x = 400 rpm, y = 75 bar | f(x,y) = 0.91
x = 325 rpm, y = 43 bar | f(x,y) = 0.8625
3D Lookup Table
Next, consider an extension of the 2D lookup table: suppose we now want to store hydraulic pump efficiency maps at multiple points in time in the same file. For this example, we’ll assume that the efficiency is based on three parameters: the pump speed, pump pressure, and time \(t\) that the pump has been operating (time is the third dimension of this lookup table). A set of hypothetical (and very unrealistic) sample efficiency maps is shown below.
Efficiency at t = 0
200 rpm |
300 rpm |
400 rpm |
500 rpm |
|
---|---|---|---|---|
50 bar |
0.85 |
0.86 |
0.87 |
0.88 |
75 bar |
0.89 |
0.90 |
0.91 |
0.92 |
100 bar |
0.93 |
0.94 |
0.95 |
0.96 |
Efficiency at t = 10 hours
200 rpm |
300 rpm |
400 rpm |
500 rpm |
|
---|---|---|---|---|
50 bar |
0.35 |
0.36 |
0.37 |
0.38 |
75 bar |
0.39 |
0.40 |
0.41 |
0.42 |
100 bar |
0.43 |
0.44 |
0.45 |
0.46 |
To construct the Multics lookup table file, first notice that there are three independent variables: speed, pressure, and time.
The first two dimensions, speed and pressure, can be handled in exactly the same way as the 2D lookup table example:
There are four speeds defined, each spaced at \(100\ rpm\) intervals beginning at \(200\ rpm\). Thus,
DIM_SIZE_1 = 4
,DIM_1_MIN = 200
, andDIM_1_STEP = 100
. Assuming that we want to use linear interpolation and “saturate” the efficiency at the maximum and minimum values defined in the lookup table, we would setDIM_1_APPROX_METHOD = 2
.The second independent variable is pressure. There are three pressure levels defined, beginning at \(50\ bar\) and spaced at \(25\ bar\) intervals. Thus,
DIM_SIZE_2 = 3
,DIM_2_MIN = 50
, andDIM_2_STEP = 25
. To use the same methods for interpolation/extrapolation as speed, we could setDIM_2_APPROX_METHOD = 2
.
The third independent variable, time, can be handled in nearly the same way as speed
and pressure. There are two times defined, \(t = 0\) and \(t = 10\), so
DIM_SIZE_3 = 2
, DIM_3_MIN = 0
, and DIM_3_STEP = 10
. Assuming we want to
use linear interpolation and extrapolation, we could set DIM_2_APPROX_METHOD = 1
.
The complete lookup table file would be (the numbers at the beginning of each line are line numbers, not part of the file):
14 3 2
2rev/min: 200 100 2
3bar: 50 25 2
4hr: 0 10 1
5-: 0.85 0.86 0.87 0.88
6-: 0.89 0.90 0.91 0.92
7-: 0.93 0.94 0.95 0.96
8-: 0.35 0.36 0.37 0.38
9-: 0.39 0.40 0.41 0.42
10-: 0.43 0.44 0.45 0.46
Notice how the data are grouped:
The lookup table file contains data for every possible combination of the independent variable values.
In Line 1, the independent variables are listed in the following order: speed (dimension 1), pressure (dimension 2), and time (dimension 3).
To add data to the table, we fix values from “right to left.” That is, first, we fix dimensions 2 and 3; we set pressure and time to their minimum values of \(50\ bar\) and \(0\ hr\), respectively. Then, the first line of data (Line 5) contains the (four) dependent variable values, one for each speed.
Next, we work “rightward,” keeping dimension 3 (time) fixed at \(0\ hr\), but incrementing dimension 2 (pressure) to \(75\ bar\). Then, Line 6 in the lookup table file contains data for this combination (\(0\ hr\), \(75\ bar\)) for each of the four speeds.
Then, we repeat Step 4: increment pressure once again to \(100\ bar\), and Line 7 of the lookup table file contains the pump efficiency for each speed for \(0\ hr\) and \(100\ bar\).
Now, we have added data for all possible combinations of pressure and speed for \(t = 0\) in Lines 5-7. Therefore, we work “rightward” once again, this time incrementing time to \(t = 10\ hr\). We can then repeat Steps 3-5 to add three new lines to the lookup table file (Lines 8-10), containing the pump efficiencies for all combinations of speed and pressure for \(t = 10\ hr\).
This completes the 3D lookup table, but notice that had there been more than two time steps, we would simply have needed to repeat Steps 3-5 for each time step to add efficiencies for all combinations of speed and pressure.
Notice that although a 3D lookup table example was discussed, this methodology can be extended to an arbitrary number of dimensions. For a higher number of dimensions, the pattern for the order in which data are stored is the same: fix the independent variables “from right to left.” Each line contains dependent variable values for each value of the first independent variable. Successive lines correspond to incrementing the second independent variable, followed by incrementing the third, and so on.
Sample code to read this file is shown below:
1#include <cmath>
2#include <iostream>
3
4#include "Multics/multics.h"
5
6double bar2PaAbs(double x) {
7 return 1e5 * (x + 1.01325);
8}
9
10double rpm2radsec(double x) {
11 return x * M_PI / 30;
12}
13
14double hr2sec(double x) {
15 return x * 3600;
16}
17
18int main()
19{
20 // Multics setup
21 CLargs args; IODict dictInOut(args);
22
23 // Define lookup table
24 LookupTable lookup_table_3D(dictInOut, "lookup_table_3D.txt");
25
26 // Display a few sample values read from the lookup table
27 std::cout
28 << "x = 300 rpm, y = 50 bar, z = 0 hr | f(x,y,z) = "
29 << lookup_table_3D.read({rpm2radsec(300), bar2PaAbs(50), hr2sec(0)}) << "\n"
30 << "x = 300 rpm, y = 50 bar, z = 10 hr | f(x,y,z) = "
31 << lookup_table_3D.read({rpm2radsec(300), bar2PaAbs(50), hr2sec(10)}) << "\n"
32 << "x = 400 rpm, y = 75 bar, z = 0 hr | f(x,y,z) = "
33 << lookup_table_3D.read({rpm2radsec(400), bar2PaAbs(75), hr2sec(0)}) << "\n"
34 << "x = 400 rpm, y = 75 bar, z = 10 hr | f(x,y,z) = "
35 << lookup_table_3D.read({rpm2radsec(400), bar2PaAbs(75), hr2sec(10)}) << "\n"
36 << "x = 250 rpm, y = 90 bar, z = 5 hr | f(x,y,z) = "
37 << lookup_table_3D.read({rpm2radsec(250), bar2PaAbs(90), hr2sec(5)}) << "\n"
38 << std::endl;
39
40 return 0;
41}
Running this code produces the following output:
x = 300 rpm, y = 50 bar, z = 0 hr | f(x,y,z) = 0.86
x = 300 rpm, y = 50 bar, z = 10 hr | f(x,y,z) = 0.36
x = 400 rpm, y = 75 bar, z = 0 hr | f(x,y,z) = 0.91
x = 400 rpm, y = 75 bar, z = 10 hr | f(x,y,z) = 0.41
x = 250 rpm, y = 90 bar, z = 5 hr | f(x,y,z) = 0.669
Comments, Whitespace, and Line Endings
Any content on a line following a
#
character is ignored. Full-line comments (lines with no content other than a comment) are not permitted.Items denoted “whitespace-separated” may be separated by either spaces or tab (
\t
) characters.Blank lines are not permitted.
On Linux and MacOS, LF line endings (
\n
) must be used. On Windows, either LF (\n
) or CRLF (\r\n
) line endings may be used.