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);
    }
Пример #2
0
    /*
     * 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);
    }