FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

DsmcCloud_.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 2009-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 \*---------------------------------------------------------------------------*/
00025 
00026 #include "DsmcCloud_.H"
00027 #include <dsmc/BinaryCollisionModel.H>
00028 #include <dsmc/WallInteractionModel.H>
00029 #include <dsmc/InflowBoundaryModel.H>
00030 #include <finiteVolume/zeroGradientFvPatchFields.H>
00031 
00032 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
00033 
00034 template<class ParcelType>
00035 Foam::scalar Foam::DsmcCloud<ParcelType>::kb = 1.380650277e-23;
00036 
00037 
00038 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
00039 
00040 template<class ParcelType>
00041 void Foam::DsmcCloud<ParcelType>::buildConstProps()
00042 {
00043     Info<< nl << "Constructing constant properties for" << endl;
00044     constProps_.setSize(typeIdList_.size());
00045 
00046     dictionary moleculeProperties
00047     (
00048         particleProperties_.subDict("moleculeProperties")
00049     );
00050 
00051     forAll(typeIdList_, i)
00052     {
00053         const word& id(typeIdList_[i]);
00054 
00055         Info<< "    " << id << endl;
00056 
00057         const dictionary& molDict(moleculeProperties.subDict(id));
00058 
00059         constProps_[i] =
00060         typename ParcelType::constantProperties::constantProperties(molDict);
00061     }
00062 }
00063 
00064 
00065 template<class ParcelType>
00066 void Foam::DsmcCloud<ParcelType>::buildCellOccupancy()
00067 {
00068     forAll(cellOccupancy_, cO)
00069     {
00070         cellOccupancy_[cO].clear();
00071     }
00072 
00073     forAllIter(typename DsmcCloud<ParcelType>, *this, iter)
00074     {
00075         cellOccupancy_[iter().cell()].append(&iter());
00076     }
00077 }
00078 
00079 
00080 template<class ParcelType>
00081 void Foam::DsmcCloud<ParcelType>::initialise
00082 (
00083     const IOdictionary& dsmcInitialiseDict
00084 )
00085 {
00086     Info<< nl << "Initialising particles" << endl;
00087 
00088     const scalar temperature
00089     (
00090         readScalar(dsmcInitialiseDict.lookup("temperature"))
00091     );
00092 
00093     const vector velocity(dsmcInitialiseDict.lookup("velocity"));
00094 
00095     const dictionary& numberDensitiesDict
00096     (
00097         dsmcInitialiseDict.subDict("numberDensities")
00098     );
00099 
00100     List<word> molecules(numberDensitiesDict.toc());
00101 
00102     Field<scalar> numberDensities(molecules.size());
00103 
00104     forAll(molecules, i)
00105     {
00106         numberDensities[i] = readScalar
00107         (
00108             numberDensitiesDict.lookup(molecules[i])
00109         );
00110     }
00111 
00112     numberDensities /= nParticle_;
00113 
00114     forAll(mesh_.cells(), cell)
00115     {
00116         const vector& cC = mesh_.cellCentres()[cell];
00117         const labelList& cellFaces = mesh_.cells()[cell];
00118         const scalar cV = mesh_.cellVolumes()[cell];
00119 
00120         label nTets = 0;
00121 
00122         // Each face is split into nEdges (or nVertices) - 2 tets.
00123         forAll(cellFaces, face)
00124         {
00125             nTets += mesh_.faces()[cellFaces[face]].size() - 2;
00126         }
00127 
00128         // Calculate the cumulative tet volumes circulating around the cell and
00129         // record the vertex labels of each.
00130         scalarList cTetVFracs(nTets, 0.0);
00131 
00132         List<labelList> tetPtIs(nTets, labelList(3,-1));
00133 
00134         // Keep track of which tet this is.
00135         label tet = 0;
00136 
00137         forAll(cellFaces, face)
00138         {
00139             const labelList& facePoints = mesh_.faces()[cellFaces[face]];
00140 
00141             label pointI = 1;
00142             while ((pointI + 1) < facePoints.size())
00143             {
00144 
00145                 const vector& pA = mesh_.points()[facePoints[0]];
00146                 const vector& pB = mesh_.points()[facePoints[pointI]];
00147                 const vector& pC = mesh_.points()[facePoints[pointI + 1]];
00148 
00149                 cTetVFracs[tet] =
00150                     mag(((pA - cC) ^ (pB - cC)) & (pC - cC))/(cV*6.0)
00151                   + cTetVFracs[max((tet - 1),0)];
00152 
00153                 tetPtIs[tet][0] = facePoints[0];
00154                 tetPtIs[tet][1] = facePoints[pointI];
00155                 tetPtIs[tet][2] = facePoints[pointI + 1];
00156 
00157                 pointI++;
00158                 tet++;
00159             }
00160         }
00161 
00162         // Force the last volume fraction value to 1.0 to avoid any
00163         // rounding/non-flat face errors giving a value < 1.0
00164         cTetVFracs[nTets - 1] = 1.0;
00165 
00166         forAll(molecules, i)
00167         {
00168             const word& moleculeName(molecules[i]);
00169 
00170             label typeId(findIndex(typeIdList_, moleculeName));
00171 
00172             if (typeId == -1)
00173             {
00174                 FatalErrorIn("Foam::DsmcCloud<ParcelType>::initialise")
00175                 << "typeId " << moleculeName << "not defined." << nl
00176                     << abort(FatalError);
00177             }
00178 
00179             const typename ParcelType::constantProperties& cP =
00180                 constProps(typeId);
00181 
00182             scalar numberDensity = numberDensities[i];
00183 
00184             // Calculate the number of particles required
00185             scalar particlesRequired = numberDensity*mesh_.cellVolumes()[cell];
00186 
00187             // Only integer numbers of particles can be inserted
00188             label nParticlesToInsert = label(particlesRequired);
00189 
00190             // Add another particle with a probability proportional to the
00191             // remainder of taking the integer part of particlesRequired
00192             if ((particlesRequired - nParticlesToInsert) > rndGen_.scalar01())
00193             {
00194                 nParticlesToInsert++;
00195             }
00196 
00197             for (label pI = 0; pI < nParticlesToInsert; pI++)
00198             {
00199                 // Choose a random point in a generic tetrahedron
00200 
00201                 scalar s = rndGen_.scalar01();
00202                 scalar t = rndGen_.scalar01();
00203                 scalar u = rndGen_.scalar01();
00204 
00205                 if (s + t > 1.0)
00206                 {
00207                     s = 1.0 - s;
00208                     t = 1.0 - t;
00209                 }
00210 
00211                 if (t + u > 1.0)
00212                 {
00213                     scalar tmp = u;
00214                     u = 1.0 - s - t;
00215                     t = 1.0 - tmp;
00216                 }
00217                 else if (s + t + u > 1.0)
00218                 {
00219                     scalar tmp = u;
00220                     u = s + t + u - 1.0;
00221                     s = 1.0 - t - tmp;
00222                 }
00223 
00224                 // Choose a tetrahedron to insert in, based on their relative
00225                 // volumes
00226                 scalar tetSelection = rndGen_.scalar01();
00227 
00228                 // Selected tetrahedron
00229                 label sTet = -1;
00230 
00231                 forAll(cTetVFracs, tet)
00232                 {
00233                     sTet = tet;
00234 
00235                     if (cTetVFracs[tet] >= tetSelection)
00236                     {
00237                         break;
00238                     }
00239                 }
00240 
00241                 vector p =
00242                     (1 - s - t - u)*cC
00243                   + s*mesh_.points()[tetPtIs[sTet][0]]
00244                   + t*mesh_.points()[tetPtIs[sTet][1]]
00245                   + u*mesh_.points()[tetPtIs[sTet][2]];
00246 
00247                 vector U = equipartitionLinearVelocity
00248                 (
00249                     temperature,
00250                     cP.mass()
00251                 );
00252 
00253                 scalar Ei = equipartitionInternalEnergy
00254                 (
00255                     temperature,
00256                     cP.internalDegreesOfFreedom()
00257                 );
00258 
00259                 U += velocity;
00260 
00261                 addNewParcel
00262                 (
00263                     p,
00264                     U,
00265                     Ei,
00266                     cell,
00267                     typeId
00268                 );
00269             }
00270         }
00271     }
00272 
00273     // Initialise the sigmaTcRMax_ field to the product of the cross section of
00274     // the most abundant species and the most probable thermal speed (Bird,
00275     // p222-223)
00276 
00277     label mostAbundantType(findMax(numberDensities));
00278 
00279     const typename ParcelType::constantProperties& cP = constProps
00280     (
00281         mostAbundantType
00282     );
00283 
00284     sigmaTcRMax_.internalField() = cP.sigmaT()*maxwellianMostProbableSpeed
00285     (
00286         temperature,
00287         cP.mass()
00288     );
00289 
00290     sigmaTcRMax_.correctBoundaryConditions();
00291 }
00292 
00293 
00294 template<class ParcelType>
00295 void Foam::DsmcCloud<ParcelType>::collisions()
00296 {
00297     buildCellOccupancy();
00298 
00299     // Temporary storage for subCells
00300     List<DynamicList<label> > subCells(8);
00301 
00302     scalar deltaT = mesh().time().deltaTValue();
00303 
00304     label collisionCandidates = 0;
00305 
00306     label collisions = 0;
00307 
00308     forAll(cellOccupancy_, celli)
00309     {
00310         const DynamicList<ParcelType*>& cellParcels(cellOccupancy_[celli]);
00311 
00312         label nC(cellParcels.size());
00313 
00314         if (nC > 1)
00315         {
00316 
00317             // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00318             // Assign particles to one of 8 Cartesian subCells
00319 
00320             // Clear temporary lists
00321             forAll(subCells, i)
00322             {
00323                 subCells[i].clear();
00324             }
00325 
00326             // Inverse addressing specifying which subCell a parcel is in
00327             List<label> whichSubCell(cellParcels.size());
00328 
00329             const point& cC = mesh_.cellCentres()[celli];
00330 
00331             forAll(cellParcels, i)
00332             {
00333                 ParcelType* p = cellParcels[i];
00334 
00335                 vector relPos = p->position() - cC;
00336 
00337                 label subCell =
00338                     pos(relPos.x()) + 2*pos(relPos.y()) + 4*pos(relPos.z());
00339 
00340                 subCells[subCell].append(i);
00341 
00342                 whichSubCell[i] = subCell;
00343             }
00344 
00345             // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00346 
00347             scalar sigmaTcRMax = sigmaTcRMax_[celli];
00348 
00349             scalar selectedPairs =
00350                 collisionSelectionRemainder_[celli]
00351               + 0.5*nC*(nC - 1)*nParticle_*sigmaTcRMax*deltaT
00352                /mesh_.cellVolumes()[celli];
00353 
00354             label nCandidates(selectedPairs);
00355 
00356             collisionSelectionRemainder_[celli] = selectedPairs - nCandidates;
00357 
00358             collisionCandidates += nCandidates;
00359 
00360             for (label c = 0; c < nCandidates; c++)
00361             {
00362                 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00363                 // subCell candidate selection procedure
00364 
00365                 // Select the first collision candidate
00366                 label candidateP = rndGen_.integer(0, nC - 1);
00367 
00368                 // Declare the second collision candidate
00369                 label candidateQ = -1;
00370 
00371                 List<label> subCellPs = subCells[whichSubCell[candidateP]];
00372 
00373                 label nSC = subCellPs.size();
00374 
00375                 if (nSC > 1)
00376                 {
00377                     // If there are two or more particle in a subCell, choose
00378                     // another from the same cell.  If the same candidate is
00379                     // chosen, choose again.
00380 
00381                     do
00382                     {
00383                         candidateQ = subCellPs[rndGen_.integer(0, nSC - 1)];
00384 
00385                     } while(candidateP == candidateQ);
00386                 }
00387                 else
00388                 {
00389                     // Select a possible second collision candidate from the
00390                     // whole cell.  If the same candidate is chosen, choose
00391                     // again.
00392 
00393                     do
00394                     {
00395                         candidateQ = rndGen_.integer(0, nC - 1);
00396 
00397                     } while(candidateP == candidateQ);
00398                 }
00399 
00400                 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00401                 // uniform candidate selection procedure
00402 
00403                 // // Select the first collision candidate
00404                 // label candidateP = rndGen_.integer(0, nC-1);
00405 
00406                 // // Select a possible second collision candidate
00407                 // label candidateQ = rndGen_.integer(0, nC-1);
00408 
00409                 // // If the same candidate is chosen, choose again
00410                 // while(candidateP == candidateQ)
00411                 // {
00412                 //     candidateQ = rndGen_.integer(0, nC-1);
00413                 // }
00414 
00415                 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00416 
00417                 ParcelType* parcelP = cellParcels[candidateP];
00418                 ParcelType* parcelQ = cellParcels[candidateQ];
00419 
00420                 label typeIdP = parcelP->typeId();
00421                 label typeIdQ = parcelQ->typeId();
00422 
00423                 scalar sigmaTcR = binaryCollision().sigmaTcR
00424                 (
00425                     typeIdP,
00426                     typeIdQ,
00427                     parcelP->U(),
00428                     parcelQ->U()
00429                 );
00430 
00431                 // Update the maximum value of sigmaTcR stored, but use the
00432                 // initial value in the acceptance-rejection criteria because
00433                 // the number of collision candidates selected was based on this
00434 
00435                 if (sigmaTcR > sigmaTcRMax_[celli])
00436                 {
00437                     sigmaTcRMax_[celli] = sigmaTcR;
00438                 }
00439 
00440                 if ((sigmaTcR/sigmaTcRMax) > rndGen_.scalar01())
00441                 {
00442                     binaryCollision().collide
00443                     (
00444                         typeIdP,
00445                         typeIdQ,
00446                         parcelP->U(),
00447                         parcelQ->U(),
00448                         parcelP->Ei(),
00449                         parcelQ->Ei()
00450                     );
00451 
00452                     collisions++;
00453                 }
00454             }
00455         }
00456     }
00457 
00458     reduce(collisions, sumOp<label>());
00459 
00460     reduce(collisionCandidates, sumOp<label>());
00461 
00462     sigmaTcRMax_.correctBoundaryConditions();
00463 
00464     if (collisionCandidates)
00465     {
00466         Info<< "    Collisions                      = "
00467             << collisions << nl
00468             << "    Acceptance rate                 = "
00469             << scalar(collisions)/scalar(collisionCandidates) << nl
00470             << endl;
00471     }
00472     else
00473     {
00474         Info<< "    No collisions" << endl;
00475     }
00476 }
00477 
00478 
00479 template<class ParcelType>
00480 void Foam::DsmcCloud<ParcelType>::resetFields()
00481 {
00482     q_ = dimensionedScalar("zero",  dimensionSet(1, 0, -3, 0, 0), 0.0);
00483 
00484     fD_ = dimensionedVector
00485     (
00486         "zero",
00487         dimensionSet(1, -1, -2, 0, 0),
00488         vector::zero
00489     );
00490 
00491     rhoN_ = dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), VSMALL);
00492 
00493     rhoM_ =  dimensionedScalar("zero",  dimensionSet(1, -3, 0, 0, 0), VSMALL);
00494 
00495     dsmcRhoN_ = dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), 0.0);
00496 
00497     linearKE_ = dimensionedScalar("zero",  dimensionSet(1, -1, -2, 0, 0), 0.0);
00498 
00499     internalE_ = dimensionedScalar("zero",  dimensionSet(1, -1, -2, 0, 0), 0.0);
00500 
00501     iDof_ = dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), VSMALL);
00502 
00503     momentum_ = dimensionedVector
00504     (
00505         "zero",
00506         dimensionSet(1, -2, -1, 0, 0),
00507         vector::zero
00508     );
00509 }
00510 
00511 
00512 template<class ParcelType>
00513 void Foam::DsmcCloud<ParcelType>::calculateFields()
00514 {
00515     scalarField& rhoN = rhoN_.internalField();
00516 
00517     scalarField& rhoM = rhoM_.internalField();
00518 
00519     scalarField& dsmcRhoN = dsmcRhoN_.internalField();
00520 
00521     scalarField& linearKE = linearKE_.internalField();
00522 
00523     scalarField& internalE = internalE_.internalField();
00524 
00525     scalarField& iDof = iDof_.internalField();
00526 
00527     vectorField& momentum = momentum_.internalField();
00528 
00529     forAllConstIter(typename DsmcCloud<ParcelType>, *this, iter)
00530     {
00531         const ParcelType& p = iter();
00532         const label cellI = p.cell();
00533 
00534         rhoN[cellI]++;
00535 
00536         rhoM[cellI] += constProps(p.typeId()).mass();
00537 
00538         dsmcRhoN[cellI]++;
00539 
00540         linearKE[cellI] += 0.5*constProps(p.typeId()).mass()*(p.U() & p.U());
00541 
00542         internalE[cellI] += p.Ei();
00543 
00544         iDof[cellI] += constProps(p.typeId()).internalDegreesOfFreedom();
00545 
00546         momentum[cellI] += constProps(p.typeId()).mass()*p.U();
00547     }
00548 
00549     rhoN *= nParticle_/mesh().cellVolumes();
00550     rhoN_.correctBoundaryConditions();
00551 
00552     rhoM *= nParticle_/mesh().cellVolumes();
00553     rhoM_.correctBoundaryConditions();
00554 
00555     dsmcRhoN_.correctBoundaryConditions();
00556 
00557     linearKE *= nParticle_/mesh().cellVolumes();
00558     linearKE_.correctBoundaryConditions();
00559 
00560     internalE *= nParticle_/mesh().cellVolumes();
00561     internalE_.correctBoundaryConditions();
00562 
00563     iDof *= nParticle_/mesh().cellVolumes();
00564     iDof_.correctBoundaryConditions();
00565 
00566     momentum *= nParticle_/mesh().cellVolumes();
00567     momentum_.correctBoundaryConditions();
00568 }
00569 
00570 
00571 // * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * * //
00572 
00573 template<class ParcelType>
00574 void Foam::DsmcCloud<ParcelType>::addNewParcel
00575 (
00576     const vector& position,
00577     const vector& U,
00578     const scalar Ei,
00579     const label cellId,
00580     const label typeId
00581 )
00582 {
00583     ParcelType* pPtr = new ParcelType
00584     (
00585         *this,
00586         position,
00587         U,
00588         Ei,
00589         cellId,
00590         typeId
00591     );
00592 
00593     this->addParticle(pPtr);
00594 }
00595 
00596 
00597 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00598 
00599 template<class ParcelType>
00600 Foam::DsmcCloud<ParcelType>::DsmcCloud
00601 (
00602     const word& cloudName,
00603     const fvMesh& mesh,
00604     bool readFields
00605 )
00606 :
00607     Cloud<ParcelType>(mesh, cloudName, false),
00608     DsmcBaseCloud(),
00609     cloudName_(cloudName),
00610     mesh_(mesh),
00611     particleProperties_
00612     (
00613         IOobject
00614         (
00615             cloudName + "Properties",
00616             mesh_.time().constant(),
00617             mesh_,
00618             IOobject::MUST_READ,
00619             IOobject::NO_WRITE
00620         )
00621     ),
00622     typeIdList_(particleProperties_.lookup("typeIdList")),
00623     nParticle_(readScalar(particleProperties_.lookup("nEquivalentParticles"))),
00624     cellOccupancy_(mesh_.nCells()),
00625     sigmaTcRMax_
00626     (
00627         IOobject
00628         (
00629             this->name() + "SigmaTcRMax",
00630             mesh_.time().timeName(),
00631             mesh_,
00632             IOobject::MUST_READ,
00633             IOobject::AUTO_WRITE
00634         ),
00635         mesh_
00636     ),
00637     collisionSelectionRemainder_(mesh_.nCells(), 0),
00638     q_
00639     (
00640         IOobject
00641         (
00642             "q",
00643             mesh_.time().timeName(),
00644             mesh_,
00645             IOobject::MUST_READ,
00646             IOobject::AUTO_WRITE
00647         ),
00648         mesh_
00649     ),
00650     fD_
00651     (
00652         IOobject
00653         (
00654             "fD",
00655             mesh_.time().timeName(),
00656             mesh_,
00657             IOobject::MUST_READ,
00658             IOobject::AUTO_WRITE
00659         ),
00660         mesh_
00661     ),
00662     rhoN_
00663     (
00664         IOobject
00665         (
00666             "rhoN",
00667             mesh_.time().timeName(),
00668             mesh_,
00669             IOobject::MUST_READ,
00670             IOobject::AUTO_WRITE
00671         ),
00672         mesh_
00673     ),
00674     rhoM_
00675     (
00676         IOobject
00677         (
00678             "rhoM",
00679             mesh_.time().timeName(),
00680             mesh_,
00681             IOobject::MUST_READ,
00682             IOobject::AUTO_WRITE
00683         ),
00684         mesh_
00685     ),
00686     dsmcRhoN_
00687     (
00688         IOobject
00689         (
00690             "dsmcRhoN",
00691             mesh_.time().timeName(),
00692             mesh_,
00693             IOobject::MUST_READ,
00694             IOobject::AUTO_WRITE
00695         ),
00696         mesh_
00697     ),
00698     linearKE_
00699     (
00700         IOobject
00701         (
00702             "linearKE",
00703             mesh_.time().timeName(),
00704             mesh_,
00705             IOobject::MUST_READ,
00706             IOobject::AUTO_WRITE
00707         ),
00708         mesh_
00709     ),
00710     internalE_
00711     (
00712         IOobject
00713         (
00714             "internalE",
00715             mesh_.time().timeName(),
00716             mesh_,
00717             IOobject::MUST_READ,
00718             IOobject::AUTO_WRITE
00719         ),
00720         mesh_
00721     ),
00722     iDof_
00723     (
00724         IOobject
00725         (
00726             "iDof",
00727             mesh_.time().timeName(),
00728             mesh_,
00729             IOobject::MUST_READ,
00730             IOobject::AUTO_WRITE
00731         ),
00732         mesh_
00733     ),
00734     momentum_
00735     (
00736         IOobject
00737         (
00738             "momentum",
00739             mesh_.time().timeName(),
00740             mesh_,
00741             IOobject::MUST_READ,
00742             IOobject::AUTO_WRITE
00743         ),
00744         mesh_
00745     ),
00746     constProps_(),
00747     rndGen_(label(149382906) + 7183*Pstream::myProcNo()),
00748     boundaryT_
00749     (
00750         volScalarField
00751         (
00752             IOobject
00753             (
00754                 "boundaryT",
00755                 mesh_.time().timeName(),
00756                 mesh_,
00757                 IOobject::MUST_READ,
00758                 IOobject::AUTO_WRITE
00759             ),
00760             mesh_
00761         )
00762     ),
00763     boundaryU_
00764     (
00765         volVectorField
00766         (
00767             IOobject
00768             (
00769                 "boundaryU",
00770                 mesh_.time().timeName(),
00771                 mesh_,
00772                 IOobject::MUST_READ,
00773                 IOobject::AUTO_WRITE
00774             ),
00775             mesh_
00776         )
00777     ),
00778     binaryCollisionModel_
00779     (
00780         BinaryCollisionModel<DsmcCloud<ParcelType> >::New
00781         (
00782             particleProperties_,
00783             *this
00784         )
00785     ),
00786     wallInteractionModel_
00787     (
00788         WallInteractionModel<DsmcCloud<ParcelType> >::New
00789         (
00790             particleProperties_,
00791             *this
00792         )
00793     ),
00794     inflowBoundaryModel_
00795     (
00796         InflowBoundaryModel<DsmcCloud<ParcelType> >::New
00797         (
00798             particleProperties_,
00799             *this
00800         )
00801     )
00802 {
00803     buildConstProps();
00804 
00805     buildCellOccupancy();
00806 
00807     // Initialise the collision selection remainder to a random value between 0
00808     // and 1.
00809     forAll(collisionSelectionRemainder_, i)
00810     {
00811         collisionSelectionRemainder_[i] = rndGen_.scalar01();
00812     }
00813 
00814     if (readFields)
00815     {
00816         ParcelType::readFields(*this);
00817     }
00818 }
00819 
00820 
00821 template<class ParcelType>
00822 Foam::DsmcCloud<ParcelType>::DsmcCloud
00823 (
00824     const word& cloudName,
00825     const fvMesh& mesh,
00826     const IOdictionary& dsmcInitialiseDict
00827 )
00828     :
00829     Cloud<ParcelType>(mesh, cloudName, false),
00830     DsmcBaseCloud(),
00831     cloudName_(cloudName),
00832     mesh_(mesh),
00833     particleProperties_
00834     (
00835         IOobject
00836         (
00837             cloudName + "Properties",
00838             mesh_.time().constant(),
00839             mesh_,
00840             IOobject::MUST_READ,
00841             IOobject::NO_WRITE
00842         )
00843     ),
00844     typeIdList_(particleProperties_.lookup("typeIdList")),
00845     nParticle_(readScalar(particleProperties_.lookup("nEquivalentParticles"))),
00846     cellOccupancy_(),
00847     sigmaTcRMax_
00848     (
00849         IOobject
00850         (
00851             this->name() + "SigmaTcRMax",
00852             mesh_.time().timeName(),
00853             mesh_,
00854             IOobject::NO_READ,
00855             IOobject::AUTO_WRITE
00856         ),
00857         mesh_,
00858         dimensionedScalar("zero",  dimensionSet(0, 3, -1, 0, 0), 0.0),
00859         zeroGradientFvPatchScalarField::typeName
00860     ),
00861     collisionSelectionRemainder_(),
00862     q_
00863     (
00864         IOobject
00865         (
00866             this->name() + "q_",
00867             mesh_.time().timeName(),
00868             mesh_,
00869             IOobject::NO_READ,
00870             IOobject::NO_WRITE
00871         ),
00872         mesh_,
00873         dimensionedScalar("zero",  dimensionSet(1, 0, -3, 0, 0), 0.0)
00874     ),
00875     fD_
00876     (
00877         IOobject
00878         (
00879             this->name() + "fD_",
00880             mesh_.time().timeName(),
00881             mesh_,
00882             IOobject::NO_READ,
00883             IOobject::NO_WRITE
00884         ),
00885         mesh_,
00886         dimensionedVector
00887         (
00888             "zero",
00889             dimensionSet(1, -1, -2, 0, 0),
00890             vector::zero
00891         )
00892     ),
00893     rhoN_
00894     (
00895         IOobject
00896         (
00897             this->name() + "rhoN_",
00898             mesh_.time().timeName(),
00899             mesh_,
00900             IOobject::NO_READ,
00901             IOobject::NO_WRITE
00902         ),
00903         mesh_,
00904         dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), VSMALL)
00905     ),
00906     rhoM_
00907     (
00908         IOobject
00909         (
00910             this->name() + "rhoM_",
00911             mesh_.time().timeName(),
00912             mesh_,
00913             IOobject::NO_READ,
00914             IOobject::NO_WRITE
00915         ),
00916         mesh_,
00917         dimensionedScalar("zero",  dimensionSet(1, -3, 0, 0, 0), VSMALL)
00918     ),
00919     dsmcRhoN_
00920     (
00921         IOobject
00922         (
00923             this->name() + "dsmcRhoN_",
00924             mesh_.time().timeName(),
00925             mesh_,
00926             IOobject::NO_READ,
00927             IOobject::NO_WRITE
00928         ),
00929         mesh_,
00930         dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), 0.0)
00931     ),
00932     linearKE_
00933     (
00934         IOobject
00935         (
00936             this->name() + "linearKE_",
00937             mesh_.time().timeName(),
00938             mesh_,
00939             IOobject::NO_READ,
00940             IOobject::NO_WRITE
00941         ),
00942         mesh_,
00943         dimensionedScalar("zero",  dimensionSet(1, -1, -2, 0, 0), 0.0)
00944     ),
00945     internalE_
00946     (
00947         IOobject
00948         (
00949             this->name() + "internalE_",
00950             mesh_.time().timeName(),
00951             mesh_,
00952             IOobject::NO_READ,
00953             IOobject::NO_WRITE
00954         ),
00955         mesh_,
00956         dimensionedScalar("zero",  dimensionSet(1, -1, -2, 0, 0), 0.0)
00957     ),
00958     iDof_
00959     (
00960         IOobject
00961         (
00962             this->name() + "iDof_",
00963             mesh_.time().timeName(),
00964             mesh_,
00965             IOobject::NO_READ,
00966             IOobject::NO_WRITE
00967         ),
00968         mesh_,
00969         dimensionedScalar("zero",  dimensionSet(0, -3, 0, 0, 0), VSMALL)
00970     ),
00971     momentum_
00972     (
00973         IOobject
00974         (
00975             this->name() + "momentum_",
00976             mesh_.time().timeName(),
00977             mesh_,
00978             IOobject::NO_READ,
00979             IOobject::NO_WRITE
00980         ),
00981         mesh_,
00982         dimensionedVector
00983         (
00984             "zero",
00985             dimensionSet(1, -2, -1, 0, 0),
00986             vector::zero
00987         )
00988     ),
00989     constProps_(),
00990     rndGen_(label(971501) + 1526*Pstream::myProcNo()),
00991     boundaryT_
00992     (
00993         volScalarField
00994         (
00995             IOobject
00996             (
00997                 "boundaryT",
00998                 mesh_.time().timeName(),
00999                 mesh_,
01000                 IOobject::NO_READ,
01001                 IOobject::NO_WRITE
01002             ),
01003             mesh_,
01004             dimensionedScalar("zero",  dimensionSet(0, 0, 0, 1, 0), 0.0)
01005         )
01006     ),
01007     boundaryU_
01008     (
01009         volVectorField
01010         (
01011             IOobject
01012             (
01013                 "boundaryU",
01014                 mesh_.time().timeName(),
01015                 mesh_,
01016                 IOobject::NO_READ,
01017                 IOobject::NO_WRITE
01018             ),
01019             mesh_,
01020             dimensionedVector
01021             (
01022                 "zero",
01023                 dimensionSet(0, 1, -1, 0, 0),
01024                 vector::zero
01025             )
01026         )
01027     ),
01028     binaryCollisionModel_(),
01029     wallInteractionModel_(),
01030     inflowBoundaryModel_()
01031 {
01032     clear();
01033 
01034     buildConstProps();
01035 
01036     initialise(dsmcInitialiseDict);
01037 }
01038 
01039 
01040 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
01041 
01042 template<class ParcelType>
01043 Foam::DsmcCloud<ParcelType>::~DsmcCloud()
01044 {}
01045 
01046 
01047 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
01048 
01049 template<class ParcelType>
01050 void Foam::DsmcCloud<ParcelType>::evolve()
01051 {
01052     typename ParcelType::trackData td(*this);
01053 
01054     // Reset the data collection fields
01055     resetFields();
01056 
01057     if (debug)
01058     {
01059         this->dumpParticlePositions();
01060     }
01061 
01062     // Insert new particles from the inflow boundary
01063     this->inflowBoundary().inflow();
01064 
01065     // Move the particles ballistically with their current velocities
01066     Cloud<ParcelType>::move(td);
01067 
01068     // Calculate new velocities via stochastic collisions
01069     collisions();
01070 
01071     // Calculate the volume field data
01072     calculateFields();
01073 }
01074 
01075 
01076 template<class ParcelType>
01077 void Foam::DsmcCloud<ParcelType>::info() const
01078 {
01079     label nDsmcParticles = this->size();
01080     reduce(nDsmcParticles, sumOp<label>());
01081 
01082     scalar nMol = nDsmcParticles*nParticle_;
01083 
01084     vector linearMomentum = linearMomentumOfSystem();
01085     reduce(linearMomentum, sumOp<vector>());
01086 
01087     scalar linearKineticEnergy = linearKineticEnergyOfSystem();
01088     reduce(linearKineticEnergy, sumOp<scalar>());
01089 
01090     scalar internalEnergy = internalEnergyOfSystem();
01091     reduce(internalEnergy, sumOp<scalar>());
01092 
01093     Info<< "Cloud name: " << this->name() << nl
01094         << "    Number of dsmc particles        = "
01095         << nDsmcParticles
01096         << endl;
01097 
01098     if (nDsmcParticles)
01099     {
01100         Info<< "    Number of molecules             = "
01101             << nMol << nl
01102             << "    Mass in system                  = "
01103             << returnReduce(massInSystem(), sumOp<scalar>()) << nl
01104             << "    Average linear momentum         = "
01105             << linearMomentum/nMol << nl
01106             << "   |Average linear momentum|        = "
01107             << mag(linearMomentum)/nMol << nl
01108             << "    Average linear kinetic energy   = "
01109             << linearKineticEnergy/nMol << nl
01110             << "    Average internal energy         = "
01111             << internalEnergy/nMol << nl
01112             << "    Average total energy            = "
01113             << (internalEnergy + linearKineticEnergy)/nMol
01114             << endl;
01115     }
01116 }
01117 
01118 
01119 template<class ParcelType>
01120 Foam::vector Foam::DsmcCloud<ParcelType>::equipartitionLinearVelocity
01121 (
01122     scalar temperature,
01123     scalar mass
01124 )
01125 {
01126     return
01127         sqrt(kb*temperature/mass)
01128        *vector
01129         (
01130             rndGen_.GaussNormal(),
01131             rndGen_.GaussNormal(),
01132             rndGen_.GaussNormal()
01133         );
01134 }
01135 
01136 
01137 template<class ParcelType>
01138 Foam::scalar Foam::DsmcCloud<ParcelType>::equipartitionInternalEnergy
01139 (
01140     scalar temperature,
01141     scalar iDof
01142 )
01143 {
01144     scalar Ei = 0.0;
01145 
01146     if (iDof < SMALL)
01147     {
01148         return Ei;
01149     }
01150     else if (iDof < 2.0 + SMALL && iDof > 2.0 - SMALL)
01151     {
01152         // Special case for iDof = 2, i.e. diatomics;
01153         Ei = -log(rndGen_.scalar01())*kb*temperature;
01154     }
01155     else
01156     {
01157         scalar a = 0.5*iDof - 1;
01158 
01159         scalar energyRatio;
01160 
01161         scalar P = -1;
01162 
01163         do
01164         {
01165             energyRatio = 10*rndGen_.scalar01();
01166 
01167             P = pow((energyRatio/a), a)*exp(a - energyRatio);
01168 
01169         } while (P < rndGen_.scalar01());
01170 
01171         Ei = energyRatio*kb*temperature;
01172     }
01173 
01174     return Ei;
01175 }
01176 
01177 
01178 template<class ParcelType>
01179 void Foam::DsmcCloud<ParcelType>::dumpParticlePositions() const
01180 {
01181     OFstream pObj
01182     (
01183         this->db().time().path()/"parcelPositions_"
01184       + this->name() + "_"
01185       + this->db().time().timeName() + ".obj"
01186     );
01187 
01188     forAllConstIter(typename DsmcCloud<ParcelType>, *this, iter)
01189     {
01190         const ParcelType& p = iter();
01191 
01192         pObj<< "v " << p.position().x()
01193             << " "  << p.position().y()
01194             << " "  << p.position().z()
01195             << nl;
01196     }
01197 
01198     pObj.flush();
01199 }
01200 
01201 
01202 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines