/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- 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 "setFlow.H" #include "volFields.H" #include "surfaceFields.H" #include "fvcFlux.H" #include "addToRunTimeSelectionTable.H" #include "fvcSurfaceIntegrate.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { namespace functionObjects { defineTypeNameAndDebug(setFlow, 0); addToRunTimeSelectionTable ( functionObject, setFlow, dictionary ); } } const Foam::Enum Foam::functionObjects::setFlow::modeTypeNames { { functionObjects::setFlow::modeType::FUNCTION, "function" }, { functionObjects::setFlow::modeType::ROTATION, "rotation" }, { functionObjects::setFlow::modeType::VORTEX2D, "vortex2D" }, { functionObjects::setFlow::modeType::VORTEX3D, "vortex3D" } }; // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::functionObjects::setFlow::setPhi(const volVectorField& U) { surfaceScalarField* phiptr = mesh_.lookupObjectRefPtr(phiName_); if (!phiptr) { return; } if (rhoName_ != "none") { const volScalarField* rhoptr = mesh_.lookupObjectPtr(rhoName_); if (rhoptr) { const volScalarField& rho = *rhoptr; *phiptr = fvc::flux(rho*U); } else { FatalErrorInFunction << "Unable to find rho field'" << rhoName_ << "' in the mesh database. Available fields are:" << mesh_.names() << exit(FatalError); } } else { *phiptr = fvc::flux(U); } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::functionObjects::setFlow::setFlow ( const word& name, const Time& runTime, const dictionary& dict ) : fvMeshFunctionObject(name, runTime, dict), UName_("U"), rhoName_("none"), phiName_("phi"), mode_(modeType::FUNCTION), reverseTime_(VGREAT), scalePtr_(nullptr), origin_(vector::zero), R_(tensor::I), omegaPtr_(nullptr), velocityPtr_(nullptr) { read(dict); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::functionObjects::setFlow::~setFlow() {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // bool Foam::functionObjects::setFlow::read(const dictionary& dict) { if (fvMeshFunctionObject::read(dict)) { Info<< name() << ":" << endl; mode_ = modeTypeNames.read(dict.lookup("mode")); Info<< " operating mode: " << modeTypeNames[mode_] << endl; if (dict.readIfPresent("U", UName_)) { Info<< " U field name: " << UName_ << endl; } if (dict.readIfPresent("rho", rhoName_)) { Info<< " rho field name: " << rhoName_ << endl; } if (dict.readIfPresent("phi", phiName_)) { Info<< " phi field name: " << phiName_ << endl; } if (dict.readIfPresent("reverseTime", reverseTime_)) { Info<< " reverse flow direction at time: " << reverseTime_ << endl; reverseTime_ = mesh_.time().userTimeToTime(reverseTime_); } // Scaling is applied across all modes scalePtr_ = Function1::New("scale", dict); switch (mode_) { case modeType::FUNCTION: { velocityPtr_ = Function1::New("velocity", dict); break; } case modeType::ROTATION: { omegaPtr_ = Function1::New("omega", dict); dict.lookup("origin") >> origin_; vector refDir(dict.lookup("refDir")); refDir /= mag(refDir) + ROOTVSMALL; vector axis(dict.lookup("axis")); axis /= mag(axis) + ROOTVSMALL; R_ = tensor(refDir, axis, refDir^axis); break; } case modeType::VORTEX2D: case modeType::VORTEX3D: { dict.lookup("origin") >> origin_; vector refDir(dict.lookup("refDir")); refDir /= mag(refDir) + ROOTVSMALL; vector axis(dict.lookup("axis")); axis /= mag(axis) + ROOTVSMALL; R_ = tensor(refDir, axis, refDir^axis); break; } } Info<< endl; return true; } return false; } bool Foam::functionObjects::setFlow::execute() { volVectorField* Uptr = mesh_.lookupObjectRefPtr(UName_); surfaceScalarField* phiptr = mesh_.lookupObjectRefPtr(phiName_); Log << nl << name() << ":" << nl; if (!Uptr || !phiptr) { Info<< " Either field " << UName_ << " or " << phiName_ << " not found in the mesh database" << nl; return true; } const scalar t = mesh_.time().timeOutputValue(); Log << " setting " << UName_ << " and " << phiName_ << nl; volVectorField& U = *Uptr; surfaceScalarField& phi = *phiptr; switch (mode_) { case modeType::FUNCTION: { const vector Uc = velocityPtr_->value(t); U == dimensionedVector("Uc", dimVelocity, Uc); U.correctBoundaryConditions(); setPhi(U); break; } case modeType::ROTATION: { const volVectorField& C = mesh_.C(); const volVectorField d ( typeName + ":d", C - dimensionedVector("origin", dimLength, origin_) ); const scalarField x(d.component(vector::X)); const scalarField z(d.component(vector::Z)); const scalar omega = omegaPtr_->value(t); vectorField& Uc = U.primitiveFieldRef(); Uc.replace(vector::X, -omega*z); Uc.replace(vector::Y, scalar(0)); Uc.replace(vector::Z, omega*x); volVectorField::Boundary& Ubf = U.boundaryFieldRef(); forAll(Ubf, patchi) { vectorField& Uf = Ubf[patchi]; if (Uf.size()) { const vectorField& Cp = C.boundaryField()[patchi]; const vectorField dp(Cp - origin_); const scalarField xp(dp.component(vector::X)); const scalarField zp(dp.component(vector::Z)); Uf.replace(vector::X, -omega*zp); Uf.replace(vector::Y, scalar(0)); Uf.replace(vector::Z, omega*xp); } } U = U & R_; U.correctBoundaryConditions(); setPhi(U); break; } case modeType::VORTEX2D: { const scalar pi = Foam::constant::mathematical::pi; const volVectorField& C = mesh_.C(); const volVectorField d ( typeName + ":d", C - dimensionedVector("origin", dimLength, origin_) ); const scalarField x(d.component(vector::X)); const scalarField z(d.component(vector::Z)); vectorField& Uc = U.primitiveFieldRef(); Uc.replace(vector::X, -sin(2*pi*z)*sqr(sin(pi*x))); Uc.replace(vector::Y, scalar(0)); Uc.replace(vector::Z, sin(2*pi*x)*sqr(sin(pi*z))); U = U & R_; // Calculating incompressible flux based on stream function // Note: R_ rotation not implemented in phi calculation const scalarField xp(mesh_.points().component(0) - origin_[0]); const scalarField yp(mesh_.points().component(1) - origin_[1]); const scalarField zp(mesh_.points().component(2) - origin_[2]); const scalarField psi((1.0/pi)*sqr(sin(pi*xp))*sqr(sin(pi*zp))); scalarField& phic = phi.primitiveFieldRef(); forAll(phic, fi) { phic[fi] = 0; const face& f = mesh_.faces()[fi]; const label nPoints = f.size(); forAll(f, fpi) { const label p1 = f[fpi]; const label p2 = f[(fpi + 1) % nPoints]; phic[fi] += 0.5*(psi[p1] + psi[p2])*(yp[p2] - yp[p1]); } } surfaceScalarField::Boundary& phibf = phi.boundaryFieldRef(); forAll(phibf, patchi) { scalarField& phif = phibf[patchi]; const label start = mesh_.boundaryMesh()[patchi].start(); forAll(phif, fi) { phif[fi] = 0; const face& f = mesh_.faces()[start + fi]; const label nPoints = f.size(); forAll(f, fpi) { const label p1 = f[fpi]; const label p2 = f[(fpi + 1) % nPoints]; phif[fi] += 0.5*(psi[p1] + psi[p2])*(yp[p2] - yp[p1]); } } } break; } case modeType::VORTEX3D: { const scalar pi = Foam::constant::mathematical::pi; const volVectorField& C = mesh_.C(); const volVectorField d ( typeName + ":d", C - dimensionedVector("origin", dimLength, origin_) ); const scalarField x(d.component(vector::X)); const scalarField y(d.component(vector::Y)); const scalarField z(d.component(vector::Z)); vectorField& Uc = U.primitiveFieldRef(); Uc.replace(vector::X, 2*sqr(sin(pi*x))*sin(2*pi*y)*sin(2*pi*z)); Uc.replace(vector::Y, -sin(2*pi*x)*sqr(sin(pi*y))*sin(2*pi*z)); Uc.replace(vector::Z, -sin(2*pi*x)*sin(2*pi*y)*sqr(sin(pi*z))); U = U & R_; U.correctBoundaryConditions(); // Calculating phi // Note: R_ rotation not implemented in phi calculation const vectorField Cf(mesh_.Cf().primitiveField() - origin_); const scalarField Xf(Cf.component(vector::X)); const scalarField Yf(Cf.component(vector::Y)); const scalarField Zf(Cf.component(vector::Z)); vectorField Uf(Xf.size()); Uf.replace(0, 2*sqr(sin(pi*Xf))*sin(2*pi*Yf)*sin(2*pi*Zf)); Uf.replace(1, -sin(2*pi*Xf)*sqr(sin(pi*Yf))*sin(2*pi*Zf)); Uf.replace(2, -sin(2*pi*Xf)*sin(2*pi*Yf)*sqr(sin(pi*Zf))); scalarField& phic = phi.primitiveFieldRef(); const vectorField& Sfc = mesh_.Sf().primitiveField(); phic = Uf & Sfc; surfaceScalarField::Boundary& phibf = phi.boundaryFieldRef(); const surfaceVectorField::Boundary& Sfbf = mesh_.Sf().boundaryField(); const surfaceVectorField::Boundary& Cfbf = mesh_.Cf().boundaryField(); forAll(phibf, patchi) { scalarField& phif = phibf[patchi]; const vectorField& Sff = Sfbf[patchi]; const vectorField& Cff = Cfbf[patchi]; const scalarField xf(Cff.component(vector::X)); const scalarField yf(Cff.component(vector::Y)); const scalarField zf(Cff.component(vector::Z)); vectorField Uf(xf.size()); Uf.replace(0, 2*sqr(sin(pi*xf))*sin(2*pi*yf)*sin(2*pi*zf)); Uf.replace(1, -sin(2*pi*xf)*sqr(sin(pi*yf))*sin(2*pi*zf)); Uf.replace(2, -sin(2*pi*xf)*sin(2*pi*yf)*sqr(sin(pi*zf))); phif = Uf & Sff; } break; } } if (t > reverseTime_) { Log << " flow direction: reverse" << nl; U.negate(); phi.negate(); } // Apply scaling const scalar s = scalePtr_->value(t); U *= s; phi *= s; U.correctBoundaryConditions(); const scalarField sumPhi(fvc::surfaceIntegrate(phi)); Log << " Continuity error: max(mag(sum(phi))) = " << gMax(mag(sumPhi)) << nl << endl; return true; } bool Foam::functionObjects::setFlow::write() { const auto& Uptr = mesh_.lookupObjectRefPtr(UName_); if (Uptr) { Uptr->write(); } const auto& phiptr = mesh_.lookupObjectRefPtr(phiName_); if (phiptr) { phiptr->write(); } return true; } // ************************************************************************* //