Skip to content
Snippets Groups Projects
ThermoSurfaceFilm.C 22 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
    
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        \\  /    A nd           | www.openfoam.com
    
    -------------------------------------------------------------------------------
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        Copyright (C) 2011-2017 OpenFOAM Foundation
    
        Copyright (C) 2019-2020 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
        OpenFOAM is free software: you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
        along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
    
    \*---------------------------------------------------------------------------*/
    
    #include "ThermoSurfaceFilm.H"
    
    #include "surfaceFilmRegionModel.H"
    #include "liquidFilmBase.H"
    
    #include "addToRunTimeSelectionTable.H"
    
    #include "unitConversion.H"
    
    #include "Pstream.H"
    
    using namespace Foam::constant::mathematical;
    
    // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
    
    template<class CloudType>
    Foam::wordList Foam::ThermoSurfaceFilm<CloudType>::interactionTypeNames_
    
    
    
    // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
    
    template<class CloudType>
    typename Foam::ThermoSurfaceFilm<CloudType>::interactionType
    Foam::ThermoSurfaceFilm<CloudType>::interactionTypeEnum(const word& it) const
    {
        forAll(interactionTypeNames_, i)
        {
            if (interactionTypeNames_[i] == it)
            {
                return interactionType(i);
            }
        }
    
    
        FatalErrorInFunction
            << "Unknown interaction type " << it
    
            << ". Valid interaction types include: " << interactionTypeNames_
            << abort(FatalError);
    
        return interactionType(0);
    }
    
    
    template<class CloudType>
    Foam::word Foam::ThermoSurfaceFilm<CloudType>::interactionTypeStr
    (
        const interactionType& it
    ) const
    {
        if (it >= interactionTypeNames_.size())
        {
    
            FatalErrorInFunction
                << "Unknown interaction type enumeration" << abort(FatalError);
    
        }
    
        return interactionTypeNames_[it];
    }
    
    
    
    // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
    
    
    template<class CloudType>
    Foam::vector Foam::ThermoSurfaceFilm<CloudType>::tangentVector
    (
        const vector& v
    ) const
    {
    
    Henry Weller's avatar
    Henry Weller committed
        vector tangent = Zero;
    
        scalar magTangent = 0.0;
    
        while (magTangent < SMALL)
        {
            vector vTest = rndGen_.sample01<vector>();
            tangent = vTest - (vTest & v)*v;
            magTangent = mag(tangent);
        }
    
        return tangent/magTangent;
    }
    
    
    template<class CloudType>
    Foam::vector Foam::ThermoSurfaceFilm<CloudType>::splashDirection
    (
        const vector& tanVec1,
        const vector& tanVec2,
        const vector& nf
    ) const
    {
    
        const scalar phiSi = twoPi*rndGen_.sample01<scalar>();
    
    
        const scalar thetaSi = degToRad(rndGen_.sample01<scalar>()*(50 - 5) + 5);
    
        const scalar alpha = sin(thetaSi);
        const scalar dcorr = cos(thetaSi);
        const vector normal = alpha*(tanVec1*cos(phiSi) + tanVec2*sin(phiSi));
        vector dirVec = dcorr*nf;
        dirVec += normal;
    
        return dirVec/mag(dirVec);
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::absorbInteraction
    (
        const parcelType& p,
        const polyPatch& pp,
    
        const scalar mass,
        bool& keepParticle
    )
    {
        if (debug)
        {
            Info<< "Parcel " << p.origId() << " absorbInteraction" << endl;
        }
    
        // Patch face normal
    
        const vector& nf = pp.faceNormals()[facei];
    
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
    
    
        // Relative parcel velocity
        const vector Urel = p.U() - Up;
    
        // Parcel normal velocity
        const vector Un = nf*(Urel & nf);
    
        // Parcel tangential velocity
        const vector Ut = Urel - Un;
    
    
        if (filmModel_)
        {
            filmModel_->addSources
            (
                pp.index(),
                facei,
                mass,                           // mass
                mass*Ut,                        // tangential momentum
                mass*mag(Un),                   // impingement pressure
                mass*p.hs()                     // energy
            );
    
            this->nParcelsTransferred()++;
    
            keepParticle = false;
        }
    }
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::absorbInteraction
    (
        areaFilm& filmModel,
        const parcelType& p,
        const polyPatch& pp,
        const label facei,
        const scalar mass,
        bool& keepParticle
    )
    {
    
        if (debug)
        {
            Info<< "Parcel " << p.origId() << " absorbInteraction" << endl;
        }
    
        // Patch face normal
        const vector& nf = pp.faceNormals()[facei];
    
        // Patch velocity
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
    
        // Relative parcel velocity
        const vector Urel = p.U() - Up;
    
        // Parcel normal velocity
        const vector Un = nf*(Urel & nf);
    
        // Parcel tangential velocity
        const vector Ut = Urel - Un;
    DebugVar(mass)
    
        filmModel.addSources
        (
            pp.index(),
    
            mass,                           // mass
            mass*Ut,                        // tangential momentum
            mass*mag(Un),                   // impingement pressure
            mass*p.hs()                     // energy
        );
    
        this->nParcelsTransferred()++;
    
        keepParticle = false;
    
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::bounceInteraction
    (
        parcelType& p,
        const polyPatch& pp,
    
        bool& keepParticle
    ) const
    {
        if (debug)
        {
            Info<< "Parcel " << p.origId() << " bounceInteraction" << endl;
        }
    
        // Patch face normal
    
        const vector& nf = pp.faceNormals()[facei];
    
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
    
    
        // Relative parcel velocity
        const vector Urel = p.U() - Up;
    
        // Flip parcel normal velocity component
        p.U() -= 2.0*nf*(Urel & nf);
    
        keepParticle = true;
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::drySplashInteraction
    (
    
        //regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
    
        const parcelType& p,
        const polyPatch& pp,
    
        bool& keepParticle
    )
    {
        if (debug)
        {
            Info<< "Parcel " << p.origId() << " drySplashInteraction" << endl;
        }
    
    
        const liquidProperties& liq = thermo_.liquids().properties()[0];
    
    
        // Patch face velocity and normal
    
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
        const vector& nf = pp.faceNormals()[facei];
    
        const scalar pc = thermo_.thermo().p()[p.cell()];
    
        // Retrieve parcel properties
        const scalar m = p.mass()*p.nParticle();
        const scalar rho = p.rho();
        const scalar d = p.d();
        const scalar sigma = liq.sigma(pc, p.T());
        const scalar mu = liq.mu(pc, p.T());
        const vector Urel = p.U() - Up;
        const vector Un = nf*(Urel & nf);
    
        // Laplace number
        const scalar La = rho*sigma*d/sqr(mu);
    
        // Weber number
        const scalar We = rho*magSqr(Un)*d/sigma;
    
        // Critical Weber number
        const scalar Wec = Adry_*pow(La, -0.183);
    
    
        if (We < Wec) // Adhesion - assume absorb
    
            absorbInteraction(p, pp, facei, m, keepParticle);
    
            // Ratio of incident mass to splashing mass
    
            const scalar mRatio = 0.2 + 0.6*rndGen_.sample01<scalar>();
            splashInteraction
    
                (p, pp, facei, mRatio, We, Wec, sigma, keepParticle);
    
        }
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::wetSplashInteraction
    (
    
        //regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
    
        parcelType& p,
        const polyPatch& pp,
    
        bool& keepParticle
    )
    {
        if (debug)
        {
            Info<< "Parcel " << p.origId() << " wetSplashInteraction" << endl;
        }
    
    
        const liquidProperties& liq = thermo_.liquids().properties()[0];
    
    
        // Patch face velocity and normal
    
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
        const vector& nf = pp.faceNormals()[facei];
    
        const scalar pc = thermo_.thermo().p()[p.cell()];
    
        // Retrieve parcel properties
        const scalar m = p.mass()*p.nParticle();
        const scalar rho = p.rho();
        const scalar d = p.d();
        vector& U = p.U();
        const scalar sigma = liq.sigma(pc, p.T());
        const scalar mu = liq.mu(pc, p.T());
        const vector Urel = p.U() - Up;
        const vector Un = nf*(Urel & nf);
        const vector Ut = Urel - Un;
    
        // Laplace number
        const scalar La = rho*sigma*d/sqr(mu);
    
        // Weber number
        const scalar We = rho*magSqr(Un)*d/sigma;
    
        // Critical Weber number
        const scalar Wec = Awet_*pow(La, -0.183);
    
    
            absorbInteraction(p, pp, facei, m, keepParticle);
    
        else if ((We >= 2) && (We < 20)) // Bounce
    
            const scalar theta = piByTwo - acos(U/mag(U) & nf);
    
            const scalar epsilon = 0.993 - theta*(1.76 - theta*(1.56 - theta*0.49));
    
    
            U = -epsilon*(Un) + 5.0/7.0*(Ut);
    
        else if ((We >= 20) && (We < Wec)) // Spread - assume absorb
    
            absorbInteraction(p, pp, facei, m, keepParticle);
    
            // Ratio of incident mass to splashing mass
    
            // splash mass can be > incident mass due to film entrainment
            const scalar mRatio = 0.2 + 0.9*rndGen_.sample01<scalar>();
            splashInteraction
    
                (p, pp, facei, mRatio, We, Wec, sigma, keepParticle);
    
        }
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::splashInteraction
    (
    
        //regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
    
        const parcelType& p,
        const polyPatch& pp,
    
        const scalar mRatio,
        const scalar We,
        const scalar Wec,
        const scalar sigma,
        bool& keepParticle
    )
    {
        // Patch face velocity and normal
        const fvMesh& mesh = this->owner().mesh();
    
        const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
        const vector& nf = pp.faceNormals()[facei];
    
    
        // Determine direction vectors tangential to patch normal
        const vector tanVec1 = tangentVector(nf);
        const vector tanVec2 = nf^tanVec1;
    
        // Retrieve parcel properties
        const scalar np = p.nParticle();
        const scalar m = p.mass()*np;
        const scalar d = p.d();
        const vector Urel = p.U() - Up;
        const vector Un = nf*(Urel & nf);
        const vector Ut = Urel - Un;
        const vector& posC = mesh.C()[p.cell()];
    
        const vector& posCf = mesh.Cf().boundaryField()[pp.index()][facei];
    
        const scalar mSplash = m*mRatio;
    
    
        // Number of splashed particles per incoming particle
    
        const scalar Ns = 5.0*(We/Wec - 1.0);
    
    
        // Average diameter of splashed particles
    
        const scalar dBarSplash = 1/cbrt(6.0)*cbrt(mRatio/Ns)*d + ROOTVSMALL;
    
    
        // Cumulative diameter splash distribution
    
    andy's avatar
    andy committed
        const scalar dMax = 0.9*cbrt(mRatio)*d;
        const scalar dMin = 0.1*dMax;
    
        const scalar K = exp(-dMin/dBarSplash) - exp(-dMax/dBarSplash);
    
    
        // Surface energy of secondary parcels [J]
    
        // Sample splash distribution to determine secondary parcel diameters
    
        scalarList dNew(parcelsPerSplash_);
    
        scalarList npNew(parcelsPerSplash_);
    
        forAll(dNew, i)
        {
            const scalar y = rndGen_.sample01<scalar>();
            dNew[i] = -dBarSplash*log(exp(-dMin/dBarSplash) - y*K);
    
            npNew[i] = mRatio*np*pow3(d)/pow3(dNew[i])/parcelsPerSplash_;
            ESigmaSec += npNew[i]*sigma*p.areaS(dNew[i]);
    
        const scalar EKIn = 0.5*m*magSqr(Un);
    
        const scalar ESigmaIn = np*sigma*p.areaS(d);
    
        const scalar Ed = max(0.8*EKIn, np*Wec/12*pi*sigma*sqr(d));
    
        const scalar EKs = EKIn + ESigmaIn - ESigmaSec - Ed;
    
    
        // Switch to absorb if insufficient energy for splash
    
            absorbInteraction(p, pp, facei, m, keepParticle);
    
        // Helper variables to calculate magUns0
    
        const scalar logD = log(d);
        const scalar coeff2 = log(dNew[0]) - logD + ROOTVSMALL;
        scalar coeff1 = 0.0;
        forAll(dNew, i)
        {
            coeff1 += sqr(log(dNew[i]) - logD);
        }
    
    
        // Magnitude of the normal velocity of the first splashed parcel
    
        const scalar magUns0 =
    
    andy's avatar
    andy committed
            sqrt(2.0*parcelsPerSplash_*EKs/mSplash/(1.0 + coeff1/sqr(coeff2)));
    
    
        // Set splashed parcel properties
        forAll(dNew, i)
        {
            const vector dirVec = splashDirection(tanVec1, tanVec2, -nf);
    
            // Create a new parcel by copying source parcel
            parcelType* pPtr = new parcelType(p);
    
    
            pPtr->origId() = pPtr->getNewParticleID();
    
    
            pPtr->origProc() = Pstream::myProcNo();
    
            if (splashParcelType_ >= 0)
            {
                pPtr->typeId() = splashParcelType_;
            }
    
    
            // Perturb new parcels towards the owner cell centre
    
            pPtr->track(0.5*rndGen_.sample01<scalar>()*(posC - posCf), 0);
    
            pPtr->nParticle() = npNew[i];
    
    
            pPtr->d() = dNew[i];
    
            pPtr->U() = dirVec*(mag(Cf_*Ut) + magUns0*(log(dNew[i]) - logD)/coeff2);
    
            // Apply correction to velocity for 2-D cases
            meshTools::constrainDirection(mesh, mesh.solutionD(), pPtr->U());
    
            // Add the new parcel
            this->owner().addParticle(pPtr);
    
            nParcelsSplashed_++;
        }
    
    
        // Transfer remaining part of parcel to film 0 - splashMass can be -ve
    
        // if entraining from the film
        const scalar mDash = m - mSplash;
    
        absorbInteraction(p, pp, facei, mDash, keepParticle);
    
    
    // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
    
    template<class CloudType>
    Foam::ThermoSurfaceFilm<CloudType>::ThermoSurfaceFilm
    (
        const dictionary& dict,
    
        CloudType& owner
    
        SurfaceFilmModel<CloudType>(dict, owner, typeName),
    
        rndGen_(owner.rndGen()),
    
        thermo_
        (
            owner.db().objectRegistry::template lookupObject<SLGThermo>("SLGThermo")
        ),
    
        filmModel_(nullptr),
    
        CpFilmPatch_(0),
        interactionType_
        (
    
            interactionTypeEnum(this->coeffDict().getWord("interactionType"))
    
        ),
        deltaWet_(0.0),
        splashParcelType_(0),
        parcelsPerSplash_(0),
        Adry_(0.0),
        Awet_(0.0),
        Cf_(0.0),
        nParcelsSplashed_(0)
    {
        Info<< "    Applying " << interactionTypeStr(interactionType_)
            << " interaction model" << endl;
    
        if (interactionType_ == itSplashBai)
        {
    
            this->coeffDict().readEntry("deltaWet", deltaWet_);
    
            splashParcelType_ =
    
                this->coeffDict().getOrDefault("splashParcelType", -1);
    
            parcelsPerSplash_ =
    
                this->coeffDict().getOrDefault("parcelsPerSplash", 2);
    
            this->coeffDict().readEntry("Adry", Adry_);
            this->coeffDict().readEntry("Awet", Awet_);
            this->coeffDict().readEntry("Cf", Cf_);
    
    
        filmModel_ =
            const_cast<regionModels::surfaceFilmModels::surfaceFilmRegionModel*>
            (
                this->owner().mesh().time().objectRegistry::template findObject
                <
                    regionModels::surfaceFilmModels::surfaceFilmRegionModel
                >
                (
                    "surfaceFilmProperties"
                )
            );
    
    template<class CloudType>
    Foam::ThermoSurfaceFilm<CloudType>::ThermoSurfaceFilm
    (
        const ThermoSurfaceFilm<CloudType>& sfm
    )
    :
        SurfaceFilmModel<CloudType>(sfm),
    
        rndGen_(sfm.rndGen_),
        thermo_(sfm.thermo_),
    
        filmModel_(nullptr),
    
        TFilmPatch_(sfm.TFilmPatch_),
    
        CpFilmPatch_(sfm.CpFilmPatch_),
        interactionType_(sfm.interactionType_),
        deltaWet_(sfm.deltaWet_),
        splashParcelType_(sfm.splashParcelType_),
        parcelsPerSplash_(sfm.parcelsPerSplash_),
        Adry_(sfm.Adry_),
        Awet_(sfm.Awet_),
        Cf_(sfm.Cf_),
        nParcelsSplashed_(sfm.nParcelsSplashed_)
    
    {
         filmModel_ =
            const_cast<regionModels::surfaceFilmModels::surfaceFilmRegionModel*>
            (
                this->owner().mesh().time().objectRegistry::template findObject
                <
                    regionModels::surfaceFilmModels::surfaceFilmRegionModel
                >
                (
                    "surfaceFilmProperties"
                )
            );
    }
    
    // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
    
    template<class CloudType>
    Foam::ThermoSurfaceFilm<CloudType>::~ThermoSurfaceFilm()
    {}
    
    
    // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
    
    template<class CloudType>
    bool Foam::ThermoSurfaceFilm<CloudType>::transferParcel
    (
    
        parcelType& p,
        const polyPatch& pp,
        bool& keepParticle
    
        const fvMesh& mesh = this->owner().mesh();
    
        const label patchi = pp.index();
    
        // Check the singleLayer film models
        if (filmModel_ && filmModel_->isRegionPatch(patchi))
    
            const label facei = pp.whichFace(p.face());
    
            switch (interactionType_)
    
                    bounceInteraction(p, pp, facei, keepParticle);
    
    
                    break;
                }
                case itAbsorb:
                {
                    const scalar m = p.nParticle()*p.mass();
    
                    absorbInteraction(p, pp, facei, m, keepParticle);
    
                    bool dry = this->deltaFilmPatch_[patchi][facei] < deltaWet_;
    
                        drySplashInteraction(p, pp, facei, keepParticle);
    
                        wetSplashInteraction( p, pp, facei, keepParticle);
    
                    FatalErrorInFunction
                        << "Unknown interaction type enumeration"
    
                        << abort(FatalError);
                }
    
            // Transfer parcel/parcel interactions complete
    
        else
        {
    
            // Check the finite area films
            wordList names =
                mesh.time().objectRegistry::template
                    sortedNames<regionModels::regionFaModel>();
    
            forAll(names, i)
            {
                const regionModels::regionFaModel* regionFa =
                    mesh.time().objectRegistry::template findObject
                    <
                        regionModels::regionFaModel
                    >(names[i]);
    
                // Check that regionFa is a areaFilm
                if (regionFa && isA<areaFilm>(*regionFa))
                {
                    areaFilm& film =
                        const_cast<areaFilm&>(refCast<const areaFilm>(*regionFa));
    
                    const label facei = pp.whichFace(p.face());
    
                    switch (interactionType_)
                    {
                        // It only supports absorp model
                        case itAbsorb:
                        {
                            const scalar m = p.nParticle()*p.mass();
                            absorbInteraction
                            (
                                film, p, pp, facei, m, keepParticle
                            );
                            break;
                        }
                        default:
                        {
                            FatalErrorInFunction
                                << "Unknown interaction type enumeration"
                                << abort(FatalError);
                        }
                    }
                    // Transfer parcel/parcel interactions complete
                    return true;
                }
            }
        }
    
    }
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::cacheFilmFields
    (
    
        const label filmPatchi,
        const label primaryPatchi,
    
        const regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel
    
    )
    {
        SurfaceFilmModel<CloudType>::cacheFilmFields
        (
    
        TFilmPatch_ = filmModel.Ts().boundaryField()[filmPatchi];
        filmModel.toPrimary(filmPatchi, TFilmPatch_);
    
        CpFilmPatch_ = filmModel.Cp().boundaryField()[filmPatchi];
        filmModel.toPrimary(filmPatchi, CpFilmPatch_);
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::cacheFilmFields
    (
        const label filmPatchi,
        const areaFilm& filmModel
    )
    {
        SurfaceFilmModel<CloudType>::cacheFilmFields
        (
            filmPatchi,
            filmModel
        );
        const volSurfaceMapping& map = filmModel.region().vsm();
    
        TFilmPatch_.setSize(filmModel.Tf().size(), Zero);
        map.mapToField(filmModel.Tf(), TFilmPatch_);
        //map.mapToField(filmModel.Cp(), CpFilmPatch_);
    }
    
    
    
    template<class CloudType>
    void Foam::ThermoSurfaceFilm<CloudType>::setParcelProperties
    (
        parcelType& p,
    
        SurfaceFilmModel<CloudType>::setParcelProperties(p, filmFacei);
    
        p.T() = TFilmPatch_[filmFacei];
        p.Cp() = CpFilmPatch_[filmFacei];
    
    void Foam::ThermoSurfaceFilm<CloudType>::info(Ostream& os)
    
        SurfaceFilmModel<CloudType>::info(os);
    
        label nSplash0 = this->template getModelProperty<label>("nParcelsSplashed");
        label nSplashTotal =
            nSplash0 + returnReduce(nParcelsSplashed_, sumOp<label>());
    
    
        os  << "      - new splash parcels          = " << nSplashTotal << endl;
    
        {
            this->setModelProperty("nParcelsSplashed", nSplashTotal);
            nParcelsSplashed_ = 0;
        }
    
    }
    
    
    // ************************************************************************* //