Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-overlapping dof handler #7

Merged
merged 25 commits into from
Sep 12, 2023
Merged

Conversation

termi-official
Copy link
Member

@termi-official termi-official commented Jun 7, 2023

This implements the dof handler for fields on non-overlapping distributed grids.

TODO

  • Improve the interface (see examples in docs for concrete information)
  • Update to new dof management interface
  • Remove legacy code

@termi-official termi-official changed the title Non-overlapping DofHandler Non-overlapping dof handler Jun 7, 2023
@termi-official
Copy link
Member Author

Depends on Ferrite-FEM/Ferrite.jl#655

@termi-official
Copy link
Member Author

termi-official commented Jun 7, 2023

This branch reached parity with Ferrite-FEM/Ferrite.jl#486 on the last commit. Reproducer for the distributed assembly of the Cooks membrane problem:

using FerriteDistributed
using IterativeSolvers
using PartitionedArrays, Metis
using BlockArrays

# Launch MPI
MPI.Init()

# First we generate a simple grid, specifying the 4 corners of Cooks membrane.
function create_cook_grid(nx, ny)
    corners = [Vec{2}((0.0,   0.0)),
               Vec{2}((48.0, 44.0)),
               Vec{2}((48.0, 60.0)),
               Vec{2}((0.0,  44.0))]
    grid = generate_grid(Triangle, (nx, ny), corners);
    ## facesets for boundary conditions
    addfaceset!(grid, "clamped", x -> norm(x[1])  0.0);
    addfaceset!(grid, "traction", x -> norm(x[1])  48.0);
    return FerriteDistributed.NODGrid(MPI.COMM_WORLD, grid)
end;

# Next we define a function to set up our cell- and facevalues.
function create_values(interpolation_u, interpolation_p)
    ## quadrature rules
    qr      = QuadratureRule{RefTriangle}(3)
    face_qr = FaceQuadratureRule{RefTriangle}(3)

    ## geometric interpolation
    interpolation_geom = Lagrange{RefTriangle,1}()^2

    ## cell and facevalues for u
    cellvalues_u = CellValues(qr, interpolation_u, interpolation_geom)
    facevalues_u = FaceValues(face_qr, interpolation_u, interpolation_geom)

    ## cellvalues for p
    cellvalues_p = CellValues(qr, interpolation_p, interpolation_geom)

    return cellvalues_u, cellvalues_p, facevalues_u
end;


# We create a DofHandler, with two fields, `:u` and `:p`,
# with possibly different interpolations
function create_dofhandler(grid, ipu, ipp)
    dh = DofHandler(grid)
    push!(dh, :u, ipu) # displacement
    push!(dh, :p, ipp) # pressure
    close!(dh)
    return dh
end;

# We also need to add Dirichlet boundary conditions on the `"clamped"` faceset.
# We specify a homogeneous Dirichlet bc on the displacement field, `:u`.
function create_bc(dh)
    ch = ConstraintHandler(dh)
    add!(ch, Dirichlet(:u, getfaceset(getgrid(dh), "clamped"), (x,t) -> zero(Vec{2}), [1,2]))
    close!(ch)
    t = 0.0
    update!(ch, t)
    return ch
end;

# The material is linear elastic, which is here specified by the shear and bulk moduli
struct LinearElasticity{T}
    G::T
    K::T
end

# Now to the assembling of the stiffness matrix. This mixed formulation leads to a blocked
# element matrix. Since Ferrite does not force us to use any particular matrix type we will
# use a `PseudoBlockArray` from `BlockArrays.jl`.
function doassemble(cellvalues_u::CellValues, cellvalues_p::CellValues,
                    facevalues_u::FaceValues, grid::FerriteDistributed.NODGrid,
                    dh::FerriteDistributed.NODDofHandler{dim}, ch::ConstraintHandler, mp::LinearElasticity) where {dim}

    assembler = start_assemble(dh, distribute_with_mpi(LinearIndices((MPI.Comm_size(MPI.COMM_WORLD),))))
    nu = getnbasefunctions(cellvalues_u)
    np = getnbasefunctions(cellvalues_p)

    fe = PseudoBlockArray(zeros(nu + np), [nu, np]) # local force vector
    ke = PseudoBlockArray(zeros(nu + np, nu + np), [nu, np], [nu, np]) # local stiffness matrix

    ## traction vector
    t = Vec{2}((0.0, 1/16))
    ## cache ɛdev outside the element routine to avoid some unnecessary allocations
    ɛdev = [zero(SymmetricTensor{2, dim}) for i in 1:getnbasefunctions(cellvalues_u)]

    for cell in CellIterator(dh)
        fill!(ke, 0)
        fill!(fe, 0)
        assemble_up!(ke, fe, cell, cellvalues_u, cellvalues_p, facevalues_u, grid, mp, ɛdev, t)
        apply_local!(ke, fe, celldofs(cell), ch)
        Ferrite.assemble!(assembler, celldofs(cell), fe, ke)
    end

    return end_assemble(assembler)
