00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifndef LINEARPARABOLICPDESYSTEMWITHCOUPLEDODESYSTEMSOLVER_HPP_
00029 #define LINEARPARABOLICPDESYSTEMWITHCOUPLEDODESYSTEMSOLVER_HPP_
00030
00031 #include "AbstractAssemblerSolverHybrid.hpp"
00032 #include "AbstractDynamicLinearPdeSolver.hpp"
00033 #include "AbstractLinearParabolicPdeSystemForCoupledOdeSystem.hpp"
00034 #include "TetrahedralMesh.hpp"
00035 #include "BoundaryConditionsContainer.hpp"
00036 #include "AbstractOdeSystemForCoupledPdeSystem.hpp"
00037 #include "CvodeAdaptor.hpp"
00038 #include "BackwardEulerIvpOdeSolver.hpp"
00039 #include "VtkMeshWriter.hpp"
00040
00041 #include <boost/shared_ptr.hpp>
00042
00052 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM=ELEMENT_DIM, unsigned PROBLEM_DIM=1>
00053 class LinearParabolicPdeSystemWithCoupledOdeSystemSolver
00054 : public AbstractAssemblerSolverHybrid<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, NORMAL>,
00055 public AbstractDynamicLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>
00056 {
00057 private:
00058
00060 AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>* mpMesh;
00061
00063 AbstractLinearParabolicPdeSystemForCoupledOdeSystem<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>* mpPdeSystem;
00064
00066 std::vector<AbstractOdeSystemForCoupledPdeSystem*> mOdeSystemsAtNodes;
00067
00069 std::vector<double> mInterpolatedOdeStateVariables;
00070
00072 boost::shared_ptr<AbstractIvpOdeSolver> mpOdeSolver;
00073
00079 double mSamplingTimeStep;
00080
00082 bool mOdeSystemsPresent;
00083
00085 out_stream mpVtkMetaFile;
00086
00090 void WriteVtkResultsToFile();
00091
00102 c_matrix<double, PROBLEM_DIM*(ELEMENT_DIM+1), PROBLEM_DIM*(ELEMENT_DIM+1)> ComputeMatrixTerm(
00103 c_vector<double, ELEMENT_DIM+1>& rPhi,
00104 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1>& rGradPhi,
00105 ChastePoint<SPACE_DIM>& rX,
00106 c_vector<double,PROBLEM_DIM>& rU,
00107 c_matrix<double, PROBLEM_DIM, SPACE_DIM>& rGradU,
00108 Element<ELEMENT_DIM, SPACE_DIM>* pElement);
00109
00120 c_vector<double, PROBLEM_DIM*(ELEMENT_DIM+1)> ComputeVectorTerm(
00121 c_vector<double, ELEMENT_DIM+1>& rPhi,
00122 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1>& rGradPhi,
00123 ChastePoint<SPACE_DIM>& rX,
00124 c_vector<double,PROBLEM_DIM>& rU,
00125 c_matrix<double,PROBLEM_DIM,SPACE_DIM>& rGradU,
00126 Element<ELEMENT_DIM, SPACE_DIM>* pElement);
00127
00131 void ResetInterpolatedQuantities();
00132
00140 void IncrementInterpolatedQuantities(double phiI, const Node<SPACE_DIM>* pNode);
00141
00150 void InitialiseForSolve(Vec initialSolution=NULL);
00151
00160 void SetupLinearSystem(Vec currentSolution, bool computeMatrix);
00161
00162 public:
00163
00173 LinearParabolicPdeSystemWithCoupledOdeSystemSolver(TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>* pMesh,
00174 AbstractLinearParabolicPdeSystemForCoupledOdeSystem<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>* pPdeSystem,
00175 BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>* pBoundaryConditions,
00176 std::vector<AbstractOdeSystemForCoupledPdeSystem*> odeSystemsAtNodes=std::vector<AbstractOdeSystemForCoupledPdeSystem*>(),
00177 boost::shared_ptr<AbstractIvpOdeSolver> pOdeSolver=boost::shared_ptr<AbstractIvpOdeSolver>());
00178
00182 ~LinearParabolicPdeSystemWithCoupledOdeSystemSolver();
00183
00190 void PrepareForSetupLinearSystem(Vec currentPdeSolution);
00191
00197 void SetOutputDirectory(std::string outputDirectory);
00198
00204 void SetSamplingTimeStep(double samplingTimeStep);
00205
00210 void SolveAndWriteResultsToFile();
00211
00218 void WriteVtkResultsToFile(Vec solution, unsigned numTimeStepsElapsed);
00219 };
00220
00222
00224
00225 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00226 c_matrix<double, PROBLEM_DIM*(ELEMENT_DIM+1), PROBLEM_DIM*(ELEMENT_DIM+1)> LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::ComputeMatrixTerm(
00227 c_vector<double, ELEMENT_DIM+1>& rPhi,
00228 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1>& rGradPhi,
00229 ChastePoint<SPACE_DIM>& rX,
00230 c_vector<double,PROBLEM_DIM>& rU,
00231 c_matrix<double, PROBLEM_DIM, SPACE_DIM>& rGradU,
00232 Element<ELEMENT_DIM, SPACE_DIM>* pElement)
00233 {
00234 double timestep_inverse = PdeSimulationTime::GetPdeTimeStepInverse();
00235 c_matrix<double, PROBLEM_DIM*(ELEMENT_DIM+1), PROBLEM_DIM*(ELEMENT_DIM+1)> matrix_term = zero_matrix<double>(PROBLEM_DIM*(ELEMENT_DIM+1), PROBLEM_DIM*(ELEMENT_DIM+1));
00236
00237
00238 for (unsigned pde_index=0; pde_index<PROBLEM_DIM; pde_index++)
00239 {
00240 double this_dudt_coefficient = mpPdeSystem->ComputeDuDtCoefficientFunction(rX, pde_index);
00241 c_matrix<double, SPACE_DIM, SPACE_DIM> this_pde_diffusion_term = mpPdeSystem->ComputeDiffusionTerm(rX, pde_index, pElement);
00242 c_matrix<double, 1*(ELEMENT_DIM+1), 1*(ELEMENT_DIM+1)> this_stiffness_matrix =
00243 prod(trans(rGradPhi), c_matrix<double, SPACE_DIM, ELEMENT_DIM+1>(prod(this_pde_diffusion_term, rGradPhi)) )
00244 + timestep_inverse * this_dudt_coefficient * outer_prod(rPhi, rPhi);
00245
00246 for (unsigned i=0; i<ELEMENT_DIM+1; i++)
00247 {
00248 for (unsigned j=0; j<ELEMENT_DIM+1; j++)
00249 {
00250 matrix_term(i*PROBLEM_DIM + pde_index, j*PROBLEM_DIM + pde_index) = this_stiffness_matrix(i,j);
00251 }
00252 }
00253 }
00254 return matrix_term;
00255 }
00256
00257 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00258 c_vector<double, PROBLEM_DIM*(ELEMENT_DIM+1)> LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::ComputeVectorTerm(
00259 c_vector<double, ELEMENT_DIM+1>& rPhi,
00260 c_matrix<double, SPACE_DIM, ELEMENT_DIM+1>& rGradPhi,
00261 ChastePoint<SPACE_DIM>& rX,
00262 c_vector<double,PROBLEM_DIM>& rU,
00263 c_matrix<double,PROBLEM_DIM,SPACE_DIM>& rGradU,
00264 Element<ELEMENT_DIM, SPACE_DIM>* pElement)
00265 {
00266 double timestep_inverse = PdeSimulationTime::GetPdeTimeStepInverse();
00267 c_vector<double, PROBLEM_DIM*(ELEMENT_DIM+1)> vector_term = zero_vector<double>(PROBLEM_DIM*(ELEMENT_DIM+1));
00268
00269
00270 for (unsigned pde_index=0; pde_index<PROBLEM_DIM; pde_index++)
00271 {
00272 double this_dudt_coefficient = mpPdeSystem->ComputeDuDtCoefficientFunction(rX, pde_index);
00273 double this_source_term = mpPdeSystem->ComputeSourceTerm(rX, rU, mInterpolatedOdeStateVariables, pde_index);
00274 c_vector<double, ELEMENT_DIM+1> this_vector_term = (this_source_term + timestep_inverse*this_dudt_coefficient*rU(pde_index))* rPhi;
00275
00276 for (unsigned i=0; i<ELEMENT_DIM+1; i++)
00277 {
00278 vector_term(i*PROBLEM_DIM + pde_index) = this_vector_term(i);
00279 }
00280 }
00281
00282 return vector_term;
00283 }
00284
00285
00286 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00287 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::ResetInterpolatedQuantities()
00288 {
00289 mInterpolatedOdeStateVariables.clear();
00290
00291 if (mOdeSystemsPresent)
00292 {
00293 unsigned num_state_variables = mOdeSystemsAtNodes[0]->GetNumberOfStateVariables();
00294 mInterpolatedOdeStateVariables.resize(num_state_variables, 0.0);
00295 }
00296 }
00297
00298 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00299 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::IncrementInterpolatedQuantities(double phiI, const Node<SPACE_DIM>* pNode)
00300 {
00301 if (mOdeSystemsPresent)
00302 {
00303 unsigned num_state_variables = mOdeSystemsAtNodes[0]->GetNumberOfStateVariables();
00304
00305 for (unsigned i=0; i<num_state_variables; i++)
00306 {
00307 mInterpolatedOdeStateVariables[i] += phiI * mOdeSystemsAtNodes[pNode->GetIndex()]->rGetStateVariables()[i];
00308 }
00309 }
00310 }
00311
00312 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00313 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::InitialiseForSolve(Vec initialSolution)
00314 {
00315 if (this->mpLinearSystem == NULL)
00316 {
00317 unsigned preallocation = mpMesh->CalculateMaximumContainingElementsPerProcess() + ELEMENT_DIM;
00318 if (ELEMENT_DIM > 1)
00319 {
00320
00321 preallocation--;
00322 }
00323 preallocation *= PROBLEM_DIM;
00324
00325
00326
00327
00328
00329
00330 this->mpLinearSystem = new LinearSystem(initialSolution, preallocation);
00331 }
00332
00333 assert(this->mpLinearSystem);
00334 this->mpLinearSystem->SetMatrixIsSymmetric(true);
00335 this->mpLinearSystem->SetKspType("cg");
00336 }
00337
00338 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00339 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::SetupLinearSystem(Vec currentSolution, bool computeMatrix)
00340 {
00341 SetupGivenLinearSystem(currentSolution, computeMatrix, this->mpLinearSystem);
00342 }
00343
00344 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00345 LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::LinearParabolicPdeSystemWithCoupledOdeSystemSolver(
00346 TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>* pMesh,
00347 AbstractLinearParabolicPdeSystemForCoupledOdeSystem<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>* pPdeSystem,
00348 BoundaryConditionsContainer<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>* pBoundaryConditions,
00349 std::vector<AbstractOdeSystemForCoupledPdeSystem*> odeSystemsAtNodes,
00350 boost::shared_ptr<AbstractIvpOdeSolver> pOdeSolver)
00351 : AbstractAssemblerSolverHybrid<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM, NORMAL>(pMesh, pBoundaryConditions),
00352 AbstractDynamicLinearPdeSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>(pMesh),
00353 mpMesh(pMesh),
00354 mpPdeSystem(pPdeSystem),
00355 mOdeSystemsAtNodes(odeSystemsAtNodes),
00356 mpOdeSolver(pOdeSolver),
00357 mSamplingTimeStep(DOUBLE_UNSET),
00358 mOdeSystemsPresent(false)
00359 {
00360 this->mpBoundaryConditions = pBoundaryConditions;
00361
00362
00363
00364
00365
00366
00367 if (!mOdeSystemsAtNodes.empty())
00368 {
00369 mOdeSystemsPresent = true;
00370 assert(mOdeSystemsAtNodes.size() == mpMesh->GetNumNodes());
00371
00372
00373
00374
00375
00376 if (!mpOdeSolver)
00377 {
00378 #ifdef CHASTE_CVODE
00379 mpOdeSolver.reset(new CvodeAdaptor);
00380 #else
00381 mpOdeSolver.reset(new BackwardEulerIvpOdeSolver(mOdeSystemsAtNodes[0]->GetNumberOfStateVariables()));
00382 #endif //CHASTE_CVODE
00383 }
00384 }
00385 }
00386
00387 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00388 LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::~LinearParabolicPdeSystemWithCoupledOdeSystemSolver()
00389 {
00390 if (mOdeSystemsPresent)
00391 {
00392 for (unsigned i=0; i<mOdeSystemsAtNodes.size(); i++)
00393 {
00394 delete mOdeSystemsAtNodes[i];
00395 }
00396 }
00397 }
00398
00399 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00400 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::PrepareForSetupLinearSystem(Vec currentPdeSolution)
00401 {
00402 if (mOdeSystemsPresent)
00403 {
00404 double time = PdeSimulationTime::GetTime();
00405 double dt = PdeSimulationTime::GetPdeTimeStep();
00406
00407 ReplicatableVector soln_repl(currentPdeSolution);
00408 std::vector<double> current_soln_this_node(PROBLEM_DIM);
00409
00410
00411 for (unsigned node_index=0; node_index<mpMesh->GetNumNodes(); node_index++)
00412 {
00413
00414 for (unsigned pde_index=0; pde_index<PROBLEM_DIM; pde_index++)
00415 {
00416 double current_soln_this_pde_this_node = soln_repl[PROBLEM_DIM*node_index + pde_index];
00417 current_soln_this_node[pde_index] = current_soln_this_pde_this_node;
00418 }
00419
00420
00421 mOdeSystemsAtNodes[node_index]->SetPdeSolution(current_soln_this_node);
00422
00423
00424 mpOdeSolver->SolveAndUpdateStateVariable(mOdeSystemsAtNodes[node_index], time, time+dt, dt);
00425 }
00426 }
00427 }
00428
00429 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00430 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::SetOutputDirectory(std::string outputDirectory)
00431 {
00432 this->mOutputDirectory = outputDirectory;
00433 }
00434
00435 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00436 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::SetSamplingTimeStep(double samplingTimeStep)
00437 {
00438 assert(samplingTimeStep >= this->mIdealTimeStep);
00439 mSamplingTimeStep = samplingTimeStep;
00440 }
00441
00442 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00443 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::SolveAndWriteResultsToFile()
00444 {
00445
00446 if (this->mOutputDirectory == "")
00447 {
00448 EXCEPTION("SetOutputDirectory() must be called prior to SolveAndWriteResultsToFile()");
00449 }
00450 if (this->mTimesSet == false)
00451 {
00452 EXCEPTION("SetTimes() must be called prior to SolveAndWriteResultsToFile()");
00453 }
00454 if (this->mIdealTimeStep <= 0.0)
00455 {
00456 EXCEPTION("SetTimeStep() must be called prior to SolveAndWriteResultsToFile()");
00457 }
00458 if (mSamplingTimeStep == DOUBLE_UNSET)
00459 {
00460 EXCEPTION("SetSamplingTimeStep() must be called prior to SolveAndWriteResultsToFile()");
00461 }
00462 if (!this->mInitialCondition)
00463 {
00464 EXCEPTION("SetInitialCondition() must be called prior to SolveAndWriteResultsToFile()");
00465 }
00466
00467 #ifdef CHASTE_VTK
00468
00469 OutputFileHandler output_file_handler(this->mOutputDirectory, false);
00470 mpVtkMetaFile = output_file_handler.OpenOutputFile("results.pvd");
00471 *mpVtkMetaFile << "<?xml version=\"1.0\"?>\n";
00472 *mpVtkMetaFile << "<VTKFile type=\"Collection\" version=\"0.1\" byte_order=\"LittleEndian\" compressor=\"vtkZLibDataCompressor\">\n";
00473 *mpVtkMetaFile << " <Collection>\n";
00474
00475
00476 Vec initial_condition = this->mInitialCondition;
00477 WriteVtkResultsToFile(initial_condition, 0);
00478
00479
00480 TimeStepper stepper(this->mTstart, this->mTend, mSamplingTimeStep);
00481
00482
00483 while (!stepper.IsTimeAtEnd())
00484 {
00485
00486 this->SetTimes(stepper.GetTime(), stepper.GetNextTime());
00487
00488
00489 Vec soln = this->Solve();
00490
00491
00492 if (this->mInitialCondition != initial_condition)
00493 {
00494 VecDestroy(this->mInitialCondition);
00495 }
00496 this->mInitialCondition = soln;
00497
00498
00499 stepper.AdvanceOneTimeStep();
00500
00501
00502 WriteVtkResultsToFile(soln, stepper.GetTotalTimeStepsTaken());
00503 }
00504
00505
00506 if (this->mInitialCondition != initial_condition)
00507 {
00508 VecDestroy(this->mInitialCondition);
00509 }
00510 this->mInitialCondition = initial_condition;
00511
00512
00513 *mpVtkMetaFile << " </Collection>\n";
00514 *mpVtkMetaFile << "</VTKFile>\n";
00515 mpVtkMetaFile->close();
00516 #else //CHASTE_VTK
00517 #define COVERAGE_IGNORE // We can't test this in regular builds
00518 EXCEPTION("VTK is not installed and is required for this functionality");
00519 #undef COVERAGE_IGNORE
00520 #endif //CHASTE_VTK
00521 }
00522
00523 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM, unsigned PROBLEM_DIM>
00524 void LinearParabolicPdeSystemWithCoupledOdeSystemSolver<ELEMENT_DIM, SPACE_DIM, PROBLEM_DIM>::WriteVtkResultsToFile(Vec solution, unsigned numTimeStepsElapsed)
00525 {
00526 #ifdef CHASTE_VTK
00527
00528
00529 std::stringstream time;
00530 time << numTimeStepsElapsed;
00531 VtkMeshWriter<ELEMENT_DIM, SPACE_DIM> mesh_writer(this->mOutputDirectory, "results_"+time.str(), false);
00532
00533
00534
00535
00536
00537
00538 ReplicatableVector solution_repl(solution);
00539 unsigned num_nodes = mpMesh->GetNumNodes();
00540 for (unsigned pde_index=0; pde_index<PROBLEM_DIM; pde_index++)
00541 {
00542
00543 std::vector<double> pde_index_data;
00544 pde_index_data.resize(num_nodes, 0.0);
00545 for (unsigned node_index=0; node_index<num_nodes; node_index++)
00546 {
00547 pde_index_data[node_index] = solution_repl[PROBLEM_DIM*node_index + pde_index];
00548 }
00549
00550
00551 std::stringstream data_name;
00552 data_name << "PDE variable " << pde_index;
00553 mesh_writer.AddPointData(data_name.str(), pde_index_data);
00554 }
00555
00556 if (mOdeSystemsPresent)
00557 {
00558
00559
00560
00561
00562
00563
00564 std::vector<std::vector<double> > ode_data;
00565 unsigned num_odes = mOdeSystemsAtNodes[0]->rGetStateVariables().size();
00566 ode_data.resize(num_odes);
00567 for (unsigned ode_index=0; ode_index<num_odes; ode_index++)
00568 {
00569 ode_data[ode_index].resize(num_nodes, 0.0);
00570 }
00571
00572 for (unsigned node_index=0; node_index<num_nodes; node_index++)
00573 {
00574 std::vector<double> all_odes_this_node = mOdeSystemsAtNodes[node_index]->rGetStateVariables();
00575 for (unsigned i=0; i<num_odes; i++)
00576 {
00577 ode_data[i][node_index] = all_odes_this_node[i];
00578 }
00579 }
00580
00581 for (unsigned ode_index=0; ode_index<num_odes; ode_index++)
00582 {
00583 std::vector<double> ode_index_data = ode_data[ode_index];
00584
00585
00586 std::stringstream data_name;
00587 data_name << "ODE variable " << ode_index;
00588 mesh_writer.AddPointData(data_name.str(), ode_index_data);
00589 }
00590 }
00591
00592 mesh_writer.WriteFilesUsingMesh(*mpMesh);
00593 *mpVtkMetaFile << " <DataSet timestep=\"";
00594 *mpVtkMetaFile << numTimeStepsElapsed;
00595 *mpVtkMetaFile << "\" group=\"\" part=\"0\" file=\"results_";
00596 *mpVtkMetaFile << numTimeStepsElapsed;
00597 *mpVtkMetaFile << ".vtu\"/>\n";
00598 #endif // CHASTE_VTK
00599 }
00600
00601 #endif