Implémentation

We start by setting the Feel++ environment

    using namespace Feel;
    po::options_description laplacianoptions( "Elasticity options" );
    laplacianoptions.add_options()
        ( "E", po::value<double>()->default_value( 1.0e6 ), "Young modulus" )
        ( "nu", po::value<double>()->default_value( 0.3 ), "Poisson ratio" )
        ( "no-solve", po::value<bool>()->default_value( false ), "No solve" )
        ( "weakdir", po::value<bool>()->default_value( false ), "use weak dirichlet" )
        ( "gamma", po::value<double>()->default_value( 100 ), "penalisation term" )
        ( "nullspace", po::value<bool>()->default_value( false ), "add null space" )
        ;

    Environment env( _argc=argc, _argv=argv,
                   _desc=laplacianoptions,
                   _about=about(_name="qs_elasticity",
                                _author="Feel++ Consortium",
                                _email="feelpp-devel@feelpp.org"));

Next, we define the Mesh \(\cal{T}_h\)

    tic();
    auto mesh = loadMesh(_mesh=new Mesh<Simplex<FEELPP_DIM,1>>);
    toc("loadMesh");

Next, we define the function space \(X^1_h\)

    tic();
    auto Vh = Pchv<1>( mesh );
    toc("Vh");

    auto u = Vh->element("u");
    auto v = Vh->element("v");
    auto nu = doption(_name="nu");
    auto E = doption(_name="E");
    auto lambda = E*nu/( (1+nu)*(1-2*nu) );
    auto mu = E/(2*(1+nu));
    auto deft = sym(gradt(u));
    auto def = sym(grad(u));
    auto Id = eye<FEELPP_DIM,FEELPP_DIM>();
    auto sigmat = lambda*trace(deft)*Id + 2*mu*deft;
    auto sigma = lambda*trace(def)*Id + 2*mu*def;
    auto f = expr<FEELPP_DIM,1>( soption(_name="functions.f"), "f" );
    auto g = expr<FEELPP_DIM,1>( soption(_name="functions.g"), "g" );

and the linear \(\ell\) and bilinear form \(a\)

    tic();
    auto l = form1( _test=Vh );
    l = integrate(_range=elements(mesh),
                  _expr=inner(f,id(v)));
    toc("l");

    tic();
    auto a = form2( _trial=Vh, _test=Vh);
    a = integrate(_range=elements(mesh),
                  _expr=inner( sigmat, grad(v) ) );

    if ( boption(_name="weakdir") )
    {
        double penaldir = doption(_name="gamma");
        a += integrate(_range=markedfaces(mesh,"Dirichlet"),
                       _expr=-inner(sigmat*N(),id(u)) + inner(-sigma*N()+std::max(2*mu,lambda)*penaldir*id(u)/hFace(),idt(u)) );

    }
    else
    {
        a+=on(_range=markedfaces(mesh,"Dirichlet"), _rhs=l, _element=u, _expr=g );
    }
    toc("a");

We solve the algebraic system \(A \disp{d} = b\)

    //! solve the linear system, find u s.t. a(u,v)=l(v) for all v
    if ( !boption( "no-solve" ) )
    {
        tic();
        boost::shared_ptr<NullSpace<double> > myNullSpace( new NullSpace<double>(backend(),qsNullSpace(Vh,mpl::int_<FEELPP_DIM>())) );
        backend()->attachNearNullSpace( myNullSpace );
        if ( boption(_name="nullspace") )
            backend()->attachNearNullSpace( myNullSpace );

        a.solve(_rhs=l,_solution=u);
        toc("a.solve");
    }

We attach to the algebraic backend the near null space that may be useful for Multigrid methods and optionally the null space. To do that we build a basis spanning the (near) null space in 2D and 3D.

template <typename SpaceType>
NullSpace<double> qsNullSpace( SpaceType const& space, mpl::int_<2> /**/ ) (1)
{
    auto mode1 = space->element( oneX() ); (2)
    auto mode2 = space->element( oneY() ); (2)
    auto mode3 = space->element( vec(Py(),-Px()) ); (3)
    NullSpace<double> userNullSpace( { mode1,mode2,mode3 } ); (4)
    return userNullSpace;
}
template <typename SpaceType>
NullSpace<double> qsNullSpace( SpaceType const& space, mpl::int_<3> /**/ ) (5)
{
    auto mode1 = space->element( oneX() ); (6)
    auto mode2 = space->element( oneY() ); (6)
    auto mode3 = space->element( oneZ() ); (6)
    auto mode4 = space->element( vec(Py(),-Px(),cst(0.)) ); (7)
    auto mode5 = space->element( vec(-Pz(),cst(0.),Px()) ); (7)
    auto mode6 = space->element( vec(cst(0.),Pz(),-Py()) ); (7)
    NullSpace<double> userNullSpace( { mode1,mode2,mode3,mode4,mode5,mode6 } ); (9)
    return userNullSpace;
}
1 basis for translation \(\{(1,0,0),(0,1,0),(0,0,1)\}\)
2 basis for rotation \(\{(y,-x,0),(-z,0,x),(0,z,-y)\}\)
3 build the null space spanned by the translation and rotation basis

Finally, we export the solution for visualization in Paraview

    tic();
    auto e = exporter( _mesh=mesh );
    e->addRegions();
    e->add( "u", u );
    e->save();
    toc("Exporter");