/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2015-2021 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 . \*---------------------------------------------------------------------------*/ #include "ConeNozzleInjection.H" #include "Function1.H" #include "unitConversion.H" #include "distributionModel.H" #include "axisAngleRotation.H" using namespace Foam::constant; template const Foam::Enum < typename Foam::ConeNozzleInjection::injectionMethod > Foam::ConeNozzleInjection::injectionMethodNames ({ { injectionMethod::imPoint, "point" }, { injectionMethod::imDisc, "disc" }, { injectionMethod::imDiscSegments, "discSegments" }, }); template const Foam::Enum < typename Foam::ConeNozzleInjection::flowType > Foam::ConeNozzleInjection::flowTypeNames ({ { flowType::ftConstantVelocity, "constantVelocity" }, { flowType::ftPressureDrivenVelocity, "pressureDrivenVelocity" }, { flowType::ftFlowRateAndDischarge, "flowRateAndDischarge" }, }); // * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * * // template void Foam::ConeNozzleInjection::setInjectionGeometry() { const auto& mesh = this->owner().mesh(); // Position positionVsTime_.reset ( Function1::New("position", this->coeffDict(), &mesh) ); positionVsTime_->userTimeToTime(this->owner().time()); if (positionVsTime_->constant()) { position_ = positionVsTime_->value(0); } // Direction directionVsTime_.reset ( Function1::New("direction", this->coeffDict(), &mesh) ); directionVsTime_->userTimeToTime(this->owner().time()); if (directionVsTime_->constant()) { direction_ = directionVsTime_->value(0); direction_.normalise(); Random& rndGen = this->owner().rndGen(); // Determine direction vectors tangential to direction vector tangent = Zero; scalar magTangent = 0.0; while(magTangent < SMALL) { vector v = rndGen.globalSample01(); tangent = v - (v & direction_)*direction_; magTangent = mag(tangent); } tanVec1_ = tangent/magTangent; tanVec2_ = direction_^tanVec1_; } } template void Foam::ConeNozzleInjection::setFlowType() { switch (flowType_) { case flowType::ftConstantVelocity: { this->coeffDict().readEntry("UMag", UMag_); break; } case flowType::ftPressureDrivenVelocity: { Pinj_.reset ( Function1::New ( "Pinj", this->coeffDict(), &this->owner().mesh() ) ); Pinj_->userTimeToTime(this->owner().time()); break; } case flowType::ftFlowRateAndDischarge: { Cd_.reset ( Function1::New ( "Cd", this->coeffDict(), &this->owner().mesh() ) ); Cd_->userTimeToTime(this->owner().time()); break; } default: { FatalErrorInFunction << "Unhandled flow type " << flowTypeNames[flowType_] << exit(FatalError); } } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template Foam::ConeNozzleInjection::ConeNozzleInjection ( const dictionary& dict, CloudType& owner, const word& modelName ) : InjectionModel(dict, owner, modelName, typeName), injectionMethod_ ( injectionMethodNames.get("injectionMethod", this->coeffDict()) ), flowType_(flowTypeNames.get("flowType", this->coeffDict())), outerDiameter_(this->coeffDict().getScalar("outerDiameter")), innerDiameter_(this->coeffDict().getScalar("innerDiameter")), duration_(this->coeffDict().getScalar("duration")), positionVsTime_(nullptr), position_(Zero), injectorCell_(-1), tetFacei_(-1), tetPti_(-1), directionVsTime_(nullptr), direction_(Zero), omegaPtr_ ( Function1::NewIfPresent ( "omega", this->coeffDict(), &owner.mesh() ) ), parcelsPerSecond_(this->coeffDict().getScalar("parcelsPerSecond")), flowRateProfile_ ( Function1::New ( "flowRateProfile", this->coeffDict(), &owner.mesh() ) ), thetaInner_ ( Function1::New ( "thetaInner", this->coeffDict(), &owner.mesh() ) ), thetaOuter_ ( Function1::New ( "thetaOuter", this->coeffDict(), &owner.mesh() ) ), sizeDistribution_ ( distributionModel::New ( this->coeffDict().subDict("sizeDistribution"), owner.rndGen() ) ), t0_(this->template getModelProperty("t0")), nInjectors_ ( this->coeffDict().template getOrDefault("nInjectors", 1) ), Uinjector_(Zero), initialInjectorDir_ ( this->coeffDict().template getOrDefault ( "initialInjectorDir", Zero ) ), tanVec1_(Zero), tanVec2_(Zero), normal_(Zero), UMag_(0.0), Cd_(nullptr), Pinj_(nullptr) { if (innerDiameter_ >= outerDiameter_) { FatalErrorInFunction << "Inner diameter must be less than the outer diameter:" << nl << " innerDiameter: " << innerDiameter_ << nl << " outerDiameter: " << outerDiameter_ << exit(FatalError); } if (nInjectors_ < SMALL) { FatalIOErrorInFunction(this->coeffDict()) << "Number of injectors in angular-segmented disc " << "must be positive" << nl << " nInjectors: " << nInjectors_ << nl << exit(FatalIOError); } // Convert from user time to reduce the number of time conversion calls const Time& time = owner.db().time(); duration_ = time.userTimeToTime(duration_); flowRateProfile_->userTimeToTime(time); thetaInner_->userTimeToTime(time); thetaOuter_->userTimeToTime(time); if (omegaPtr_) { omegaPtr_->userTimeToTime(time); } setInjectionGeometry(); setFlowType(); // Set total volume to inject this->volumeTotal_ = flowRateProfile_->integrate(0.0, duration_); updateMesh(); } template Foam::ConeNozzleInjection::ConeNozzleInjection ( const ConeNozzleInjection& im ) : InjectionModel(im), injectionMethod_(im.injectionMethod_), flowType_(im.flowType_), outerDiameter_(im.outerDiameter_), innerDiameter_(im.innerDiameter_), duration_(im.duration_), positionVsTime_(im.positionVsTime_.clone()), position_(im.position_), injectorCell_(im.injectorCell_), tetFacei_(im.tetFacei_), tetPti_(im.tetPti_), directionVsTime_(im.directionVsTime_.clone()), direction_(im.direction_), omegaPtr_(im.omegaPtr_.clone()), parcelsPerSecond_(im.parcelsPerSecond_), flowRateProfile_(im.flowRateProfile_.clone()), thetaInner_(im.thetaInner_.clone()), thetaOuter_(im.thetaOuter_.clone()), sizeDistribution_(im.sizeDistribution_.clone()), t0_(im.t0_), nInjectors_(im.nInjectors_), Uinjector_(im.Uinjector_), initialInjectorDir_(im.initialInjectorDir_), tanVec1_(im.tanVec1_), tanVec2_(im.tanVec2_), normal_(im.normal_), UMag_(im.UMag_), Cd_(im.Cd_.clone()), Pinj_(im.Pinj_.clone()) {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template void Foam::ConeNozzleInjection::updateMesh() { // Set/cache the injector cell info for static methods if (positionVsTime_->constant()) { position_ = positionVsTime_->value(0); this->findCellAtPosition ( injectorCell_, tetFacei_, tetPti_, position_ ); } } template Foam::scalar Foam::ConeNozzleInjection::timeEnd() const { return this->SOI_ + duration_; } template Foam::label Foam::ConeNozzleInjection::parcelsToInject ( const scalar time0, const scalar time1 ) { if ((time0 >= 0.0) && (time0 < duration_)) { return floor((time1 - time0)*parcelsPerSecond_); } return 0; } template Foam::scalar Foam::ConeNozzleInjection::volumeToInject ( const scalar time0, const scalar time1 ) { if ((time0 >= 0.0) && (time0 < duration_)) { return flowRateProfile_->integrate(time0, time1); } return 0.0; } template void Foam::ConeNozzleInjection::setPositionAndCell ( const label, const label, const scalar time, vector& position, label& cellOwner, label& tetFacei, label& tetPti ) { Random& rndGen = this->owner().rndGen(); const scalar t = time - this->SOI_; if (!directionVsTime_->constant()) { direction_ = directionVsTime_->value(t); direction_.normalise(); // Determine direction vectors tangential to direction vector tangent = Zero; scalar magTangent = 0.0; while(magTangent < SMALL) { vector v = rndGen.globalSample01(); tangent = v - (v & direction_)*direction_; magTangent = mag(tangent); } tanVec1_ = tangent/magTangent; tanVec2_ = direction_^tanVec1_; } scalar beta = mathematical::twoPi*rndGen.globalSample01(); normal_ = tanVec1_*cos(beta) + tanVec2_*sin(beta); switch (injectionMethod_) { case injectionMethod::imPoint: { if (positionVsTime_->constant()) { position = position_; cellOwner = injectorCell_; tetFacei = tetFacei_; tetPti = tetPti_; } else { position = positionVsTime_->value(t); // Estimate the moving injector velocity const vector position0(positionVsTime_->value(t0_)); const scalar dt = t - t0_; if (dt > 0) { Uinjector_ = (position - position0)/dt; } this->findCellAtPosition ( cellOwner, tetFacei, tetPti, position ); } break; } case injectionMethod::imDisc: { scalar frac = rndGen.globalSample01(); scalar dr = outerDiameter_ - innerDiameter_; scalar r = 0.5*(innerDiameter_ + frac*dr); position = positionVsTime_->value(t) + r*normal_; // Estimate the moving injector velocity const vector position0(positionVsTime_->value(t0_) + r*normal_); const scalar dt = t - t0_; if (dt > 0) { Uinjector_ = (position - position0)/dt; } this->findCellAtPosition ( cellOwner, tetFacei, tetPti, position ); break; } case injectionMethod::imDiscSegments: { // Calculate the uniform angular increment in radians const scalar angleIncrement = mathematical::twoPi/nInjectors_; // Randomly set the index of injector angles const label injectorIndex = rndGen.globalPosition