Contents

The following contains an overview of a typical case including a field-based Lagrangian cloud. The syntax and layout on disk are explained, as are many of the settings. Comparison with the particle-based Lagrangian cloud is made to aid transition for users familiar with the old system and syntax.

1 Case Layout

The following shows the directory layout for a basic case with a cloud named myCloud:

myCase
├── 0
│   ├── epsilon
│   ├── k
│   ├── Lagrangian
│   │   └── myCloud
│   │       ├── d
│   │       ├── number
│   │       └── U
│   ├── nut
│   ├── p
│   └── U
├── Allclean
├── Allrun
├── constant
│   ├── fvModels
│   ├── g
│   ├── Lagrangian
│   │   └── myCloud
│   │       ├── LagrangianModels
│   │       └── physicalProperties
│   ├── momentumTransport
│   └── physicalProperties
└── system
    ├── blockMeshDict
    ├── controlDict
    ├── decomposeParDict
    ├── functions
    ├── fvSchemes
    ├── fvSolution
    └── Lagrangian
        └── myCloud
            ├── LagrangianSchemes
            └── LagrangianSolution

The settings related to the cloud are all within subdirectories named Lagrangian/myCloud. The particle-based Lagrangian layout is similar, except that it uses an un-capitalised lagrangian outer directory name. This is necessary (in both cases) for it to be possible to have multiple clouds in operation in a single case.

The new Lagrangian files mirror those of finite-volume. Fields are defined in 0/Lagrangian/myCloud, models and physical properties in constant/Lagrangian/myCloud, and schemes and solution parameters in system/Lagrangian/myCloud. These sections will be explained in greater depth in the sections below.

1.1 FvModel and FunctionObject Clouds

A cloud is constructed and solved by either an fvModel or a functionObject.

An fvModel cloud is “two-way” coupled; i.e., the drag, heat transfer, mass transfer, etc…, applies to both the Lagrangian particles and the carrier phase. A functionObject cloud, by contrast, is only “one-way” coupled. The source terms that the Lagrangian particles experience are not applied to the carrier phase. The carrier is therefore unaffected by a functionObject cloud. One-way coupled clouds might be used when the particle loading is very low, or when the cloud is being used only for post-processing purposes.

The evolution sequence of the cloud and its carrier are also slightly different between the fvModel or a functionObject clouds. An fvModel cloud will be solved for at the start of the time-step before the carrier phase equations have been solved, whilst a functionObject cloud will be solved at the end. The principle is that an fvModel cloud is primarily used to provide sources to an Eulerian solution, so Eulerian is thought to be more “dependent” on Lagrangian than the other way around. The Lagrangian evolution and the resulting Eulerian sources should therefore be computed before the carrier equations are solved. A functionObject cloud, on the other hand, does not affect the carrier, so it makes sense for it to solve after the carrier so that it is using the most up-to-date carrier properties possible.

An fvModel can also be corrected by the outer PIMPLE iteration, whilst a functionObject cloud cannot. Outer-iteration is new to the field-based Lagrangian library; the particle-based library does not support it.

1.2 Cloud Types

There are currently four different clouds. These control what fundamental properties are solved and are therefore analogous to the solvers in the Eulerian finite volume system. The clouds are:

  • kinematicParticle: A cloud with spherical, constant density, particles in which the carrier interaction is characterised by a cloud/carrier density ratio. Neither the cloud nor the carrier needs the absolute value of the density to be defined. The carrier must therefore also have constant density.

  • kinematicParcel: As kinematicParticle, but each Lagrangian element is a “parcel” which represents multiple particles

  • particle: A cloud with spherical, variable density, particles

  • parcel: As particle, but but each Lagrangian element is a “parcel” which represents multiple particles

All these clouds have a velocity field and a diameter field. The non-kinematic clouds also have a density field. The parcel clouds also have a number field. The number field controls how many identical physical particles are represented by each Lagrangian element, or “parcel”. Parcels are useful because they help manage the expense of simulating large numbers of physical particles, and they also provide the ability to simultaneously specify the number, size (distribution) and volume or mass flow rate of particles at an injection.

The case shown above has d, number and U fields, so it is set up to simulate an kinematicParcel cloud.

As the field-based Lagrangian library is developed, the intention is to add more clouds with additional fundamental properties such as temperature, energy, species fractions, and so on.

Each cloud has a corresponding fvModel and a functionObject with the same name as the cloud, plus the suffix Cloud. So, to enable an kinematicParcel cloud as an fvModel, the following can be added in constant/fvModels:

