API
The main types below are exported. Remaining functions are not exported to avoid polluting the name space. Tip: To simplify calling the following functions it is possible to write import FerriteProblems as FP as is done in the examples.
Main types
FerriteProblems.FerriteProblem — TypeFerriteProblem(def::FEDefinition, post=nothing, io=nothing; kwargs...)
FerriteProblem(def::FEDefinition, post, savefolder::String; kwargs...)Create a FerriteProblem from def. Postprocessing can be added as post, see FESolvers.postprocess!. File input/output using FerriteIO can be added with io. It is also possible to just give the savefolder, i.e. where to save the output when the default FerriteIO can be used.
Supported keyword arguments are
autodiffbuffer::Bool: ShouldFerriteAssembly.jl'sAutoDiffCellBufferbe used? This will make the assembly faster if automatic differentiation is used, and can also be used without automatic differentiation (but with a slight extra computational overhead)threading::Bool: Should threading be used?
FerriteProblems.FEDefinition — TypeFEDefinition(
domainspec::Union{DomainSpec,Dict{String,DomainSpec}};
ch, lh=LoadHandler(dh),
convergence_criterion=AbsoluteResidual(),
initial_conditions=NamedTuple(),
)Create the FEDefinition which can later be used to create a complete FerriteProblem. domainspec is the FerriteAssembly domain specification.
In addition, the following keyword arguments can be given
ch: The constraint handler,ConstraintHandler(Ferrite.jl)lh: The external load handler,LoadHandler(FerriteAssembly.jl)initial_conditions: NamedTuple with a functionf(x)for each field that has a nonzero initial condition. Used by theFerrite.jl'sapply_analytical!function. Example:initial_conditions = (u = x -> Vec((x[1]/10, 0.0)), p = x -> -100*x[2]). For fields not given here, the initial condition is zeros everywhere.convergence_criterion: Determines how to calculate the convergence measure including scaling. SeeConvergenceCriterion
FerriteProblems.FerriteIO — TypeFerriteIO(
folder::String, def::FEDefinition, post=nothing;
def_file="FEDefinition.jld2",
postfile="FEPost.jld2",
T=Float64,
nsteps_per_file=typemax(Int),
switchsize=Inf
)Constructor for creating a FerriteIO when simulating.
FerriteIO(filepath::String)Constructor for reading a FerriteIO that was saved during a simulation. The default filename is FerriteIO.jld2, located in the savefolder given to the FerriteProblem constructor when setting up the simulation.
Can be called either with the do-block (recommended),
FerriteIO(filepath) do io
# Do whatever with io
endor by manually closing,
io = FerriteIO(filepath)
# Do whatever with io
close(io)Convergence criteria
In normal usage, the following convergence criteria can be used
FerriteProblems.ConvergenceCriterion — TypeConvergenceCriterionThe abstract type ConvergenceCriterion is the supertype for all convergence criteria.
FerriteProblems.AbsoluteResidual — TypeAbsoluteResidual()The default convergence criterion that calculates the convergence measure as √(sum([r[i]^2 for i ∈ free dofs]) without any scaling.
FerriteProblems.RelativeResidualElementScaling — TypeRelativeResidualElementScaling(;p = 2, minfactors::Union{AbstractFloat,NamedTuple}=eps())Use Ferriteassembly.ElementResidualScaling with the exponent p to calculate the scaling for each field individually, based on the Lp-norm of each cell's residual. To avoid issues when all cells have zero residual (e.g. in the first time step), supply minfactors as the minimum scaling factor. The convergence measure is calculated with the following pseudo-code
val = 0.0
for field in Ferrite.getfieldnames(dh)
factor = max(element_residual_scaling[field], minfactors[field])
dofs = free_field_dofs[field] # Get the non-constrained dofs for `field`
val += sum(i->(r[i]/factor)^2, dofs)
end
return √valwhere the same minfactor is used for all fields if only a scalar value is given.
To create custom convergence criteria, the following functions may require overloading.
FerriteProblems.TolScaling — TypeTolScaling(criterion::ConvergenceCriterion, def::FEDefinition)The TolScaling type contains the criterion that determines how to scale the residuals to determine convergence. The constructor is specialized on typeof(criterion), creating the following fields:
assemscaling:scalingto be used byFerriteAssemblyto give potential scaling contribution based on each element's residualbuffer: Used to pre-calculate values, such as dof-ranges for each field that is used when calculating the convergence measure.
FESolvers.calculate_convergence_measure — Functioncalculate_convergence_measure(problem, Δa, iter) -> AbstractFloatCalculate a value to be compared with the tolerance of the nonlinear solver. A standard case when using Ferrite.jl is norm(getresidual(problem)[Ferrite.free_dofs(ch)]) where ch::Ferrite.ConstraintHandler. Δa is the update of the unknowns from the previous iteration. Note that iter=1 implies Δa=0
FerriteProblems.make_assemscaling — Functionmake_assemscaling(criterion, def)Create the scaling for use in FerriteAssembly if required by the given criterion. The default, if not overloaded, returns nothing.
Access functions
FerriteAssembly.get_dofhandler — MethodFerriteProblems.get_dofhandler(p::FerriteProblem)Get dh::Ferrite.AbstractDofHandler from the FerriteProblem (Technically overloaded from FerriteAssembly, but accessible via FerriteProblems)
FerriteProblems.get_constrainthandler — MethodFerriteProblems.get_constrainthandler(p::FerriteProblem)Get the ConstraintHandler from the FerriteProblem
FerriteProblems.get_loadhandler — MethodFerriteProblems.get_loadhandler(p::FerriteProblem)Get the LoadHandler from the FerriteProblem
FerriteAssembly.get_material — MethodFerriteProblems.get_material(p::FerriteProblem)
FerriteProblems.get_material(p::FerriteProblem, domain_name::String)Get the material in p. For multiple domains, it is necessary to give the domain_name for where to get the material. Note that this is type-unstable and should be avoided in performance-critical code sections. This function belongs to FerriteAssembly.jl, but can be accessed via FerriteProblems.get_material.
FESolvers.getjacobian — MethodFerriteProblems.getjacobian(p::FerriteProblem)Get the current jacobian matrix from p. Note that this function belongs to FESolvers.jl, but can be accessed via FerriteProblems.getjacobian
FESolvers.getunknowns — MethodFerriteProblems.getunknowns(p::FerriteProblem)Get the current vector of unknowns from p. Note that this function belongs to FESolvers.jl, but can be accessed via FerriteProblems.getunknowns
FESolvers.getresidual — MethodFerriteProblems.getresidual(p::FerriteProblem)Get the current residual vector from p. Note that this function belongs to FESolvers.jl, but can be accessed via FerriteProblems.getresidual
FerriteProblems.get_external_force — MethodFerriteProblems.getneumannforce(p::FerriteProblem)Get the current external force vector caused by Neumann boundary conditions. Note that this vector does not include external forces added during the cell assembly; only forces added with the NeumannHandler
FerriteProblems.get_old_unknowns — MethodFerriteProblems.get_old_unknowns(p::FerriteProblem)Get the vector of unknowns from the previously converged step
FerriteAssembly.get_state — MethodFerriteProblems.get_state(p::FerriteProblem)Get the current state variables
FerriteAssembly.get_old_state — MethodFerriteProblems.getoldstate(p::FerriteProblem)Get the state variables from the previously converged step
FerriteProblems.get_time — MethodFerriteProblems.get_time(p::FerriteProblem)Get the current time
FerriteProblems.get_old_time — MethodFerriteProblems.getoldtime(p::FerriteProblem)Get time of the previous converged step
Saving and loading data
FESolvers.handle_notconverged! — MethodFESolvers.handle_notconverged!(post, p::FerriteProblem, solver)Optional overload which is called when the problem doesn't converge for the current attempt. Allows for example to modify the problem or add special postprocessing to investigate convergence issues.
If the problem is modified, and an adaptive time stepper is used, the adaptive time stepper will still consider the problem as not converged, and adapt the time-stepping accordingly. Currently, there is no interface to prevent this, and usage with a fixed time stepping might make more sense. However, by modifying the solver's state, it is possible to trick the adaptive time stepper to not modify the time step, but this requires using non-stable and non-public API.
FESolvers.postprocess! — MethodFESolvers.postprocess!(p::FerriteProblem, solver)When FESolvers call this function for p::FerriteProblem, the following function
FESolvers.postprocess!(post, p::FerriteProblem, solver)is called where post=p.post (unless you define a different override). This allows you to easily define the dispatch on your postprocessing type as FESolvers.postprocess!(post::MyPostType, p, solver)
FerriteProblems.close_postprocessing — Functionclose_postprocessing(post::MyPostType, p::FerriteProblem)This function is called to close any open files manually created during the postprocessing with the custom postprocessing type MyPostType. Note that the file streams in p.io::FerriteIO are automatically closed and don't require any special handling.
FerriteProblems.addstep! — FunctionFerriteProblems.addstep!(io::FerriteIO, p::FerriteProblem)Add a new step to be saved by io at the time gettime(p) Must be called before adding any new data
FerriteProblems.gettimedata — Functiongettimedata(io::FerriteIO)
gettimedata(io::FerriteIO, step)FerriteProblems.savedofdata! — FunctionFerriteProblems.savedofdata!(io::FerriteIO, vals, dt_order=0, field="dof")Save data pertaining to each degree of freedom. Use a different field than "dof"` to save data located at each dof, but not the actual dof values (e.g. the residual vector)
FerriteProblems.getdofdata — FunctionFerriteProblems.getdofdata(io::FerriteIO, step, field="dof"; dt_order=0)Get the data saved by FerriteProblems.savedofdata! in step for field.
FerriteProblems.savenodedata! — FunctionFerriteProblems.savenodedata!(io::FerriteIO, vals, field, dt_order=0)Save data located at each node. By convention this should be indexed by the node numbers in the grid. (E.g. a Vector for all nodes or a Dict{Int} with keys the node numbers)
FerriteProblems.getnodedata — FunctionFerriteProblems.getnodedata(io::FerriteIO, step, field; dt_order=0)Get the data saved by FerriteProblems.savenodedata! in step for field.
FerriteProblems.savecelldata! — FunctionFerriteProblems.savecelldata!(io::FerriteIO, vals, field, dt_order=0)Save data for each cell. By convention this should be indexed by the cell numbers in the grid. (E.g. a Vector for all cells or a Dict{Int} with keys the cell indices)
FerriteProblems.getcelldata — FunctionFerriteProblems.getcelldata(io::FerriteIO, step, field; dt_order=0)Get the data saved by FerriteProblems.savecelldata! in step for field.
FerriteProblems.saveipdata! — FunctionFerriteProblems.saveipdata!(io::FerriteIO, vals, field, dt_order=0)Save data for each integration point in cells in the grid. By convention the data for each cell should be indexed by the cell numbers in the grid. (E.g. a Vector for all cells or a Dict{Int} with keys the cell indices) Note that it is on the user to know how the integration points are numbered, i.e. which QuadratureRule that was used.
FerriteProblems.getipdata — FunctionFerriteProblems.getipdata(io::FerriteIO, step, field; dt_order=0)Get the data saved by FerriteProblems.saveipdata! in step for field.
FerriteProblems.saveglobaldata! — FunctionFerriteProblems.saveglobaldata!(io::FerriteIO, vals, field, dt_order=0)Save data that is global to the entire simulation, i.e. global quantites such as reaction forces, total dissipation, etc.
FerriteProblems.getglobaldata — FunctionFerriteProblems.getglobaldata(io::FerriteIO, step, field; dt_order=0)Get the data saved by FerriteProblems.saveglobaldata! in step for field.
FerriteProblems.getdef — FunctionFerriteProblems.getdef(io::FerriteIO)Load the FEDefinition from the results saved by io
FerriteProblems.getpost — FunctionFerriteProblems.getpost(io::FerriteIO)Load the user defined post from the results saved by io