void Foam::primitiveMesh::makeCellCentresAndVols
(
    const vectorField& fCtrs,
    const vectorField& fAreas,
    vectorField& cellCtrs,
    scalarField& cellVols
) const
{
    // Clear the fields for accumulation
    cellCtrs = vector::zero;
    cellVols = 0.0;

    const labelList& own = faceOwner();
    const labelList& nei = faceNeighbour();

    // first estimate the approximate cell centre as the average of
    // face centres

    vectorField cEst(nCells(), vector::zero);
    labelField nCellFaces(nCells(), 0);

    forAll(own, facei)
    {
        cEst[own[facei]] += fCtrs[facei];
        nCellFaces[own[facei]] += 1;
    }
const Foam::labelListList& Foam::primitiveMesh::cellPoints() const
{
    if (!cpPtr_)
    {
        if (debug)
        {
            Pout<< "primitiveMesh::cellPoints() : "
                << "calculating cellPoints" << endl;

            if (debug == -1)
            {
                // For checking calls:abort so we can quickly hunt down
                // origin of call
                FatalErrorIn("primitiveMesh::cellPoints()")
                    << abort(FatalError);
            }
        }

        // Invert pointCells
        cpPtr_ = new labelListList(nCells());
        invertManyToMany(nCells(), pointCells(), *cpPtr_);
    }

    return *cpPtr_;
}
Esempio n. 3
0
void Foam::fvMesh::mapOldVolumes(const mapPolyMesh& meshMap)
{
    const labelList& cellMap = meshMap.cellMap();

    // Map the old volume. Just map to new cell labels.
    if (V0Ptr_)
    {
        if (debug)
        {
            InfoIn("void fvMesh::mapOldVolumes(const mapPolyMesh& meshMap)")
                << "Mapping old cell volumes." << endl;
        }

        scalarField& V0 = *V0Ptr_;

        scalarField savedV0(V0);
        V0.setSize(nCells());

        forAll (V0, i)
        {
            if (cellMap[i] > -1)
            {
                V0[i] = savedV0[cellMap[i]];
            }
            else
            {
                V0[i] = 0.0;
            }
        }
    }

    // Map the old-old volume. Just map to new cell labels.
    if (V00Ptr_)
    {
        if (debug)
        {
            InfoIn("void fvMesh::mapOldVolumes(const mapPolyMesh& meshMap)")
                << "Mapping old-old cell volumes." << endl;
        }

        scalarField& V00 = *V00Ptr_;

        scalarField savedV00(V00);
        V00.setSize(nCells());

        forAll (V00, i)
        {
            if (cellMap[i] > -1)
            {
                V00[i] = savedV00[cellMap[i]];
            }
            else
            {
                V00[i] = 0.0;
            }
        }
    }
}
void Foam::primitiveMesh::calcCellShapes() const
{
    if (debug)
    {
        Pout<< "primitiveMesh::calcCellShapes() : calculating cellShapes"
            << endl;
    }

    // It is an error to attempt to recalculate faceCells
    // if the pointer is already set
    if (cellShapesPtr_)
    {
        FatalErrorInFunction
            << "cellShapes already calculated"
            << abort(FatalError);
    }
    else
    {
        cellShapesPtr_ = new cellShapeList(nCells());
        cellShapeList& cellShapes = *cellShapesPtr_;

        forAll(cellShapes, celli)
        {
            cellShapes[celli] = degenerateMatcher::match(*this, celli);
        }
    }
Esempio n. 5
0
Foam::List<Foam::FixedList<Foam::label, 8>> Foam::block::cells() const
{
    const label ni = density().x();
    const label nj = density().y();
    const label nk = density().z();

    List<FixedList<label, 8>> cells(nCells());

    label celli = 0;

    for (label k=0; k<nk; k++)
    {
        for (label j=0; j<nj; j++)
        {
            for (label i=0; i<ni; i++)
            {
                cells[celli][0] = pointLabel(i,   j,   k);
                cells[celli][1] = pointLabel(i+1, j,   k);
                cells[celli][2] = pointLabel(i+1, j+1, k);
                cells[celli][3] = pointLabel(i,   j+1, k);
                cells[celli][4] = pointLabel(i,   j,   k+1);
                cells[celli][5] = pointLabel(i+1, j,   k+1);
                cells[celli][6] = pointLabel(i+1, j+1, k+1);
                cells[celli][7] = pointLabel(i,   j+1, k+1);

                celli++;
            }
        }
    }

    return cells;
}
void Foam::block::createCells() const
{
    const label ni = meshDensity().x();
    const label nj = meshDensity().y();
    const label nk = meshDensity().z();

    //
    // generate cells
    //
    cells_.clear();
    cells_.setSize(nCells());

    label cellNo = 0;

    for (label k = 0; k < nk; k++)
    {
        for (label j = 0; j < nj; j++)
        {
            for (label i = 0; i < ni; i++)
            {
                cells_[cellNo].setSize(8);

                cells_[cellNo][0] = vtxLabel(i, j, k);
                cells_[cellNo][1] = vtxLabel(i+1, j, k);
                cells_[cellNo][2] = vtxLabel(i+1, j+1, k);
                cells_[cellNo][3] = vtxLabel(i, j+1, k);
                cells_[cellNo][4] = vtxLabel(i, j, k+1);
                cells_[cellNo][5] = vtxLabel(i+1, j, k+1);
                cells_[cellNo][6] = vtxLabel(i+1, j+1, k+1);
                cells_[cellNo][7] = vtxLabel(i, j+1, k+1);
                cellNo++;
            }
        }
    }
}
void primitiveMesh::makeCellCentresAndVols
(
    const vectorField& fCtrs,
    const vectorField& fAreas,
    vectorField& cellCtrs,
    scalarField& cellVols
) const
{
    // Clear the fields for accumulation
    cellCtrs = vector::zero;
    cellVols = 0.0;
    
    const labelList& own = faceOwner();
    const labelList& nei = faceNeighbour();

    // next calculate exact cell volume and centre

    scalarField r1(nCells(), -1);
    scalarField r2(nCells(), -1);
    
    forAll (faces(), faceI)
    {
        if (!isRadialFace(fCtrs[faceI], fAreas[faceI]))
        {
            cellVols[own[faceI]] += fCtrs[faceI] & fAreas[faceI];
            cellCtrs[own[faceI]] += fCtrs[faceI];
            if (r1[own[faceI]] < 0)
            {
                r1 = mag(fCtrs[faceI]);
            }
            else
            {
                r2[own[faceI]] = mag(fCtrs[faceI]);
            }
            
            if (faceI < nInternalFaces())
            {
                cellVols[nei[faceI]] += fCtrs[faceI] & fAreas[faceI];
                cellCtrs[nei[faceI]] += fCtrs[faceI];
                r2[nei[faceI]] = mag(fCtrs[faceI]);
            }
        }
    }
    cellVols *= 1./3.;
    cellCtrs *= 0.5*(r1+r2)/mag(cellCtrs);
}
Esempio n. 8
0
void Foam::primitiveMesh::calcCellCells() const
{
    // Loop through faceCells and mark up neighbours

    if (debug)
    {
        Pout<< "primitiveMesh::calcCellCells() : calculating cellCells"
            << endl;

        if (debug == -1)
        {
            // For checking calls:abort so we can quickly hunt down
            // origin of call
            FatalErrorIn("primitiveMesh::calcCellCells()")
                << abort(FatalError);
        }
    }

    // It is an error to attempt to recalculate cellCells
    // if the pointer is already set
    if (ccPtr_)
    {
        FatalErrorIn("primitiveMesh::calcCellCells() const")
            << "cellCells already calculated"
            << abort(FatalError);
    }
    else
    {
        // 1. Count number of internal faces per cell

        labelList ncc(nCells(), 0);

        const labelList& own = faceOwner();
        const labelList& nei = faceNeighbour();

        forAll (nei, faceI)
        {
            ncc[own[faceI]]++;
            ncc[nei[faceI]]++;
        }

        // Create the storage
        ccPtr_ = new labelListList(ncc.size());
        labelListList& cellCellAddr = *ccPtr_;



        // 2. Size and fill cellFaceAddr

        forAll (cellCellAddr, cellI)
        {
            cellCellAddr[cellI].setSize(ncc[cellI]);
        }
void Foam::primitiveMesh::calcCellCentresAndVols() const
{
    if (debug)
    {
        Pout<< "primitiveMesh::calcCellCentresAndVols() : "
            << "Calculating cell centres and cell volumes"
            << endl;
    }

    // It is an error to attempt to recalculate cellCentres
    // if the pointer is already set
    if (cellCentresPtr_ || cellVolumesPtr_)
    {
        FatalErrorIn("primitiveMesh::calcCellCentresAndVols() const")
            << "Cell centres or cell volumes already calculated"
            << abort(FatalError);
    }

    // set the accumulated cell centre to zero vector
    cellCentresPtr_ = new vectorField(nCells());
    vectorField& cellCtrs = *cellCentresPtr_;

    // Initialise cell volumes to 0
    cellVolumesPtr_ = new scalarField(nCells());
    scalarField& cellVols = *cellVolumesPtr_;

    // Make centres and volumes
    makeCellCentresAndVols(faceCentres(), faceAreas(), cellCtrs, cellVols);

    if (debug)
    {
        Pout<< "primitiveMesh::calcCellCentresAndVols() : "
            << "Finished calculating cell centres and cell volumes"
            << endl;
    }
}
Esempio n. 10
0
Foam::polyMesh::polyMesh(const IOobject& io)
:
    objectRegistry(io),
    primitiveMesh(),
    points_
    (
        IOobject
        (
            "points",
            time().findInstance(meshDir(), "points"),
            meshSubDir,
            *this,
            IOobject::MUST_READ,
            IOobject::NO_WRITE
        )
    ),
    faces_
    (
        IOobject
        (
            "faces",
            time().findInstance(meshDir(), "faces"),
            meshSubDir,
            *this,
            IOobject::MUST_READ,
            IOobject::NO_WRITE
        )
    ),
    owner_
    (
        IOobject
        (
            "owner",
            faces_.instance(),
            meshSubDir,
            *this,
            IOobject::READ_IF_PRESENT,
            IOobject::NO_WRITE
        )
    ),
    neighbour_
    (
        IOobject
        (
            "neighbour",
            faces_.instance(),
            meshSubDir,
            *this,
            IOobject::READ_IF_PRESENT,
            IOobject::NO_WRITE
        )
    ),
    clearedPrimitives_(false),
    boundary_
    (
        IOobject
        (
            "boundary",
            time().findInstance(meshDir(), "boundary"),
            meshSubDir,
            *this,
            IOobject::MUST_READ,
            IOobject::NO_WRITE
        ),
        *this
    ),
    bounds_(points_),
    comm_(UPstream::worldComm),
    geometricD_(Vector<label>::zero),
    solutionD_(Vector<label>::zero),
    tetBasePtIsPtr_(NULL),
    cellTreePtr_(NULL),
    pointZones_
    (
        IOobject
        (
            "pointZones",
            time().findInstance
            (
                meshDir(),
                "pointZones",
                IOobject::READ_IF_PRESENT
            ),
            meshSubDir,
            *this,
            IOobject::READ_IF_PRESENT,
            IOobject::NO_WRITE
        ),
        *this
    ),
    faceZones_
    (
        IOobject
        (
            "faceZones",
            time().findInstance
            (
                meshDir(),
                "faceZones",
                IOobject::READ_IF_PRESENT
            ),
            meshSubDir,
            *this,
            IOobject::READ_IF_PRESENT,
            IOobject::NO_WRITE
        ),
        *this
    ),
    cellZones_
    (
        IOobject
        (
            "cellZones",
            time().findInstance
            (
                meshDir(),
                "cellZones",
                IOobject::READ_IF_PRESENT
            ),
            meshSubDir,
            *this,
            IOobject::READ_IF_PRESENT,
            IOobject::NO_WRITE
        ),
        *this
    ),
    globalMeshDataPtr_(NULL),
    moving_(false),
    topoChanging_(false),
    curMotionTimeIndex_(time().timeIndex()),
    oldPointsPtr_(NULL)
{
    if (exists(owner_.objectPath()))
    {
        initMesh();
    }
    else
    {
        cellCompactIOList cLst
        (
            IOobject
            (
                "cells",
                time().findInstance(meshDir(), "cells"),
                meshSubDir,
                *this,
                IOobject::MUST_READ,
                IOobject::NO_WRITE
            )
        );

        // Set the primitive mesh
        initMesh(cLst);

        owner_.write();
        neighbour_.write();
    }

    // Calculate topology for the patches (processor-processor comms etc.)
    boundary_.updateMesh();

    // Calculate the geometry for the patches (transformation tensors etc.)
    boundary_.calcGeometry();

    // Warn if global empty mesh
    if (returnReduce(nPoints(), sumOp<label>()) == 0)
    {
        WarningIn("polyMesh(const IOobject&)")
            << "no points in mesh" << endl;
    }
    if (returnReduce(nCells(), sumOp<label>()) == 0)
    {
        WarningIn("polyMesh(const IOobject&)")
            << "no cells in mesh" << endl;
    }

    // Initialise demand-driven data
    calcDirections();
}
Esempio n. 11
0
void Foam::fvMesh::mapFields(const mapPolyMesh& meshMap)
{
    if (debug)
    {
        Info<< "fvMesh::mapFields :"
            << " nOldCells:" << meshMap.nOldCells()
            << " nCells:" << nCells()
            << " nOldFaces:" << meshMap.nOldFaces()
            << " nFaces:" << nFaces()
            << endl;
    }


    // We require geometric properties valid for the old mesh
    if
    (
        meshMap.cellMap().size() != nCells()
     || meshMap.faceMap().size() != nFaces()
    )
    {
        FatalErrorIn("fvMesh::mapFields(const mapPolyMesh&)")
            << "mapPolyMesh does not correspond to the old mesh."
            << " nCells:" << nCells()
            << " cellMap:" << meshMap.cellMap().size()
            << " nOldCells:" << meshMap.nOldCells()
            << " nFaces:" << nFaces()
            << " faceMap:" << meshMap.faceMap().size()
            << " nOldFaces:" << meshMap.nOldFaces()
            << exit(FatalError);
    }

    // Create a mapper
    const fvMeshMapper mapper(*this, meshMap);

    // Map all the volFields in the objectRegistry
    MapGeometricFields<scalar, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<vector, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<sphericalTensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<tensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);

    // Map all the surfaceFields in the objectRegistry
    MapGeometricFields<scalar, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<vector, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<tensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);

    // Map all the dimensionedFields in the objectRegistry
    MapDimensionedFields<scalar, fvMeshMapper, volMesh>(mapper);
    MapDimensionedFields<vector, fvMeshMapper, volMesh>(mapper);
    MapDimensionedFields<sphericalTensor, fvMeshMapper, volMesh>(mapper);
    MapDimensionedFields<symmTensor, fvMeshMapper, volMesh>(mapper);
    MapDimensionedFields<tensor, fvMeshMapper, volMesh>(mapper);

    // Map all the clouds in the objectRegistry
    mapClouds(*this, meshMap);

//TODO implement on gpu
/*
    const labelList& cellMap = meshMap.cellMap();

    // Map the old volume. Just map to new cell labels.
    if (V0Ptr_)
    {
        scalargpuField& V0 = (*V0Ptr_).getField();

        scalargpuField savedV0(V0);
        V0.setSize(nCells());

        forAll(V0, i)
        {
            if (cellMap[i] > -1)
            {
                V0[i] = savedV0[cellMap[i]];
            }
            else
            {
                V0[i] = 0.0;
            }
        }

        // Inject volume of merged cells
        label nMerged = 0;
        forAll(meshMap.reverseCellMap(), oldCellI)
        {
            label index = meshMap.reverseCellMap()[oldCellI];

            if (index < -1)
            {
                label cellI = -index-2;

                V0[cellI] += savedV0[oldCellI];

                nMerged++;
            }
        }

        if (debug)
        {
            Info<< "Mapping old time volume V0. Merged "
                << nMerged << " out of " << nCells() << " cells" << endl;
        }
    }


    // Map the old-old volume. Just map to new cell labels.
    if (V00Ptr_)
    {
        scalargpuField& V00 = (*V00Ptr_).getField();

        scalargpuField savedV00(V00);
        V00.setSize(nCells());

        forAll(V00, i)
        {
            if (cellMap[i] > -1)
            {
                V00[i] = savedV00[cellMap[i]];
            }
            else
            {
                V00[i] = 0.0;
            }
        }

        // Inject volume of merged cells
        label nMerged = 0;
        forAll(meshMap.reverseCellMap(), oldCellI)
        {
            label index = meshMap.reverseCellMap()[oldCellI];

            if (index < -1)
            {
                label cellI = -index-2;

                V00[cellI] += savedV00[oldCellI];
                nMerged++;
            }
        }

        if (debug)
        {
            Info<< "Mapping old time volume V00. Merged "
                << nMerged << " out of " << nCells() << " cells" << endl;
        }
    }
*/
}
void Foam::primitiveMesh::calcCellEdges() const
{
    // Loop through all faces and mark up cells with edges of the face.
    // Check for duplicates

    if (debug)
    {
        Pout<< "primitiveMesh::calcCellEdges() : "
            << "calculating cellEdges"
            << endl;

        if (debug == -1)
        {
            // For checking calls:abort so we can quickly hunt down
            // origin of call
            FatalErrorIn("primitiveMesh::calcCellEdges()")
                << abort(FatalError);
        }
    }

    // It is an error to attempt to recalculate cellEdges
    // if the pointer is already set
    if (cePtr_)
    {
        FatalErrorIn("primitiveMesh::calcCellEdges() const")
            << "cellEdges already calculated"
            << abort(FatalError);
    }
    else
    {
        // Set up temporary storage
        List<DynamicList<label, edgesPerCell_> > ce(nCells());


        // Get reference to faceCells and faceEdges
        const labelList& own = faceOwner();
        const labelList& nei = faceNeighbour();
        const labelListList& fe = faceEdges();

        // loop through the list again and add edges; checking for duplicates
        forAll(own, faceI)
        {
            DynamicList<label, edgesPerCell_>& curCellEdges = ce[own[faceI]];

            const labelList& curEdges = fe[faceI];

            forAll(curEdges, edgeI)
            {
                if (findIndex(curCellEdges, curEdges[edgeI]) == -1)
                {
                    // Add the edge
                    curCellEdges.append(curEdges[edgeI]);
                }
            }
        }

        forAll(nei, faceI)
        {
            DynamicList<label, edgesPerCell_>& curCellEdges = ce[nei[faceI]];

            const labelList& curEdges = fe[faceI];

            forAll(curEdges, edgeI)
            {
                if (findIndex(curCellEdges, curEdges[edgeI]) == -1)
                {
                    // add the edge
                    curCellEdges.append(curEdges[edgeI]);
                }
            }
        }
Esempio n. 13
0
void Mesh::buildInternalFaces()
{
    if(faces_built) {
        return;
    }
    faces_built = true;


    //build naive list of faces
    int num_naive_faces{0};
    for (auto i : IRange(0, nCells())) {
        num_naive_faces += numCellFaces(getCellType(i));
    }

    internal_faces_.clear();
    internal_faces_.reserve(num_naive_faces);
    std::vector<int> cell_nodes(8);

    for (auto i : IRange(0, nCells())) {

        getCellNodes(i, cell_nodes);
        auto ct = getCellType(i);

        for(auto f : IRange(0, numCellFaces(ct))) {
            auto F = CellFace::canonicalCellFace(ct,f);
            for(auto n : IRange(0,F.n_nodes)) {
                F.nodes[n] = cell_nodes[F.nodes[n]];
            }
            F.left = i;
            F.left_flocal = f;
            F.orient();
            internal_faces_.push_back(F);
        }
    }

    std::sort(internal_faces_.begin(), internal_faces_.end(),
              [](const CellFace &L, const CellFace &R) { return L.nodes < R.nodes; }
    );

    for (auto i : IRange(1, static_cast<int>(internal_faces_.size()))) {
        auto &F = internal_faces_[i];
        auto &Fprev = internal_faces_[i - 1];
        if (F.nodes == Fprev.nodes) {

            Fprev.left = std::max(Fprev.left, F.left);
            Fprev.right = std::max(Fprev.right, F.right);
            Fprev.left_flocal = std::max(Fprev.left_flocal, F.left_flocal);
            Fprev.right_flocal = std::max(Fprev.right_flocal, F.right_flocal);
            Fprev.left_rot = std::max(Fprev.left_rot, F.left_rot);
            Fprev.right_rot = std::max(Fprev.right_rot, F.right_rot);
        }

    }

    auto new_end = std::unique(internal_faces_.begin(),
                               internal_faces_.end(),
                               [](const CellFace &L, const CellFace &R) { return L.nodes == R.nodes; });

    auto new_size = std::distance(internal_faces_.begin(), new_end);
    internal_faces_.resize(new_size);
    internal_faces_.shrink_to_fit();

    for (auto i : IRange(0, static_cast<int>(internal_faces_.size()))) {
        if (internal_faces_[i].left < 0 || internal_faces_[i].right < 0) {
            boundary_face_idxs_.push_back(i);
        }
    }
}
Type Foam::functionObjects::fieldValues::volFieldValue::processValues
(
    const Field<Type>& values,
    const scalarField& V,
    const scalarField& weightField
) const
{
    Type result = Zero;
    switch (operation_)
    {
        case opSum:
        {
            result = gSum(values);
            break;
        }
        case opSumMag:
        {
            result = gSum(cmptMag(values));
            break;
        }
        case opAverage:
        {
            result = gSum(values)/nCells();
            break;
        }
        case opWeightedAverage:
        {
            result = gSum(weightField*values)/gSum(weightField);
            break;
        }
        case opVolAverage:
        {
            result = gSum(V*values)/this->V();
            break;
        }
        case opWeightedVolAverage:
        {
            result = gSum(weightField*V*values)/gSum(weightField*V);
            break;
        }
        case opVolIntegrate:
        {
            result = gSum(V*values);
            break;
        }
        case opMin:
        {
            result = gMin(values);
            break;
        }
        case opMax:
        {
            result = gMax(values);
            break;
        }
        case opCoV:
        {
            Type meanValue = gSum(values*V)/this->V();

            const label nComp = pTraits<Type>::nComponents;

            for (direction d=0; d<nComp; ++d)
            {
                scalarField vals(values.component(d));
                scalar mean = component(meanValue, d);
                scalar& res = setComponent(result, d);

                res = sqrt(gSum(V*sqr(vals - mean))/this->V())/mean;
            }

            break;
        }
        case opNone:
        {}
    }

    return result;
}
Esempio n. 15
0
void Foam::fvMesh::mapFields(const mapPolyMesh& meshMap)
{
    // Create a mapper
    const fvMeshMapper mapper(*this, meshMap);

    // Map all the volFields in the objectRegistry
    MapGeometricFields<scalar, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<vector, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<sphericalTensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);
    MapGeometricFields<tensor, fvPatchField, fvMeshMapper, volMesh>
    (mapper);

    // Map all the surfaceFields in the objectRegistry
    MapGeometricFields<scalar, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<vector, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<symmTensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);
    MapGeometricFields<tensor, fvsPatchField, fvMeshMapper, surfaceMesh>
    (mapper);

    // Map all the clouds in the objectRegistry
    mapClouds(*this, meshMap);


    const labelList& cellMap = meshMap.cellMap();

    // Map the old volume. Just map to new cell labels.
    if (V0Ptr_)
    {
        scalarField& V0 = *V0Ptr_;

        scalarField savedV0(V0);
        V0.setSize(nCells());

        forAll(V0, i)
        {
            if (cellMap[i] > -1)
            {
                V0[i] = savedV0[cellMap[i]];
            }
            else
            {
                V0[i] = 0.0;
            }
        }
    }

    // Map the old-old volume. Just map to new cell labels.
    if (V00Ptr_)
    {
        scalarField& V00 = *V00Ptr_;

        scalarField savedV00(V00);
        V00.setSize(nCells());

        forAll(V00, i)
        {
            if (cellMap[i] > -1)
            {
                V00[i] = savedV00[cellMap[i]];
            }
            else
            {
                V00[i] = 0.0;
            }
        }
    }
}
void CPUUncontrolledApproximation::perform() {
    const auto &ctx = kernel->context();
    auto &stateModel = kernel->getCPUKernelStateModel();
    auto nl = stateModel.getNeighborList();
    auto &data = nl->data();
    const auto &box = ctx.boxSize().data();
    const auto &pbc = ctx.periodicBoundaryConditions().data();

    if (ctx.recordReactionsWithPositions()) {
        kernel->getCPUKernelStateModel().reactionRecords().clear();
    }
    if (ctx.recordReactionCounts()) {
        stateModel.resetReactionCounts();
    }

    // gather events
    std::vector<std::promise<std::size_t>> n_events_promises(kernel->getNThreads());
    std::vector<event_promise_t> promises(kernel->getNThreads());
    {

        auto &pool = kernel->pool();

        std::vector<std::function<void(std::size_t)>> executables;
        executables.reserve(kernel->getNThreads());

        std::size_t grainSize = data.size() / kernel->getNThreads();
        std::size_t nlGrainSize = nl->nCells() / kernel->getNThreads();

        auto it = data.cbegin();
        std::size_t it_nl = 0;
        for (auto i = 0U; i < kernel->getNThreads()-1 ; ++i) {
            auto itNext = std::min(it+grainSize, data.cend());

            auto nlNext = std::min(it_nl + nlGrainSize, nl->nCells());
            auto bounds_nl = std::make_tuple(it_nl, nlNext);

            pool.push(findEvents, it, itNext, bounds_nl, kernel, timeStep(), false, std::cref(*nl),
                      std::ref(promises.at(i)), std::ref(n_events_promises.at(i)));

            it = itNext;
            it_nl = nlNext;
        }
        pool.push(findEvents, it, data.cend(), std::make_tuple(it_nl, nl->nCells()), kernel, timeStep(), false,
                  std::cref(*nl), std::ref(promises.back()), std::ref(n_events_promises.back()));
    }

    // collect events
    std::vector<event_t> events;
    {
        std::size_t n_events = 0;
        for (auto &&f : n_events_promises) {
            n_events += f.get_future().get();
        }
        events.reserve(n_events);
        for (auto &&f : promises) {
            auto eventUpdate = std::move(f.get_future().get());
            auto mBegin = std::make_move_iterator(eventUpdate.begin());
            auto mEnd = std::make_move_iterator(eventUpdate.end());
            events.insert(events.end(), mBegin, mEnd);
        }
    }

    // shuffle reactions
    std::shuffle(events.begin(), events.end(), std::mt19937(std::random_device()()));

    // execute reactions
    {
        data_t::EntriesUpdate newParticles{};
        std::vector<data_t::size_type> decayedEntries{};

        // todo better conflict detection?
        for (auto it = events.begin(); it != events.end(); ++it) {
            auto &event = *it;
            if (event.cumulativeRate == 0) {
                auto entry1 = event.idx1;
                if (event.nEducts == 1) {
                    auto reaction = ctx.reactions().order1ByType(event.t1)[event.reactionIndex];
                    if (ctx.recordReactionsWithPositions()) {
                        record_t record;
                        record.id = reaction->id();
                        performReaction(&data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, &record);
                        bcs::fixPosition(record.where, box, pbc);
                        kernel->getCPUKernelStateModel().reactionRecords().push_back(record);
                    } else {
                        performReaction(&data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, nullptr);
                    }
                    if (ctx.recordReactionCounts()) {
                        auto &counts = stateModel.reactionCounts();
                        counts.at(reaction->id())++;
                    }
                    for (auto _it2 = it + 1; _it2 != events.end(); ++_it2) {
                        if (_it2->idx1 == entry1 || _it2->idx2 == entry1) {
                            _it2->cumulativeRate = 1;
                        }
                    }
                } else {
                    auto reaction = ctx.reactions().order2ByType(event.t1, event.t2)[event.reactionIndex];
                    if (ctx.recordReactionsWithPositions()) {
                        record_t record;
                        record.id = reaction->id();
                        performReaction(&data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, &record);
                        bcs::fixPosition(record.where, box, pbc);
                        kernel->getCPUKernelStateModel().reactionRecords().push_back(record);
                    } else {
                        performReaction(&data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, nullptr);
                    }
                    if (ctx.recordReactionCounts()) {
                        auto &counts = stateModel.reactionCounts();
                        counts.at(reaction->id())++;
                    }
                    for (auto _it2 = it + 1; _it2 != events.end(); ++_it2) {
                        if (_it2->idx1 == entry1 || _it2->idx2 == entry1 ||
                            _it2->idx1 == event.idx2 || _it2->idx2 == event.idx2) {
                            _it2->cumulativeRate = 1;
                        }
                    }
                }
            }
        }
        data.update(std::make_pair(std::move(newParticles), std::move(decayedEntries)));
    }
}