void TestScalingWithMethod() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); double mesh_volume = mesh.GetVolume(); mesh.Scale(1.0); TS_ASSERT_DELTA(mesh_volume, mesh.GetVolume(), 1e-6); mesh.Scale(2.0, 3.0, 4.0); TS_ASSERT_DELTA(24.0*mesh_volume, mesh.GetVolume(), 1e-6); ChastePoint<3> corner_after = mesh.GetNode(6)->GetPoint(); TS_ASSERT_DELTA(corner_after[0], 2.0, 1e-7); TS_ASSERT_DELTA(corner_after[1], 3.0, 1e-7); TS_ASSERT_DELTA(corner_after[2], 4.0, 1e-7); mesh.Scale(0.5, 1.0/3.0, 0.25); TS_ASSERT_DELTA(mesh_volume,mesh.GetVolume(),1e-6); corner_after = mesh.GetNode(6)->GetPoint(); TS_ASSERT_DELTA(corner_after[0], 1.0, 1e-7); TS_ASSERT_DELTA(corner_after[1], 1.0, 1e-7); TS_ASSERT_DELTA(corner_after[2], 1.0, 1e-7); }
void TestValidate() { // Load a 2D square mesh with 1 central non-boundary node TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_4_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); BoundaryConditionsContainer<2,2,1> bcc; // No BCs yet, so shouldn't validate TS_ASSERT(!bcc.Validate(&mesh)); // Add some BCs ConstBoundaryCondition<2> *bc = new ConstBoundaryCondition<2>(0.0); bcc.AddDirichletBoundaryCondition(mesh.GetNode(0), bc); bcc.AddDirichletBoundaryCondition(mesh.GetNode(1), bc); bcc.AddDirichletBoundaryCondition(mesh.GetNode(3), bc); TetrahedralMesh<2,2>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorEnd(); iter--; bcc.AddNeumannBoundaryCondition(*iter, bc); // 2 to 3 iter--; bcc.AddNeumannBoundaryCondition(*iter, bc); // 1 to 2 TS_ASSERT(bcc.Validate(&mesh)); }
void TestTranslation3DWithUblas() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); double volume = mesh.GetVolume(); double surface_area = mesh.GetSurfaceArea(); Node<3>* p_node1 = mesh.GetNode(36); ChastePoint<3> point1 = p_node1->GetPoint(); Node<3>* p_node2 = mesh.GetNode(23); ChastePoint<3> point2 = p_node2->GetPoint(); c_vector<double, 3> old_location1 = point1.rGetLocation(); c_vector<double, 3> old_location2 = point2.rGetLocation(); // Set translation Vector c_vector<double, 3> trans_vec; trans_vec(0) = 2.0; trans_vec(1) = 2.0; trans_vec(2) = 2.0; // Translate mesh.Translate(trans_vec); c_vector<double, 3> new_location1 = point1.rGetLocation(); c_vector<double, 3> new_location2 = point2.rGetLocation(); // Check Volume and Surface Area are invariant TS_ASSERT_DELTA(mesh.GetVolume(), volume, 1e-6); TS_ASSERT_DELTA(mesh.GetSurfaceArea(), surface_area, 1e-6); // Spot check a couple of nodes TS_ASSERT_DELTA(inner_prod(new_location1-old_location1, trans_vec), 0, 1e-6); TS_ASSERT_DELTA(inner_prod(new_location2-old_location2, trans_vec), 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); }
/** * 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 TestTranslationMethod() throw (Exception) { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Pick a random node and store spatial position Node<3>* p_node = mesh.GetNode(10); ChastePoint<3> original_coordinate = p_node->GetPoint(); double mesh_volume = mesh.GetVolume(); const double x_movement = 1.0; const double y_movement = 2.5; const double z_movement = -3.75; mesh.Translate(x_movement, y_movement, z_movement); ChastePoint<3> new_coordinate = p_node->GetPoint(); double new_mesh_volume = mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume, new_mesh_volume, 1e-6); TS_ASSERT_DELTA(original_coordinate[0], new_coordinate[0]-x_movement, 1e-6); TS_ASSERT_DELTA(original_coordinate[1], new_coordinate[1]-y_movement, 1e-6); TS_ASSERT_DELTA(original_coordinate[2], new_coordinate[2]-z_movement, 1e-6); }
void TestAcinarImpedance() throw(Exception) { TetrahedralMesh<1,3> mesh; TrianglesMeshReader<1,3> mesh_reader("mesh/test/data/y_branch_3d_mesh"); mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_THROWS_CONTAINS(SimpleImpedanceProblem(mesh, 1u), "Outlet node is not a boundary node"); SimpleImpedanceProblem problem(mesh, 0u); problem.rGetMesh(); //for coverage unsigned node_index = 3; //Arbitrary terminal node problem.SetElastance(2*M_PI/2.0); TS_ASSERT_DELTA(real(problem.CalculateAcinusImpedance(mesh.GetNode(node_index), 0.0)), 0.0, 1e-6); TS_ASSERT_DELTA(real(problem.CalculateAcinusImpedance(mesh.GetNode(node_index), 1.0)), 0.0, 1e-6); TS_ASSERT_DELTA(imag(problem.CalculateAcinusImpedance(mesh.GetNode(node_index), 1.0)), -1.0, 1e-6); }
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); }
void TestDefineZeroDirichletOnMeshBoundary() { // Load a 2D square mesh with 1 central non-boundary node TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_4_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); BoundaryConditionsContainer<2,2,1> bcc; bcc.DefineZeroDirichletOnMeshBoundary(&mesh); // Check boundary nodes have the right condition for (int i=0; i<4; i++) { double value = bcc.GetDirichletBCValue(mesh.GetNode(i)); TS_ASSERT_DELTA(value, 0.0, 1e-12); } // Check non-boundary node has no condition TS_ASSERT(!bcc.HasDirichletBoundaryCondition(mesh.GetNode(4))); }
void TestXaxisRotation3DWithMethod() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); 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); double mesh_volume = mesh.GetVolume(); mesh.RotateX(M_PI/2.0); double new_mesh_volume = mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume,new_mesh_volume,1e-6); ChastePoint<3> corner_after = mesh.GetNode(6)->GetPoint(); TS_ASSERT_DELTA(corner_after[0], 1.0, 1e-7); TS_ASSERT_DELTA(corner_after[1], 1.0, 1e-7); TS_ASSERT_DELTA(corner_after[2], -1.0, 1e-7); }
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 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 TestGeneralConvolution3DWithMethod() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); double mesh_volume = mesh.GetVolume(); mesh.Translate(2.3, 3.1, 1.7); mesh.RotateZ(1.4); mesh.RotateY(0.3); mesh.RotateX(0.7); double new_mesh_volume = mesh.GetVolume(); TS_ASSERT_DELTA(mesh_volume, new_mesh_volume, 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); }
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 TestArchiving() { OutputFileHandler handler("bcc_archive", false); handler.SetArchiveDirectory(); std::string archive_filename = ArchiveLocationInfo::GetProcessUniqueFilePath("bcc.arch"); // Load a 2D square mesh with 1 central non-boundary node TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_4_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); { BoundaryConditionsContainer<2,2,2>* p_bcc = new BoundaryConditionsContainer<2,2,2>; // No BCs yet, so shouldn't validate TS_ASSERT_EQUALS(p_bcc->Validate(&mesh), false); // Add some Neumann BCs ConstBoundaryCondition<2> *bc1 = new ConstBoundaryCondition<2>(2.0); ConstBoundaryCondition<2> *bc2 = new ConstBoundaryCondition<2>(-3.0); TetrahedralMesh<2,2>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorEnd(); iter--; p_bcc->AddNeumannBoundaryCondition(*iter, bc1, 0); p_bcc->AddNeumannBoundaryCondition(*iter, bc2, 1); iter--; p_bcc->AddNeumannBoundaryCondition(*iter, bc1, 0); iter--; p_bcc->AddNeumannBoundaryCondition(*iter, bc2, 1); // Add some Dirichlet BCs ConstBoundaryCondition<2> *bc3 = new ConstBoundaryCondition<2>(2.0); p_bcc->AddDirichletBoundaryCondition(mesh.GetNode(2), bc3); p_bcc->AddDirichletBoundaryCondition(mesh.GetNode(3), bc3); // Archive std::ofstream ofs(archive_filename.c_str()); boost::archive::text_oarchive output_arch(ofs); output_arch & p_bcc; delete p_bcc; } { std::ifstream ifs(archive_filename.c_str(), std::ios::binary); boost::archive::text_iarchive input_arch(ifs); // Load container... BoundaryConditionsContainer<2,2,2>* p_bcc; input_arch >> p_bcc; // ...and fill it p_bcc->LoadFromArchive( input_arch, &mesh ); TetrahedralMesh<2,2>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorEnd(); iter--; TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 0), 2.0, 1e-9); TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 1), -3.0, 1e-9); iter--; TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 0), 2.0, 1e-9); TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 1), 0.0, 1e-9); iter--; TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 0), 0.0, 1e-9); TS_ASSERT_DELTA(p_bcc->GetNeumannBCValue(*iter, ChastePoint<2>(), 1), -3.0, 1e-9); TS_ASSERT_DELTA(p_bcc->GetDirichletBCValue(mesh.GetNode(2)), 2.0, 1.e-6); TS_ASSERT_DELTA(p_bcc->GetDirichletBCValue(mesh.GetNode(3)), 2.0, 1.e-6); delete p_bcc; } }
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 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); }
/** * 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 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 */ }
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); }
void TestConductionVelocityInCrossFibreDirection2d() { ReplicatableVector final_voltage_ici; ReplicatableVector final_voltage_svi; ReplicatableVector final_voltage_svit; HeartConfig::Instance()->SetSimulationDuration(5.0); //ms //HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.0005, 0.01, 0.01); //See comment below HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.005, 0.01, 0.01); // much lower conductivity in cross-fibre direction - ICI will struggle HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 0.17)); TetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.02 /*h*/, 0.5, 0.3); // ICI - nodal current interpolation - the default { HeartConfig::Instance()->SetOutputDirectory("MonodomainIci2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(false); BlockCellFactory<2> cell_factory; MonodomainProblem<2> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_ici.ReplicatePetscVector(monodomain_problem.GetSolution()); } // SVI - state variable interpolation { HeartConfig::Instance()->SetOutputDirectory("MonodomainSvi2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactory<2> cell_factory; MonodomainProblem<2> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_svi.ReplicatePetscVector(monodomain_problem.GetSolution()); } #ifdef CHASTE_CVODE ReplicatableVector final_voltage_svi_cvode; // SVI - state variable interpolation with CVODE cells { HeartConfig::Instance()->SetOutputDirectory("MonodomainSvi2dCvode"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactoryCvode<2> cell_factory; MonodomainProblem<2> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_svi_cvode.ReplicatePetscVector(monodomain_problem.GetSolution()); } #endif //CHASTE_CVODE // SVIT - state variable interpolation on non-distributed tetrahedral mesh { HeartConfig::Instance()->SetOutputDirectory("MonodomainSviTet2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactory<2> cell_factory; MonodomainProblem<2> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_svit.ReplicatePetscVector(monodomain_problem.GetSolution()); } // Visualised results with h=0.02 and h=0.01 - results looks sensible according to // paper: // 1. SVI h=0.01 and h=0.02 match more closely than ICI results - ie SVI converges faster // 2. CV in fibre direction faster for ICI (both values of h) // 3. CV in cross fibre direction: (i) h=0.01, faster for ICI; h=0.02 slower for ICI. // (Matches results in paper) double ici_20 = -17.1939; // These numbers are from a solve with ODE timestep of 0.0005, double svi_20 = -62.6336; // i.e. ten times smaller than that used in the test at present double ici_130 = 15.4282; // (for speed), hence large tolerances below. double svi_130 = 30.7389; // The tolerances are still nowhere near overlapping - i.e. ICI different to SVI // node 20 (for h=0.02) is on the x-axis (fibre direction), SVI CV is slower TS_ASSERT_DELTA(mesh.GetNode(20)->rGetLocation()[0], 0.4, 1e-9); TS_ASSERT_DELTA(mesh.GetNode(20)->rGetLocation()[1], 0.0, 1e-9); TS_ASSERT_DELTA(final_voltage_ici[20], ici_20, 8.0); // These tolerances show difference in parallel, TS_ASSERT_DELTA(final_voltage_svi[20], svi_20, 3.0); // note that SVI is more stable in the presence of multicore... #ifdef CHASTE_CVODE TS_ASSERT_DELTA(final_voltage_svi_cvode[20], svi_20, 3.0); #endif //Cvode TS_ASSERT_DELTA(final_voltage_svit[20], svi_20, 3.0); // node 130 (for h=0.02) is on the y-axis (cross-fibre direction), ICI CV is slower TS_ASSERT_DELTA(mesh.GetNode(130)->rGetLocation()[0], 0.0, 1e-9); TS_ASSERT_DELTA(mesh.GetNode(130)->rGetLocation()[1], 0.1, 1e-9); TS_ASSERT_DELTA(final_voltage_ici[130], ici_130, 1.0); TS_ASSERT_DELTA(final_voltage_svi[130], svi_130, 0.2); #ifdef CHASTE_CVODE TS_ASSERT_DELTA(final_voltage_svi_cvode[130], svi_130, 0.3); // different CVODE versions = slightly different answer! #endif //cvode TS_ASSERT_DELTA(final_voltage_svit[130], svi_130, 0.2); }
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); }
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); } }
void TestSolveTreeImpedance() { TetrahedralMesh<1,3> mesh; TrianglesMeshReader<1,3> mesh_reader("mesh/test/data/y_branch_3d_mesh"); mesh.ConstructFromMeshReader(mesh_reader); SimpleImpedanceProblem problem(mesh, 0u); //Pure resistance calculation problem.SetRho(0.0); problem.SetMu(M_PI); problem.SetElastance(0.0); problem.SetFrequency(0.0); problem.Solve(); double small_r = (mesh.GetNode(2u)->rGetNodeAttributes()[0] + mesh.GetNode(1u)->rGetNodeAttributes()[0])/2.0; double small_l = sqrt(2.0); double big_r = (mesh.GetNode(1u)->rGetNodeAttributes()[0] + mesh.GetNode(0u)->rGetNodeAttributes()[0])/2.0; double big_l = 1.0; double small_Raw = problem.CalculateElementResistance(small_r, small_l); double big_Raw = problem.CalculateElementResistance(big_r, big_l); TS_ASSERT_DELTA(real(problem.GetImpedance()), big_Raw + 1.0/(2.0/small_Raw), 1e-6); TS_ASSERT_DELTA(imag(problem.GetImpedance()), 0.0, 1e-6); //Pure airway inertance calculation (no acinus elastance) problem.SetRho(1); problem.SetMu(0.0); problem.SetElastance(0.0); problem.SetFrequency(1.0); problem.Solve(); double small_Ini = problem.CalculateElementInertance(small_r, small_l); double big_Ini = problem.CalculateElementInertance(big_r, big_l); TS_ASSERT_DELTA(real(problem.GetImpedance()), 0.0, 1e-6); TS_ASSERT_DELTA(imag(problem.GetImpedance()), 2*M_PI*big_Ini + 1.0/(2.0/(2*M_PI*small_Ini)), 1e-6); //Pure acinar elastance calculation problem.SetRho(0.0); problem.SetMu(0.0); problem.SetElastance(4*M_PI); problem.SetFrequency(1.0); problem.Solve(); std::complex<double> i(0, 1); std::complex<double> acinus_elastance = -i; std::complex<double> total_elastance = 2.0/(1.0/acinus_elastance); TS_ASSERT_DELTA(real(problem.GetImpedance()), 0.0, 1e-6); TS_ASSERT_DELTA(imag(problem.GetImpedance()), imag(total_elastance), 1e-6); //Pure zero flow calculation - check that divide by zero errors are delt with. problem.SetRho(0.0); problem.SetMu(0.0); problem.SetElastance(0.0); problem.SetFrequency(0.0); problem.Solve(); TS_ASSERT_DELTA(real(problem.GetImpedance()), 0.0, 1e-6); TS_ASSERT_DELTA(imag(problem.GetImpedance()), 0.0, 1e-6); }
/* 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 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); }