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