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

fdWrapper2D.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 
00028 #ifndef FINITE_DIFFERENCE_WRAPPER_2D_HH
00029 #define FINITE_DIFFERENCE_WRAPPER_2D_HH
00030 
00031 #include "finiteDifference/fdWrapper2D.h"
00032 #include "finiteDifference/fdFunctional2D.h"
00033 #include "atomicBlock/reductiveDataProcessorWrapper2D.h"
00034 #include "atomicBlock/dataProcessorWrapper2D.h"
00035 #include "multiBlock/reductiveMultiDataProcessorWrapper2D.h"
00036 #include "multiBlock/multiDataProcessorWrapper2D.h"
00037 #include "multiGrid/gridConversion2D.h"
00038 
00039 
00040 namespace plb {
00041 
00042 template<typename T>
00043 void computeXderivative(MultiScalarField2D<T>& value, MultiScalarField2D<T>& derivative, Box2D const& domain) {
00044     plint boundaryWidth = 1;
00045     applyProcessingFunctional (
00046             new BoxXderivativeFunctional2D<T>, domain, value, derivative, boundaryWidth );
00047 }
00048 
00049 template<typename T>
00050 std::auto_ptr<MultiScalarField2D<T> > computeXderivative(MultiScalarField2D<T>& value, Box2D const& domain) {
00051     MultiScalarField2D<T>* derivative = new MultiScalarField2D<T>(value, domain);
00052     computeXderivative(value, *derivative, domain);
00053     return std::auto_ptr<MultiScalarField2D<T> >(derivative);
00054 }
00055 
00056 template<typename T>
00057 std::auto_ptr<MultiScalarField2D<T> > computeXderivative(MultiScalarField2D<T>& value) {
00058     return computeXderivative(value, value.getBoundingBox());
00059 }
00060 
00061 
00062 template<typename T>
00063 void computeYderivative(MultiScalarField2D<T>& value, MultiScalarField2D<T>& derivative, Box2D const& domain) {
00064     plint boundaryWidth = 1;
00065     applyProcessingFunctional (
00066             new BoxYderivativeFunctional2D<T>, domain, value, derivative, boundaryWidth );
00067 }
00068 
00069 template<typename T>
00070 std::auto_ptr<MultiScalarField2D<T> > computeYderivative(MultiScalarField2D<T>& value, Box2D const& domain) {
00071     MultiScalarField2D<T>* derivative = new MultiScalarField2D<T>(value, domain);
00072     computeYderivative(value, *derivative, domain);
00073     return std::auto_ptr<MultiScalarField2D<T> >(derivative);
00074 }
00075 
00076 template<typename T>
00077 std::auto_ptr<MultiScalarField2D<T> > computeYderivative(MultiScalarField2D<T>& value) {
00078     return computeYderivative(value, value.getBoundingBox());
00079 }
00080 
00081 template<typename T>
00082 void computeGradientNorm(MultiScalarField2D<T>& value, MultiScalarField2D<T>& derivative, Box2D const& domain) {
00083     plint boundaryWidth = 1;
00084     applyProcessingFunctional (
00085             new BoxGradientNormFunctional2D<T>, domain, value, derivative, boundaryWidth );
00086 }
00087 
00088 template<typename T>
00089 std::auto_ptr<MultiScalarField2D<T> > computeGradientNorm(MultiScalarField2D<T>& value, Box2D const& domain) {
00090     MultiScalarField2D<T>* derivative = new MultiScalarField2D<T>(value, domain);
00091     computeGradientNorm(value, *derivative, domain);
00092     return std::auto_ptr<MultiScalarField2D<T> >(derivative);
00093 }
00094 
00095 template<typename T>
00096 std::auto_ptr<MultiScalarField2D<T> > computeGradientNorm(MultiScalarField2D<T>& value) {
00097     return computeGradientNorm(value, value.getBoundingBox());
00098 }
00099 
00100 
00101 template<typename T>
00102 std::auto_ptr<MultiScalarField2D<T> > computePoissonRHS(MultiTensorField2D<T,2>& velocity, Box2D const& domain)
00103 {
00104     std::auto_ptr<MultiScalarField2D<T> > ux = extractComponent(velocity, domain, 0);
00105     std::auto_ptr<MultiScalarField2D<T> > uy = extractComponent(velocity, domain, 1);
00106 
00107     std::auto_ptr<MultiScalarField2D<T> > dx_ux = computeXderivative(*ux, domain);
00108     std::auto_ptr<MultiScalarField2D<T> > dy_ux = computeYderivative(*ux, domain);
00109     std::auto_ptr<MultiScalarField2D<T> > dx_uy = computeXderivative(*uy, domain);
00110     std::auto_ptr<MultiScalarField2D<T> > dy_uy = computeYderivative(*uy, domain);
00111 
00112     std::auto_ptr<MultiScalarField2D<T> > term1 = multiply(*dx_ux, *dx_ux, domain);
00113     std::auto_ptr<MultiScalarField2D<T> > term2 = multiply((T)2, *multiply(*dx_uy, *dy_ux, domain), domain);
00114     std::auto_ptr<MultiScalarField2D<T> > term3 = multiply(*dy_uy, *dy_uy, domain);
00115 
00116     std::auto_ptr<MultiScalarField2D<T> > rhs = add(*term1, *add(*term2, *term3));
00117     return rhs;
00118 }
00119 
00120 template<typename T>
00121 std::auto_ptr<MultiScalarField2D<T> > computePoissonRHS(MultiTensorField2D<T,2>& velocity) {
00122     return computePoissonRHS(velocity, velocity.getBoundingBox());
00123 }
00124 
00125 template<typename T>
00126 void poissonIterate(MultiScalarField2D<T>& oldPressure, MultiScalarField2D<T>& newPressure,
00127                     MultiScalarField2D<T>& rhs, T beta, Box2D const& domain)
00128 {
00129     std::vector<MultiScalarField2D<T>* > fields;
00130     fields.push_back(&oldPressure);
00131     fields.push_back(&newPressure);
00132     fields.push_back(&rhs);
00133     plint boundaryWidth=1;
00134     applyProcessingFunctional (
00135             new BoxPoissonIteration2D<T>(beta), domain, fields, boundaryWidth );
00136 }
00137 
00138 template<typename T>
00139 T computePoissonResidue(MultiScalarField2D<T>& pressure, MultiScalarField2D<T>& rhs, Box2D const& domain) {
00140     BoxPoissonResidueFunctional2D<T> functional;
00141     applyProcessingFunctional(functional, domain, pressure, rhs);
00142     return functional.getMaxResidue();
00143 }
00144 
00145 /* ************ Wrapper for one Jacobi iteration *************** */
00146 template<typename T> 
00147 void JacobiIteration( MultiScalarField2D<T>& u_h, MultiScalarField2D<T>& new_u_h,
00148                       MultiScalarField2D<T>& rhs, Box2D const& domain ){
00149     
00150     std::vector<MultiScalarField2D<T>* > fields;
00151     fields.push_back(&u_h);
00152     fields.push_back(&new_u_h);
00153     fields.push_back(&rhs);
00154     plint boundaryWidth=1;
00155     applyProcessingFunctional (
00156             new JacobiIteration2D<T>(), domain, fields, boundaryWidth );
00157 
00158 }
00159 
00160 /* ************ Wrapper for one Gauss-Seidel iteration *************** */
00161 template<typename T> 
00162 void GaussSeidelIteration( MultiScalarField2D<T>& u_h, MultiScalarField2D<T>& jacobi_u_h,
00163                            MultiScalarField2D<T>& new_u_h, MultiScalarField2D<T>& rhs, Box2D const& domain )
00164 {
00165     std::vector<MultiScalarField2D<T>* > fields;
00166     fields.push_back(&u_h);
00167     fields.push_back(&jacobi_u_h);
00168     fields.push_back(&new_u_h);
00169     fields.push_back(&rhs);
00170     plint boundaryWidth=1;
00171     applyProcessingFunctional (
00172             new GaussSeidelIteration2D<T>(), domain, fields, boundaryWidth );
00173 }
00174 
00175 /* ************ Wrapper for Gauss-Seidel defect computation *************** */
00176 template<typename T> 
00177 MultiScalarField2D<T>* computeGaussSeidelDefect(MultiScalarField2D<T>& u_h, MultiScalarField2D<T>& rhs, 
00178                                                 Box2D const& domain)
00179 {
00180     MultiScalarField2D<T>* residual = new MultiScalarField2D<T>(u_h);
00181     std::vector<MultiScalarField2D<T>* > fields;
00182     fields.push_back(&u_h);
00183     fields.push_back(residual);
00184     fields.push_back(&rhs);
00185     plint boundaryWidth=1;
00186     applyProcessingFunctional (
00187             new GaussSeidelDefect2D<T>(), domain, fields,boundaryWidth);
00188             
00189     return residual;
00190 } 
00191 
00192 template<typename T>
00193 T computeEuclidianNorm(MultiScalarField2D<T>& matrix, Box2D const& domain){
00194     T av = computeAverage( *multiply(matrix,matrix),domain);
00195     return sqrt(av);
00196 }
00197 
00198 
00199 /* ************ Gauss-Seidel Solver *************** */
00200 template<typename T>
00201 void GaussSeidelSolver( MultiScalarField2D<T>& initialValue,
00202                         MultiScalarField2D<T>& result,
00203                         MultiScalarField2D<T>& rhs, Box2D const& domain, T tolerance, plint maxIter )
00204 {
00205     T originalNorm;
00206     T newNorm;
00207     plint infoIt=100;
00208     
00209     // to contain the Jacobi result
00210     MultiScalarField2D<T> jacobiValue(initialValue); 
00211     jacobiValue.reset();
00212     
00213     // computation of the initial residual. We will stop when currentResidual=tolerance*initialResidual
00214     // or when we have made maxIter iterations
00215     MultiScalarField2D<T>* defect = computeGaussSeidelDefect<T>(initialValue, rhs, domain);
00216     originalNorm = computeEuclidianNorm<T>(*defect, domain);
00217     delete defect;
00218 
00219     // MAIN LOOP
00220     for (plint iT=0; iT<maxIter; ++iT){
00221         // use one Jacobi iteration
00222         JacobiIteration<T>(initialValue, jacobiValue, rhs, domain);
00223         
00224         // with this jacobiValue, make one gauss-sidel iteration (eliminate the asynchronie for parallelism)
00225         GaussSeidelIteration<T>(initialValue, jacobiValue, result, rhs, domain);
00226         
00227         // compute the new residual norm to know if we continue
00228         defect = computeGaussSeidelDefect<T>(result, rhs, domain);
00229         newNorm = computeEuclidianNorm<T>(*defect, domain);
00230         delete defect;
00231         
00232         if (newNorm < tolerance*originalNorm) 
00233         {
00234             pcout << "Gauss-Seidel iterations: " << iT << std::endl;
00235             break;
00236         }
00237         
00238         // result becomes the new initial value
00239         result.swap(initialValue);
00240         
00241         if (iT%infoIt==0 && iT>0){
00242             pcout << "Gauss-Seidel iteration " << iT << " : error=" << newNorm/originalNorm 
00243                   << " : NORM=" << newNorm << std::endl;
00244         }
00245     }
00246     pcout << "Gauss-Seidel iterations: " << maxIter << std::endl;
00247 }
00248 
00249 /* ************ MultiGrid method *************** */
00251 template<typename T>
00252 MultiScalarField2D<T>* smooth( MultiScalarField2D<T>& initialValue, 
00253                                MultiScalarField2D<T>& rhs, Box2D const& domain,
00254                                plint smoothIters){
00255     // create the solution for Jacobi
00256     MultiScalarField2D<T> jacobiValue(initialValue); 
00257     jacobiValue.reset();
00258     
00259     // copy initialValue
00260     MultiScalarField2D<T> initialCopy(initialValue);
00261     // the result holder
00262     MultiScalarField2D<T>* newValue = new MultiScalarField2D<T>(initialValue);
00263     
00264     // iteration over the original system: one Jacobi + one Gauss-Seidel
00265     plint v1 = smoothIters;
00266     for (plint iV1=0; iV1< v1; ++iV1){
00267         newValue->swap(initialCopy);
00268         JacobiIteration<T>(initialCopy, jacobiValue, rhs, domain);
00269         GaussSeidelIteration<T>(initialCopy, jacobiValue, *newValue, rhs, domain);
00270     }
00271    
00272     return newValue;
00273 }
00274 
00276 template<typename T>
00277 MultiScalarField2D<T>* smoothAndInterpolate( MultiScalarField2D<T>& initialValue, 
00278                                              MultiScalarField2D<T>& rhs, Box2D const& domain,
00279                                              plint smoothIters )
00280 {
00281     // smooth
00282     MultiScalarField2D<T>* coarseNewValue = smooth(initialValue,rhs,domain,smoothIters);
00283     // interpolate
00284     MultiScalarField2D<T>* fineNewValue = new MultiScalarField2D<T>(*refine<T>(*coarseNewValue,1,-1,-1,-1));
00285     delete coarseNewValue;
00286     
00287     return fineNewValue;
00288 }
00289 
00291 template<typename T>
00292 MultiScalarField2D<T>* smoothAndComputeError( MultiScalarField2D<T>& initialValue, MultiScalarField2D<T>& rhs, 
00293                          Box2D const& domain, T& newError, plint smoothIters ){
00294     
00295     MultiScalarField2D<T>* newValue = smooth(initialValue,rhs,domain,smoothIters);
00296     // compute the new defect norm
00297     MultiScalarField2D<T>* newDefect = computeGaussSeidelDefect<T>(*newValue, rhs, domain);
00298     newError = computeEuclidianNorm<T>(*newDefect, domain);
00299     delete newDefect;
00300     
00301     return newValue;
00302 }
00303 
00305 template<typename T>
00306 MultiScalarField2D<T>* smoothAndComputeCoarseDefect( MultiScalarField2D<T>& initialValue, 
00307                                                MultiScalarField2D<T>& rhs, Box2D const& domain,
00308                                                plint smoothIters ){
00309     
00310     MultiScalarField2D<T>* newValue = smooth(initialValue,rhs,domain,smoothIters);
00311     // compute the new defect norm
00312     MultiScalarField2D<T>* newDefect = computeGaussSeidelDefect<T>(*newValue, rhs, domain);
00313     MultiScalarField2D<T>* coarseDefect = new MultiScalarField2D<T>(*coarsen<T>(*newDefect, 1, -1,1,1));
00314     delete newDefect;
00315     
00316     return coarseDefect;
00317 }
00318 
00319 template<typename T> 
00320 void generateRHS(MultiScalarField2D<T>& originalRHS, std::vector<MultiScalarField2D<T>* >& rhs){
00321     plint vectorSize = (plint) rhs.size();
00322     rhs[vectorSize-1] = new MultiScalarField2D<T>(originalRHS);
00323     for (plint iLevel=vectorSize-2; iLevel>=0; iLevel--){
00324         rhs[iLevel] = new MultiScalarField2D<T>( *coarsen<T>(*rhs[iLevel+1],1,-1,1,1));
00325     }
00326 }
00327 
00328 template<typename T>
00329 T multiGridVCycle( MultiScalarField2D<T>& initialValue, MultiScalarField2D<T>& newValue,
00330                    MultiScalarField2D<T>& rhs, Box2D const& domain, plint depth ){
00331     
00332     
00333     PLB_PRECONDITION(depth>=1);
00334         
00335     // containers of the values to not lose anything
00336     std::vector<MultiScalarField2D<T>* > newValues(depth+1);
00337     std::vector<MultiScalarField2D<T>* > defects(depth+1);
00338     
00339     plint smoothIters1 = 5;
00340     plint smoothIters2 = 5;
00341     // we go down until the level before the coarsest
00342     newValues[depth] = smooth<T>(initialValue,rhs,initialValue.getBoundingBox(), smoothIters1);
00343     defects[depth]  = computeGaussSeidelDefect<T>(*newValues[depth],rhs,initialValue.getBoundingBox());
00344     defects[depth-1] = new MultiScalarField2D<T>(*coarsen<T>(*defects[depth],1,-1,1,1));
00345     *defects[depth-1] = *plb::multiply(-1.0,*defects[depth-1]);
00346     
00348     for (plint iLevel=depth-1; iLevel>=1; iLevel--){
00349         
00350         // create initial solution = 0
00351         MultiScalarField2D<T> initialSolution(*defects[iLevel]);
00352         initialSolution.reset();
00353         // smooth the new system
00354         newValues[iLevel] = smooth<T>( initialSolution,*defects[iLevel],
00355                                          initialSolution.getBoundingBox(), smoothIters1);
00356         MultiScalarField2D<T>* tempDefect =
00357             computeGaussSeidelDefect<T>(*newValues[iLevel],*defects[iLevel],newValues[iLevel]->getBoundingBox());
00358         defects[iLevel-1] = new 
00359                     MultiScalarField2D<T>(*coarsen<T>(*tempDefect,1,-1,1,1));
00360         *defects[iLevel-1] = *plb::multiply(-1.0,*defects[iLevel-1]); // change the sign
00361         delete tempDefect;
00362     }
00363     
00365     // resolve the system exactly for the coarse system
00366     MultiScalarField2D<T> initialValueCoarse(*defects[0]);
00367     MultiScalarField2D<T> resultCoarse(*defects[0]); // container for the coarse solution to the correction scheme
00368     initialValueCoarse.reset(); // for the correction 0 is a good first approximation
00369     
00370     T tolerance = 1e-5;
00371     plint maxIter = 100;
00372     GaussSeidelSolver<T>( initialValueCoarse, resultCoarse,
00373                           *defects[0],resultCoarse.getBoundingBox(),tolerance, maxIter );
00374 
00375     newValues[0] = new MultiScalarField2D<T>(resultCoarse);
00376     // we interpolate the error computed in the coarse grid and add it to the original result from level 1
00377     MultiScalarField2D<T>* v_h = new MultiScalarField2D<T>(*refine<T>( *newValues[0], 1, -1 ,-1,-1));
00378     // compute new approximation as newValue = u_h + v_h
00379     *newValues[1] = *plb::add(*newValues[1],*v_h);
00380     delete v_h;
00381     
00383     // go up interpolating and smoothing up to the level before the finest
00384     for (plint iLevel=1; iLevel<depth; ++iLevel){
00385         MultiScalarField2D<T>* tempNewVal = smoothAndInterpolate<T>( *newValues[iLevel], *defects[iLevel], 
00386                                                                     newValues[iLevel]->getBoundingBox(), smoothIters2);
00387         *newValues[iLevel+1] = *plb::add(*newValues[iLevel+1],*tempNewVal);
00388         delete tempNewVal;
00389 
00390     }
00391     
00392     // we smooth and compute the new error in the finest level
00393     T newError = 0.0;
00394     MultiScalarField2D<T>* result = smoothAndComputeError( *newValues[depth], rhs,
00395                                                            newValues[depth]->getBoundingBox(),
00396                                                            newError, smoothIters2 );
00397     // copy new initial value
00398     newValue = *result;
00399     delete result;
00400     
00401     // CLEAN-UP
00402     for (plint iLevel = 0; iLevel <= depth; ++iLevel){
00403         delete newValues[iLevel];
00404         delete defects[iLevel];
00405     }
00406     
00407     return newError;
00408 }
00409 
00410 template<typename T>
00411 std::vector<MultiScalarField2D<T>* > fullMultiGrid( MultiScalarField2D<T>& initialValue, 
00412                                                     MultiScalarField2D<T>& originalRhs, Box2D const& domain,         
00413                                                     plint gridLevels, plint ncycles )
00414 {
00415     PLB_PRECONDITION( gridLevels>=2 && ncycles>=1 );
00416     
00417     std::vector<MultiScalarField2D<T>* > rhs(gridLevels);
00418     std::vector<MultiScalarField2D<T>* > solutions(gridLevels);
00419     
00420     // create all the right-hand sides from restriction over rhs
00421     generateRHS(originalRhs, rhs);
00422     
00423     // Initial solution computed exactly on the coarsest grid
00424     MultiScalarField2D<T> initialSolution(*rhs[0]);
00425     MultiScalarField2D<T> initialValueCoarse(*rhs[0]);
00426     initialValueCoarse.reset();
00427     GaussSeidelSolver<T>(initialValueCoarse, initialSolution, *rhs[0],initialSolution.getBoundingBox());
00428     solutions[0] = new MultiScalarField2D<T>(initialSolution);
00429     
00430     // MAIN LOOP (at each time we add one more level)
00431     for (plint iLevel=1; iLevel<gridLevels; ++iLevel){
00432         pcout << "Level: " << iLevel << std::endl;
00433         // interpolate the iLevel-1 solution
00434         MultiScalarField2D<T>* initialSolutionLevel = new
00435                 MultiScalarField2D<T>( *refine<T>(*solutions[iLevel-1],1,-1,-1,-1) );
00436         MultiScalarField2D<T> newValue(*initialSolutionLevel);
00437         // for each level we itere ncycles
00438         for (plint iNcycle=0; iNcycle<ncycles*iLevel; ++iNcycle){
00439             // we use a V cycle with the current number of grids
00440             T error;
00441             error = multiGridVCycle<T>( *initialSolutionLevel, newValue, *rhs[iLevel], domain, iLevel);
00442             pcout << "\tError=" << error << std::endl;
00443             newValue.swap(*initialSolutionLevel);
00444         }
00445         
00446         // save the current level solution
00447         solutions[iLevel] = new MultiScalarField2D<T>(*initialSolutionLevel);
00448         delete initialSolutionLevel;        
00449     }
00450     
00451     // CLEAN-UP
00452     for (plint iLevel=0; iLevel<gridLevels; ++iLevel){
00453         delete rhs[iLevel];
00454     }
00455     
00456     return solutions;
00457 }
00458 
00459 template<typename T>
00460 std::vector<MultiScalarField2D<T>* > simpleMultiGrid( MultiScalarField2D<T>& initialValue, 
00461                                                     MultiScalarField2D<T>& originalRhs, Box2D const& domain,         
00462                                                     plint gridLevels )
00463 {
00464     PLB_PRECONDITION( gridLevels>=2 );
00465     
00466     std::vector<MultiScalarField2D<T>* > rhs(gridLevels);
00467     std::vector<MultiScalarField2D<T>* > solutions(gridLevels);
00468     
00469     // create all the right-hand sides from restriction over rhs
00470     generateRHS(originalRhs, rhs);
00471     
00472     // Initial solution computed exactly on the coarsest grid
00473     pcout << "Level: 0" << std::endl;
00474     pcout << "Size of the field : " << rhs[0]->getNx() << " x " << rhs[0]->getNy() << std::endl;
00475     MultiScalarField2D<T> initialSolution(*rhs[0]);
00476     MultiScalarField2D<T> initialValueCoarse(*rhs[0]);
00477     initialValueCoarse.reset();
00478     GaussSeidelSolver<T>(initialValueCoarse, initialSolution, *rhs[0],initialSolution.getBoundingBox());
00479     solutions[0] = new MultiScalarField2D<T>(initialSolution);
00480     
00481     // MAIN LOOP (at each time we add one more level)
00482     for (plint iLevel=1; iLevel<gridLevels; ++iLevel){
00483         pcout << "Level: " << iLevel << std::endl;
00484         
00485         // interpolate the iLevel-1 solution
00486         MultiScalarField2D<T>* initialSolutionLevel = new
00487                 MultiScalarField2D<T>( *refine<T>(*solutions[iLevel-1],1,-1,-1,-1) );
00488         MultiScalarField2D<T> newValue(*initialSolutionLevel);
00489         pcout << "Size of the field : " << newValue.getNx() << " x " << newValue.getNy() << std::endl;
00490         GaussSeidelSolver<T>(*initialSolutionLevel, newValue, *rhs[iLevel],initialSolutionLevel->getBoundingBox());
00491         
00492         // save the current level solution
00493         solutions[iLevel] = new MultiScalarField2D<T>(*initialSolutionLevel);
00494         delete initialSolutionLevel;
00495     }
00496     
00497     // CLEAN-UP
00498     for (plint iLevel=0; iLevel<gridLevels; ++iLevel){
00499         delete rhs[iLevel];
00500     }
00501     
00502     return solutions;
00503 }
00504 
00505 
00506 
00507 
00508 }  // namespace plb
00509 
00510 #endif  // FINITE_DIFFERENCE_WRAPPER_2D_HH