Home

Awesome

jFEMTools

Lifecycle Travis AppVeyor Coverage Status codecov.io

Tools for FEM and VEM code

VEM implementation based on:

FEM implementation based on

FEM

    ContinuousLagrange(:Shape, order)

where :Shape = :Triangle, :Quad, :Hexahedron, :Tetrahedron

Example

See examples directory for more information. Below, a simple Poisson PDE with homogeneous Dirichlet boundary conditions, in a 3D unitary cube.

using jFEMTools
import Tensors: Vec,  ⋅
using SparseArrays
import WriteVTK
const jF = jFEMTools

# Problem: 

# Δu = f  in Ω
# u  = 0  in ∂Ω

# We start  generating a simple grid with 10x10x10 Hexahedron elements
# on unitary cube. The generator defaults to the unit hyper_cube.
mesh = hyper_rectagle_mesh2(HexahedronCell,(10,10,10))

# ### Initiate finite element function Space
dim = jF.getdim(mesh)
P1 = ContinuousLagrange(:Hexahedron,1)
Wh = FEMFunctionSpace(mesh, P1, 1)

# Declare variables
u_h = TrialFunction(Wh)
#v_h = TestFunction(Wh)

# ### Degrees of freedom
# We create the `DofHandler` and then add a single field called `u_h`.
dh = DofHandler(mesh,[u_h])

# This function returns a sparse matrix with the correct elements stored.
K = jF.create_sparsity_pattern(dh);

# ### Boundary conditions
dbc = Dirichlet(dh, u_h, "boundary", 0.0)

# ### RHS function
f(x::Vec{dim}) where {dim} = 2*π^2*sin(π*x[1])*sin(π*x[2])*sin(π*x[3])

# ### Assembling the linear system
function doassemble(Wh, K::SparseMatrixCSC, dh::jF.DofHandler)
  # Allocate the element stiffness matrix and element force vector
  # global and local matrices
  n_basefuncs = jF.getnbasefunctions(Wh)
  Ke = zeros(n_basefuncs, n_basefuncs)
  fe = zeros(n_basefuncs)
  b = zeros(jF.ndofs(dh))
  cell_dofs = Vector{Int}(undef, jF.ndofs_per_cell(dh))
  assembler = jF.start_assemble(K, b)

  # It is now time to loop over all the cells in our mesh
  @inbounds for (cellcount, cell) in enumerate(CellIterator(mesh))
      # We recompute local data for each cell
      fill!(Ke, 0)
      fill!(fe, 0)
      jF.reinit!(Wh, cell)

      # Loop over all the quadrature points in the cell and
      # assemble the contribution to `Ke` and `fe`. The integration weight
      # can be queried using `getdetJdV`.
      for q_point in 1:jF.getnquadpoints(Wh)
          dΩ = jF.getdetJdV(Wh, q_point)
          fh = jF.function_value(f, Wh, cell, q_point)
          # For each quadrature point we loop over all the (local) shape functions.
          # We need the value and gradient of the testfunction `v` and also the gradient
          # of the trial function `u`.
          for i in 1:n_basefuncs
              v  = jF.shape_value(Wh, q_point, i)
              ∇v = jF.shape_gradient(Wh, q_point, i)
              fe[i] += fh*v * dΩ
              for j in 1:n_basefuncs
                  ∇u = jF.shape_gradient(Wh, q_point, j)
                  Ke[i, j] += (∇v ⋅ ∇u) * dΩ
              end
          end
      end
      # The last step in the element loop is to assemble `Ke` and `fe`
      # into the global `K` and `f` with `assemble!`.
      jF.celldofs!(cell_dofs, dh, cell)
      jF.assemble!(assembler, cell_dofs, fe, Ke)
  end
  return K, b
end

# ### Solution of the system
K, b = doassemble(Wh, K, dh);

# To account for the boundary conditions we use the `apply!` function.
apply!(K,b,dbc);
u = K \ b;

# Save approximation on vtu file
vi = jFEMTools.vertexdofs(dh, u_h);
vtk_file = vtk_grid("poisson3D", mesh)
vtk_file["u", WriteVTK.VTKPointData()] = u[vi]
outfiles = WriteVTK.vtk_save(vtk_file)

VEM

Example

We solve a Poisson PDE using Virtual Element Method:

using jFEMTools
import Tensors
const jF = jFEMTools

mesh = unitSquareMesh2(RectangleCell, (3,3));

# forcing function
rhs(x::Tensors.Vec{2}) = 2*π^2*sin(π*x[1])*sin(π*x[2])

degree = 2;
dim = jF.getdim(mesh);
element = PoissonVirtualElement(dim,degree);
u = TrialFunction(VEMFunctionSpace(mesh,element))
dofs = DofHandler(mesh, [u]);
operators = VEMOperators(dofs, u;load = rhs);

K = assemble_stiffnessMat(operators);
b = assemble_load(operators);

# Boundary condition
g(x::Tensors.Vec{2}) = sin(π * x[2])*sin(π * x[1]);
dbc = Dirichlet(dofs,u,"boundary",g);
apply!(K,b,dbc);

#Solve
x = K\b

#Plot numerical solution against exact, using Makie
using Makie
import AbstractPlotting
include("src/plot_recipes.jl")
scene = Scene(resolution = (400, 200))
# Plot approximation
vi = jFEMTools.vertexdofs(dofs, u)
plot!(scene, mesh, color = x[vi])

#Plot exact solution
vv = get_vertices_matrix(mesh)
xx = [g(vv) for vv in mesh.vertices];
plot!(scene, mesh, color = xx)