00001 #ifdef CH_LANG_CC 00002 /* 00003 * _______ __ 00004 * / ___/ / ___ __ _ / / ___ 00005 * / /__/ _ \/ _ \/ V \/ _ \/ _ \ 00006 * \___/_//_/\___/_/_/_/_.__/\___/ 00007 * Please refer to Copyright.txt, in Chombo's root directory. 00008 */ 00009 #endif 00010 00011 #ifndef _BASELEVELHEATSOLVER_H__ 00012 #define _BASELEVELHEATSOLVER_H__ 00013 00014 #include <iostream> 00015 #include <math.h> 00016 #include "SPACE.H" 00017 #include <stdlib.h> 00018 #include <REAL.H> 00019 #include <Box.H> 00020 #include <DisjointBoxLayout.H> 00021 #include <LevelData.H> 00022 #include <ProblemDomain.H> 00023 #include "AMRTGA.H" 00024 #include "NamespaceHeader.H" 00025 00026 //! \class BaseLevelHeatSolver 00027 //! This base class implements the 2nd-order implicit L0-stable time 00028 //! integration algorithm developed by Twizell, Gumel, and Arigu for 00029 //! solving elliptic equations. It relies upon linear algebraic operations 00030 //! defined in the underlying Helmholtz operators. 00031 //! \tparam LevelDataType The type used to store data at a grid level. 00032 //! This is usually LevelData<T>, where T is some 00033 //! cell-centered FArrayBox type. 00034 //! \tparam FluxDataType The type used to store flux data at a grid 00035 //! level. This is usually an array box clas that stores 00036 //! fluxes. 00037 //! \tparam FluxRegisterType The type used to store flux register data for 00038 //! interactions between different grid levels. 00039 //! This is usually a flux register class. 00040 template <class LevelDataType, 00041 class FluxDataType, 00042 class FluxRegisterType> 00043 class BaseLevelHeatSolver 00044 { 00045 00046 public: 00047 00048 /// 00049 BaseLevelHeatSolver(const Vector<DisjointBoxLayout>& a_grids, 00050 const Vector<int>& a_refRat, 00051 const ProblemDomain& a_level0Domain, 00052 RefCountedPtr<AMRLevelOpFactory<LevelDataType> >& a_opFact, 00053 const RefCountedPtr<AMRMultiGrid<LevelDataType> >& a_solver): 00054 m_grids(a_grids), 00055 m_refRat(a_refRat), 00056 m_level0Domain(a_level0Domain), 00057 m_ops(), 00058 m_solver(a_solver) 00059 { 00060 m_ops.resize(a_grids.size()); 00061 Vector< AMRLevelOp<LevelDataType> * >& amrops = m_solver->getAMROperators(); 00062 00063 for (int ilev = 0; ilev < m_ops.size(); ilev++) 00064 { 00065 m_ops[ilev] = dynamic_cast<LevelTGAHelmOp<LevelDataType,FluxDataType>* >(amrops[ilev]); 00066 if (m_ops[ilev]==NULL) 00067 { 00068 MayDay::Error("dynamic cast failed---is that operator really a TGAHelmOp?"); 00069 } 00070 } 00071 } 00072 00073 //! Destructor, called after destructors of BaseLevelHeatSolver subclasses. 00074 virtual ~BaseLevelHeatSolver() 00075 { 00076 } 00077 00078 //! Integrates the helmholtz equation represented by this object, placing 00079 //! the new solution in \a a_phiNew. 00080 //! \param a_phiNew The new solution (the value of phi at time n + 1) will 00081 //! be stored here. 00082 //! \param a_phiOld The old solution (the value of phi at time n). 00083 //! \param a_src The source term on the right hand side of the Helmholtz 00084 //! equation. 00085 //! \param a_flux This will store the flux computed at the current grid 00086 //! level during the solution of the Helmholtz equation. 00087 //! \param a_fineFluxRegPtr A pointer to the flux register representing the 00088 //! finer grid level adjacent to this one, or NULL 00089 //! if there is no finer grid level. 00090 //! \param a_crseFluxRegPtr A pointer to the flux register representing the 00091 //! coarser grid level adjacent to this one, or NULL 00092 //! if there is no coarser grid level. 00093 //! \param a_oldTime The time at the beginning of the integration step at 00094 //! the current grid level. 00095 //! \param a_crseOldTime The time at the beginning of the integration step 00096 //! at the coarser adjacent grid level. This parameter 00097 //! is ignored if there is no coarser grid level. 00098 //! \param a_crseNewTime The time at the end of the integration step 00099 //! at the coarser adjacent grid level. This parameter 00100 //! is ignored if there is no coarser grid level. 00101 //! \param a_dt The size of the integration step at the current grid level. 00102 //! \param a_level The current grid level. 00103 //! \param a_zeroPhi If set to true, \a a_phiNew will be set to zero before 00104 //! the integration takes place. Otherwise, a_phiNew is 00105 //! assumed to be an initial estimate for the solution in 00106 //! the iterative linear solve. 00107 //! \param a_fluxStartComponent An index identifying the component at which 00108 //! flux data begins within \a a_fineFluxRegPtr 00109 //! and \a a_crseFluxRegPtr. 00110 virtual void updateSoln(LevelDataType& a_phiNew, 00111 LevelDataType& a_phiOld, 00112 LevelDataType& a_src, 00113 LevelData<FluxDataType>& a_flux, 00114 FluxRegisterType* a_fineFluxRegPtr, 00115 FluxRegisterType* a_crseFluxRegPtr, 00116 const LevelDataType* a_crsePhiOldPtr, 00117 const LevelDataType* a_crsePhiNewPtr, 00118 Real a_oldTime, 00119 Real a_crseOldTime, 00120 Real a_crseNewTime, 00121 Real a_dt, 00122 int a_level, 00123 bool a_zeroPhi = true, 00124 bool a_rhsAlreadyKappaWeighted= false, 00125 int a_fluxStartComponent = 0) = 0; 00126 00127 /// 00128 virtual 00129 void updateSoln(LevelDataType& a_phiNew, 00130 LevelDataType& a_phiOld, 00131 LevelDataType& a_src, 00132 FluxRegisterType* a_fineFluxRegPtr, 00133 FluxRegisterType* a_crseFluxRegPtr, 00134 const LevelDataType* a_crsePhiOldPtr, 00135 const LevelDataType* a_crsePhiNewPtr, 00136 Real a_oldTime, 00137 Real a_crseOldTime, 00138 Real a_crseNewTime, 00139 Real a_dt, 00140 int a_level, 00141 bool a_zeroPhi = true, 00142 bool a_rhsAlreadyKappaWeighted = false, 00143 int a_fluxStartComponent = 0) 00144 { 00145 MayDay::Error("need to overwrite this function with one that calls with your flux or call more general one"); 00146 } 00147 00148 //! Computes the time-centered diffusion term L(phi). This can be used to 00149 //! find contributions to the solution from diffusion. The diffusion term 00150 //! is computed by computing a finite difference approximation for d phi/dt 00151 //! using the updated and original values of phi and the time step. Most of 00152 //! the arguments given here are passed along to updateSoln and retain their 00153 //! significance therein. 00154 //! \param a_diffusiveTerm The diffusion term L(phi) will be stored here. 00155 //! \param a_phiOld The old solution (the value of phi at time n). 00156 //! \param a_src The source term on the right hand side of the Helmholtz 00157 //! equation (used to fine the new value of phi). 00158 //! \param a_fineFluxRegPtr A pointer to the flux register representing the 00159 //! finer grid level adjacent to this one, or NULL 00160 //! if there is no finer grid level. 00161 //! \param a_crseFluxRegPtr A pointer to the flux register representing the 00162 //! coarser grid level adjacent to this one, or NULL 00163 //! if there is no coarser grid level. 00164 //! \param a_crsePhiOldTime A pointer to the value of phi at the beginning 00165 //! of the integration step at the coarser grid 00166 //! level adjacent to this one, or NULL if there 00167 //! is no coarser grid level. 00168 //! \param a_crsePhiNewTime A pointer to the value of phi at the end 00169 //! of the integration step at the coarser grid 00170 //! level adjacent to this one, or NULL if there 00171 //! is no coarser grid level. 00172 //! \param a_oldTime The time at the beginning of the integration step at 00173 //! the current grid level. 00174 //! \param a_crseOldTime The time at the beginning of the integration step 00175 //! at the coarser adjacent grid level. This parameter 00176 //! is ignored if there is no coarser grid level. 00177 //! \param a_crseNewTime The time at the end of the integration step 00178 //! at the coarser adjacent grid level. This parameter 00179 //! is ignored if there is no coarser grid level. 00180 //! \param a_dt The size of the integration step at the current grid level. 00181 //! \param a_level The current grid level. 00182 //! \param a_zeroPhi If set to true, the new value of phi will be set to 00183 //! zero before the integration takes place. Otherwise, it 00184 //! will be set to the value in \a a_diffusiveTerm. 00185 virtual void computeDiffusion(LevelDataType& a_diffusiveTerm, 00186 LevelDataType& a_phiOld, 00187 LevelDataType& a_src, 00188 LevelData<FluxDataType>& a_flux, 00189 FluxRegisterType* a_fineFluxRegPtr, 00190 FluxRegisterType* a_crseFluxRegPtr, 00191 const LevelDataType* a_crsePhiOldPtr, 00192 const LevelDataType* a_crsePhiNewPtr, 00193 Real a_oldTime, 00194 Real a_crseOldTime, 00195 Real a_crseNewTime, 00196 Real a_dt, 00197 int a_level, 00198 bool a_zeroPhi = true, 00199 bool a_rhsAlreadyKappaWeighted = false 00200 ) 00201 { 00202 // The operator has no time-dependent parameters. Life is easier. 00203 00204 // first compute updated solution 00205 LevelDataType phiNew; 00206 00207 m_ops[a_level]->create(phiNew, a_phiOld); 00208 m_ops[a_level]->setToZero(phiNew); 00209 if (!a_zeroPhi) 00210 { 00211 m_ops[a_level]->assign(phiNew, a_phiOld); 00212 } 00213 00214 updateSoln(phiNew, a_phiOld, a_src, a_flux, 00215 a_fineFluxRegPtr, a_crseFluxRegPtr, 00216 a_crsePhiOldPtr, a_crsePhiNewPtr, 00217 a_oldTime, a_crseOldTime, 00218 a_crseNewTime, a_dt, a_level, a_zeroPhi, a_rhsAlreadyKappaWeighted); 00219 00220 // now subtract everything off to leave us with diffusive term 00221 m_ops[a_level]->incr(phiNew, a_phiOld, -1.0); 00222 m_ops[a_level]->scale(phiNew, 1.0/a_dt); 00223 00224 //now multiply by a if there is an a 00225 m_ops[a_level]->diagonalScale(phiNew, false); 00226 00227 // and finally, subtract off a_src 00228 m_ops[a_level]->incr(phiNew, a_src, -1.0); 00229 00230 // what's left should be the time-centered diffusive part of the update 00231 m_ops[a_level]->assign(a_diffusiveTerm, phiNew); 00232 } 00233 00234 /// 00235 /** 00236 calls set time and calls operator with given alpha and beta 00237 */ 00238 virtual void applyOperator(LevelDataType& a_ans, 00239 const LevelDataType& a_phi, 00240 const LevelDataType* a_phiC, 00241 int a_level, 00242 Real a_alpha, 00243 Real a_beta, 00244 bool a_applyBC) 00245 { 00246 m_ops[a_level]->setAlphaAndBeta(a_alpha, a_beta); 00247 00248 LevelDataType zero; 00249 m_ops[a_level]->create(zero, a_ans); 00250 m_ops[a_level]->setToZero(zero); 00251 00252 // set a_ans = helm(a_phi) 00253 // = (I + factor*laplacian)(a_phi) 00254 if (a_applyBC) 00255 { 00256 if ( (a_phiC == NULL) || (a_level==0)) 00257 { 00258 m_ops[a_level]->applyOp(a_ans, a_phi, false); 00259 } 00260 else 00261 { 00262 m_ops[a_level]->AMROperatorNF(a_ans, a_phi, *a_phiC, false); 00263 } 00264 } 00265 else 00266 { 00267 m_ops[a_level]->applyOpNoBoundary(a_ans, a_phi); 00268 } 00269 } 00270 00271 //! Applies the Helmholtz operator to the solution \a a_phi at the given 00272 //! grid level. This will set \a a_ans to 00273 //! (I + \a a_mu * \a a_dt * L(\a a_phi). 00274 //! \param a_ans This will store the value of the Helmholtz operator applied 00275 //! to \a a_phi. 00276 //! \param a_phi The value of the solution to which the Helmholtz operator 00277 //! is applied. 00278 //! \param a_phiC A pointer to the value of the solution at the coarser 00279 //! adjacent grid level, or NULL if there is no coarser grid 00280 //! level. 00281 //! \param a_level The grid level at which the Helmholtz operator is applied. 00282 //! \param a_mu A number between 0 and 1 that defines the time within the 00283 //! integration step at which the Helmholtz operator is evaluated. 00284 //! \param a_dt The size of the integration step at the current grid level. 00285 //! \param a_applyBC If set to true, inhomogeneous boundary conditions will 00286 //! be applied to the Helmholtz operator (both at the domain 00287 //! boundary and at the coarse-fine grid interfaces). If 00288 //! set to false, homogeneous boundary conditions will 00289 //! applied to the Helmholtz operator at these locations. 00290 //! protected because not flexible (alpha == 1) 00291 void applyHelm(LevelDataType& a_ans, 00292 const LevelDataType& a_phi, 00293 const LevelDataType* a_phiC, 00294 int a_level, 00295 Real a_mu, 00296 Real a_dt, 00297 bool a_homogeneousBC) 00298 { 00299 // Set the operator's alpha and beta coefficients. 00300 Real factor = a_mu*a_dt; 00301 m_ops[a_level]->setAlphaAndBeta(1.0, factor); 00302 00303 LevelDataType zero; 00304 m_ops[a_level]->create(zero, a_ans); 00305 m_ops[a_level]->setToZero(zero); 00306 00307 // set a_ans = helm(a_phi) 00308 // = (I + factor*laplacian)(a_phi) 00309 if ( (a_phiC == NULL) || (a_level==0)) 00310 { 00311 m_ops[a_level]->applyOp(a_ans, a_phi, a_homogeneousBC); 00312 } 00313 else 00314 { 00315 m_ops[a_level]->AMROperatorNF(a_ans, a_phi, *a_phiC, a_homogeneousBC); 00316 } 00317 } 00318 00319 //! Adds flux contributions from the Helmholtz operator at the current 00320 //! grid level. 00321 //! \param a_diffusiveFlux The flux to which Helmholtz operator flux 00322 //! contributions will be accumulated. 00323 //! \param a_phi The solution at the current grid level to which the 00324 //! Helmholtz operator is applied in order to evaluate the flux. 00325 //! \param a_level The grid level at which the flux is computed. 00326 //! \param a_mu A number between 0 and 1 that defines the time within the 00327 //! integration step at which the Helmholtz operator is evaluated. 00328 //! \param a_dt The size of the integration step at the current grid level. 00329 //! \param a_sign A factor applied to the derivative term in the Helmholtz 00330 //! equation. This allows the sign to be made positive or 00331 //! negative as is necessary for the flux calculation. 00332 //! \param a_setToZero If true, \a a_diffusiveFlux will be set to zero before 00333 //! the fluxes are accumulated to it. Otherwise, fluxes 00334 //! will be added to the existing value. 00335 void incrementFlux(LevelData<FluxDataType>& a_diffusiveFlux, 00336 LevelDataType& a_phi, 00337 int a_level, 00338 Real a_mu, 00339 Real a_dt, 00340 Real a_sign, 00341 bool a_setToZero) 00342 { 00343 Real factor = a_sign*a_dt*a_mu; 00344 m_ops[a_level]->setAlphaAndBeta(1.0, factor); 00345 00346 // increment flux 00347 m_ops[a_level]->fillGrad(a_phi); 00348 for (DataIterator dit = a_phi.dataIterator(); dit.ok(); ++dit) 00349 { 00350 FluxDataType& thisFlux = a_diffusiveFlux[dit]; 00351 FluxDataType tempFlux; 00352 tempFlux.define(thisFlux); 00353 00354 tempFlux.setVal(0.0); 00355 if (a_setToZero) 00356 { 00357 thisFlux.setVal(0.0); 00358 } 00359 00360 m_ops[a_level]->getFlux(tempFlux, a_phi, m_grids[a_level][dit], dit(), 1.0); 00361 thisFlux += tempFlux; 00362 } 00363 } 00364 00365 //! Solves the Helmholtz equation 00366 //! (I - \a a_mu * \a a_dt * L(\a a_phi) = \a a_rhs 00367 //! for \a a_phi. Here it is assumed that a solution \a a_phiC exists 00368 //! on a coarser grid level. 00369 //! \param a_phi This will store the solution to the Helmholtz equation on 00370 //! the given grid level. 00371 //! \param a_phiC The value of the solution given at the coarser adjacent 00372 //! grid level. If there is no coarser grid level, this 00373 //! parameter is ignored. 00374 //! \param a_rhs The right hand side of the Helmholtz equation at the given 00375 //! grid level. 00376 //! \param a_level The grid level at which the Helmholtz equation is solved. 00377 //! \param a_mu A number between 0 and 1 that defines the time within the 00378 //! integration step at which the Helmholtz operator is evaluated. 00379 //! \param a_dt The size of the integration step at the current grid level. 00380 //! \param a_zeroPhi If set to true, \a a_phi will be set to zero before 00381 //! the (iterative) solve takes place. Otherwise, a_phi is 00382 //! assumed to be an initial estimate for the solution. 00383 void solveHelm(LevelDataType& a_phi, 00384 LevelDataType& a_phiC, 00385 LevelDataType& a_rhs, 00386 int a_level, 00387 Real a_mu, 00388 Real a_dt, 00389 bool a_zeroPhi = true) 00390 { 00391 if (a_zeroPhi) 00392 { 00393 m_ops[a_level]->setToZero(a_phi); 00394 } 00395 Vector<LevelDataType* > phi(m_grids.size(), NULL); 00396 Vector<LevelDataType* > rhs(m_grids.size(), NULL); 00397 phi[a_level] = &a_phi; 00398 rhs[a_level] = &a_rhs; 00399 if (a_level > 0) 00400 { 00401 phi[a_level-1] = &a_phiC; 00402 } 00403 00404 Real factor = -a_dt*a_mu; 00405 resetSolverAlphaAndBeta(1.0, factor); 00406 00407 m_solver->solve(phi, rhs, a_level, a_level, a_zeroPhi); 00408 int solverExitStatus = m_solver->m_exitStatus; 00409 if (solverExitStatus==2 || solverExitStatus==4 || solverExitStatus==6) 00410 { 00411 // These status codes correspond to the cases in which 00412 // norm is neither small enough nor reduced enough. 00413 // Either we've reached the maximum number of iterations, 00414 // or we've hung. 00415 pout() << "BaseLevelTGA:: WARNING: solver exitStatus == " 00416 << solverExitStatus << std::endl; 00417 } 00418 } 00419 00420 //! Sets the alpha and beta parameters in each Helmholtz operator to the 00421 //! given values. 00422 //! \param a_alpha The identity term coefficient in the Helmholtz operator. 00423 //! \param a_beta The coefficient in front of the discrete derivative term 00424 //! in the Helmholtz operator. 00425 void resetSolverAlphaAndBeta(const Real& a_alpha, 00426 const Real& a_beta) 00427 { 00428 Vector<MGLevelOp<LevelDataType>* > ops = m_solver->getAllOperators(); 00429 for (int iop = 0; iop < ops.size(); iop++) 00430 { 00431 LevelTGAHelmOp<LevelDataType, FluxDataType>* helmop = 00432 dynamic_cast<LevelTGAHelmOp<LevelDataType, FluxDataType>*>(ops[iop]); 00433 helmop->setAlphaAndBeta(a_alpha, a_beta); 00434 } 00435 for (int iop = 0; iop < m_ops.size(); iop++) 00436 { 00437 m_ops[iop]->setAlphaAndBeta(a_alpha, a_beta); 00438 } 00439 } 00440 00441 //! Creates a new Helmholtz operator for use by the TGA integrator. 00442 //! \param a_indexSpace The ProblemDomain on which the new operator is 00443 //! defined. 00444 //! \param a_opFact A factory typename LevelDataTypehat generates new Helmholtz operators. 00445 //! \returns A pointer to a newly-allocated LevelTGAHelmOp instance. 00446 virtual LevelTGAHelmOp<LevelDataType, FluxDataType>* 00447 newOp(const ProblemDomain& a_indexSpace, 00448 RefCountedPtr<AMRLevelOpFactory<LevelDataType> >& a_opFact) 00449 { 00450 LevelTGAHelmOp<LevelDataType, FluxDataType>* retval = 00451 dynamic_cast<LevelTGAHelmOp<LevelDataType, FluxDataType>*>(a_opFact->AMRnewOp(a_indexSpace)); 00452 00453 return retval; 00454 } 00455 00456 //! Returns the number of grid levels on which this integrator operates. 00457 int size() const 00458 { 00459 return m_grids.size(); 00460 } 00461 00462 protected: 00463 00464 //! Interpolates a given quantity linearly in time using its beginning- and 00465 //! end-of-step values and placing the result in \a a_data. 00466 //! \param a_oldData The value of the quantity at the beginning of the time 00467 //! step at the grid level identified by \a a_level. 00468 //! \param a_newData The value of the quantity at the end of the time 00469 //! step at the grid level identified by \a a_level. 00470 //! \param a_time The time at which the quantity is to be interpolated 00471 //! within the integration step. 00472 //! \param a_oldTime The beginning of the integration step at the grid level 00473 //! identified by \a a_level. 00474 //! \param a_newTime The end of the integration step at the grid level 00475 //! identified by \a a_level. 00476 //! \param a_level An index identifying the grid level at which the 00477 //! interpolation takes place. 00478 void timeInterp(LevelDataType& a_data, 00479 const LevelDataType& a_oldData, 00480 const LevelDataType& a_newData, 00481 Real a_time, 00482 Real a_oldTime, 00483 Real a_newTime, 00484 int a_level) 00485 { 00486 Real eps = 1.0e-10; 00487 CH_assert(a_newTime >= a_oldTime); 00488 Real diff = (a_newTime - a_oldTime); 00489 this->m_ops[a_level]->setToZero(a_data); 00490 if (diff < eps) 00491 { 00492 //no real time advance and don't want to divide by zero 00493 this->m_ops[a_level]->incr(a_data, a_oldData, 1.0); 00494 } 00495 else 00496 { 00497 Real factor = (a_time-a_oldTime)/(a_newTime - a_oldTime); 00498 this->m_ops[a_level]->incr(a_data, a_oldData, 1.0-factor); 00499 this->m_ops[a_level]->incr(a_data, a_newData, factor); 00500 } 00501 } 00502 00503 //! The disjoint box layouts at every AMR grid level. 00504 Vector<DisjointBoxLayout> m_grids ; 00505 00506 //! The refinement ratios between AMR grid levels. 00507 Vector<int> m_refRat; 00508 00509 //! The coarsest domain on which the Helmholtz equation is integrated. 00510 ProblemDomain m_level0Domain; 00511 00512 //! An array of the solver's Helmholtz operators at each grid level, 00513 //! casted to LevelTGAHelmOp instances. These are owned by the solver, 00514 //! so we shouldn't delete these. 00515 Vector< LevelTGAHelmOp<LevelDataType, FluxDataType>* > m_ops; 00516 00517 //! The multigrid solver used to solve the Helmholtz equation. 00518 RefCountedPtr<AMRMultiGrid<LevelDataType> > m_solver; 00519 }; 00520 00521 #include "NamespaceFooter.H" 00522 #endif