Linear Time Dependent Problem

This example is the same example as FESolvers.jl's transient heat flow which was taken from Ferrite.jl's transient heat flow. Please see the theoretical derivations in those examples, with the specific formulation used here in the former.

Commented Program

Now we solve the problem by using FerriteProblems. The full program, without comments, can be found in the next section.

First we load required packages

using Ferrite, FerriteProblems, FerriteAssembly, FESolvers
import FerriteProblems as FP
import FerriteAssembly as FA
import FerriteAssembly.ExampleElements: TransientFourier

Physics

The transient heat element, TransientFourier, is available from FerriteAssembly.ExampleElements, noting that in the original example, the heat capacity, c=1, is implicitly assumed.

material() = TransientFourier(#=k=#1.0-3, #=c=#1.0)
material (generic function with 1 method)

The material doesn't include the heat source: We later add this with FerriteAssembly's LoadHandler, which works similar to the ConstraintHandler.

Problem setup

We start by a function that will create the problem definition

function create_definition()
    grid = generate_grid(Quadrilateral, (100, 100));
    ip = Lagrange{RefQuadrilateral,1}()
    cellvalues = CellValues(
        QuadratureRule{RefQuadrilateral}(2), ip);

    dh = DofHandler(grid); add!(dh, :u, ip); close!(dh)

    # Boundary conditions
    # Zero pressure on ∂Ω₁ and linear ramp followed by constant pressure on ∂Ω₂
    max_temp = 100; t_rise = 100
    ch = ConstraintHandler(dh);
    ∂Ω₁ = union(getfaceset.((grid,), ["left", "right"])...)
    add!(ch, Dirichlet(:u, ∂Ω₁, (x, t) -> 0));
    ∂Ω₂ = union(getfaceset.((grid,), ["top", "bottom"])...)
    add!(ch, Dirichlet(:u, ∂Ω₂, (x, t) -> max_temp * clamp(t / t_rise, 0, 1)))
    close!(ch)

    # Body load: constant heat source, f=5.0e-1
    lh = LoadHandler(dh)
    add!(lh, BodyLoad(:u, 2, Returns(5.0e-1)))

    # Create and return the `FEDefinition`
    domainspec = DomainSpec(dh, material(), cellvalues)
    return FEDefinition(domainspec; ch, lh)
end;

Postprocessing

After defining all the physics and problem setup, we must decide what data to save. In this example, we use the vtk-file exports as in the original example. To this end, we define the custom postprocessing struct

struct TH_PostProcessing{PVD}
    pvd::PVD
end
TH_PostProcessing() = TH_PostProcessing(paraview_collection("transient-heat.pvd"));

And the postprocessing function that is called after each time step

function FESolvers.postprocess!(post::TH_PostProcessing, p, solver)
    step = FESolvers.get_step(solver)
    if step < 5 || mod(step, 20) == 0
        @info "postprocessing step $step"
        dh = FP.get_dofhandler(p)
        vtk_grid("transient-heat-$step", dh) do vtk
            vtk_point_data(vtk, dh, FP.getunknowns(p))
            vtk_save(vtk)
            post.pvd[step] = vtk
        end
    end
end;

At the end of the simulation, we want to finish all IO operations. We can then define the function close_postprocessing which will be called even in the case that an error is thrown during the simulation

function FP.close_postprocessing(post::TH_PostProcessing, p)
    vtk_save(post.pvd)
end;

And now we create the problem type, and define the QuasiStaticSolver with the LinearProblemSolver as well as fixed time steps

def = create_definition()
post = TH_PostProcessing()
problem = FerriteProblem(def, post)
solver = QuasiStaticSolver(;nlsolver=LinearProblemSolver(), timestepper=FixedTimeStepper(collect(0.0:1.0:200)));

Finally, we can solve the problem

solve_problem!(problem, solver);
[ Info: postprocessing step 1
[ Info: postprocessing step 2
[ Info: postprocessing step 3
[ Info: postprocessing step 4
[ Info: postprocessing step 20
[ Info: postprocessing step 40
[ Info: postprocessing step 60
[ Info: postprocessing step 80
[ Info: postprocessing step 100
[ Info: postprocessing step 120
[ Info: postprocessing step 140
[ Info: postprocessing step 160
[ Info: postprocessing step 180
[ Info: postprocessing step 200

Plain program

Here follows a version of the program without any comments. The file is also available here: transient_heat.jl.

using Ferrite, FerriteProblems, FerriteAssembly, FESolvers
import FerriteProblems as FP
import FerriteAssembly as FA
import FerriteAssembly.ExampleElements: TransientFourier

material() = TransientFourier(#=k=#1.0-3, #=c=#1.0)

function create_definition()
    grid = generate_grid(Quadrilateral, (100, 100));
    ip = Lagrange{RefQuadrilateral,1}()
    cellvalues = CellValues(
        QuadratureRule{RefQuadrilateral}(2), ip);

    dh = DofHandler(grid); add!(dh, :u, ip); close!(dh)

    # Boundary conditions
    # Zero pressure on ∂Ω₁ and linear ramp followed by constant pressure on ∂Ω₂
    max_temp = 100; t_rise = 100
    ch = ConstraintHandler(dh);
    ∂Ω₁ = union(getfaceset.((grid,), ["left", "right"])...)
    add!(ch, Dirichlet(:u, ∂Ω₁, (x, t) -> 0));
    ∂Ω₂ = union(getfaceset.((grid,), ["top", "bottom"])...)
    add!(ch, Dirichlet(:u, ∂Ω₂, (x, t) -> max_temp * clamp(t / t_rise, 0, 1)))
    close!(ch)

    # Body load: constant heat source, f=5.0e-1
    lh = LoadHandler(dh)
    add!(lh, BodyLoad(:u, 2, Returns(5.0e-1)))

    # Create and return the `FEDefinition`
    domainspec = DomainSpec(dh, material(), cellvalues)
    return FEDefinition(domainspec; ch, lh)
end;

struct TH_PostProcessing{PVD}
    pvd::PVD
end
TH_PostProcessing() = TH_PostProcessing(paraview_collection("transient-heat.pvd"));

function FESolvers.postprocess!(post::TH_PostProcessing, p, solver)
    step = FESolvers.get_step(solver)
    if step < 5 || mod(step, 20) == 0
        @info "postprocessing step $step"
        dh = FP.get_dofhandler(p)
        vtk_grid("transient-heat-$step", dh) do vtk
            vtk_point_data(vtk, dh, FP.getunknowns(p))
            vtk_save(vtk)
            post.pvd[step] = vtk
        end
    end
end;

function FP.close_postprocessing(post::TH_PostProcessing, p)
    vtk_save(post.pvd)
end;

def = create_definition()
post = TH_PostProcessing()
problem = FerriteProblem(def, post)
solver = QuasiStaticSolver(;nlsolver=LinearProblemSolver(), timestepper=FixedTimeStepper(collect(0.0:1.0:200)));

solve_problem!(problem, solver);

# This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl

This page was generated using Literate.jl.