void TestAssembler2d() throw (Exception) { QuadraticMesh<2> mesh; TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/canonical_triangle_quadratic", 2, 2, false); mesh.ConstructFromMeshReader(mesh_reader); ContinuumMechanicsProblemDefinition<2> problem_defn(mesh); double t1 = 2.6854233; double t2 = 3.2574578; // for the boundary element on the y=0 surface, create a traction std::vector<BoundaryElement<1,2>*> boundary_elems; std::vector<c_vector<double,2> > tractions; c_vector<double,2> traction; traction(0) = t1; traction(1) = t2; for (TetrahedralMesh<2,2>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin(); iter != mesh.GetBoundaryElementIteratorEnd(); ++iter) { if (fabs((*iter)->CalculateCentroid()[1])<1e-4) { BoundaryElement<1,2>* p_element = *iter; boundary_elems.push_back(p_element); tractions.push_back(traction); } } assert(boundary_elems.size()==1); problem_defn.SetTractionBoundaryConditions(boundary_elems, tractions); ContinuumMechanicsNeumannBcsAssembler<2> assembler(&mesh, &problem_defn); TS_ASSERT_THROWS_THIS(assembler.AssembleVector(), "Vector to be assembled has not been set"); Vec bad_sized_vec = PetscTools::CreateVec(2); assembler.SetVectorToAssemble(bad_sized_vec, true); TS_ASSERT_THROWS_THIS(assembler.AssembleVector(), "Vector provided to be assembled has size 2, not expected size of 18 ((dim+1)*num_nodes)"); Vec vec = PetscTools::CreateVec(3*mesh.GetNumNodes()); assembler.SetVectorToAssemble(vec, true); assembler.AssembleVector(); ReplicatableVector vec_repl(vec); // Note: on a 1d boundary element, intgl phi_i dx = 1/6 for the bases on the vertices // and intgl phi_i dx = 4/6 for the basis at the interior node. (Here the integrals // are over the canonical 1d element, [0,1], which is also the physical element for this // mesh. // node 0 is on the surface, and is a vertex TS_ASSERT_DELTA(vec_repl[0], t1/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[1], t2/6.0, 1e-8); // node 1 is on the surface, and is a vertex TS_ASSERT_DELTA(vec_repl[3], t1/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4], t2/6.0, 1e-8); // nodes 2, 3, 4 are not on the surface for(unsigned i=2; i<5; i++) { TS_ASSERT_DELTA(vec_repl[3*i], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[3*i], 0.0, 1e-8); } // node 5 is on the surface, and is an interior node TS_ASSERT_DELTA(vec_repl[15], 4*t1/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[16], 4*t2/6.0, 1e-8); // the rest of the vector is the pressure block and should be zero. TS_ASSERT_DELTA(vec_repl[2], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[5], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[8], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[11], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[14], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[17], 0.0, 1e-8); PetscTools::Destroy(vec); PetscTools::Destroy(bad_sized_vec); }
/* * Test that the matrix is calculated correctly on the cannonical triangle. * Tests against the analytical solution calculated by hand. */ void TestAssembler() throw(Exception) { QuadraticMesh<2> mesh; TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/canonical_triangle_quadratic", 2, 2, false); mesh.ConstructFromMeshReader(mesh_reader); double mu = 2.0; c_vector<double,2> body_force; double g1 = 1.34254; double g2 = 75.3422; body_force(0) = g1; body_force(1) = g2; StokesFlowProblemDefinition<2> problem_defn(mesh); problem_defn.SetViscosity(mu); problem_defn.SetBodyForce(body_force); StokesFlowAssembler<2> assembler(&mesh, &problem_defn); // The tests below test the assembler against hand-calculated variables for // an OLD weak form (corresponding to different boundary conditions), not the // current Stokes weak form. This factor converts the assembler to use the old // weak form. See documentation for this variable for more details. assembler.mScaleFactor = 0.0; Vec vec = PetscTools::CreateVec(18); Mat mat; PetscTools::SetupMat(mat, 18, 18, 18); assembler.SetVectorToAssemble(vec, true); assembler.SetMatrixToAssemble(mat, true); assembler.Assemble(); PetscMatTools::Finalise(mat); double A[6][6] = { { 1.0, 1.0/6.0, 1.0/6.0, 0.0, -2.0/3.0, -2.0/3.0}, { 1.0/6.0, 1.0/2.0, 0.0, 0.0, 0.0, -2.0/3.0}, { 1.0/6.0, 0.0, 1.0/2.0, 0.0, -2.0/3.0, 0.0}, { 0.0, 0.0, 0.0, 8.0/3.0, -4.0/3.0, -4.0/3.0}, { -2.0/3.0, 0.0, -2.0/3.0, -4.0/3.0, 8.0/3.0, 0.0}, { -2.0/3.0, -2.0/3.0, 0.0, -4.0/3.0, 0.0, 8.0/3.0} }; double Bx[6][3] = { { -1.0/6.0, 0.0, 0.0}, { 0.0, 1.0/6.0, 0.0}, { 0.0, 0.0, 0.0}, { 1.0/6.0, 1.0/6.0, 1.0/3.0}, { -1.0/6.0, -1.0/6.0, -1.0/3.0}, { 1.0/6.0, -1.0/6.0, 0.0}, }; double By[6][3] = { { -1.0/6.0, 0.0, 0.0}, { 0.0, 0.0, 0.0}, { 0.0, 0.0, 1.0/6.0}, { 1.0/6.0, 1.0/3.0, 1.0/6.0}, { 1.0/6.0, 0.0, -1.0/6.0}, { -1.0/6.0, -1.0/3.0, -1.0/6.0}, }; c_matrix<double,18,18> exact_A = zero_matrix<double>(18); // The diagonal 6x6 blocks for (unsigned i=0; i<6; i++) { for (unsigned j=0; j<6; j++) { exact_A(3*i, 3*j) = mu*A[i][j]; exact_A(3*i+1,3*j+1) = mu*A[i][j]; } } // The 6x3 Blocks for (unsigned i=0; i<6; i++) { for (unsigned j=0; j<3; j++) { exact_A(3*i,3*j+2) = -Bx[i][j]; exact_A(3*i+1,3*j+2) = -By[i][j]; //- as -Div(U)=0 exact_A(3*j+2,3*i) = -Bx[i][j]; exact_A(3*j+2,3*i+1) = -By[i][j]; } } int lo, hi; MatGetOwnershipRange(mat, &lo, &hi); for (unsigned i=lo; i<(unsigned)hi; i++) { for (unsigned j=0; j<18; j++) { TS_ASSERT_DELTA(PetscMatTools::GetElement(mat,i,j), exact_A(i,j), 1e-9); } } ReplicatableVector vec_repl(vec); // The first 6 entries in the vector correspond to nodes 0, 1, 2, i.e. the vertices. // For these nodes, it can be shown that the integral of the corresponding // basis function is zero, i.e. \intgl_{canonical element} \phi_i dV = 0.0 for i=0,1,2, phi_i the // i-th QUADRATIC basis. for(unsigned i=0; i<3; i++) { TS_ASSERT_DELTA(vec_repl[3*i], g1*0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[3*i+1], g2*0.0, 1e-8); } // The next 6 entries in the vector correspond to nodes 3, 4, 5, i.e. the internal edges. // For these nodes, it can be shown that the integral of the corresponding // basis function is 1/6, i.e. \intgl_{canonical element} \phi_i dV = 1/6 for i=3,4,5, phi_i the // i-th QUADRATIC basis. for(unsigned i=3; i<6; i++) { TS_ASSERT_DELTA(vec_repl[3*i], g1/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[3*i+1], g2/6.0, 1e-8); } // The pressure-block of the RHS vector should be zero. TS_ASSERT_DELTA(vec_repl[2], 0.0, 1e-9); TS_ASSERT_DELTA(vec_repl[5], 0.0, 1e-9); TS_ASSERT_DELTA(vec_repl[8], 0.0, 1e-9); TS_ASSERT_DELTA(vec_repl[11], 0.0, 1e-9); TS_ASSERT_DELTA(vec_repl[14], 0.0, 1e-9); TS_ASSERT_DELTA(vec_repl[17], 0.0, 1e-9); // Replace the body force with a functional body force (see MyBodyForce) above, and // assemble the vector again. This bit isn't so much to test the vector, but // to test the physical location being integrated at is interpolated correctly // and passed into the force function - see asserts in MyBodyForce. mesh.Translate(0.5, 0.8); Vec vec2 = PetscTools::CreateVec(18); assembler.SetVectorToAssemble(vec2, true); problem_defn.SetBodyForce(MyBodyForce); assembler.Assemble(); ReplicatableVector vec_repl2(vec2); for(unsigned i=3; i<6; i++) { TS_ASSERT_DELTA(vec_repl2[3*i], 10.0/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl2[3*i+1], 20.0/6.0, 1e-8); } PetscTools::Destroy(vec); PetscTools::Destroy(vec2); PetscTools::Destroy(mat); }
void TestAssembler3d() throw (Exception) { QuadraticMesh<3> mesh(1.0, 1.0, 1.0, 1.0); ContinuumMechanicsProblemDefinition<3> problem_defn(mesh); double t1 = 2.6854233; double t2 = 3.2574578; double t3 = 4.5342308; // for the boundary element on the z=0 surface, create a traction std::vector<BoundaryElement<2,3>*> boundary_elems; std::vector<c_vector<double,3> > tractions; c_vector<double,3> traction; traction(0) = t1; traction(1) = t2; traction(2) = t3; for (TetrahedralMesh<3,3>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin(); iter != mesh.GetBoundaryElementIteratorEnd(); ++iter) { if (fabs((*iter)->CalculateCentroid()[2])<1e-4) { BoundaryElement<2,3>* p_element = *iter; boundary_elems.push_back(p_element); tractions.push_back(traction); } } assert(boundary_elems.size()==2); problem_defn.SetTractionBoundaryConditions(boundary_elems, tractions); Vec vec = PetscTools::CreateVec(4*mesh.GetNumNodes()); ContinuumMechanicsNeumannBcsAssembler<3> assembler(&mesh, &problem_defn); assembler.SetVectorToAssemble(vec, true); assembler.AssembleVector(); ReplicatableVector vec_repl(vec); // For boundary elements in 3d - ie for 2d elements - and with QUADRATIC basis functions, we have // \intgl_{canonical element} \phi_i dV = 0.0 for i=0,1,2 (ie bases on vertices) // and // \intgl_{canonical element} \phi_i dV = 1/6 for i=3,4,5 (ie bases on mid-nodes) for(unsigned i=0; i<mesh.GetNumNodes(); i++) { double x = mesh.GetNode(i)->rGetLocation()[0]; double y = mesh.GetNode(i)->rGetLocation()[1]; double z = mesh.GetNode(i)->rGetLocation()[2]; if(fabs(z)<1e-8) // ie if z=0 { if( fabs(x-0.5)<1e-8 || fabs(y-0.5)<1e-8 ) // if x=0.5 or y=0.5 { unsigned num_surf_elems_contained_in = 1; if( fabs(x+y-1.0)<1e-8 ) // if x=0.5 AND y=0.5 { num_surf_elems_contained_in = 2; } // interior node on traction surface TS_ASSERT_DELTA(vec_repl[4*i], num_surf_elems_contained_in * t1/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+1], num_surf_elems_contained_in * t2/6.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+2], num_surf_elems_contained_in * t3/6.0, 1e-8); } else { TS_ASSERT_DELTA(vec_repl[4*i], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+1], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+2], 0.0, 1e-8); } } else { TS_ASSERT_DELTA(vec_repl[4*i], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+1], 0.0, 1e-8); TS_ASSERT_DELTA(vec_repl[4*i+2], 0.0, 1e-8); } } for(unsigned i=0; i<mesh.GetNumNodes(); i++) { TS_ASSERT_DELTA(vec_repl[4*i+3], 0.0, 1e-8); } PetscTools::Destroy(vec); }
void TestApplyToLinearSystem() { const int SIZE = 10; LinearSystem linear_system(SIZE, SIZE); for (int i=0; i<SIZE; i++) { for (int j=0; j<SIZE; j++) { // LHS matrix is all 1s linear_system.SetMatrixElement(i,j,1); } // RHS vector is all 2s linear_system.SetRhsVectorElement(i,2); } linear_system.AssembleIntermediateLinearSystem(); Node<3>* nodes_array[SIZE]; BoundaryConditionsContainer<3,3,1> bcc3; // Apply dirichlet boundary conditions to all but last node for (int i=0; i<SIZE-1; i++) { nodes_array[i] = new Node<3>(i,true); ConstBoundaryCondition<3>* p_boundary_condition = new ConstBoundaryCondition<3>(-1); bcc3.AddDirichletBoundaryCondition(nodes_array[i], p_boundary_condition); } ////////////////////////// // 2010 AD code from here ////////////////////////// // apply dirichlet bcs to matrix but not rhs vector bcc3.ApplyDirichletToLinearProblem(linear_system, true, false); ReplicatableVector vec_repl(linear_system.GetRhsVector()); for (unsigned i=0; i<(unsigned)SIZE; i++) { TS_ASSERT_EQUALS(vec_repl[i], 2.0); } // now apply to the rhs vector bcc3.ApplyDirichletToLinearProblem(linear_system, false, true); linear_system.AssembleFinalLinearSystem(); ////////////////////////// // 2007 AD code from here ////////////////////////// /* * Based on the original system and the boundary conditions applied in a non-symmetric * manner, the resulting linear system looks like: * * 1 0 0 ... 0 * 0 1 0 ... 0 * 0 0 1 ... 0 * ... * 1 1 1 ... 1 * */ /// \todo: this is very naughty. Must be checked in parallel as well. PetscInt lo, hi; PetscMatTools::GetOwnershipRange(linear_system.rGetLhsMatrix(), lo, hi); for(int row=lo; row<hi; row++) { if(row<SIZE-1) { for (int column=0; column<row; column++) { TS_ASSERT_EQUALS(linear_system.GetMatrixElement(row,column), 0); } TS_ASSERT_EQUALS(linear_system.GetMatrixElement(row,row), 1); for (int column=row+1; column<SIZE; column++) { TS_ASSERT_EQUALS(linear_system.GetMatrixElement(row,column), 0); } } if(row==SIZE-1) { for (int column=0; column<SIZE; column++) { TS_ASSERT_EQUALS(linear_system.GetMatrixElement(row,column), 1); } } } Vec solution = linear_system.Solve(); DistributedVectorFactory factory(solution); DistributedVector d_solution = factory.CreateDistributedVector( solution ); for (DistributedVector::Iterator index = d_solution.Begin(); index != d_solution.End(); ++index) { double expected = index.Global < SIZE-1 ? -1.0 : 11.0; TS_ASSERT_DELTA(d_solution[index], expected, 1e-6 ); } for (int i=0; i<SIZE-1; i++) { delete nodes_array[i]; } PetscTools::Destroy(solution); }