void Foam::lduMatrix::operator*=(const scalarField& sf)
{
    if (diagPtr_)
    {
        *diagPtr_ *= sf;
    }

    if (upperPtr_)
    {
        scalarField& upper = *upperPtr_;

        const labelUList& l = lduAddr().lowerAddr();

        for (register label face=0; face<upper.size(); face++)
        {
            upper[face] *= sf[l[face]];
        }
    }

    if (lowerPtr_)
    {
        scalarField& lower = *lowerPtr_;

        const labelUList& u = lduAddr().upperAddr();

        for (register label face=0; face<lower.size(); face++)
        {
            lower[face] *= sf[u[face]];
        }
    }
}
void Foam::lduMatrix::operator*=(const scalarField& sf)
{
    if (diagPtr_)
    {
        *diagPtr_ *= sf;
    }

    if (upperPtr_)
    {
        scalarField& upper = *upperPtr_;

        const unallocLabelList& l = lduAddr().lowerAddr();

        for (register label odcI = 0; odcI < upper.size(); odcI++)
        {
            upper[odcI] *= sf[l[odcI]];
        }
    }

    if (lowerPtr_)
    {
        scalarField& lower = *lowerPtr_;

        const unallocLabelList& u = lduAddr().upperAddr();

        for (register label odcI = 0; odcI < lower.size(); odcI++)
        {
            lower[odcI] *= sf[u[odcI]];
        }
    }
}
Exemple #3
0
Foam::tmp<Foam::scalarField > Foam::lduMatrix::H1() const
{
    tmp<scalarField > tH1
    (
        new scalarField(lduAddr().size(), 0.0)
    );

    if (lowerPtr_ || upperPtr_)
    {
        scalarField& H1_ = tH1();

        scalar*  H1Ptr = H1_.begin();

        const label*  uPtr = lduAddr().upperAddr().begin();
        const label*  lPtr = lduAddr().lowerAddr().begin();

        const scalar*  lowerPtr = lower().begin();
        const scalar*  upperPtr = upper().begin();

        register const label nFaces = upper().size();

        for (register label face=0; face<nFaces; face++)
        {
            H1Ptr[uPtr[face]] -= lowerPtr[face];
            H1Ptr[lPtr[face]] -= upperPtr[face];
        }
    }

    return tH1;
}
void Foam::lduMatrix::H(Foam::gpuField<Type>& Hpsi,const gpuField<Type>& psi) const
{
    Hpsi = pTraits<Type>::zero;

    if (lowerPtr_ || upperPtr_)
    {
        const scalargpuField& Lower = this->lower();
        const scalargpuField& Upper = this->upper();

        const labelgpuList& l = lduAddr().lowerAddr();
        const labelgpuList& u = lduAddr().upperAddr();
        
        matrixOperation
        (
            Hpsi.begin(),
            Hpsi,
            lduAddr(),
            matrixCoeffsMultiplyFunctor<Type,scalar,negateUnaryOperatorFunctor<Type,Type> >
            (
                psi.data(),
                Upper.data(),
                u.data(),
                negateUnaryOperatorFunctor<Type,Type>()
            ),
            matrixCoeffsMultiplyFunctor<Type,scalar,negateUnaryOperatorFunctor<Type,Type> >
            (
                psi.data(),
                Lower.data(),
                l.data(),
                negateUnaryOperatorFunctor<Type,Type>()
            )
        );                                        
    }
}
Foam::tmp<Foam::scalarField > Foam::lduMatrix::H1() const
{
    tmp<scalarField > tH1
    (
        new scalarField(lduAddr().size(), 0.0)
    );

    if (lowerPtr_ || upperPtr_)
    {
        scalarField& H1_ = tH1.ref();

        scalar* __restrict__ H1Ptr = H1_.begin();

        const label* __restrict__ uPtr = lduAddr().upperAddr().begin();
        const label* __restrict__ lPtr = lduAddr().lowerAddr().begin();

        const scalar* __restrict__ lowerPtr = lower().begin();
        const scalar* __restrict__ upperPtr = upper().begin();

        const label nFaces = upper().size();

        for (label face=0; face<nFaces; face++)
        {
            H1Ptr[uPtr[face]] -= lowerPtr[face];
            H1Ptr[lPtr[face]] -= upperPtr[face];
        }
    }
Foam::tmp<Foam::scalarField > Foam::lduMatrix::H1() const
{
    tmp<scalarField > tH1
    (
        new scalarField(lduAddr().size(), 0.0)
    );

    if (lowerPtr_ || upperPtr_)
    {
        scalarField& H1_ = tH1();

        scalar* __restrict__ H1Ptr = H1_.begin();

        const label* __restrict__ uPtr = lduAddr().upperAddr().begin();
        const label* __restrict__ lPtr = lduAddr().lowerAddr().begin();

        const scalar* __restrict__ lowerPtr = lower().begin();
        const scalar* __restrict__ upperPtr = upper().begin();

        register const label nOdcIs = upper().size();

        for (register label odcI = 0; odcI < nOdcIs; odcI++)
        {
            H1Ptr[uPtr[odcI]] -= lowerPtr[odcI];
            H1Ptr[lPtr[odcI]] -= upperPtr[odcI];
        }
    }
Exemple #7
0
Foam::tmp<Foam::Field<Type> > Foam::lduMatrix::H(const Field<Type>& psi) const
{
    tmp<Field<Type> > tHpsi
    (
        new Field<Type>(lduAddr().size(), pTraits<Type>::zero)
    );

    if (lowerPtr_ || upperPtr_)
    {
        Field<Type> & Hpsi = tHpsi();

        Type*  HpsiPtr = Hpsi.begin();

        const Type*  psiPtr = psi.begin();

        const label*  uPtr = lduAddr().upperAddr().begin();
        const label*  lPtr = lduAddr().lowerAddr().begin();

        const scalar*  lowerPtr = lower().begin();
        const scalar*  upperPtr = upper().begin();

        register const label nFaces = upper().size();

        for (register label face=0; face<nFaces; face++)
        {
            HpsiPtr[uPtr[face]] -= lowerPtr[face]*psiPtr[lPtr[face]];
            HpsiPtr[lPtr[face]] -= upperPtr[face]*psiPtr[uPtr[face]];
        }
    }

    return tHpsi;
}
Exemple #8
0
Foam::tmp<Foam::Field<Type> >
Foam::lduMatrix::faceH(const Field<Type>& psi) const
{
    if (lowerPtr_ || upperPtr_)
    {
        const scalarField& Lower = const_cast<const lduMatrix&>(*this).lower();
        const scalarField& Upper = const_cast<const lduMatrix&>(*this).upper();

        const labelUList& l = lduAddr().lowerAddr();
        const labelUList& u = lduAddr().upperAddr();

        tmp<Field<Type> > tfaceHpsi(new Field<Type> (Lower.size()));
        Field<Type> & faceHpsi = tfaceHpsi();

        for (register label face=0; face<l.size(); face++)
        {
            faceHpsi[face] =
                Upper[face]*psi[u[face]]
                - Lower[face]*psi[l[face]];
        }

        return tfaceHpsi;
    }
    else
    {
        FatalErrorIn("lduMatrix::faceH(const Field<Type>& psi) const")
                << "Cannot calculate faceH"
                " the matrix does not have any off-diagonal coefficients."
                << exit(FatalError);

        return tmp<Field<Type> >(NULL);
    }
}
void Foam::lduMatrix::Amul
(
    scalarField& Apsi,
    const tmp<scalarField>& tpsi,
    const FieldField<Field, scalar>& interfaceBouCoeffs,
    const lduInterfaceFieldPtrsList& interfaces,
    const direction cmpt
) const
{
    scalar* __restrict__ ApsiPtr = Apsi.begin();

    const scalarField& psi = tpsi();
    const scalar* const __restrict__ psiPtr = psi.begin();

    const scalar* const __restrict__ diagPtr = diag().begin();

    const label* const __restrict__ uPtr = lduAddr().upperAddr().begin();
    const label* const __restrict__ lPtr = lduAddr().lowerAddr().begin();

    const scalar* const __restrict__ upperPtr = upper().begin();
    const scalar* const __restrict__ lowerPtr = lower().begin();

    // Initialise the update of interfaced interfaces
    initMatrixInterfaces
    (
        interfaceBouCoeffs,
        interfaces,
        psi,
        Apsi,
        cmpt
    );

    const label nCells = diag().size();
    for (label cell=0; cell<nCells; cell++)
    {
        ApsiPtr[cell] = diagPtr[cell]*psiPtr[cell];
    }


    const label nFaces = upper().size();

    for (label face=0; face<nFaces; face++)
    {
        ApsiPtr[uPtr[face]] += lowerPtr[face]*psiPtr[lPtr[face]];
        ApsiPtr[lPtr[face]] += upperPtr[face]*psiPtr[uPtr[face]];
    }

    // Update interface interfaces
    updateMatrixInterfaces
    (
        interfaceBouCoeffs,
        interfaces,
        psi,
        Apsi,
        cmpt
    );

    tpsi.clear();
}
void Foam::LduMatrix<Type, DType, LUType>::Amul
(
    Field<Type>& Apsi,
    const tmp<Field<Type>>& tpsi
) const
{
    Type* __restrict__ ApsiPtr = Apsi.begin();

    const Field<Type>& psi = tpsi();
    const Type* const __restrict__ psiPtr = psi.begin();

    const DType* const __restrict__ diagPtr = diag().begin();

    const label* const __restrict__ uPtr = lduAddr().upperAddr().begin();
    const label* const __restrict__ lPtr = lduAddr().lowerAddr().begin();

    const LUType* const __restrict__ upperPtr = upper().begin();
    const LUType* const __restrict__ lowerPtr = lower().begin();

    // Initialise the update of interfaced interfaces
    initMatrixInterfaces
    (
        interfacesUpper_,
        psi,
        Apsi
    );

    const label nCells = diag().size();
    for (label cell=0; cell<nCells; cell++)
    {
        ApsiPtr[cell] = dot(diagPtr[cell], psiPtr[cell]);
    }


    const label nFaces = upper().size();
    for (label face=0; face<nFaces; face++)
    {
        ApsiPtr[uPtr[face]] += dot(lowerPtr[face], psiPtr[lPtr[face]]);
        ApsiPtr[lPtr[face]] += dot(upperPtr[face], psiPtr[uPtr[face]]);
    }

    // Update interface interfaces
    updateMatrixInterfaces
    (
        interfacesUpper_,
        psi,
        Apsi
    );

    tpsi.clear();
}
void Foam::lduMatrix::negSumDiag()
{
    const scalarField& Lower = const_cast<const lduMatrix&>(*this).lower();
    const scalarField& Upper = const_cast<const lduMatrix&>(*this).upper();
    scalarField& Diag = diag();

    const labelUList& l = lduAddr().lowerAddr();
    const labelUList& u = lduAddr().upperAddr();

    for (register label face=0; face<l.size(); face++)
    {
        Diag[l[face]] -= Lower[face];
        Diag[u[face]] -= Upper[face];
    }
}
Exemple #12
0
void Foam::lduMatrix::negSumDiag()
{
    const scalarField& Lower = const_cast<const lduMatrix&>(*this).lower();
    const scalarField& Upper = const_cast<const lduMatrix&>(*this).upper();
    scalarField& Diag = diag();

    const unallocLabelList& l = lduAddr().lowerAddr();
    const unallocLabelList& u = lduAddr().upperAddr();

    for (register label odcI = 0; odcI < l.size(); odcI++)
    {
        Diag[l[odcI]] -= Lower[odcI];
        Diag[u[odcI]] -= Upper[odcI];
    }
}
void Foam::lduMatrix::sumA
(
    scalarField& sumA,
    const FieldField<Field, scalar>& interfaceBouCoeffs,
    const lduInterfaceFieldPtrsList& interfaces
) const
{
    scalar* __restrict__ sumAPtr = sumA.begin();

    const scalar* __restrict__ diagPtr = diag().begin();

    const label* __restrict__ uPtr = lduAddr().upperAddr().begin();
    const label* __restrict__ lPtr = lduAddr().lowerAddr().begin();

    const scalar* __restrict__ lowerPtr = lower().begin();
    const scalar* __restrict__ upperPtr = upper().begin();

    const label nCells = diag().size();
    const label nFaces = upper().size();

    for (label cell=0; cell<nCells; cell++)
    {
        sumAPtr[cell] = diagPtr[cell];
    }

    for (label face=0; face<nFaces; face++)
    {
        sumAPtr[uPtr[face]] += lowerPtr[face];
        sumAPtr[lPtr[face]] += upperPtr[face];
    }

    // Add the interface internal coefficients to diagonal
    // and the interface boundary coefficients to the sum-off-diagonal
    forAll(interfaces, patchi)
    {
        if (interfaces.set(patchi))
        {
            const labelUList& pa = lduAddr().patchAddr(patchi);
            const scalarField& pCoeffs = interfaceBouCoeffs[patchi];

            forAll(pa, face)
            {
                sumAPtr[pa[face]] -= pCoeffs[face];
            }
        }
    }
}
Exemple #14
0
void Foam::lduMatrix::sumMagOffDiag
(
    scalarField& sumOff
) const
{
    const scalarField& Lower = const_cast<const lduMatrix&>(*this).lower();
    const scalarField& Upper = const_cast<const lduMatrix&>(*this).upper();

    const unallocLabelList& l = lduAddr().lowerAddr();
    const unallocLabelList& u = lduAddr().upperAddr();

    for (register label odcI = 0; odcI < l.size(); odcI++)
    {
        sumOff[u[odcI]] += mag(Lower[odcI]);
        sumOff[l[odcI]] += mag(Upper[odcI]);
    }
}
void Foam::lduMatrix::sumMagOffDiag
(
    scalarField& sumOff
) const
{
    const scalarField& Lower = const_cast<const lduMatrix&>(*this).lower();
    const scalarField& Upper = const_cast<const lduMatrix&>(*this).upper();

    const labelUList& l = lduAddr().lowerAddr();
    const labelUList& u = lduAddr().upperAddr();

    for (register label face = 0; face < l.size(); face++)
    {
        sumOff[u[face]] += mag(Lower[face]);
        sumOff[l[face]] += mag(Upper[face]);
    }
}
Foam::tmp<Foam::gpuField<Type> >
Foam::lduMatrix::faceH(const gpuField<Type>& psi) const
{
    if (lowerPtr_ || upperPtr_)
    {
        const scalargpuField& Lower = const_cast<const lduMatrix&>(*this).lower();
        const scalargpuField& Upper = const_cast<const lduMatrix&>(*this).upper();

        const labelgpuList& l = lduAddr().lowerAddr();
        const labelgpuList& u = lduAddr().upperAddr();

        tmp<gpuField<Type> > tfaceHpsi(new gpuField<Type> (Lower.size()));
        gpuField<Type> & faceHpsi = tfaceHpsi();

        thrust::transform
        ( 
            thrust::make_zip_iterator(thrust::make_tuple
            (
                Upper.begin(),  
                thrust::make_permutation_iterator(psi.begin(), u.begin()),
                Lower.begin(),
                thrust::make_permutation_iterator(psi.begin(), l.begin())
            )),
            thrust::make_zip_iterator(thrust::make_tuple
            (
                Upper.end() ,  
                thrust::make_permutation_iterator(psi.begin(), u.end()),
                Lower.end(),
                thrust::make_permutation_iterator(psi.begin(), l.end())
            )),
            faceHpsi.begin(),
            lduMatrixfaceHFunctor<Type>()
        );

        return tfaceHpsi;
    }
    else
    {
        FatalErrorIn("lduMatrix::faceH(const Field<Type>& psi) const")
            << "Cannot calculate faceH"
               " the matrix does not have any off-diagonal coefficients."
            << exit(FatalError);

        return tmp<gpuField<Type> >(NULL);
    }
}
Exemple #17
0
Foam::Field<DType>& Foam::LduMatrix<Type, DType, LUType>::diag()
{
    if (!diagPtr_)
    {
        diagPtr_ = new Field<DType>(lduAddr().size(), Zero);
    }

    return *diagPtr_;
}
Exemple #18
0
Foam::Field<Type>& Foam::LduMatrix<Type, DType, LUType>::source()
{
    if (!sourcePtr_)
    {
        sourcePtr_ = new Field<Type>(lduAddr().size(), Zero);
    }

    return *sourcePtr_;
}
Exemple #19
0
Foam::scalarField& Foam::lduMatrix::diag()
{
    if (!diagPtr_)
    {
        diagPtr_ = new scalarField(lduAddr().size(), 0.0);
    }

    return *diagPtr_;
}
void Foam::LduMatrix<Type, DType, LUType>::sumA
(
    Field<Type>& sumA
) const
{
    Type* __restrict__ sumAPtr = sumA.begin();

    const DType* __restrict__ diagPtr = diag().begin();

    const label* __restrict__ uPtr = lduAddr().upperAddr().begin();
    const label* __restrict__ lPtr = lduAddr().lowerAddr().begin();

    const LUType* __restrict__ lowerPtr = lower().begin();
    const LUType* __restrict__ upperPtr = upper().begin();

    const label nCells = diag().size();
    const label nFaces = upper().size();

    for (label cell=0; cell<nCells; cell++)
    {
        sumAPtr[cell] = dot(diagPtr[cell], pTraits<Type>::one);
    }

    for (label face=0; face<nFaces; face++)
    {
        sumAPtr[uPtr[face]] += dot(lowerPtr[face], pTraits<Type>::one);
        sumAPtr[lPtr[face]] += dot(upperPtr[face], pTraits<Type>::one);
    }

    // Add the interface internal coefficients to diagonal
    // and the interface boundary coefficients to the sum-off-diagonal
    forAll(interfaces_, patchi)
    {
        if (interfaces_.set(patchi))
        {
            const unallocLabelList& pa = lduAddr().patchAddr(patchi);
            const Field<LUType>& pCoeffs = interfacesUpper_[patchi];

            forAll(pa, face)
            {
                sumAPtr[pa[face]] -= dot(pCoeffs[face], pTraits<Type>::one);
            }
        }
    }
Foam::tmp<Foam::gpuField<Type> > Foam::lduMatrix::H(const gpuField<Type>& psi) const
{
    tmp<gpuField<Type> > tHpsi
    (
        new gpuField<Type>(lduAddr().size(), pTraits<Type>::zero)
    );

    H(tHpsi(),psi);

    return tHpsi;
}
Foam::tmp<Foam::gpuField<Type> > Foam::lduMatrix::H(const gpuField<Type>& psi) const
{
    tmp<gpuField<Type> > tHpsi
    (
        new gpuField<Type>(lduAddr().size(), pTraits<Type>::zero)
    );

    if (lowerPtr_ || upperPtr_)
    {
        gpuField<Type> & Hpsi = tHpsi();

        const scalargpuField& Lower = this->lower();
        const scalargpuField& Upper = this->upper();

        const labelgpuList& l = lduAddr().lowerAddr();
        const labelgpuList& u = lduAddr().upperAddr();
        
        matrixOperation
        (
            Hpsi.begin(),
            Hpsi,
            lduAddr(),
            matrixCoeffsMultiplyFunctor<Type,scalar,negateUnaryOperatorFunctor<Type,Type> >
            (
                psi.data(),
                Upper.data(),
                u.data(),
                negateUnaryOperatorFunctor<Type,Type>()
            ),
            matrixCoeffsMultiplyFunctor<Type,scalar,negateUnaryOperatorFunctor<Type,Type> >
            (
                psi.data(),
                Lower.data(),
                l.data(),
                negateUnaryOperatorFunctor<Type,Type>()
            )
        );                                        
    }

    return tHpsi;
}
Exemple #23
0
Foam::scalarField& Foam::lduMatrix::upper()
{
    if (!upperPtr_)
    {
        if (lowerPtr_)
        {
            upperPtr_ = new scalarField(*lowerPtr_);
        }
        else
        {
            upperPtr_ = new scalarField(lduAddr().lowerAddr().size(), 0.0);
        }
    }

    return *upperPtr_;
}
Exemple #24
0
Foam::Field<LUType>& Foam::LduMatrix<Type, DType, LUType>::lower()
{
    if (!lowerPtr_)
    {
        if (upperPtr_)
        {
            lowerPtr_ = new Field<LUType>(*upperPtr_);
        }
        else
        {
            lowerPtr_ = new Field<LUType>
            (
                lduAddr().lowerAddr().size(),
                Zero
            );
        }
    }

    return *lowerPtr_;
}
Foam::tmp<Foam::Field<Type> >
Foam::BlockLduMatrix<Type>::decoupledH(const Field<Type>& x) const
{
    typedef typename TypeCoeffField::scalarTypeField scalarTypeField;
    typedef typename TypeCoeffField::linearTypeField linearTypeField;

    // Create result
    tmp<Field<Type> > tresult
    (
        new Field<Type>(lduAddr().size(), pTraits<Type>::zero)
    );
    Field<Type>& result = tresult();

    const unallocLabelList& u = lduAddr().upperAddr();
    const unallocLabelList& l = lduAddr().lowerAddr();

    const TypeCoeffField& Upper = this->upper();

    // Create multiplication function object
    typename BlockCoeff<Type>::multiply mult;

    // Lower multiplication

    if (symmetric())
    {
        if (Upper.activeType() == blockCoeffBase::SCALAR)
        {
            const scalarTypeField& activeUpper = Upper.asScalar();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                result[u[coeffI]] -= mult(activeUpper[coeffI], x[l[coeffI]]);
            }
        }
        else if (Upper.activeType() == blockCoeffBase::LINEAR)
        {
            const linearTypeField& activeUpper = Upper.asLinear();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                result[u[coeffI]] -= mult(activeUpper[coeffI], x[l[coeffI]]);
            }
        }
    }
    else // Asymmetric matrix
    {
        const TypeCoeffField& Lower = this->lower();

        if (Lower.activeType() == blockCoeffBase::SCALAR)
        {
            const scalarTypeField& activeLower = Lower.asScalar();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                result[u[coeffI]] -= mult(activeLower[coeffI], x[l[coeffI]]);
            }
        }
        else if (Lower.activeType() == blockCoeffBase::LINEAR)
        {
            const linearTypeField& activeLower = Lower.asLinear();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                result[u[coeffI]] -= mult(activeLower[coeffI], x[l[coeffI]]);
            }
        }
    }


    // Upper multiplication

    if (Upper.activeType() == blockCoeffBase::SCALAR)
    {
        const scalarTypeField& activeUpper = Upper.asScalar();

        for (register label coeffI = 0; coeffI < u.size(); coeffI++)
        {
            result[l[coeffI]] -= mult(activeUpper[coeffI], x[u[coeffI]]);
        }
    }
    else if (Upper.activeType() == blockCoeffBase::LINEAR)
    {
        const linearTypeField& activeUpper = Upper.asLinear();

        for (register label coeffI = 0; coeffI < u.size(); coeffI++)
        {
            result[l[coeffI]] -= mult(activeUpper[coeffI], x[u[coeffI]]);
        }
    }

    return tresult;
}
void Foam::lduMatrix::residual
(
    scalarField& rA,
    const scalarField& psi,
    const scalarField& source,
    const FieldField<Field, scalar>& interfaceBouCoeffs,
    const lduInterfaceFieldPtrsList& interfaces,
    const direction cmpt
) const
{
    scalar* __restrict__ rAPtr = rA.begin();

    const scalar* const __restrict__ psiPtr = psi.begin();
    const scalar* const __restrict__ diagPtr = diag().begin();
    const scalar* const __restrict__ sourcePtr = source.begin();

    const label* const __restrict__ uPtr = lduAddr().upperAddr().begin();
    const label* const __restrict__ lPtr = lduAddr().lowerAddr().begin();

    const scalar* const __restrict__ upperPtr = upper().begin();
    const scalar* const __restrict__ lowerPtr = lower().begin();

    // Parallel boundary initialisation.
    // Note: there is a change of sign in the coupled
    // interface update.  The reason for this is that the
    // internal coefficients are all located at the l.h.s. of
    // the matrix whereas the "implicit" coefficients on the
    // coupled boundaries are all created as if the
    // coefficient contribution is of a source-kind (i.e. they
    // have a sign as if they are on the r.h.s. of the matrix.
    // To compensate for this, it is necessary to turn the
    // sign of the contribution.

    FieldField<Field, scalar> mBouCoeffs(interfaceBouCoeffs.size());

    forAll(mBouCoeffs, patchi)
    {
        if (interfaces.set(patchi))
        {
            mBouCoeffs.set(patchi, -interfaceBouCoeffs[patchi]);
        }
    }

    // Initialise the update of interfaced interfaces
    initMatrixInterfaces
    (
        mBouCoeffs,
        interfaces,
        psi,
        rA,
        cmpt
    );

    const label nCells = diag().size();
    for (label cell=0; cell<nCells; cell++)
    {
        rAPtr[cell] = sourcePtr[cell] - diagPtr[cell]*psiPtr[cell];
    }


    const label nFaces = upper().size();

    for (label face=0; face<nFaces; face++)
    {
        rAPtr[uPtr[face]] -= lowerPtr[face]*psiPtr[lPtr[face]];
        rAPtr[lPtr[face]] -= upperPtr[face]*psiPtr[uPtr[face]];
    }

    // Update interface interfaces
    updateMatrixInterfaces
    (
        mBouCoeffs,
        interfaces,
        psi,
        rA,
        cmpt
    );
}
void Foam::BlockLduMatrix<Type>::AmulCore
(
    TypeField& Ax,
    const TypeField& x
) const
{
    typedef typename TypeCoeffField::scalarTypeField scalarTypeField;
    typedef typename TypeCoeffField::linearTypeField linearTypeField;
    typedef typename TypeCoeffField::squareTypeField squareTypeField;

    const unallocLabelList& u = lduAddr().upperAddr();
    const unallocLabelList& l = lduAddr().lowerAddr();

    const TypeCoeffField& Diag = this->diag();
    const TypeCoeffField& Upper = this->upper();

    // Create multiplication function object
    typename BlockCoeff<Type>::multiply mult;

    // Diagonal multiplication, no indirection
    multiply(Ax, Diag, x);

    // Lower multiplication

    if (symmetric())
    {
        if (Upper.activeType() == blockCoeffBase::SCALAR)
        {
            const scalarTypeField& activeUpper = Upper.asScalar();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                Ax[u[coeffI]] += mult(activeUpper[coeffI], x[l[coeffI]]);
            }
        }
        else if (Upper.activeType() == blockCoeffBase::LINEAR)
        {
            const linearTypeField& activeUpper = Upper.asLinear();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                Ax[u[coeffI]] += mult(activeUpper[coeffI], x[l[coeffI]]);
            }
        }
        else if (Upper.activeType() == blockCoeffBase::SQUARE)
        {
            const squareTypeField& activeUpper = Upper.asSquare();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                // Use transpose upper coefficient
                Ax[u[coeffI]] +=
                    mult(activeUpper[coeffI].T(), x[l[coeffI]]);
            }
        }
    }
    else // Asymmetric matrix
    {
        const TypeCoeffField& Lower = this->lower();

        if (Lower.activeType() == blockCoeffBase::SCALAR)
        {
            const scalarTypeField& activeLower = Lower.asScalar();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                Ax[u[coeffI]] += mult(activeLower[coeffI], x[l[coeffI]]);
            }
        }
        else if (Lower.activeType() == blockCoeffBase::LINEAR)
        {
            const linearTypeField& activeLower = Lower.asLinear();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                Ax[u[coeffI]] += mult(activeLower[coeffI], x[l[coeffI]]);
            }
        }
        else if (Lower.activeType() == blockCoeffBase::SQUARE)
        {
            const squareTypeField& activeLower = Lower.asSquare();

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                Ax[u[coeffI]] += mult(activeLower[coeffI], x[l[coeffI]]);
            }
        }
    }


    // Upper multiplication

    if (Upper.activeType() == blockCoeffBase::SCALAR)
    {
        const scalarTypeField& activeUpper = Upper.asScalar();

        for (register label coeffI = 0; coeffI < u.size(); coeffI++)
        {
            Ax[l[coeffI]] += mult(activeUpper[coeffI], x[u[coeffI]]);
        }
    }
    else if (Upper.activeType() == blockCoeffBase::LINEAR)
    {
        const linearTypeField& activeUpper = Upper.asLinear();

        for (register label coeffI = 0; coeffI < u.size(); coeffI++)
        {
            Ax[l[coeffI]] += mult(activeUpper[coeffI], x[u[coeffI]]);
        }
    }
    else if (Upper.activeType() == blockCoeffBase::SQUARE)
    {
        const squareTypeField& activeUpper = Upper.asSquare();

        for (register label coeffI = 0; coeffI < u.size(); coeffI++)
        {
            Ax[l[coeffI]] += mult(activeUpper[coeffI], x[u[coeffI]]);
        }
    }
}
void Foam::BlockLduMatrix<Type>::segregateB
(
    TypeField& sMul,
    const TypeField& x
) const
{
    typedef typename TypeCoeffField::linearType linearType;
    typedef typename TypeCoeffField::squareType squareType;

    typedef typename TypeCoeffField::linearTypeField linearTypeField;
    typedef typename TypeCoeffField::squareTypeField squareTypeField;

    const unallocLabelList& u = lduAddr().upperAddr();
    const unallocLabelList& l = lduAddr().lowerAddr();

    // Diagonal multiplication
    if (thereIsDiag())
    {
        if (diag().activeType() == blockCoeffBase::SQUARE)
        {
            const squareTypeField& activeDiag = this->diag().asSquare();
            linearTypeField lf(activeDiag.size());
            squareTypeField sf(activeDiag.size());

            // Expand and contract
            contractLinear(lf, activeDiag);
            expandLinear(sf, lf);

            sMul -= (activeDiag - sf) & x;
        }
    }

    // Lower multiplication

    if (thereIsLower())
    {
        if (lower().activeType() == blockCoeffBase::SQUARE)
        {
            const squareTypeField& activeLower = this->lower().asSquare();

            // Auxiliary variables used in expand/contract
            linearType lt;
            squareType st;

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                contractLinear(lt, activeLower[coeffI]);
                expandLinear(st, lt);

                sMul[u[coeffI]] -= (activeLower[coeffI] - st) & x[l[coeffI]];
            }
        }
    }

    // Upper multiplication

    if (thereIsUpper())
    {
        if (upper().activeType() == blockCoeffBase::SQUARE)
        {
            const squareTypeField& activeUpper = this->upper().asSquare();

            // Auxiliary variables used in expand/contract
            linearType lt;
            squareType st;

            for (register label coeffI = 0; coeffI < u.size(); coeffI++)
            {
                contractLinear(lt, activeUpper[coeffI]);
                expandLinear(st, lt);

                sMul[l[coeffI]] -= (activeUpper[coeffI] - st) & x[u[coeffI]];
            }

            // If the matrix is symmetric, the lower triangular product
            // is also needed
            if (symmetric())
            {
                for (register label coeffI = 0; coeffI < u.size(); coeffI++)
                {
                    // Use transpose upper coefficient
                    contractLinear(lt, activeUpper[coeffI]);
                    expandLinear(st, lt);
                    sMul[u[coeffI]] -=
                        (activeUpper[coeffI].T() - st) & x[l[coeffI]];
                }
            }
        }
    }
}
    // deal with the ordering of the faces, the algorithm is split
    // into two parts.  For original faces, the internal faces are
    // distributed to their owner cells.  Once all internal faces are
    // distributed, the boundary faces are visited and if they are in
    // the mirror plane they are added to the master cells (the future
    // boundary faces are not touched).  After the first phase, the
    // internal faces are collected in the cell order and numbering
    // information is added.  Then, the internal faces are mirrored
    // and the face numbering data is stored for the mirrored section.
    // Once all the internal faces are mirrored, the boundary faces
    // are added by mirroring the faces patch by patch.

    // Distribute internal faces
    labelListList newCellFaces(oldCells.size());

    const unallocLabelList& oldOwnerStart = lduAddr().ownerStartAddr();

    forAll (newCellFaces, cellI)
    {
        labelList& curFaces = newCellFaces[cellI];

        const label s = oldOwnerStart[cellI];
        const label e = oldOwnerStart[cellI + 1];

        curFaces.setSize(e - s);

        forAll (curFaces, i)
        {
            curFaces[i] = s + i;
        }
    }
    // deal with the ordering of the faces, the algorithm is split
    // into two parts.  For original faces, the internal faces are
    // distributed to their owner cells.  Once all internal faces are
    // distributed, the boundary faces are visited and if they are in
    // the mirror plane they are added to the master cells (the future
    // boundary faces are not touched).  After the first phase, the
    // internal faces are collected in the cell order and numbering
    // information is added.  Then, the internal faces are mirrored
    // and the face numbering data is stored for the mirrored section.
    // Once all the internal faces are mirrored, the boundary faces
    // are added by mirroring the faces patch by patch.

    // Distribute internal faces
    labelListList newCellFaces(oldCells.size());

    const labelUList& oldOwnerStart = lduAddr().ownerStartAddr();

    forAll(newCellFaces, cellI)
    {
        labelList& curFaces = newCellFaces[cellI];

        const label s = oldOwnerStart[cellI];
        const label e = oldOwnerStart[cellI + 1];

        curFaces.setSize(e - s);

        forAll(curFaces, i)
        {
            curFaces[i] = s + i;
        }
    }