Contrib equationReader/Programming
Most of the programming features can be gleaned from the equationReader demo application. Please also refer to that.
Contents
1 Creating an equationReader object
To add equationReader to an application:
- Put #include "IOEquationReader.H" at the top of your main source file;
- Put #include "createEquationReader.H" somewhere after createTime;
2 Adding data sources
You need to add data sources - this is where equationReader looks for its variables.
2.1 Beware of duplicate sources
Currently, equationReader does not check if you are adding multiple variables of the same name. When this happens, you never know which source will be used. I didn't add it because it didn't occur to me until I started writing this paragraph. Expect it in the future.
2.2 Is the data permanent?
Data must be permanently available. For instance, mesh.C() is a valid data source because it returns a &reference. But turbulence->R() is not valid because it returns an object (or tmp<object>), and hence is derived from other permanent sources.
To use derived data sources, there are two options.
1. Create a permanent copy, and update it at every timestep. This is demonstrated in the equationReaderDemo application.
2. Create an activeVariable.
2.3 Active variables
(Advanced developers)
An activeVariable is one that does not permanently store its data, and provides values on-demand to equationReader. The key to this is it must be able to calculate a single cell value on-demand, and not the entire field at once. The interface is given in the equationReader/equationVariable/equationVariable.H file.
2.4 Functions to add data sources
To add data sources:
- for scalars, dimensionedScalars, scalarFields, GeometricScalarFields, etc.:
eqns.scalarSources().addSource(scalarObject);
- or if it doesn't have its own name (i.e. scalars and scalarFields) or you want to assign it a different name:
eqns.scalarSources().addData(scalarObject, name);
- for vectors, dimensionedVectors, vectorFields, GeometricVectorFields, etc.:
eqns.vectorSources().addSource(vectorObject);
- or if it doesn't have its own name (i.e. scalars and scalarFields) or you want to assign it a different name:
eqns.vectorSources().addSource(vectorObject, name);
- and so on for other Types (tensor, diagTensor, symmTensor, and sphericalTensor);
- for dictionaries or activeVariables:
eqns.addSource(dataObject);
3 Reading in the equations
To read equations from a dictionary use:
eqns.readEquation(dictionaryName, equationName);
4 Searching the equations
equationReader allows you to search its equations. Similar to the dictionary object, this will return true if equationName exists:
eqns.found(equationName);
The evaluate functions below call for eqnNameOrIndex. This means you can either use a word (the equationName), or a label (the equationIndex). The equationIndex is faster, as equationReader doesn't have to perform its own lookup. Never assume the equationIndex is equal to the order in which the equations were read. If the equations depend on one another, they may not always be in the same index. To learn the equationIndex, use:
equationIndex = eqns.lookup(equationName);
5 Evaluating equations
Once you are done adding data sources, and reading equations, you can start evaluating equations.
5.1 All data sources required
When evaluating an equation, equationReader needs access to all the possible variables and other equations that equation might depend on. If that variable or equation isn't found, equationReader produces a FatalError. Therefore it is a mistake to try adding more data sources after the first evaluation.
5.2 No mesh available
equationReader doesn't care about the mesh... all it cares about are the sizes of the the fields. The size of the variable fields must match. Index checking is expensive, so it is only available in FULLDEBUG mode. These rules apply:
- a single-element variable (e.g. a scalar, or a dimensionedVector) is assumed uniform throughout the entire domain, and can be used in any equation;
- a field variable (e.g. a scalarField, or a DimensionedVectorField) does not have a boundary field, therefore it is only available to equations of other fields or internal fields. Attempting to use it in a GeometricField is a mistake; and
- a GeometricField variable can be used with any equation.
There are two indices to indicate field / boundary field position:
- cellIndex - this is the position within a field (e.g. cell number in the internal field, or face number on a boundary patch);
- geoIndex:
- 0 = the internal field;
- greater than 0 = the boundary patches. The geoIndex is therefore 1-indexed on the boundaryField: patchI = geoIndex - 1.
If you omit either of these in the evaluation equations, they are assumed equal to zero.
5.3 Evaluation functions
- for single element types:
scalarA = eqns.evaluateScalar ( eqnNameOrIndex, [cellIndex], [geoIndex] ); vectorA.x() = eqns.evaluateScalar ( xEqnNameOrIndex, [cellIndex], [geoIndex] ); // and so on for all its components tensorA.xx() = eqns.evaluateScalar ( xxEqnNameOrIndex, [cellIndex], [geoIndex] ); // and so on for all its components
- for dimensionedScalars:
dimensionedScalarA = eqns.evaluateDimensioned ( eqnNameOrIndex, [cellIndex], [geoIndex] );
- for other dimensionedTypes - there is no elegant dimensionChecking... use this hack:
vectorA.x() = eqns.evaluateScalar ( xEqnNameOrIndex, [cellIndex], [geoIndex] ); vectorA.y() = eqns.evaluateScalar ( yEqnNameOrIndex, [cellIndex], [geoIndex] ); vectorA.z() = eqns.evaluateScalar ( zEqnNameOrIndex, [cellIndex], [geoIndex] ); vectorA.dimensions() = eqns.evaluateDimensions(xEqnNameOrIndex); vectorA.dimensions() = eqns.evaluateDimensions(yEqnNameOrIndex); vectorA.dimensions() = eqns.evaluateDimensions(zEqnNameOrIndex);
- for scalarFields:
eqns.evaluateScalarField(resultScalarField, eqnNameOrIndex, [geoIndex]);
- or
eqns.evaluateTypeField ( resultScalarField, dummyWord, eqnNameOrIndex, [geoIndex] );
- for vectorFields:
eqns.evaluateTypeField ( resultVectorField, "x", // this is the component name xEqnNameOrIndex, [geoIndex] ); // and so on for the "y" and "z" components
- and so on for other typeFields;
- for DimensionedScalarFields:
eqns.evaluateDimensionedScalarField ( resultDimensionedScalarField, eqnNameOrIndex, [geoIndex] );
- do not use evaluateDimensionedTypeField - this will fail for scalars;
- for DimensionedVectorFields:
eqns.evaluateDimensionedTypeField ( resultDimensionedVectorField, xEqnNameOrIndex, "x", [geoIndex] ); // and so on for the "y" and "z" components
- and so on for other DimensionedTypeFields;
- for GeometricScalarFields
eqns.evaluateGeometricScalarField ( resultGeometricScalarField, eqnNameOrIndex );
- do not use evaluateGeometricTypeField - this will fail for scalars;
- for GeometricVectorFields
eqns.evaluateGeometricTypeField ( resultGeometricTypeField, "x", xEqnNameOrIndex ); // and so on for the "y" and "z" components
- and so on for other GeometricTypeFields;