end;

# The element routine integrates the local stiffness and force vector for all elements.
# Since the problem results in a symmetric matrix we choose to only assemble the lower part,
# and then symmetrize it after the loop over the quadrature points.
function assemble_up!(Ke, fe, cell, cellvalues_u, cellvalues_p, facevalues_u, grid, mp, ɛdev, t)

    n_basefuncs_u = getnbasefunctions(cellvalues_u)
    n_basefuncs_p = getnbasefunctions(cellvalues_p)
    u▄, p▄ = 1, 2
    reinit!(cellvalues_u, cell)
    reinit!(cellvalues_p, cell)

    ## We only assemble lower half triangle of the stiffness matrix and then symmetrize it.
    @inbounds for q_point in 1:getnquadpoints(cellvalues_u)
        for i in 1:n_basefuncs_u
            ɛdev[i] = dev(symmetric(shape_gradient(cellvalues_u, q_point, i)))
        end= getdetJdV(cellvalues_u, q_point)
        for i in 1:n_basefuncs_u
            divδu = shape_divergence(cellvalues_u, q_point, i)
            δu = shape_value(cellvalues_u, q_point, i)
            for j in 1:i
                Ke[BlockIndex((u▄, u▄), (i, j))] += 2 * mp.G * ɛdev[i]  ɛdev[j] *end
        end

        for i in 1:n_basefuncs_p
            δp = shape_value(cellvalues_p, q_point, i)
            for j in 1:n_basefuncs_u
                divδu = shape_divergence(cellvalues_u, q_point, j)
                Ke[BlockIndex((p▄, u▄), (i, j))] += -δp * divδu *end
            for j in 1:i
                p = shape_value(cellvalues_p, q_point, j)
                Ke[BlockIndex((p▄, p▄), (i, j))] += - 1/mp.K * δp * p *end

        end
    end

    symmetrize_lower!(Ke)

    ## We integrate the Neumann boundary using the facevalues.
    ## We loop over all the faces in the cell, then check if the face
    ## is in our `"traction"` faceset.
    @inbounds for face in 1:nfaces(cell)
        if (cellid(cell), face)  getfaceset(grid, "traction")
            reinit!(facevalues_u, cell, face)
            for q_point in 1:getnquadpoints(facevalues_u)
                dΓ = getdetJdV(facevalues_u, q_point)
                for i in 1:n_basefuncs_u
                    δu = shape_value(facevalues_u, q_point, i)
                    fe[i] += (δu  t) *end
            end
        end
    end
end

function symmetrize_lower!(K)
    for i in 1:size(K,1)
        for j in i+1:size(K,1)
            K[i,j] = K[j,i]
        end
    end
end;


function solve(ν, interpolation_u, interpolation_p)
    ## material
    Emod = 1.
    Gmod = Emod / 2(1 + ν)
    Kmod = Emod * ν / ((1+ν) * (1-2ν))
    mp = LinearElasticity(Gmod, Kmod)

    ## grid, dofhandler, boundary condition
    #n = 2
    grid = create_cook_grid(50, 40)
    dh = create_dofhandler(grid, interpolation_u, interpolation_p)
    ch = create_bc(dh)
    vtk_grid("cook_dgrid", dh) do vtk
        vtk_partitioning(vtk, grid)
    end
    ## cellvalues
    cellvalues_u, cellvalues_p, facevalues_u = create_values(interpolation_u, interpolation_p)

    ## assembly and solve
    K, f = doassemble(cellvalues_u, cellvalues_p, facevalues_u, grid, dh, ch, mp);
    # apply!(K, f, ch)
    u = cg(K, f);

    ## export
    filename = "cook_distributed_" * (isa(interpolation_u, Lagrange{RefTriangle,1}) ? "linear" : "quadratic") *
                         "_linear"
    vtk_grid(filename, dh) do vtkfile
        vtk_point_data(vtkfile, dh, u)
        vtk_partitioning(vtkfile, grid)
    end
    return u
end

linear    = Lagrange{RefTriangle,1}()
quadratic = Lagrange{RefTriangle,2}()
# u1 = solve(0.4999999, linear^2, linear);
u2 = solve(0.4999999, quadratic^2, linear);

@termi-official termi-official merged commit d48e1f1 into main Sep 12, 2023
@termi-official termi-official deleted the do/non-overlapping-dofhandler branch September 12, 2023 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant