This C++ library is used for LUT (Look-Up Tables).
It implements a lookup for normalized LUT with shared memory usage.
| Dependency | Version | Notes |
|---|---|---|
| CMake | 3.20+ | |
| C++ compiler | C++20 | |
| HDF5 | C library + headers | Required by HighFive to read/write the LUT's HDF5 format (see below). Not fetched automatically — must already be installed/loadable on your system. OpenFOAM installations commonly pull this in already, since OpenFOAM itself links HDF5. |
| HighFive v3.2.0 | Fetched automatically via CMake's FetchContent — no separate install needed. |
cmake -S . -B build
cmake --build build -j$(nproc)
cmake --install buildPass -DBUILD_WITH_TESTING=ON to also build the test suite under test/.
This library is used standalone here, but within OxySim-129 it's built automatically as a submodule (add_subdirectory(extern/flut-reader)), so you don't need to build it separately there.
The project consists of 6 folders.
- src
- Contains the main implementations:
containers.*: Classes / Structs for the handling of the table dataparser/*: Available parsers to fill the data containersabstract_table.hpp: abstract interface definition of the LUTlookup_table.*: implementation of the abstract interfaceinterpolator.*: implementation of the ND interpolation
- Contains the main implementations:
- test
- contains testsuites for testing the library
- data
- contains data files used for testing
- scripts
- contains helpful scripts that are not a part of the library
- flut_creation_demo
- a worked example (
flamelet_demo.ipynb) that builds a premixed-flamelet LUT with Cantera from scratch and writes it out in the HDF5 format described below, for use with OxySim-129; see "Building a Lookup Table" below. Needs its own Python environment, perflut_creation_demo/requirements.txt(the notebook's own first cell documents how to set one up)
- a worked example (
- doc
- images referenced from this README (
doc/images/)
- images referenced from this README (
A LUT is a single HDF5 file. It can be built with any tool that writes HDF5 (the demo in
flut_creation_demo/flamelet_demo.ipynb builds one from scratch with Cantera + h5py, using the
helpers in flut_creation_demo/h5_utilities.py). This section documents the layout the C++ reader
(src/parser/pyflut-hdf5-highfive.cpp, fileType = "pyflut") expects.
The file has three required top-level entries:
/data— a single dense, rectilinear array of shape(dim0_size, dim1_size, ..., dimN_size, n_outputs). The first N axes are the input dimensions, in the order given by each input variable'sIndex(see below); the last axis holds the output variables, in the order given by/output_dict. Only up to 6 input dimensions (N ≤ 6) are supported —Interpolator::interpolatethrows for more./output_dict— one scalar integer dataset per output variable, named after the variable, whose value is that variable's index into the last axis of/data. Indices must be a contiguous0..n_outputs-1range./input_dict/<name>— one group per input dimension (<name>need not match the physical variable name the library exposes, seePhysical_namebelow), each containing:Index(int) — the axis of/datathis dimension corresponds to (0..N-1).Values(1D array) — the grid coordinates along this axis, strictly monotonically increasing. These are the valueslookup()compares against, so if the dimension is normalized they must be given already in normalized space (e.g.[0, 1]), not physical units.Normalization_settings/Normalize(bool) — whether this dimension is normalized. Iffalse,Valuesand the corresponding entry of the input vector passed tolookup()are both in physical units, and no further settings are needed.Normalization_settings/Settings/*(only ifNormalizeistrue):Physical_name(string) — the variable name exposed to callers viagetInputVariables()and expected at this position inlookup()'s input vector, in physical (un-normalized) units. This can differ from<name>, e.g. groupHnorm→Physical_name = "h".Normalization_type(string) —"default"or"variance"(see below).
Two normalization types are implemented (FLUT::Normalization in src/lookup_table.cpp):
"default"— min-max normalization,xnorm = (x - xmin) / (xmax - xmin), clamped to[0, 1]outside the bounds. Requires two extraSettingsentries,Min_name_normalizationandMax_name_normalization, giving the names of two output variables (i.e. they must also exist in/output_dict//data) that hold this dimension's lower/upper physical bound at each point of the preceding dimensions — these bounds are interpolated the same way as any other output before being used to normalize the input value. This allows the bounds to vary across the table (e.g. an enthalpy range that depends on mixture fraction), not just be a single global min/max."variance"— scales a variance-like quantity by the maximum variance a[0,1]-bounded variable of a given mean can have:xnorm = variance / (baseValue * (1 - baseValue)), clamped to[0, 1]. RequiresBase_variable_name, thePhysical_nameof another input variable in the same table; its physical value from thelookup()input vector is used asbaseValue. Indata/06_testFLUT_PDF.h5, theZvar_normdimension usesBase_variable_name = "Z"to normalize a mixture-fraction variance byZ * (1 - Z).
/metadata and /pyflut_settings groups (arbitrary nested key-value data, see
flut_creation_demo/h5_utilities.py:write_dictionary_to_hdf5) are conventional but not read by
the C++ parser — they're for downstream tooling (e.g. a Python pyflut reader) or provenance,
not required by this library.
The library is designed to use the LookupTable via the interfaces defined in abstract_table.hpp.
Some example functionalities are given below:
// Initialization
// Initialization with all outputVariables in the table
std::string pathToTable = "table.h5";
std::vector<std::string> outputVariables = {};
FLUT::LookupTable table(pathToTable, outputVariables, "pyflut");
// Initialization with a subset of variables
std::string pathToTable = "table.h5";
std::vector<std::string> outputVariables = {"alpha", "T", "visc"};
FLUT::LookupTable table(pathToTable, outputVariables, "pyflut");
// Initialization and loading of output variable data separately
// Initialize table
std::string pathToTable = "table.h5";
FLUT::LookupTable table(pathToTable);
// Load outputVariables
std::vector<std::string> outputVariables = {"alpha", "T", "visc"};
table.readOutputVariableData(outputVariables);
// Perform lookup
std::vector<double> inputValues = {0.5, 0.4, 0.3};
std::vector<double> resultVector = table.lookup(inputValues);
// Retrieve information on LUT data
// Get inputVariables
std::vector<std::string> inputVariableNames = table.getInputVariables();
// Get availableOutputVariables
std::vector<std::string> outputVariableNames = table.getAvailableOutputVariables();
// Get loadedOutputVariables
std::vector<std::string> outputVariableNames = table.getLoadedOutputVariables();
// Print information of the table to commandLine
table.printInfo();
// Some additional functions for sanity checks
// Checks if the outputVariable is inside
// the data and throws an error otherwise.
table.containsOutputVariable("variableName")
The DataSharedMemory defined in src/containers.* implements shared memory handling. In order to prevent memory duplication the data is loaded into a POSIX Shared Memory Object using shm_open and then mapped into the virtual memory address of the process using this data via mmap. The shared memory object can be viewed by listing the files under /dev/shm. The library automatically cleans the initialized shared memory object but if needed it can also be manually cleared by calling rm on it.
FLUT receives physical input quantities from an external software and calls the table implementation that is hardcoded into the code. The physical quantities that belong to a normalized input variable in the data table are normalized and the rest are passed as it is to the next step. Inside the hypercube construction values that surround the normalized quantities are selected from the data table and passed into interpolation. During the interpolation step a multivariate linear interpolation is applied to the points of the hypercube. The result of the interpolation is passed back to the external software.
The call graph below shows the function call hierarchy and amounts for Hdf5TableMemOpt. The graph is read using a depth first approach left to right and top to bottom. d is the amount of input dimensions.
A list of functions and short explanations that are implemented in the LookupTable class
- lookup
- top level function that calls normalize, constructHypercube, interpolate
- findIndexForDimension
- finds the first value greater than the input value
- getTableEntryAtIndex
- gets the values for the requested outputs from the buffer corresponding to the indices given to the function
- interpolate
- applies multivariate linear interpolation to the inputs
- constructHypercube
- creates a hypercube to be used in interpolation
- normalize
- calls physicalToNormalize for normalization and creates a vector of dimension bounds using the normalized values
- physicalToNormalized
- if the input dimension is normalized calls normalizeValue, else returns the input value as is
- normalizeValue
- normalizes the value and returns it
- extractIndex
- generates an index of a hypercube
- getNormEntryAtIndex
- retrieves the interval on which a value should be normalized, based on the previously calculated indices
The code is based on a student computer science project at TU Darmstadt by Adrian Schmidt, Berkehan Atikeler, Florian Magin, Gregor Carmesin, Sargon Malko.
The project was supervised by Matthias Steinhausen who further refactored and optimized the code. More recent modifications were made by Philipp Koob and Pascal Steffens. For questions or bugs please either open an issue or write to steffens@stfs.tu-darmstadt.de.
If you use this software, please cite it using the metadata in CITATION.cff, or click "Cite this repository" in the GitHub sidebar. A DOI for citing a specific version is also available: 10.5281/zenodo.23238258.

