$treeview $search $mathjax
Palabos  Version 1.1
$projectbrief
$projectbrief
$searchbox

shanChenProcessor2D.hh

Go to the documentation of this file.
00001 /* This file is part of the Palabos library.
00002  *
00003  * Copyright (C) 2011 FlowKit Sarl
00004  * Avenue de Chailly 23
00005  * 1012 Lausanne, Switzerland
00006  * E-mail contact: contact@flowkit.com
00007  *
00008  * The most recent release of Palabos can be downloaded at 
00009  * <http://www.palabos.org/>
00010  *
00011  * The library Palabos is free software: you can redistribute it and/or
00012  * modify it under the terms of the GNU Affero General Public License as
00013  * published by the Free Software Foundation, either version 3 of the
00014  * License, or (at your option) any later version.
00015  *
00016  * The library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU Affero General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Affero General Public License
00022  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00023 */
00024 
00025 /* The original version of this file was written by Orestis Malaspinas
00026  * and Andrea Parmigiani.
00027  */
00028 
00029 #ifndef SHAN_CHEN_PROCESSOR_2D_HH
00030 #define SHAN_CHEN_PROCESSOR_2D_HH
00031 
00032 #include "multiPhysics/shanChenProcessor2D.h"
00033 #include "core/util.h"
00034 #include "finiteDifference/finiteDifference2D.h"
00035 #include "latticeBoltzmann/momentTemplates.h"
00036 #include "latticeBoltzmann/externalFieldAccess.h"
00037 #include "multiPhysics/multiPhaseTemplates2D.h"
00038 
00039 
00040 namespace plb {
00041 
00042 /* *************** ShanChenMultiComponentProcessor2D ***************** */
00043 
00044 template<typename T, template<typename U> class Descriptor>
00045 ShanChenMultiComponentProcessor2D <T,Descriptor>::ShanChenMultiComponentProcessor2D(T G_)
00046     : G(G_)
00047 { }
00048 
00049 template<typename T, template<typename U> class Descriptor>
00050 ShanChenMultiComponentProcessor2D <T,Descriptor>::ShanChenMultiComponentProcessor2D (
00051         T G_, std::vector<T> const& imposedOmega_)
00052     : G(G_),
00053       imposedOmega(imposedOmega_)
00054 { }
00055 
00056 template<typename T, template<typename U> class Descriptor>
00057 void ShanChenMultiComponentProcessor2D<T,Descriptor>::process (
00058         Box2D domain,
00059         std::vector<BlockLattice2D<T,Descriptor>*> lattices )
00060 {
00061     // Number of species (or components) which are coupled in this Shan/Chen multi-component fluid.
00062     plint numSpecies = (plint) lattices.size();
00063     // Short-hand notation for the lattice descriptor
00064     typedef Descriptor<T> D;
00065     // Handle to external scalars
00066     enum {
00067         densityOffset  = D::ExternalField::densityBeginsAt,
00068         momentumOffset = D::ExternalField::momentumBeginsAt
00069     };
00070     
00071     // Compute per-lattice density  and momentum on every site and on each
00072     //   lattice, and store result in external scalars;  envelope cells are included,
00073     //   because they are needed to compute the interaction potential in the following.
00074     //   Note that the per-lattice value of the momentum is stored temporarily only, as
00075     //   it is corrected later on, based on the common fluid velocity.
00076     for (plint iSpecies=0; iSpecies<numSpecies; ++iSpecies) {
00077         for (plint iX=domain.x0-1; iX<=domain.x1+1; ++iX) {
00078             for (plint iY=domain.y0-1; iY<=domain.y1+1; ++iY) {
00079                 // Get "intelligent" value of density through cell object, to account
00080                 //   for the fact that the density value can be user-defined, for example
00081                 //   on boundaries.
00082                 Cell<T,Descriptor>& cell = lattices[iSpecies]->get(iX,iY);
00083                 Array<T,Descriptor<T>::d> j;
00084                 T rhoBar;
00085                 cell.getDynamics().computeRhoBarJ(cell,rhoBar,j);
00086                 *cell.getExternal(densityOffset) = Descriptor<T>::fullRho(rhoBar);
00087                 j.to_cArray(cell.getExternal(momentumOffset));
00088             }
00089         }
00090     }
00091 
00092     // Temporary variable for the relaxation parameters omega.
00093     std::vector<T> omega(numSpecies), invOmega(numSpecies);
00094     // Temporary variable for the common velocity.
00095     Array<T,Descriptor<T>::d> uTot;
00096     // Temporary variable for the interaction potential.
00097     std::vector<Array<T,D::d> > rhoContribution(numSpecies);
00098 
00099     // If omega is constant and imposed by the user, copy its value to
00100     //   the vector "omega", and compute the inverse.
00101     if (!imposedOmega.empty()) {
00102         omega = imposedOmega;
00103         for (pluint iOmega=0; iOmega<omega.size(); ++iOmega) {
00104             invOmega[iOmega] = (T)1 / omega[iOmega];
00105         }
00106     }
00107 
00108     // Compute the interaction force between the species, and store it by
00109     //   means of a velocity correction in the external velocity field.
00110     for (plint iX=domain.x0; iX<=domain.x1; ++iX) {
00111         for (plint iY=domain.y0; iY<=domain.y1; ++iY) {
00112             // Computation of the common density over all populations, weighted by
00113             //   the relaxation parameters omega.
00114             T weightedDensity = T();
00115             for (plint iSpecies=0; iSpecies<numSpecies; ++iSpecies) {
00116                 Cell<T,Descriptor> const& cell = lattices[iSpecies]->get(iX,iY);
00117                 // Take this opportunity to read omega from the cell, unless the value
00118                 //   of omega is constant and imposed by the user.
00119                 if (imposedOmega.empty()) {
00120                     omega[iSpecies] = cell.getDynamics().getOmega();
00121                     invOmega[iSpecies] = (T)1/omega[iSpecies];
00122                 }
00123                 weightedDensity += omega[iSpecies] * (*cell.getExternal(densityOffset));
00124             }
00125             // Computation of the common velocity, shared among all populations.
00126             for (int iD = 0; iD < Descriptor<T>::d; ++iD) {
00127                 uTot[iD] = T();
00128                 for (plint iSpecies=0; iSpecies<numSpecies; ++iSpecies) {
00129                     T *momentum = lattices[iSpecies]->get(iX,iY).getExternal(momentumOffset);
00130                     uTot[iD] += momentum[iD] * omega[iSpecies];
00131                 }
00132                 uTot[iD] /= weightedDensity;
00133             }
00134 
00135             // Computation of the interaction potential.
00136             for (plint iSpecies=0; iSpecies<numSpecies; ++iSpecies) {
00137                 multiPhaseTemplates2D<T,Descriptor>::shanChenInteraction (
00138                         *lattices[iSpecies],rhoContribution[iSpecies],iX,iY );
00139             }
00140             
00141             // Computation and storage of the final velocity, consisting
00142             //   of uTot plus the momentum difference due to interaction
00143             //   potential and external force
00144             for (plint iSpecies=0; iSpecies<numSpecies; ++iSpecies) {
00145                 Cell<T,Descriptor>& cell = lattices[iSpecies]->get(iX,iY);
00146                 T *momentum = cell.getExternal(momentumOffset);
00147                 for (int iD = 0; iD < D::d; ++iD) {
00148                     momentum[iD] = uTot[iD];
00149                     // Initialize force contribution with force from external fields if there
00150                     //   is any, or with zero otherwise.
00151                     T forceContribution = getExternalForceComponent(cell, iD);
00152                     // Then, add a contribution from the potential of all other species.
00153                     for (plint iPartnerSpecies=0; iPartnerSpecies<numSpecies; ++iPartnerSpecies) {
00154                         if (iPartnerSpecies != iSpecies) {
00155                             forceContribution -= G * rhoContribution[iPartnerSpecies][iD];
00156                         }
00157                     }
00158                     momentum[iD] += invOmega[iSpecies]*forceContribution;
00159                     // Multiply by rho to convert from velocity to momentum.
00160                     momentum[iD] *= *cell.getExternal(densityOffset);
00161                 }
00162             }
00163         }
00164     }
00165 }
00166 
00167 
00168 template<typename T, template<typename U> class Descriptor>
00169 ShanChenMultiComponentProcessor2D<T,Descriptor>*
00170     ShanChenMultiComponentProcessor2D<T,Descriptor>::clone() const
00171 {
00172     return new ShanChenMultiComponentProcessor2D<T,Descriptor>(*this);
00173 }
00174 
00175 template<typename T, template<typename U> class Descriptor>
00176 void ShanChenMultiComponentProcessor2D<T,Descriptor>::getTypeOfModification(std::vector<modif::ModifT>& modified) const
00177 {
00178     // All blocks are modified by the Shan/Chen processor.
00179     for (pluint iBlock=0; iBlock<modified.size(); ++iBlock) {
00180         modified[iBlock] = modif::staticVariables;
00181     }
00182 }
00183 
00184 
00185 /* *************** ShanChenSingleComponentProcessor2D ***************** */
00186 
00187 template<typename T, template<typename U> class Descriptor>
00188 ShanChenSingleComponentProcessor2D<T,Descriptor>::ShanChenSingleComponentProcessor2D (
00189         T G_, interparticlePotential::PsiFunction<T>* Psi_ )
00190     : G(G_),
00191       Psi(Psi_)
00192 { }
00193 
00194 template<typename T, template<typename U> class Descriptor>
00195 ShanChenSingleComponentProcessor2D<T,Descriptor>::~ShanChenSingleComponentProcessor2D() {
00196     // Pointer to Psi function is owned; delete it in the destructor.
00197     delete Psi;
00198 }
00199 
00200 template<typename T, template<typename U> class Descriptor>
00201 ShanChenSingleComponentProcessor2D<T,Descriptor>::ShanChenSingleComponentProcessor2D (
00202         ShanChenSingleComponentProcessor2D<T,Descriptor> const& rhs )
00203     : G(rhs.G),
00204       Psi(rhs.Psi->clone())
00205 { }
00206 
00207 template<typename T, template<typename U> class Descriptor>
00208 ShanChenSingleComponentProcessor2D<T,Descriptor>&
00209     ShanChenSingleComponentProcessor2D<T,Descriptor>::operator= (
00210         ShanChenSingleComponentProcessor2D<T,Descriptor> const& rhs )
00211 {
00212     G = rhs.G;
00213     delete Psi; Psi = rhs.Psi->clone();
00214     return *this;
00215 }
00216 
00217 template<typename T, template<typename U> class Descriptor>
00218 ShanChenSingleComponentProcessor2D<T,Descriptor>*
00219     ShanChenSingleComponentProcessor2D<T,Descriptor>::clone() const
00220 {
00221     return new ShanChenSingleComponentProcessor2D<T,Descriptor>(*this);
00222 }
00223 
00224 template<typename T, template<typename U> class Descriptor>
00225 void ShanChenSingleComponentProcessor2D<T,Descriptor>::getTypeOfModification(std::vector<modif::ModifT>& modified) const
00226 {
00227     modified[0] = modif::nothing;
00228 }
00229 
00230 template<typename T, template<typename U> class Descriptor>
00231 void ShanChenSingleComponentProcessor2D<T,Descriptor>::process (
00232         Box2D domain, BlockLattice2D<T,Descriptor>& lattice )
00233 {
00234     // Short-hand notation for the lattice descriptor
00235     typedef Descriptor<T> D;
00236     // Handle to external scalars
00237     enum {
00238         densityOffset  = D::ExternalField::densityBeginsAt,
00239         momentumOffset = D::ExternalField::momentumBeginsAt
00240     };
00241 
00242     plint nx = domain.getNx() + 2;  // Include a one-cell boundary
00243     plint ny = domain.getNy() + 2;  // Include a one-cell boundary
00244     plint offsetX = domain.x0-1;
00245     plint offsetY = domain.y0-1;
00246     ScalarField2D<T> psiField(nx,ny);
00247     
00248     // Compute density and momentum on every site and store result in external scalars;
00249     //   furthermore, evaluate the interaction potential Psi and store it into a ScalarField.
00250     //   Envelope cells are included, because they are needed to compute the interaction potential
00251     //   in the following. Note that the value of the momentum is stored temporarily only, as
00252     //   it is corrected later on to include corrections due to the interaction potential.
00253     for (plint iX=domain.x0-1; iX<=domain.x1+1; ++iX) {
00254         for (plint iY=domain.y0-1; iY<=domain.y1+1; ++iY) {
00255             // Get "intelligent" value of density through cell object, to account
00256             //   for the fact that the density value can be user-defined, for example
00257             //   on boundaries.
00258             Cell<T,Descriptor>& cell = lattice.get(iX,iY);
00259             T rho = cell.computeDensity();
00260             // Evaluate potential function psi.
00261             psiField.get(iX-offsetX, iY-offsetY) = Psi->compute(rho);
00262             // Store density into the corresponding external scalar.
00263             *cell.getExternal(densityOffset) = rho;
00264             // Compute momentum through direct access to particle populations, and store
00265             //   result in corresponding external scalars. Note that Cell::computeVelocity
00266             //   cannot be used, because it returns the velocity of the external scalars,
00267             //   not the velocity computed from the particle populations.
00268             Array<T,Descriptor<T>::d> j;
00269             momentTemplates<T,Descriptor>::get_j(cell,j);
00270             for (int iD=0; iD<Descriptor<T>::d; ++iD) {
00271                 *(cell.getExternal(momentumOffset)+iD) = j[iD];
00272             }
00273         }
00274     }
00275 
00276     // Compute the interparticle forces, and store they by means of a 
00277     //   velocity correction in the external velocity field.
00278     for (plint iX=domain.x0; iX<=domain.x1; ++iX) {
00279         for (plint iY=domain.y0; iY<=domain.y1; ++iY) {
00280             Array<T,D::d> rhoContribution;
00281             rhoContribution.resetToZero();
00282             // Compute the term \sum_i ( t_i psi(x+c_i,t) c_i )
00283             for (plint iPop = 0; iPop < D::q; ++iPop) {
00284                 plint nextX = iX + D::c[iPop][0];
00285                 plint nextY = iY + D::c[iPop][1];
00286                 T psi = psiField.get(nextX-offsetX, nextY-offsetY);
00287                 for (int iD = 0; iD < D::d; ++iD) {
00288                     rhoContribution[iD] += D::t[iPop] * psi * D::c[iPop][iD];
00289                 }
00290             }
00291 
00292             // Computation and storage of the final momentum, including tho momentum
00293             //   difference due to interaction potential and the external force.
00294             Cell<T,Descriptor>& cell = lattice.get(iX,iY);
00295             T *momentum = cell.getExternal(momentumOffset);
00296             for (int iD = 0; iD < D::d; ++iD) {
00297                 // Initialize force contribution with force from external fields if there
00298                 //   is any, or with zero otherwise.
00299                 T forceContribution = getExternalForceComponent(cell, iD);
00300                 // Add interaction term.
00301                 T psi = psiField.get(iX-offsetX, iY-offsetY);
00302                 forceContribution -= G * psi * rhoContribution[iD];
00303                 // Include into total momentum.
00304                 momentum[iD] += (T)1/cell.getDynamics().getOmega()*forceContribution;
00305             }
00306         }
00307     }
00308 }
00309 
00310 }  // namespace plb
00311 
00312 #endif  // SHAN_CHEN_PROCESSOR_2D_HH