Contrib equationReader/Programming

From OpenFOAMWiki

Most of the programming features can be gleaned from the equationReader demo application. Please also refer to that.

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;