myCloud
{
    type        kinematicParcelCloud;
    libs        ("libLagrangianCloud.so");
}

And the equivalent to create a functionObject cloud in system/functions is:

myCloud
{
    type        kinematicParcelCloud;
    libs        ("libLagrangianCloud.so");
    executeControl timeStep;
    writeControl writeTime;
}

Note that the cloud name is taken from the name of the dictionary, which is also the name of the fvModel or functionObject.

This system, where every cloud has its own fvModel/functionObject is somewhat different to the selection of the particle-based clouds. Those clouds have a single fvModel and functionObject for all cloud types, and then a sub-selection system for the specific cloud model. This additional level of indirection was considered confusing and not necessary in the field-based system.

2 Lagrangian Fields

Lagrangian properties are stored in fields. As for finite-volume, the user needs to provide these fields at the start time to initialise the simulation. The syntax of these fields is very similar to that for finite-volume.

An example velocity field file (0/Lagrangian/myCloud/U), is shown below:

/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Version:  dev
     \\/     M anipulation  |
\*---------------------------------------------------------------------------*/
FoamFile
{
    format      ascii;
    class       LagrangianVectorField;
    location    "0/Lagrangian/myCloud";
    object      U;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions      [velocity];

internalField   uniform (0 0 0);

boundaryField
{
    #includeEtc "caseDicts/setConstraintTypes"

    walls
    {
        type            reboundVelocity;
        e               0.97;
        mu              0.09;
    }

    open
    {
        type            escapeVelocity;
    }
}


sources
{
    injection1
    {
        type            coneVelocity;
        Umean           (1 0 0);
        thetaInner      0 [deg];
        thetaOuter      5 [deg];
    }
}

// ************************************************************************* //

The four top level entries are as follows:

  • The dimensions entry states the dimensions are that of velocity.

  • The internalField entry states that all particles are initially at rest. It is possible/probable that there are no particles at the start. In which case an empty list could be specified instead. E.g.:

         internalField   nonuniform 0();
    

  • The boundaryField entry determines how the property is affected when a particle encounters a patch. In this case, on the walls patch the velocity is modified to represent a rebound, and on the open patch the velocity is not modified and the particle escapes.

    In most simple cases, velocity is the only field that is instantaneously affected by the boundary. If a field is not affected then a calculated condition can be specified instead. E.g.:

         boundaryField
         {
             #includeEtc "caseDicts/setConstraintTypes"
             ".*"
             {
                 type            calculated;
             }
         }
    

  • The sources entry defines what property values are applied to elements that are created by injection-like models and/or what values are added to by transfer-like models. In this case, it states that the injection1 model should initialise the velocities of injected elements so that they spray in a cone shape with a given angle and mean velocity.

    In the 0/Lagrangian/myCloud/d field file, this entry might be used to specify that all injected particles have the same diameter. E.g.:

         sources
         {
             injection1
             {
                 type            uniformFixedValue;
                 uniformValue    1 [mm];
             }
         }
    

    And similarly, the uniformFixedValue condition could be used in the 0/Lagrangian/myCloud/number field file to specify a certain number of particles per parcel.

    Alternatively, a common requirement is to create a distribution of particle diameters, and then adjust the number of particles per parcel to match a desired flow rate. This can be achieved with a pair of interacting source conditions: distributionDiameter and flowRateNumber. For example, to create a distribution of diameters in the 0/Lagrangian/myCloud/d file, the following could be used:

         sources
         {
             injection1
             {
                 type            distributionDiameter;
                 distribution
                 {
                     type            RosinRammler;
                     Q               0;
                     min             0.1 [mm];
                     max             1.5 [mm];
                     d               1.4 [mm];
                     n               2 [];
                 }
             }
         }
    

    The flow rate can then be controlled in the 0/Lagrangian/myCloud/number as follows:

         sources
         {
             injection1
             {
                 type            flowRateNumber;
                 uniformSize     volume;
                 volumetricFlowRate 0.7 [l/s];
             }
         }
    

    Note the uniformSize entry in the flowRateNumber condition. This specifies what “size” of the parcels is kept constant across all the injected parcels. In this case, the injected parcels all have the same volume (so parcels with smaller diameters have a greater number of particles). Other possibilities are number, surfaceArea and mass.

3 Lagrangian Physical Properties

Lagrangian physical properties are defined in a physicalProperties in the relevant Lagrangian subdirectory of constant (in the above, for example, the file is constant/Lagrangian/myCloud/physicalProperties). This is consistent with where physical properties are defined for Eulerian simulations.

Currently, only the kinematic clouds require this file, and the only setting in it is the density ratio between the particles and the carrier, rhoByRhoc. As more clouds are implemented (and in particular clouds with thermodynamic modelling) more properties and property models will be specified here.

An example is shown below for completeness:

/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Version:  dev
     \\/     M anipulation  |
\*---------------------------------------------------------------------------*/
FoamFile
{
    format      ascii;
    class       dictionary;
    location    "constant/Lagrangian/myCloud";
    object      physicalProperties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

rhoByRhoc       833.3333333333334;

// ************************************************************************* //

4 Lagrangian Models

Lagrangian models are specified in a file called LagrangianModels in the relevant Lagrangian subdirectory of constant (in the above, for example, the file is constant/Lagrangian/myCloud/LagrangianModels). The models are specified in a simple flat list in a manner similar to that which is used for fvModels.

All models are specified in the LagrangianModels, and are intended to be specified here in the future. The LagrangianModel interface is designed to be general enough to facilitate injections, forces, heat and mass transfers and phase changes, turbulent dispersion, breakup, and more. This is in contrast to the particle-based system in which different interfaces and syntaxes and locations are used for different model categories. Creating a truly general interface to such models is challenging, but it is hoped that the resulting consistency of implementation and syntax provides substantial net benefit in the understanding both for users and those maintaining and developing the code.

The following is an example of a LagrangianModels file with all the currently available models listed (some as comments):

/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Version:  dev
     \\/     M anipulation  |
\*---------------------------------------------------------------------------*/
FoamFile
{
    format      ascii;
    class       dictionary;
    location    "constant/Lagrangian/myCloud";
    object      LagrangianModels;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

injection
{
    // Continuously inject at a point
    type        pointInjection;
    point       (1 0 0) [cm];
    numberRate  1e5;

    // Instantaneously inject at a list of locations
    /*
    type        manualInjection;
    file        "manualInjectionPositions";
    units       [mm];
    time        0.01;
    /*

    // Instantaneously inject to fill a cell-zone to a given density
    /*
    type        volumeInjection;
    cellZone    injection;
    numberDensity 2 [mm^-3];

    time        0.025;
    /*

    // Continuously inject through a patch
    /*
    type        patchInjection;
    patch       top;
    numberRate  100000;
    /*

    // Continuously inject across a disk
    /*
    type        diskInjection;
    centre      (2 0 0) [cm];
    axis        (1 0 0);
    innerDiameter 2 [mm];
    outerDiameter 2 [mm];
    numberRate  100000;
    */
}

gravity
{
    type        gravity;
}

drag
{
    // Standard drag force for isolated spherical particles
    /*
    type        SchillerNaumannDrag;
    */

    // Drag force for densely packed spherical particles
    type        GidaspowErgunWenYuDrag;
    alphaMax    0.65;
}

lift
{

    type        SaffmanMeiLift;
}

pressureGradientForce
{
    type        pressureGradientForce;
}

virtualMass
{
    type        constantCoefficientVirtualMass;
    Cvm         0.5;
}

turbulentDispersion
{
    type        turbulentDispersion;
}

// ************************************************************************* //

Note that the injection models listed above only specify the location and number of elements created. The property values of those elements are controlled entirely using the sources entry in the relevant field file. This separation means the injection models are of limited scope and relatively modest complexity. The principle here is that a larger number of smaller, truly interoperable components results in a flexible system that is more functional than the sum of its parts.

5 Lagrangian Schemes

Lagrangian schemes are specified in a LagrangianSchemes file, in the relevant Lagrangian subdirectory of system (in the above, for example, the file is system/Lagrangian/myCloud/LagrangianSchemes). Much like for fvSchemes, this file contains settings related to numerics. The way in which tracking, interpolation, time-integration, averaging, accumulation and application of sources is done is all controlled here.

An example of this file is given below:

/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Version:  dev
     \\/     M anipulation  |
\*---------------------------------------------------------------------------*/
FoamFile
{
    format      ascii;
    class       dictionary;
    location    "system/Lagrangian/myCloud";
    object      LagrangianSchemes;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

tracking    linear; // parabolic;

ddtSchemes
{
    default     Euler; // CrankNicolson;
}

SpSchemes
{
    default     none;
    Sp(D,U)     implicit;
    Sp(D,Uc)    explicit;
    Sp(L,U)     implicit;
    Sp(L,Uc)    explicit;
}

averagingSchemes
{
    default     none;
    alpha       cellPoint; // cell

}

interpolationSchemes
{
    default     cell;
    Uc          cellPoint;
}

accumulationSchemes
{
    default     none;
    v           cell;
    S(v*U)      cellPoint;
}

// ************************************************************************* //

The top level entries are as follows:

  • Tracking is the method with which particles are moved through the mesh. linear is a first order method in which particles are assumed to move through each cell with a constant velocity. parabolic is a second order method in which the velocity varies with a constant acceleration.

  • Time schemes control how the properties of the particles are advanced in time. Euler is a first order implicit scheme. CrankNicolson is a second order scheme in which implicit and explicit contributions are equally weighted.

    CrankNicolson is required for parabolic tracking. The combination of parabolic tracking and CrankNicolson time-evolution is notable in that it conserves the mechanical energy of the particles.

  • Source schemes control how the sources are applied to the particles and to the carrier. They can be either implicit or explicit.

    Note that the conservation of properties (momentum, heat, etc…) that are transferred between the particles and their carrier is only maintained if the phase which is solved first has implicit sources and the phase which is solved last has explicit sources.

    In the above example, Sp(D,U) means the (potentially) implicit drag source term as applied to the particles’ velocity equation, whilst Sp(D,Uc) is the corresponding term applied to the carrier’s velocity equation. The terms with L are similar, but relate to lift rather than drag.

  • Averaging schemes control how particle properties are interpolated back to the particles. cell does unweighted averages into the cells to create a piecewise constant average. cellPoint averages at the cell centres and at the points using the coordinates within the tetrahedral decomposition to weight the contributions. The result is a continuous piecewise linear function for the average value, but this comes at a higher cost (notably, it involves parallel communication).

  • Interpolation schemes control how carrier properties are interpolated to the particles. Typically either cell or cellPoint is chosen, but there are many other options. These options are the same as for the particle-based Lagrangian library. Note that cellPoint is almost universally used for the carrier velocity, as it is important not to create a velocity that has a component into a wall, as this can cause particles to hit the wall at an infinite frequency.

  • Accumulation schemes control how Lagrangian data is summed in order to create a integral quantity on the Eulerian mesh. cell does a basic sum in which a particle contributes to its containing cell only. cellPoint, by contrast, adds both to the cell and to the nearby points, using the coordinates within the tetrahedral decomposition to weight the contributions. The result is more distributed and rather smoother, but it comes at a higher cost.

    In the above example, v is the volume (the accumulation of which is likely to be informing a particle volume-fraction calculation), and S(v*U) denotes the source term into the volume-weighted velocity equation.

6 Lagrangian Solution

Lagrangian solution parameters are specified in a LagrangianSolution file, in the relevant Lagrangian subdirectory of system (in the above, for example, the file is system/Lagrangian/myCloud/LagrangianSolution). This file contains controls relating to the solution algorithm. Currently all the settings here relate to controlling the numbers of iterations and sub-cycles.

At the moment, there are only four controls in this file, though it is thought that this is likely to increase as the system is developed further. An example file with all four controls is shown below:

/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Version:  dev
     \\/     M anipulation  |
\*---------------------------------------------------------------------------*/
FoamFile
{
    format      ascii;
    class       dictionary;
    location    "system/Lagrangian/myCloud";
    object      LagrangianSolution;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

outerCorrectors yes;

maxTimeStepFraction 0.3;

maxCellLengthScaleFraction 0.3;

nCorrectors     1;

// ************************************************************************* //

The entries are as follows:

  • The outerCorrectors control determines if the cloud is solved every time the outer PIMPLE loop is executed, or if it is just solved once at the start. This setting is only required by fvModel clouds and when multiple outer correctors are being used.

  • The maxTimeStepFraction control specifies the maximum fraction of the time-step that particles may be tracked before their equations are solved and their properties updated. The lower this value, the more sub-steps the particles do.

  • The maxCellLengthScaleFraction is similar to maxTimeStepFraction, but it specifies a maximum fraction of the local cell size that may be tracked between each property update.

  • The nCorrectors setting controls the number of times the equations are solved and the properties updated for each tracking sub-step. This can improve the convergence and implicit-ness of the inner solution in a similar manner to inner PIMPLE iterations. It does not result in additional tracking.

Together, the two max*Fraction controls effectively specify the number of sub-steps that the particles do. In the parcel-based system, these controls were merged into a single number, which was referred to as maxCo; presumably meaning a maximum Courant number. In the field-based system it was concluded that this isn’t formally a Courant number, it would be better to name it more explicitly, and that it was only possible to do so if the two ways in which it was used were specified separately.

cellValueSourceCorrection

The particle-based system has a setting called cellValueSourceCorrection. This adds the exchanged momentum (heat, mass, …) of the current particle to the interpolated value of the velocity (temperature, density, …) so that subsequent uses of the interpolated value were more up to date. The idea (it is assumed) was that this reduces the splitting error associated with solving the particles and the carrier separately.

In the field-based system, this idea has been discarded. It might be of benefit if the particle sources are directly causing sub-time-step transient behaviour, but in many other cases (e.g., the carrier and the particles are at equilibrium) then it is likely to have a detrimental effect.

Instead, coupling can be improved by means of outer iteration, which is now possible for fvModel based clouds. In addition, carrier properties are now interpolated in time as well as in space; e.g.; if a particle is one quarter of the way through its evolution, then the carrier velocity it experiences is 75% of the old-time value and 25% of the new-time value. These two properties mean that, if necessary, it it possible to iterate the system to a point where the splitting error is formally second-order.

7 Lagrangian Function Objects

A number of function objects have been implemented that interact with the field-based Lagrangian system. These functions are “normal” function objects and are specified in system/functions in the same way as function objects that interact with the Eulerian system.

7.1 Non-Cloud Function Objects

Two function objects are “non-cloud” in that they don’t relate to the cloud solution procedure; they only operate on and post-process Lagrangian field data. These are LagrangianFieldValue, which performs reduction operations on Lagrangian values, and LagrangianDistribution, which outputs a plots of the distribution of Lagrangian values.

The following functions entry will, for example, sum the kinetic energy (KE) and gravitational potential energy (GPE) of all the particles in the cloud, and write out a plot of these sums against time:

sumEnergy
{
    type        LagrangianFieldValue;
    libs        ("libLagrangianFunctionObjects.so");
    Lagrangian  myCloud;
    operation   sum;
    fields      (KE GPE);
    weightField number;
}

And this functions entry will write a plot of the diameter distribution:

diameterDistribution
{
    type        LagrangianDistribution;
    libs        ("libLagrangianFunctionObjects.so");
    Lagrangian  myCloud;
    field       d;
    weightField number;
    nBins       20;
    setFormat   raw;
}

7.2 Cloud Function Objects

There are another fourteen “cloud” functions which do interact with the cloud solution procedure (i.e., they have to know when particles move, hit faces, etc…). They are still, however, specified in system/functions. Their specification is trivial; none of them require any settings other than the model type, the library and the name of the cloud. So, specific examples will not be given here. They can all be specified in the following general way.

<cloudFunctionName>
{
    type        <cloudFunctionType>;
    libs        ("libLagrangianCloudFunctionObjects.so");
    cloud       myCloud;
}

Where <cloudFunctionName> should be set to a unique name by the user to meaningfully identify the function, and <cloudFunctionType> is the type of the function itself; i.e., one of the following:

  • cloudVolume writes a Lagrangian field of the volumes of the particles

  • cloudSurfaceArea writes a Lagrangian field of the surface areas of the particles

  • cloudMass writes a Lagrangian field of the masses of the particles

  • cloudVolumeFraction writes an Eulerian vol-field of the volume fraction of the particles. I.e., the result of an accumulation scheme being applied to the particle volumes, and then divided by the cell volumes.

  • cloudSurfaceAreaPerUnitVolume writes an Eulerian vol-field of the surface area of the particles per unit volume. I.e., the result of an accumulation scheme being applied to the particle surface areas, and then divided by the cell volumes.

  • cloudLagrangianVolumeFraction writes a Lagrangian field of the particulate volume fraction. I.e., the result of an averaging scheme being applied to the particle volumes.

  • cloudAge writes a Lagrangian field containing the “age” of the particles; i.e., the amount of physical time that has elapsed since they were injected

  • cloudKineticEnergy writes a Lagrangian field of the kinetic energy of the particles

  • cloudGravitationalPotentialEnergy writes a Lagrangian field of the gravitational potential energy of the particles

  • cloudNumberFlux writes an Eulerian surface-field of the number flux of the particles

  • cloudVolumeFlux writes an Eulerian surface-field of the volume flux of the particles

  • cloudMassFlux writes an Eulerian surface-field of the mass flux of the particles

  • cloudBoundaryCollisionNumberFlux writes an Eulerian boundary-field of the number flux of particles that rebound off the patch faces

  • cloudBoundaryCollisionForce writes an Eulerian boundary-field of the force exerted on the patch faces

Field-Lagrangian in OpenFOAM