void TestGenerateBasicRandomWithNoSpecifiedProliferativeCellType() throw(Exception) { // Create mesh TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_2_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create cells std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes()); // Test that cells were generated correctly TS_ASSERT_EQUALS(cells.size(), mesh.GetNumNodes()); for (unsigned i=0; i<cells.size(); i++) { TS_ASSERT_EQUALS(cells[i]->GetCellCycleModel()->GetDimension(), 2u); TS_ASSERT_EQUALS(cells[i]->GetCellProliferativeType()->IsType<StemCellProliferativeType>(), true); // Should lie between -24 and 0 double birth_time=cells[i]->GetBirthTime(); ///\todo Breaks Intel 10? TS_ASSERT_LESS_THAN_EQUALS(birth_time, 0.0); TS_ASSERT_LESS_THAN_EQUALS(-24.0, birth_time); } }
void TestGenerateBasicRandomWithFixedDurationGenerationBasedCellCycleModel() throw(Exception) { // Create mesh TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_2_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create cells MAKE_PTR(TransitCellProliferativeType, p_transit_type); std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasicRandom(cells, mesh.GetNumNodes(), p_transit_type); // Test that cells were generated correctly TS_ASSERT_EQUALS(cells.size(), mesh.GetNumNodes()); for (unsigned i=0; i<cells.size(); i++) { // Should lie between -24 and 0 TS_ASSERT_LESS_THAN_EQUALS(cells[i]->GetBirthTime(), 0.0); TS_ASSERT_LESS_THAN_EQUALS(-24.0, cells[i]->GetBirthTime()); TS_ASSERT_EQUALS(cells[i]->GetCellCycleModel()->GetDimension(), 2u); TS_ASSERT_EQUALS(cells[i]->GetCellProliferativeType(), p_transit_type); } // Test exact random numbers as test re-seeds random number generator. TS_ASSERT_DELTA(cells[0]->GetBirthTime(), -7.1141, 1e-4); TS_ASSERT_DELTA(cells[1]->GetBirthTime(), -10.1311, 1e-4); TS_ASSERT_DELTA(cells[2]->GetBirthTime(), -10.2953, 1e-4); }
void TestBathIntracellularStimulation() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(10.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath1d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_1d"); c_vector<double,1> centre; centre(0) = 0.5; BathCellFactory<1> cell_factory(-1e6, centre); // stimulates x=0.5 node BidomainWithBathProblem<1> bidomain_problem( &cell_factory ); TrianglesMeshReader<1,1> reader("mesh/test/data/1D_0_to_1_100_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(reader); // set the x<0.25 and x>0.75 regions as the bath region for(unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; if( (x<0.25) || (x>0.75) ) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test V = 0 for all bath nodes for(unsigned i=0; i<mesh.GetNumNodes(); i++) { if(HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } } // test symmetry of V and phi_e for(unsigned i=0; i<=(mesh.GetNumNodes()-1)/2; i++) { unsigned opposite = mesh.GetNumNodes()-i-1; TS_ASSERT_DELTA(sol_repl[2*i], sol_repl[2*opposite], 2e-3); // V TS_ASSERT_DELTA(sol_repl[2*i+1], sol_repl[2*opposite+1], 2e-3); // phi_e } // a couple of hardcoded values TS_ASSERT_DELTA(sol_repl[2*50], 3.7684, 1e-3); TS_ASSERT_DELTA(sol_repl[2*70], 5.1777, 1e-3); }
/** * Simple Parabolic PDE u' = del squared u * * With u = 0 on the boundaries of the unit cube. Subject to the initial * condition u(0,x,y,z)=sin( PI x)sin( PI y)sin( PI z). */ void TestSimpleLinearParabolicSolver3DZeroDirich() { // read mesh on [0,1]x[0,1]x[0,1] TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Instantiate PDE object HeatEquation<3> pde; // Boundary conditions - zero dirichlet everywhere on boundary BoundaryConditionsContainer<3,3,1> bcc; bcc.DefineZeroDirichletOnMeshBoundary(&mesh); // Solver SimpleLinearParabolicSolver<3,3> solver(&mesh,&pde,&bcc); /* * Choose initial condition sin(x*pi)*sin(y*pi)*sin(z*pi) as * this is an eigenfunction of the heat equation. */ std::vector<double> init_cond(mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double y = mesh.GetNode(i)->GetPoint()[1]; double z = mesh.GetNode(i)->GetPoint()[2]; init_cond[i] = sin(x*M_PI)*sin(y*M_PI)*sin(z*M_PI); } Vec initial_condition = PetscTools::CreateVec(init_cond); double t_end = 0.1; solver.SetTimes(0, t_end); solver.SetTimeStep(0.001); solver.SetInitialCondition(initial_condition); Vec result = solver.Solve(); ReplicatableVector result_repl(result); // Check solution is u = e^{-3*t*pi*pi} sin(x*pi)*sin(y*pi)*sin(z*pi), t=0.1 for (unsigned i=0; i<result_repl.GetSize(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double y = mesh.GetNode(i)->GetPoint()[1]; double z = mesh.GetNode(i)->GetPoint()[2]; double u = exp(-3*t_end*M_PI*M_PI)*sin(x*M_PI)*sin(y*M_PI)*sin(z*M_PI); TS_ASSERT_DELTA(result_repl[i], u, 0.1); } PetscTools::Destroy(initial_condition); PetscTools::Destroy(result); }
void TestDistancesToFaceDumb() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_21_nodes_side/Cube21"); // 5x5x5mm cube (internode distance = 0.25mm) TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), 9261u); // 21x21x21 nodes TS_ASSERT_EQUALS(mesh.GetNumElements(), 48000u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 4800u); DistributedTetrahedralMesh<3,3> parallel_mesh(DistributedTetrahedralMeshPartitionType::DUMB); // No reordering parallel_mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_EQUALS(parallel_mesh.GetNumNodes(), 9261u); // 21x21x21 nodes TS_ASSERT_EQUALS(parallel_mesh.GetNumElements(), 48000u); TS_ASSERT_EQUALS(parallel_mesh.GetNumBoundaryElements(), 4800u); std::vector<unsigned> map_left; for (unsigned index=0; index<mesh.GetNumNodes(); index++) { // Get the nodes at the left face of the cube if (mesh.GetNode(index)->rGetLocation()[0] + 0.25 < 1e-6) { map_left.push_back(index); } } TS_ASSERT_EQUALS(map_left.size(), 21u*21u); DistanceMapCalculator<3,3> distance_calculator(mesh); std::vector<double> distances; distance_calculator.ComputeDistanceMap(map_left, distances); DistanceMapCalculator<3,3> parallel_distance_calculator(parallel_mesh); std::vector<double> parallel_distances; parallel_distance_calculator.ComputeDistanceMap(map_left, parallel_distances); TS_ASSERT_EQUALS(distance_calculator.mRoundCounter, 1u); TS_ASSERT_DELTA(parallel_distance_calculator.mRoundCounter, 2u, 1u);// 1 2 or 3 for (unsigned index=0; index<distances.size(); index++) { // The distance should be equal to the x-coordinate of the point (minus the offset of the left face of the cube) c_vector<double, 3> node = mesh.GetNode(index)->rGetLocation(); TS_ASSERT_DELTA(distances[index], node[0]+0.25,1e-11); TS_ASSERT_DELTA(parallel_distances[index], node[0]+0.25,1e-11); } }
void Test2DMeshRotation() { TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/2D_0_to_1mm_200_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); double angle = M_PI; mesh.Rotate(angle); TetrahedralMesh<2,2> original_mesh; original_mesh.ConstructFromMeshReader(mesh_reader); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // Find new coordinates of the translated node Node<2>* p_node = mesh.GetNode(i); ChastePoint<2> new_coordinate = p_node->GetPoint(); // Get original node Node<2>* p_original_node = original_mesh.GetNode(i); ChastePoint<2> original_coordinate = p_original_node->GetPoint(); // Run a test to make sure the node has gone to the correct place TS_ASSERT_DELTA(original_coordinate[0], -new_coordinate[0], 1e-5); TS_ASSERT_DELTA(original_coordinate[1], -new_coordinate[1], 1e-5); } // Check volume conservation double mesh_volume = mesh.GetVolume(); double original_mesh_volume = original_mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume, original_mesh_volume, 1e-5); }
void TestRefreshMeshByScaling() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_DELTA(mesh.GetVolume(), 1.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 6.0, 1e-6); // Change coordinates for (unsigned i=0; i<mesh.GetNumNodes(); i++) { Node<3>* p_node = mesh.GetNode(i); ChastePoint<3> point = p_node->GetPoint(); point.SetCoordinate(0, point[0]*2.0); point.SetCoordinate(1, point[1]*2.0); point.SetCoordinate(2, point[2]*2.0); p_node->SetPoint(point); } mesh.RefreshMesh(); TS_ASSERT_DELTA(mesh.GetVolume(), 8.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 24.0, 1e-6); }
void TestXaxisRotation3DWithHomogeneousUblas() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_DELTA(mesh.GetVolume(), 1.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 6.0, 1e-6); // Change coordinates c_matrix<double, 4, 4> x_rotation_matrix = identity_matrix<double>(4); double theta = M_PI/2; x_rotation_matrix(1,1) = cos(theta); x_rotation_matrix(1,2) = sin(theta); x_rotation_matrix(2,1) = -sin(theta); x_rotation_matrix(2,2) = cos(theta); ChastePoint<3> corner_before = mesh.GetNode(6)->GetPoint(); TS_ASSERT_EQUALS(corner_before[0], 1.0); TS_ASSERT_EQUALS(corner_before[1], 1.0); TS_ASSERT_EQUALS(corner_before[2], 1.0); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { Node<3>* p_node = mesh.GetNode(i); ChastePoint<3> point = p_node->GetPoint(); c_vector<double, 4> point_location; point_location[0] = point[0]; point_location[1] = point[1]; point_location[2] = point[2]; point_location[3] = 1.0; c_vector<double, 4> new_point_location = prod(x_rotation_matrix, point_location); TS_ASSERT_EQUALS(new_point_location[3], 1.0); point.SetCoordinate(0, new_point_location[0]); point.SetCoordinate(1, new_point_location[1]); point.SetCoordinate(2, new_point_location[2]); p_node->SetPoint(point); } ChastePoint<3> corner_after = mesh.GetNode(6)->GetPoint(); TS_ASSERT_EQUALS(corner_after[0], 1.0); TS_ASSERT_EQUALS(corner_after[1], 1.0); TS_ASSERT_DELTA(corner_after[2], -1.0, 1e-7); mesh.RefreshMesh(); TS_ASSERT_DELTA(mesh.GetVolume(), 1.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 6.0, 1e-6); }
void TestReadMeshes(void) throw(Exception) { { READER_2D reader("mesh/test/data/square_4_elements_gmsh.msh"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), 5u); TS_ASSERT_EQUALS(mesh.GetNumElements(), 4u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 4u); } { READER_3D reader("mesh/test/data/simple_cube_gmsh.msh"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), 14u); TS_ASSERT_EQUALS(mesh.GetNumElements(), 24u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 24u); } { READER_2D reader("mesh/test/data/quad_square_4_elements_gmsh.msh",2,2); QuadraticMesh<2> mesh; mesh.ConstructFromMeshReader(reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), 13u); TS_ASSERT_EQUALS(mesh.GetNumElements(), 4u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 4u); } { READER_3D reader("mesh/test/data/quad_cube_gmsh.msh",2,2); QuadraticMesh<3> mesh; mesh.ConstructFromMeshReader(reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), 63u); TS_ASSERT_EQUALS(mesh.GetNumElements(), 24u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 24u); } }
void Test3dBathIntracellularStimulation() { HeartConfig::Instance()->SetSimulationDuration(1); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath3d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_3d"); c_vector<double,3> centre; centre(0) = 0.05; centre(1) = 0.05; centre(2) = 0.05; BathCellFactory<3> cell_factory(-2.5e7, centre); // stimulates x=0.05 node BidomainProblem<3> bidomain_problem( &cell_factory, true ); TetrahedralMesh<3,3> mesh; mesh.ConstructRegularSlabMesh(0.01, 0.1, 0.1, 0.1); // Set everything outside a central sphere (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; double z = mesh.GetElement(i)->CalculateCentroid()[2]; if (sqrt((x-0.05)*(x-0.05) + (y-0.05)*(y-0.05) + (z-0.05)*(z-0.05)) > 0.04) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test V = 0 for all bath nodes for (unsigned i=0; i<mesh.GetNumNodes(); i++) { if (HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } } // a hardcoded value TS_ASSERT_DELTA(sol_repl[2*404], 39.6833, 1e-3); }
void TestGenerateBasicWithFixedDurationGenerationBasedCellCycleModel() throw(Exception) { // Create mesh TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_2_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create cells std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasic(cells, mesh.GetNumNodes()); // Test that cells were generated correctly TS_ASSERT_EQUALS(cells.size(), mesh.GetNumNodes()); for (unsigned i=0; i<cells.size(); i++) { TS_ASSERT_DELTA(cells[i]->GetBirthTime(), -(double)(i), 1e-9); TS_ASSERT_EQUALS(cells[i]->GetCellCycleModel()->GetDimension(), 2u); } // Test with extra input argument std::vector<unsigned> location_indices; location_indices.push_back(2); location_indices.push_back(7); location_indices.push_back(9); std::vector<CellPtr> cells2; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator2; cells_generator2.GenerateBasic(cells2, 3, location_indices); TS_ASSERT_EQUALS(cells2.size(), 3u); TS_ASSERT_DELTA(cells2[0]->GetBirthTime(), -2.0, 1e-4); TS_ASSERT_DELTA(cells2[1]->GetBirthTime(), -7.0, 1e-4); TS_ASSERT_DELTA(cells2[2]->GetBirthTime(), -9.0, 1e-4); }
void Test3DMeshTranslationWithUblasMethod() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Translations - add a constant vector to each node c_vector<double,3> displacement; displacement(0) = 1; displacement(1) = 1; displacement(2) = 1; // Translate the mesh along the vector displacement mesh.Translate(displacement); TetrahedralMesh<3,3> original_mesh; original_mesh.ConstructFromMeshReader(mesh_reader); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // Find new coordinates of the translated node Node<3>* p_node = mesh.GetNode(i); ChastePoint<3> new_coordinate = p_node->GetPoint(); // Get original node Node<3>* p_original_node = original_mesh.GetNode(i); ChastePoint<3> original_coordinate = p_original_node->GetPoint(); // Run a test to make sure the node has gone to the correct place TS_ASSERT_DELTA(original_coordinate[0] + displacement[0], new_coordinate[0], 1e-5); TS_ASSERT_DELTA(original_coordinate[1] + displacement[1], new_coordinate[1], 1e-5); TS_ASSERT_DELTA(original_coordinate[2] + displacement[2], new_coordinate[2], 1e-5); } // Check volume conservation double mesh_volume = mesh.GetVolume(); double original_mesh_volume = original_mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume, original_mesh_volume, 1e-5); }
std::vector<unsigned> NonlinearElasticityTools<DIM>::GetNodesByComponentValue(TetrahedralMesh<DIM,DIM>& rMesh, unsigned component, double value) { std::vector<unsigned> fixed_nodes; double tol = 1e-8; for (unsigned i=0; i<rMesh.GetNumNodes(); i++) { if ( fabs(rMesh.GetNode(i)->rGetLocation()[component] - value)<1e-8) { fixed_nodes.push_back(i); } } if (fixed_nodes.size() == 0) { EXCEPTION("Could not find any nodes on requested surface (note: tolerance = "<<tol<<")"); } return fixed_nodes; }
void Test3DAngleAxisRotation() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); c_vector<double,3> axis; axis(0) = 1; axis(1) = 0; axis(2) = 0; double angle = M_PI; mesh.Rotate(axis, angle); TetrahedralMesh<3,3> original_mesh; original_mesh.ConstructFromMeshReader(mesh_reader); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { Node<3>* p_node = mesh.GetNode(i); ChastePoint<3> new_coordinate = p_node->GetPoint(); // Get original node Node<3>* p_original_node = original_mesh.GetNode(i); ChastePoint<3> original_coordinate = p_original_node->GetPoint(); // Run a test to make sure the node has gone to the correct place TS_ASSERT_DELTA(original_coordinate[0], new_coordinate[0], 1e-5); TS_ASSERT_DELTA(original_coordinate[1], -new_coordinate[1], 1e-5); TS_ASSERT_DELTA(original_coordinate[2], -new_coordinate[2], 1e-5); } // Check volume conservation double mesh_volume = mesh.GetVolume(); double original_mesh_volume = original_mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume, original_mesh_volume, 1e-5); }
void TestRemeshFullTree() throw(Exception) { TrianglesMeshReader<1,3> reader("lung/test/data/TestSubject002"); TetrahedralMesh<1,3> mesh; mesh.ConstructFromMeshReader(reader); AirwayPropertiesCalculator calculator(mesh, 0); TS_ASSERT_EQUALS( mesh.GetNumNodes(), 136625u); TS_ASSERT_EQUALS( mesh.GetNumElements(), 136624u); //Create remesher object AirwayRemesher remesher(mesh, 0u); MutableMesh<1,3> output_mesh_one; remesher.Remesh(output_mesh_one, calculator.GetBranches()[0]->GetPoiseuilleResistance()*1e7); //Key the tolerance relative to the trachea TS_ASSERT_EQUALS( output_mesh_one.GetNumNodes(), 168045u); TS_ASSERT_EQUALS( output_mesh_one.GetNumElements(), 168044u); // //To visualise // // VtkMeshWriter<1,3> writer("TestAirwayRemesher", "Novartis002_remeshed"); // std::vector<double> radii(output_mesh_one.GetNumElements()); // // for(TetrahedralMesh<1,3>::ElementIterator iter = output_mesh_one.GetElementIteratorBegin(); // iter != output_mesh_one.GetElementIteratorEnd(); // ++iter) // { // radii[iter->GetIndex()] = iter->GetAttribute(); // } // // writer.AddCellData("radii", radii); // writer.WriteFilesUsingMesh(output_mesh_one); // // TrianglesMeshWriter<1,3> writer2("TestAirwayRemesher", "Novartis002_remeshed", false); // writer2.WriteFilesUsingMesh(output_mesh_one); }
// this method loads the output file from the previous method and computes the activation // time (defined as the time V becomes positive) for each node. void ConvertToActivationMap(double h, double dt, bool useSvi) { //TetrahedralMesh<3,3> mesh1; //double h1=0.01; // 0.01, 0.02, 0.05 //mesh1.ConstructRegularSlabMesh(h1, 2.0, 0.7, 0.3); //MeshalyzerMeshWriter<3,3> writer("Mesh0.01", "mesh01"); //writer.WriteFilesUsingMesh(mesh1); TetrahedralMesh<3,3> mesh; double printing_dt=0.1; mesh.ConstructRegularSlabMesh(h, 2.0, 0.7, 0.3); std::stringstream input_dir; input_dir << "Benchmark" << "_h" << h << "_dt" << dt; Hdf5DataReader reader(input_dir.str(),"results"); unsigned num_timesteps = reader.GetUnlimitedDimensionValues().size(); DistributedVectorFactory factory(mesh.GetNumNodes()); Vec voltage = factory.CreateVec(); std::vector<double> activation_times(mesh.GetNumNodes(), -1.0); std::vector<double> last_negative_voltage(mesh.GetNumNodes(), 1.0); for(unsigned timestep=0; timestep<num_timesteps; timestep++) { reader.GetVariableOverNodes(voltage, "V", timestep); ReplicatableVector voltage_repl(voltage); for(unsigned i=0; i<mesh.GetNumNodes(); i++) { double V = voltage_repl[i]; if(V > 0 && activation_times[i] < 0.0) { double old = last_negative_voltage[i]; assert(old < 0); activation_times[i] = (timestep-V/(V-old))*printing_dt; } else if (V<=0) { last_negative_voltage[i]=V; } } } OutputFileHandler handler("ActivationMaps", false); if (PetscTools::AmMaster() == false) { return; } //Only master proceeds to write c_vector<double, 3> top_corner; top_corner[0] = 2.0; top_corner[1] = 0.7; top_corner[2] = 0.3; c_vector<double, 3> unit_diagonal = top_corner/norm_2(top_corner); std::stringstream output_file1; output_file1 << "diagonal" << "_h" << h << "_dt" << dt; if (useSvi) { output_file1 << "_svi.dat"; } else { output_file1 << "_ici.dat"; } out_stream p_diag_file = handler.OpenOutputFile(output_file1.str()); for(unsigned i=0; i<mesh.GetNumNodes(); i++) { c_vector<double, 3> position = mesh.GetNode(i)->rGetLocation(); c_vector<double, 3> projected_diagonal = unit_diagonal*inner_prod(unit_diagonal, position); double off_diagonal = norm_2(position - projected_diagonal); if (off_diagonal < h/3) { double distance = norm_2(position); (*p_diag_file) << distance<<"\t"<< activation_times[i]<<"\t"<<off_diagonal<<"\n"; if( fabs(position[0]-2.0) < 1e-8) { std::cout << "h, dt = " << h << ", " << dt << "\n\t"; std::cout << "activation_times[" << i << "] = " << activation_times[i] << "\n"; } } } p_diag_file->close(); std::stringstream output_file; output_file << "activation" << "_h" << h << "_dt" << dt << ".dat"; out_stream p_file = handler.OpenOutputFile(output_file.str()); for(unsigned i=0; i<activation_times.size(); i++) { *p_file << activation_times[i] << "\n"; } p_file->close(); for(unsigned i=0; i<activation_times.size(); i++) { if(activation_times[i] < 0.0) { std::cout << "\n\n\n**Some nodes unactivated**\n\n\n"; output_file << "__error"; out_stream p_file2 = handler.OpenOutputFile(output_file.str()); p_file2->close(); return; } } }
/* * == Test 2: Solving a linear parabolic PDE == * * Now we solve a parabolic PDE. We choose a simple problem so that the code changes * needed from the elliptic case are clearer. We will solve * du/dt = div(grad u) + u, in 3d, with boundary conditions u=1 on the boundary, and initial * conditions u=1. * */ void TestSolvingParabolicPde() throw(Exception) { /* Create a 10 by 10 by 10 mesh in 3D, this time using the {{{ConstructRegularSlabMesh}}} method * on the mesh. The first parameter is the cartesian space-step and the other three parameters are the width, height and depth of the mesh.*/ TetrahedralMesh<3,3> mesh; mesh.ConstructRegularSlabMesh(0.1, 1.0, 1.0, 1.0); /* Our PDE object should be a class that is derived from the {{{AbstractLinearParabolicPde}}}. * We could write it ourselves as in the previous test, but since the PDE we want to solve is * so simple, it has already been defined (look it up! - it is located in pde/test/pdes). */ HeatEquationWithSourceTerm<3> pde; /* Create a new boundary conditions container and specify u=1.0 on the boundary. */ BoundaryConditionsContainer<3,3,1> bcc; bcc.DefineConstantDirichletOnMeshBoundary(&mesh, 1.0); /* Create an instance of the solver, passing in the mesh, pde and boundary conditions. */ SimpleLinearParabolicSolver<3,3> solver(&mesh,&pde,&bcc); /* For parabolic problems, initial conditions are also needed. The solver will expect * a PETSc vector, where the i-th entry is the initial solution at node i, to be passed * in. To create this PETSc {{{Vec}}}, we will use a helper function in the {{{PetscTools}}} * class to create a {{{Vec}}} of size num_nodes, with each entry set to 1.0. Then we * set the initial condition on the solver. */ Vec initial_condition = PetscTools::CreateAndSetVec(mesh.GetNumNodes(), 1.0); solver.SetInitialCondition(initial_condition); /* Next define the start time, end time, and timestep, and set them. */ double t_start = 0; double t_end = 1; double dt = 0.01; solver.SetTimes(t_start, t_end); solver.SetTimeStep(dt); /* HOW_TO_TAG PDE * Output results to file for time-dependent PDE solvers */ /* When we call Solve() below we will just get the solution at the final time. If we want * to have intermediate solutions written to file, we do the following. We start by * specifying an output directory and filename prefix for our results file: */ solver.SetOutputDirectoryAndPrefix("ParabolicSolverTutorial","results"); /* When an output directory has been specified, the solver writes output in HDF5 format. To * convert this to another output format, we call the relevant method. Here, we convert * the output to plain text files (VTK or cmgui formats are also possible). We also say how * often to write the data, telling the solver to output results to file every tenth timestep. * The solver will create one file for each variable (in this case there is only one variable) * and for each time, so for example, the file * `results_Variable_0_10` is the results for u, over all nodes, at the 11th printed time. * Have a look in the output directory after running the test. (For comments on visualising the data in * matlab or octave, see the end of the tutorial UserTutorials/WritingPdeSolvers.) */ solver.SetOutputToTxt(true); solver.SetPrintingTimestepMultiple(10); /* Now we can solve the problem. The {{{Vec}}} that is returned can be passed into a * {{{ReplicatableVector}}} as before. */ Vec solution = solver.Solve(); ReplicatableVector solution_repl(solution); /* Let's also solve the equivalent static PDE, i.e. set du/dt=0, so 0=div(gradu) + u. This * is easy, as the PDE class has already been defined. */ SimplePoissonEquation<3,3> static_pde; SimpleLinearEllipticSolver<3,3> static_solver(&mesh, &static_pde, &bcc); Vec static_solution = static_solver.Solve(); ReplicatableVector static_solution_repl(static_solution); /* We can now compare the solution of the parabolic PDE at t=1 with the static solution, * to see if the static equilibrium solution was reached in the former. (Ideally we should * compute some relative error, but we just compute an absolute error for simplicity.) */ for (unsigned i=0; i<static_solution_repl.GetSize(); i++) { TS_ASSERT_DELTA( solution_repl[i], static_solution_repl[i], 1e-3); } /* All PETSc vectors should be destroyed when they are no longer needed. */ PetscTools::Destroy(initial_condition); PetscTools::Destroy(solution); PetscTools::Destroy(static_solution); }
VoltageInterpolaterOntoMechanicsMesh<DIM>::VoltageInterpolaterOntoMechanicsMesh( TetrahedralMesh<DIM,DIM>& rElectricsMesh, QuadraticMesh<DIM>& rMechanicsMesh, std::vector<std::string>& rVariableNames, std::string directory, std::string inputFileNamePrefix) { // Read the data from the HDF5 file Hdf5DataReader reader(directory,inputFileNamePrefix); unsigned num_timesteps = reader.GetUnlimitedDimensionValues().size(); // set up the elements and weights for the coarse nodes in the fine mesh FineCoarseMeshPair<DIM> mesh_pair(rElectricsMesh, rMechanicsMesh); mesh_pair.SetUpBoxesOnFineMesh(); mesh_pair.ComputeFineElementsAndWeightsForCoarseNodes(true); assert(mesh_pair.rGetElementsAndWeights().size()==rMechanicsMesh.GetNumNodes()); // create and setup a writer Hdf5DataWriter* p_writer = new Hdf5DataWriter(*rMechanicsMesh.GetDistributedVectorFactory(), directory, "voltage_mechanics_mesh", false, //don't clean false); std::vector<int> columns_id; for (unsigned var_index = 0; var_index < rVariableNames.size(); var_index++) { std::string var_name = rVariableNames[var_index]; columns_id.push_back( p_writer->DefineVariable(var_name,"mV") ); } p_writer->DefineUnlimitedDimension("Time","msecs", num_timesteps); p_writer->DefineFixedDimension( rMechanicsMesh.GetNumNodes() ); p_writer->EndDefineMode(); assert(columns_id.size() == rVariableNames.size()); // set up a vector to read into DistributedVectorFactory factory(rElectricsMesh.GetNumNodes()); Vec voltage = factory.CreateVec(); std::vector<double> interpolated_voltages(rMechanicsMesh.GetNumNodes()); Vec voltage_coarse = NULL; for(unsigned time_step=0; time_step<num_timesteps; time_step++) { for (unsigned var_index = 0; var_index < rVariableNames.size(); var_index++) { std::string var_name = rVariableNames[var_index]; // read reader.GetVariableOverNodes(voltage, var_name, time_step); ReplicatableVector voltage_repl(voltage); // interpolate for(unsigned i=0; i<mesh_pair.rGetElementsAndWeights().size(); i++) { double interpolated_voltage = 0; Element<DIM,DIM>& element = *(rElectricsMesh.GetElement(mesh_pair.rGetElementsAndWeights()[i].ElementNum)); for(unsigned node_index = 0; node_index<element.GetNumNodes(); node_index++) { unsigned global_node_index = element.GetNodeGlobalIndex(node_index); interpolated_voltage += voltage_repl[global_node_index]*mesh_pair.rGetElementsAndWeights()[i].Weights(node_index); } interpolated_voltages[i] = interpolated_voltage; } if(voltage_coarse!=NULL) { PetscTools::Destroy(voltage_coarse); } voltage_coarse = PetscTools::CreateVec(interpolated_voltages); // write p_writer->PutVector(columns_id[var_index], voltage_coarse); } p_writer->PutUnlimitedVariable(time_step); p_writer->AdvanceAlongUnlimitedDimension(); } if(voltage_coarse!=NULL) { PetscTools::Destroy(voltage); PetscTools::Destroy(voltage_coarse); } // delete to flush delete p_writer; // Convert the new data to CMGUI format. // alter the directory in HeartConfig as that is where Hdf5ToCmguiConverter decides // where to output std::string config_directory = HeartConfig::Instance()->GetOutputDirectory(); HeartConfig::Instance()->SetOutputDirectory(directory); Hdf5ToCmguiConverter<DIM,DIM> converter(FileFinder(directory, RelativeTo::ChasteTestOutput), "voltage_mechanics_mesh", &rMechanicsMesh, false); HeartConfig::Instance()->SetOutputDirectory(config_directory); }
void TestGeneralConvolution3DWithHomogeneousUblas() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_DELTA(mesh.GetVolume(), 1.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 6.0, 1e-6); // Change coordinates c_matrix<double, 4, 4> x_rotation_matrix = identity_matrix<double>(4); c_matrix<double, 4, 4> y_rotation_matrix = identity_matrix<double>(4); c_matrix<double, 4, 4> z_rotation_matrix = identity_matrix<double>(4); c_matrix<double, 4, 4> translation_matrix = identity_matrix<double>(4); double theta = 0.7; double phi = 0.3; double psi = 1.4; x_rotation_matrix(1,1) = cos(theta); x_rotation_matrix(1,2) = sin(theta); x_rotation_matrix(2,1) = -sin(theta); x_rotation_matrix(2,2) = cos(theta); y_rotation_matrix(0,0) = cos(phi); y_rotation_matrix(0,2) = -sin(phi); y_rotation_matrix(2,0) = sin(phi); y_rotation_matrix(2,2) = cos(phi); z_rotation_matrix(0,0) = cos(psi); z_rotation_matrix(0,1) = sin(psi); z_rotation_matrix(1,0) = -sin(psi); z_rotation_matrix(1,1) = cos(psi); translation_matrix(0,3) = 2.3; translation_matrix(1,3) = 3.1; translation_matrix(2,3) = 1.7; /* Note: because we are using column-major vectors this tranformation: RotX(theta) . RotY(phi) . RotZ(psi) . Trans(...) is actually being applied right-to-left See test below. */ c_matrix<double, 4, 4> transformation_matrix = prod (x_rotation_matrix, y_rotation_matrix); transformation_matrix = prod (transformation_matrix, z_rotation_matrix); transformation_matrix = prod (transformation_matrix, translation_matrix); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { Node<3>* p_node = mesh.GetNode(i); ChastePoint<3> point = p_node->GetPoint(); c_vector<double, 4> point_location; point_location[0] = point[0]; point_location[1] = point[1]; point_location[2] = point[2]; point_location[3] = 1.0; c_vector<double, 4> new_point_location = prod(transformation_matrix, point_location); TS_ASSERT_EQUALS(new_point_location[3], 1.0); point.SetCoordinate(0,new_point_location[0]); point.SetCoordinate(1,new_point_location[1]); point.SetCoordinate(2,new_point_location[2]); p_node->SetPoint(point); } mesh.RefreshMesh(); TS_ASSERT_DELTA(mesh.GetVolume(), 1.0, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), 6.0, 1e-6); ChastePoint<3> corner_after = mesh.GetNode(6)->GetPoint(); TS_ASSERT_DELTA(corner_after[0], 3.59782, 5e-5); TS_ASSERT_DELTA(corner_after[1], 0.583418, 5e-5); TS_ASSERT_DELTA(corner_after[2], 4.65889, 5e-5); // Write to file TrianglesMeshWriter<3,3> mesh_writer("","TransformedMesh"); mesh_writer.WriteFilesUsingMesh(mesh); /* * Now try tetview /tmp/chaste/testoutput/TransformedMesh */ }
// see #1061 void Test3dBathExtracellularStimulusOneEdgeGroundedOnOppositeEdge() { HeartConfig::Instance()->SetSimulationDuration(6); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath3dExtraStimGrounded"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_3d"); HeartConfig::Instance()->SetOdeTimeStep(0.005); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,3> centre; centre(0) = 0.1; centre(1) = 0.1; centre(2) = 0.1; BathCellFactory<3> cell_factory( 0.0, centre); BidomainProblem<3> bidomain_problem( &cell_factory, true ); TrianglesMeshReader<3,3> reader("mesh/test/data/cube_2mm_1016_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(reader); // Set everything outside a central sphere (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; double z = mesh.GetElement(i)->CalculateCentroid()[2]; if (sqrt((x-0.1)*(x-0.1) + (y-0.1)*(y-0.1) + (z-0.1)*(z-0.1)) > 0.03) { mesh.GetElement(i)->SetAttribute(1); } } bidomain_problem.SetMesh(&mesh); //boundary flux for Phi_e double boundary_flux = -4e3; double duration = 2.5; //ms HeartConfig::Instance()->SetElectrodeParameters(true,0,boundary_flux, 0.0, duration); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // test V = 0 for all bath nodes if (HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0) { ap_triggered = true; } } TS_ASSERT(ap_triggered); }
void TestHeatEquationWithSourceWithCoupledOdeSystemIn1dWithZeroNeumann() { // Create mesh of domain [0,1] TrianglesMeshReader<1,1> mesh_reader("mesh/test/data/1D_0_to_1_100_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create PDE system object HeatEquationWithSourceForCoupledOdeSystem<1> pde; // Define zero Neumann boundary conditions BoundaryConditionsContainer<1,1,1> bcc; ConstBoundaryCondition<1>* p_boundary_condition = new ConstBoundaryCondition<1>(0.0); TetrahedralMesh<1,1>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin(); bcc.AddNeumannBoundaryCondition(*iter, p_boundary_condition); iter = mesh.GetBoundaryElementIteratorEnd(); iter--; bcc.AddNeumannBoundaryCondition(*iter, p_boundary_condition); // Create the correct number of ODE systems double a = 5.0; std::vector<AbstractOdeSystemForCoupledPdeSystem*> ode_systems; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { ode_systems.push_back(new OdeSystemForCoupledHeatEquationWithSource(a)); } // Create PDE system solver LinearParabolicPdeSystemWithCoupledOdeSystemSolver<1,1,1> solver(&mesh, &pde, &bcc, ode_systems); // Test setting end time and timestep TS_ASSERT_THROWS_THIS(solver.SetTimes(1.0, 0.0), "Start time has to be less than end time"); TS_ASSERT_THROWS_THIS(solver.SetTimeStep(0.0), "Time step has to be greater than zero"); // Set end time and timestep double t_end = 0.1; solver.SetTimes(0, t_end); solver.SetTimeStep(0.001); // Set initial condition u(x,0) = 1 + cos(pi*x) std::vector<double> init_cond(mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; init_cond[i] = 1 + cos(M_PI*x); } Vec initial_condition = PetscTools::CreateVec(init_cond); solver.SetInitialCondition(initial_condition); // Solve PDE system and store result Vec result = solver.Solve(); ReplicatableVector result_repl(result); /* * Test that solution is given by * * u(x,t) = 1 + (1 - exp(-a*t))/a + exp(-pi*pi*t)*cos(pi*x), * v(x,t) = exp(-a*t), * * with t = t_end. */ for (unsigned i=0; i<result_repl.GetSize(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double u = 1 + (1 - exp(-a*t_end))/a + exp(-M_PI*M_PI*t_end)*cos(M_PI*x); TS_ASSERT_DELTA(result_repl[i], u, 0.1); double u_from_v = solver.GetOdeSystemAtNode(i)->rGetPdeSolution()[0]; TS_ASSERT_DELTA(result_repl[i], u_from_v, 0.1); double v = exp(-a*t_end); TS_ASSERT_DELTA(ode_systems[i]->rGetStateVariables()[0], v, 0.1); } // Test the method GetOdeSystemAtNode() for (unsigned i=0; i<mesh.GetNumNodes(); i++) { TS_ASSERT(solver.GetOdeSystemAtNode(i) != NULL); TS_ASSERT_DELTA(static_cast<OdeSystemForCoupledHeatEquationWithSource*>(solver.GetOdeSystemAtNode(i))->GetA(), 5.0, 1e-6); } // Tidy up PetscTools::Destroy(initial_condition); PetscTools::Destroy(result); }
/** * This test provides an example of how to solve a coupled PDE system * where there is no coupled ODE system, and can be used as a template * for solving standard reaction-diffusion problems arising in the * study of pattern formation on fixed domains. */ void TestSchnackenbergCoupledPdeSystemIn1dWithNonZeroDirichlet() { // Create mesh of domain [0,1] TrianglesMeshReader<1,1> mesh_reader("mesh/test/data/1D_0_to_1_1000_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create PDE system object SchnackenbergCoupledPdeSystem<1> pde(1e-4, 1e-2, 0.1, 0.2, 0.3, 0.1); // Create non-zero Dirichlet boundary conditions for each state variable BoundaryConditionsContainer<1,1,2> bcc; ConstBoundaryCondition<1>* p_bc_for_u = new ConstBoundaryCondition<1>(2.0); ConstBoundaryCondition<1>* p_bc_for_v = new ConstBoundaryCondition<1>(0.75); bcc.AddDirichletBoundaryCondition(mesh.GetNode(0), p_bc_for_u, 0); bcc.AddDirichletBoundaryCondition(mesh.GetNode(0), p_bc_for_v, 1); bcc.AddDirichletBoundaryCondition(mesh.GetNode(mesh.GetNumNodes()-1), p_bc_for_u, 0); bcc.AddDirichletBoundaryCondition(mesh.GetNode(mesh.GetNumNodes()-1), p_bc_for_v, 1); // Create PDE system solver LinearParabolicPdeSystemWithCoupledOdeSystemSolver<1,1,2> solver(&mesh, &pde, &bcc); // Set end time and time step double t_end = 10; solver.SetTimes(0, t_end); solver.SetTimeStep(1e-1); // Create initial conditions that are random perturbations of the uniform steady state std::vector<double> init_conds(2*mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { init_conds[2*i] = fabs(2.0 + RandomNumberGenerator::Instance()->ranf()); init_conds[2*i + 1] = fabs(0.75 + RandomNumberGenerator::Instance()->ranf()); } Vec initial_condition = PetscTools::CreateVec(init_conds); solver.SetInitialCondition(initial_condition); // Solve PDE system and store result Vec solution = solver.Solve(); ReplicatableVector solution_repl(solution); // Write results for visualization in gnuplot OutputFileHandler handler("TestSchnackenbergCoupledPdeSystemIn1dWithNonZeroDirichlet", false); out_stream results_file = handler.OpenOutputFile("schnackenberg.dat"); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->rGetLocation()[0]; double u = solution_repl[2*i]; double v = solution_repl[2*i + 1]; (*results_file) << x << "\t" << u << "\t" << v << "\n" << std::flush; } results_file->close(); std::string results_filename = handler.GetOutputDirectoryFullPath() + "schnackenberg.dat"; NumericFileComparison comp_results(results_filename, "pde/test/data/schnackenberg.dat"); TS_ASSERT(comp_results.CompareFiles(1e-3)); // Tidy up PetscTools::Destroy(initial_condition); PetscTools::Destroy(solution); }
void TestSolveAndWriteResultsToFileMethod() { // Create mesh of the domain [0,1]x[0,1] TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_128_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create PDE system object HeatEquationForCoupledOdeSystem<2> pde; // Define zero Dirichlet boundary conditions on entire boundary BoundaryConditionsContainer<2,2,1> bcc; bcc.DefineZeroDirichletOnMeshBoundary(&mesh); // Create the correct number of ODE systems double a = 5.0; std::vector<AbstractOdeSystemForCoupledPdeSystem*> ode_systems; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { ode_systems.push_back(new OdeSystemForCoupledHeatEquation(a)); } // Create PDE system solver LinearParabolicPdeSystemWithCoupledOdeSystemSolver<2,2,1> solver(&mesh, &pde, &bcc, ode_systems); // Set end time and timestep (end time is not a multiple of timestep, for coverage) double t_end = 0.105; /* * Set initial condition * * u(x,y,0) = sin(pi*x)*sin(pi*y), * * which is an eigenfunction of the heat equation. */ std::vector<double> init_cond(mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double y = mesh.GetNode(i)->GetPoint()[1]; init_cond[i] = sin(M_PI*x)*sin(M_PI*y); } Vec initial_condition = PetscTools::CreateVec(init_cond); // Need an output folder TS_ASSERT_THROWS_THIS(solver.SolveAndWriteResultsToFile(), "SetOutputDirectory() must be called prior to SolveAndWriteResultsToFile()"); solver.SetOutputDirectory("TestHeatEquationForCoupledOdeSystemIn2dWithZeroDirichletWithOutput"); // Need a time interval TS_ASSERT_THROWS_THIS(solver.SolveAndWriteResultsToFile(), "SetTimes() must be called prior to SolveAndWriteResultsToFile()"); solver.SetTimes(0, t_end); // Need a timestep TS_ASSERT_THROWS_THIS(solver.SolveAndWriteResultsToFile(), "SetTimeStep() must be called prior to SolveAndWriteResultsToFile()"); solver.SetTimeStep(0.01); // Need sampling interval TS_ASSERT_THROWS_THIS(solver.SolveAndWriteResultsToFile(), "SetSamplingTimeStep() must be called prior to SolveAndWriteResultsToFile()"); solver.SetSamplingTimeStep(0.1); // Need initial condition TS_ASSERT_THROWS_THIS(solver.SolveAndWriteResultsToFile(), "SetInitialCondition() must be called prior to SolveAndWriteResultsToFile()"); solver.SetInitialCondition(initial_condition); solver.SolveAndWriteResultsToFile(); //#ifdef CHASTE_VTK ///\todo #1967 Check that the file was output and has expected content //#endif // CHASTE_VTK // Tidy up PetscTools::Destroy(initial_condition); }
// see #1061 void Test2dBathExtracellularStimulusOneEdgeGroundedOnOppositeEdge() { HeartConfig::Instance()->SetSimulationDuration(40); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath2dExtraStimGrounded"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d"); HeartConfig::Instance()->SetOdeTimeStep(0.005); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; centre(1) = 0.05; BathCellFactory<2> cell_factory( 0.0, centre); BidomainProblem<2> bidomain_problem( &cell_factory, true ); TrianglesMeshReader<2,2> reader("mesh/test/data/2D_0_to_1mm_400_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(reader); // Set everything outside a central circle (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; if (sqrt((x-0.05)*(x-0.05) + (y-0.05)*(y-0.05)) > 0.02) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); //boundary flux for Phi_e //-4e3 is enough to trigger an action potential, -3e3 is below threshold, -5e3 crashes the cell model. double boundary_flux = -9e3; double duration = 2.5; //ms HeartConfig::Instance()->SetElectrodeParameters(true,0,boundary_flux, 0.0, duration); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; /* * We are checking the last time step. This test will only make sure that an upstroke is triggered. * We ran longer simulation for 350 ms and a nice AP was observed. */ for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // test V = 0 for all bath nodes if (mesh.GetNode(i)->GetRegion()==1) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0) { ap_triggered = true; } } TS_ASSERT(ap_triggered); }
/* * Define a particular test. */ void TestSchnackenbergSystemOnButterflyMesh() throw (Exception) { /* As usual, we first create a mesh. Here we are using a 2d mesh of a butterfly-shaped domain. */ TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/butterfly"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); /* We scale the mesh to an appropriate size. */ mesh.Scale(0.2, 0.2); /* Next, we instantiate the PDE system to be solved. We pass the parameter values into the * constructor. (The order is D,,1,, D,,2,, k,,1,, k,,-1,, k,,2,, k,,3,,) */ SchnackenbergCoupledPdeSystem<2> pde(1e-4, 1e-2, 0.1, 0.2, 0.3, 0.1); /* * Then we have to define the boundary conditions. As we are in 2d, {{{SPACE_DIM}}}=2 and * {{{ELEMENT_DIM}}}=2. We also have two unknowns u and v, * so in this case {{{PROBLEM_DIM}}}=2. The value of each boundary condition is * given by the spatially uniform steady state solution of the Schnackenberg system, * given by u = (k,,1,, + k,,2,,)/k,,-1,,, v = k,,2,,k,,-1,,^2^/k,,3,,(k,,1,, + k,,2,,)^2^. */ BoundaryConditionsContainer<2,2,2> bcc; ConstBoundaryCondition<2>* p_bc_for_u = new ConstBoundaryCondition<2>(2.0); ConstBoundaryCondition<2>* p_bc_for_v = new ConstBoundaryCondition<2>(0.75); for (TetrahedralMesh<2,2>::BoundaryNodeIterator node_iter = mesh.GetBoundaryNodeIteratorBegin(); node_iter != mesh.GetBoundaryNodeIteratorEnd(); ++node_iter) { bcc.AddDirichletBoundaryCondition(*node_iter, p_bc_for_u, 0); bcc.AddDirichletBoundaryCondition(*node_iter, p_bc_for_v, 1); } /* This is the solver for solving coupled systems of linear parabolic PDEs and ODEs, * which takes in the mesh, the PDE system, the boundary conditions and optionally * a vector of ODE systems (one for each node in the mesh). Since in this example * we are solving a system of coupled PDEs only, we do not supply this last argument. */ LinearParabolicPdeSystemWithCoupledOdeSystemSolver<2,2,2> solver(&mesh, &pde, &bcc); /* Then we set the end time and time step and the output directory to which results will be written. */ double t_end = 10; solver.SetTimes(0, t_end); solver.SetTimeStep(1e-1); solver.SetSamplingTimeStep(1); solver.SetOutputDirectory("TestSchnackenbergSystemOnButterflyMesh"); /* We create a vector of initial conditions for u and v that are random perturbations * of the spatially uniform steady state and pass this to the solver. */ std::vector<double> init_conds(2*mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { init_conds[2*i] = fabs(2.0 + RandomNumberGenerator::Instance()->ranf()); init_conds[2*i + 1] = fabs(0.75 + RandomNumberGenerator::Instance()->ranf()); } Vec initial_condition = PetscTools::CreateVec(init_conds); solver.SetInitialCondition(initial_condition); /* We now solve the PDE system and write results to VTK files, for * visualization using Paraview. Results will be written to CHASTE_TEST_OUTPUT/TestSchnackenbergSystemOnButterflyMesh * as a results.pvd file and several results_[time].vtu files. * You should see something like [[Image(u.png, 350px)]] for u and [[Image(v.png, 350px)]] for v. */ solver.SolveAndWriteResultsToFile(); /* * All PETSc {{{Vec}}}s should be destroyed when they are no longer needed. */ PetscTools::Destroy(initial_condition); }
void TestMonodomain3d() throw(Exception) { /* HOW_TO_TAG Cardiac/Problem definition * Generate a slab (cuboid) mesh rather than read a mesh in, and pass it to solver */ /* We will auto-generate a mesh this time, and pass it in, rather than * provide a mesh file name. This is how to generate a cuboid mesh with * a given spatial stepsize h */ TetrahedralMesh<3,3> mesh; double h=0.02; mesh.ConstructRegularSlabMesh(h, 0.8 /*length*/, 0.3 /*width*/, 0.3 /*depth*/); /* (In 2D the call is identical, but without the depth parameter). * * EMPTYLINE * * Set the simulation duration, etc, and create an instance of the cell factory. * One thing that should be noted for monodomain problems, the ''intracellular * conductivity'' is used as the monodomain effective conductivity (not a * harmonic mean of intra and extracellular conductivities). So if you want to * alter the monodomain conductivity call * `HeartConfig::Instance()->SetIntracellularConductivities` */ HeartConfig::Instance()->SetSimulationDuration(5); //ms HeartConfig::Instance()->SetOutputDirectory("Monodomain3dExample"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.005, 0.01, 0.1); BenchmarkCellFactory cell_factory; /* Now we declare the problem class, `MonodomainProblem<3>` instead of `BidomainProblem<2>`. * The interface for both is the same. */ MonodomainProblem<3> monodomain_problem( &cell_factory ); /* If a mesh-file-name hasn't been set using `HeartConfig`, we have to pass in * a mesh using the `SetMesh` method (must be called before `Initialise`). */ monodomain_problem.SetMesh(&mesh); /* By default data for all nodes is output, but for big simulations, sometimes this * might not be required, and the action potential only at certain nodes required. * The following code shows how to output the results at the first, middle and last * nodes, for example. (The output is written to the HDF5 file; regular visualisation output * will be turned off. HDF5 files can be read using Matlab). We are not using this in this * simulation however (hence the boolean being set to false). */ bool partial_output = false; if(partial_output) { std::vector<unsigned> nodes_to_be_output; nodes_to_be_output.push_back(0); nodes_to_be_output.push_back((unsigned)round( (mesh.GetNumNodes()-1)/2 )); nodes_to_be_output.push_back(mesh.GetNumNodes()-1); monodomain_problem.SetOutputNodes(nodes_to_be_output); } /* `SetWriteInfo` is a useful method that means that the min/max voltage is * printed as the simulation runs (useful for verifying that cells are stimulated * and the wave propagating, for example) (although note scons does buffer output * before printing to screen) */ monodomain_problem.SetWriteInfo(); /* Finally, call `Initialise` and `Solve` as before */ monodomain_problem.Initialise(); monodomain_problem.Solve(); /* This part is just to check nothing has accidentally been changed in this example */ ReplicatableVector voltage(monodomain_problem.GetSolution()); TS_ASSERT_DELTA(voltage[0], 34.9032, 1e-2); }
void TestHeatEquationWithCoupledOdeSystemIn1dWithMixed() { // Create mesh of domain [0,1] TrianglesMeshReader<1,1> mesh_reader("mesh/test/data/1D_0_to_1_100_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create PDE system object HeatEquationForCoupledOdeSystem<1> pde; // Define non-zero Neumann boundary condition at x=0 BoundaryConditionsContainer<1,1,1> bcc; ConstBoundaryCondition<1>* p_boundary_condition = new ConstBoundaryCondition<1>(1.0); TetrahedralMesh<1,1>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin(); bcc.AddNeumannBoundaryCondition(*iter, p_boundary_condition); // Define zero Dirichlet boundary condition at x=1 ConstBoundaryCondition<1>* p_boundary_condition2 = new ConstBoundaryCondition<1>(0.0); TetrahedralMesh<1,1>::BoundaryNodeIterator node_iter = mesh.GetBoundaryNodeIteratorEnd(); --node_iter; bcc.AddDirichletBoundaryCondition(*node_iter, p_boundary_condition2); // Create the correct number of ODE systems double a = 5.0; std::vector<AbstractOdeSystemForCoupledPdeSystem*> ode_systems; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { ode_systems.push_back(new OdeSystemForCoupledHeatEquation(a)); } // Create PDE system solver LinearParabolicPdeSystemWithCoupledOdeSystemSolver<1,1,1> solver(&mesh, &pde, &bcc, ode_systems); // Set end time and timestep double t_end = 0.1; solver.SetTimes(0, t_end); solver.SetTimeStep(0.001); // Set initial condition u(x,0) = 1 - x std::vector<double> init_cond(mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; init_cond[i] = 1 - x; } Vec initial_condition = PetscTools::CreateVec(init_cond); solver.SetInitialCondition(initial_condition); // Solve PDE system and store result Vec result = solver.Solve(); ReplicatableVector result_repl(result); /* * Test that solution is given by * * u(x,t) = 1 - x, * v(x,t) = 1 + a*(1-x)*t, * * with t = t_end. */ for (unsigned i=0; i<result_repl.GetSize(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double u = 1 - x; TS_ASSERT_DELTA(result_repl[i], u, 0.1); double v = 1 + a*(1-x)*t_end; TS_ASSERT_DELTA(ode_systems[i]->rGetStateVariables()[0], v, 0.1); } // Tidy up PetscTools::Destroy(initial_condition); PetscTools::Destroy(result); }
void TestDistancesToCorner() throw (Exception) { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_21_nodes_side/Cube21"); // 5x5x5mm cube (internode distance = 0.25mm) TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); unsigned num_nodes=9261u; TS_ASSERT_EQUALS(mesh.GetNumNodes(), num_nodes); // 21x21x21 nodes TS_ASSERT_EQUALS(mesh.GetNumElements(), 48000u); TS_ASSERT_EQUALS(mesh.GetNumBoundaryElements(), 4800u); DistributedTetrahedralMesh<3,3> parallel_mesh(DistributedTetrahedralMeshPartitionType::DUMB); // No reordering; parallel_mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_EQUALS(parallel_mesh.GetNumNodes(), num_nodes); // 21x21x21 nodes TS_ASSERT_EQUALS(parallel_mesh.GetNumElements(), 48000u); TS_ASSERT_EQUALS(parallel_mesh.GetNumBoundaryElements(), 4800u); unsigned far_index=9260u; c_vector<double,3> far_corner=mesh.GetNode(far_index)->rGetLocation(); TS_ASSERT_DELTA( far_corner[0], 0.25, 1e-11); TS_ASSERT_DELTA( far_corner[1], 0.25, 1e-11); TS_ASSERT_DELTA( far_corner[2], 0.25, 1e-11); try { c_vector<double,3> parallel_far_corner=parallel_mesh.GetNode(far_index)->rGetLocation(); TS_ASSERT_DELTA( parallel_far_corner[0], 0.25, 1e-11); TS_ASSERT_DELTA( parallel_far_corner[1], 0.25, 1e-11); TS_ASSERT_DELTA( parallel_far_corner[2], 0.25, 1e-11); } catch (Exception&) { } std::vector<unsigned> map_far_corner; map_far_corner.push_back(far_index); DistanceMapCalculator<3,3> distance_calculator(mesh); std::vector<double> distances; distance_calculator.ComputeDistanceMap(map_far_corner, distances); DistanceMapCalculator<3,3> parallel_distance_calculator(parallel_mesh); std::vector<double> parallel_distances; parallel_distance_calculator.ComputeDistanceMap(map_far_corner, parallel_distances); TS_ASSERT_EQUALS(distance_calculator.mRoundCounter, 1u); //Nodes in mesh are order such that a dumb partitioning will give a sequential handover from proc0 to proc1... TS_ASSERT_EQUALS(parallel_distance_calculator.mRoundCounter, PetscTools::GetNumProcs()); //Note unsigned division is okay here TS_ASSERT_DELTA(parallel_distance_calculator.mPopCounter, num_nodes/PetscTools::GetNumProcs(), 1u); TS_ASSERT_DELTA(distance_calculator.mPopCounter, num_nodes, 1u); for (unsigned index=0; index<distances.size(); index++) { c_vector<double, 3> node = mesh.GetNode(index)->rGetLocation(); //Straightline distance double euclidean_distance = norm_2(far_corner - node); // x + y + z distance double manhattan_distance = norm_1(far_corner - node); //If they differ, then allow the in-mesh distance to be in between double error_bound = (manhattan_distance - euclidean_distance)/2.0; //If they don't differ, then we expect the in-mesh distance to be similar if (error_bound < 1e-15) { error_bound = 1e-15; } TS_ASSERT_LESS_THAN_EQUALS(distances[index], manhattan_distance+DBL_EPSILON); TS_ASSERT_LESS_THAN_EQUALS(euclidean_distance, distances[index]+DBL_EPSILON); TS_ASSERT_DELTA(distances[index], euclidean_distance, error_bound); TS_ASSERT_DELTA(distances[index], parallel_distances[index], 1e-15); } // Test some point-to-point distances RandomNumberGenerator::Instance()->Reseed(1); unsigned trials=25; unsigned pops=0; unsigned sequential_pops=0; for (unsigned i=0; i<trials; i++) { unsigned index=RandomNumberGenerator::Instance()->randMod(parallel_distances.size()); TS_ASSERT_DELTA(parallel_distance_calculator.SingleDistance(9260u, index), parallel_distances[index], 1e-15); TS_ASSERT_DELTA(distance_calculator.SingleDistance(9260u, index), parallel_distances[index], 1e-15); pops += parallel_distance_calculator.mPopCounter; sequential_pops += distance_calculator.mPopCounter; TS_ASSERT_LESS_THAN_EQUALS(parallel_distance_calculator.mRoundCounter, PetscTools::GetNumProcs()+2); } // Without A*: TS_ASSERT_DELTA(sequential_pops/(double)trials, num_nodes/2, 300); TS_ASSERT_LESS_THAN(sequential_pops/(double)trials, num_nodes/20.0); if (PetscTools::IsSequential()) { //Early termination TS_ASSERT_EQUALS(pops, sequential_pops); } else { //Early termination on remote processes is not yet possible //This may lead to multiple updates from remote //A* Leads to even more updates on average // Without A*: TS_ASSERT_DELTA(pops/(double)trials, num_nodes/PetscTools::GetNumProcs(), 700.0); TS_ASSERT_LESS_THAN(pops/(double)trials, num_nodes/10.0 ); } //Reverse - to check that cached information is flushed. for (unsigned i=0; i<3; i++) { unsigned index=RandomNumberGenerator::Instance()->randMod(parallel_distances.size()); TS_ASSERT_DELTA(parallel_distance_calculator.SingleDistance(index, 9260u), parallel_distances[index], 1e-15); } }
/* Define a particular test. Note the {{{throw(Exception)}}} at the end of the * declaration. This causes {{{Exception}}} messages to be printed out if an * {{{Exception}}} is thrown, rather than just getting the message "terminate * called after throwing an instance of 'Exception' " */ void TestSolvingNonlinearEllipticPde() throw(Exception) { /* As usual, first create a mesh. */ TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_128_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); /* Next, instantiate the PDE to be solved. */ MyNonlinearPde pde; /* * Then we have to define the boundary conditions. First, the Dirichlet boundary * condition, u=0 on x=0, using the boundary node iterator. */ BoundaryConditionsContainer<2,2,1> bcc; ConstBoundaryCondition<2>* p_zero_bc = new ConstBoundaryCondition<2>(0.0); for (TetrahedralMesh<2,2>::BoundaryNodeIterator node_iter = mesh.GetBoundaryNodeIteratorBegin(); node_iter != mesh.GetBoundaryNodeIteratorEnd(); node_iter++) { if (fabs((*node_iter)->GetPoint()[1]) < 1e-12) { bcc.AddDirichletBoundaryCondition(*node_iter, p_zero_bc); } } /* And then the Neumman conditions. Neumann boundary condition are defined on * surface elements, and for this problem, the Neumman boundary value depends * on the position in space, so we make use of the {{{FunctionalBoundaryCondition}}} * object, which contains a pointer to a function, and just returns the value * of that function for the required point when the {{{GetValue}}} method is called. */ FunctionalBoundaryCondition<2>* p_functional_bc = new FunctionalBoundaryCondition<2>(&MyNeummanFunction); /* Loop over surface elements. */ for (TetrahedralMesh<2,2>::BoundaryElementIterator elt_iter = mesh.GetBoundaryElementIteratorBegin(); elt_iter != mesh.GetBoundaryElementIteratorEnd(); elt_iter++) { /* Get the y value of any node (here, the zero-th). */ double y = (*elt_iter)->GetNodeLocation(0,1); /* If y=1... */ if (fabs(y-1.0) < 1e-12) { /* ... then associate the functional boundary condition, (Dgradu).n = y, * with the surface element... */ bcc.AddNeumannBoundaryCondition(*elt_iter, p_functional_bc); } else { /* ...else associate the zero boundary condition (i.e. zero flux) with this * element. */ bcc.AddNeumannBoundaryCondition(*elt_iter, p_zero_bc); } } /* Note that in the above loop, the zero Neumman boundary condition was applied * to all surface elements for which y!=1, which included the Dirichlet surface * y=0. This is OK, as Dirichlet boundary conditions are applied to the finite * element matrix after Neumman boundary conditions, where the appropriate rows * in the matrix are overwritten. * * This is the solver for solving nonlinear problems, which, as usual, * takes in the mesh, the PDE, and the boundary conditions. */ SimpleNonlinearEllipticSolver<2,2> solver(&mesh, &pde, &bcc); /* The solver also needs to be given an initial guess, which will be * a PETSc vector. We can make use of a helper method to create it. */ Vec initial_guess = PetscTools::CreateAndSetVec(mesh.GetNumNodes(), 0.25); /* '''Optional:''' To use Chaste's Newton solver to solve nonlinear vector equations that are * assembled, rather than the default PETSc nonlinear solvers, we can * do the following: */ SimpleNewtonNonlinearSolver newton_solver; solver.SetNonlinearSolver(&newton_solver); /* '''Optional:''' We can also manually set tolerances, and whether to print statistics, with * this nonlinear vector equation solver */ newton_solver.SetTolerance(1e-10); newton_solver.SetWriteStats(); /* Now call {{{Solve}}}, passing in the initial guess */ Vec answer = solver.Solve(initial_guess); /* Note that we could have got the solver to not use an analytical Jacobian * and use a numerically-calculated Jacobian instead, by passing in false as a second * parameter: */ //Vec answer = solver.Solve(initial_guess, false); /* Once solved, we can check the obtained solution against the analytical * solution. */ ReplicatableVector answer_repl(answer); for (unsigned i=0; i<answer_repl.GetSize(); i++) { double y = mesh.GetNode(i)->GetPoint()[1]; double exact_u = sqrt(y*(4-y)); TS_ASSERT_DELTA(answer_repl[i], exact_u, 0.15); } /* Finally, we have to remember to destroy the PETSc {{{Vec}}}s. */ PetscTools::Destroy(initial_guess); PetscTools::Destroy(answer); }
void TestHeatEquationWithCoupledOdeSystemIn2dWithZeroDirichlet() { // Create mesh of the domain [0,1]x[0,1] TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_4096_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Create PDE system object HeatEquationForCoupledOdeSystem<2> pde; // Define zero Dirichlet boundary conditions on entire boundary BoundaryConditionsContainer<2,2,1> bcc; bcc.DefineZeroDirichletOnMeshBoundary(&mesh); // Create the correct number of ODE systems double a = 5.0; std::vector<AbstractOdeSystemForCoupledPdeSystem*> ode_systems; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { ode_systems.push_back(new OdeSystemForCoupledHeatEquation(a)); } // Create PDE system solver LinearParabolicPdeSystemWithCoupledOdeSystemSolver<2,2,1> solver(&mesh, &pde, &bcc, ode_systems); // Set end time and timestep double t_end = 0.01; solver.SetTimes(0, t_end); solver.SetTimeStep(0.001); /* * Set initial condition * * u(x,y,0) = sin(pi*x)*sin(pi*y), * * which is an eigenfunction of the heat equation. */ std::vector<double> init_cond(mesh.GetNumNodes()); for (unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double y = mesh.GetNode(i)->GetPoint()[1]; init_cond[i] = sin(M_PI*x)*sin(M_PI*y); } Vec initial_condition = PetscTools::CreateVec(init_cond); solver.SetInitialCondition(initial_condition); // Solve PDE system and store result Vec result = solver.Solve(); ReplicatableVector result_repl(result); /* * Test that solution is given by * * u(x,y,t) = e^{-2*pi*pi*t} sin(pi*x)*sin(pi*y), * v(x,y,t) = 1 + (1 - e^{-2*pi*pi*t})*sin(pi*x)*sin(pi*y)*a/(2*pi*pi), * * with t = t_end. */ for (unsigned i=0; i<result_repl.GetSize(); i++) { double x = mesh.GetNode(i)->GetPoint()[0]; double y = mesh.GetNode(i)->GetPoint()[1]; double u = exp(-2*M_PI*M_PI*t_end)*sin(M_PI*x)*sin(M_PI*y); double v = 1.0 + (a/(2*M_PI*M_PI))*(1 - exp(-2*M_PI*M_PI*t_end))*sin(M_PI*x)*sin(M_PI*y); TS_ASSERT_DELTA(result_repl[i], u, 0.01); TS_ASSERT_DELTA(ode_systems[i]->rGetStateVariables()[0], v, 0.01); } // Tidy up PetscTools::Destroy(initial_condition); PetscTools::Destroy(result); }