Setup

Setup API

FerriteAssembly.DomainSpec — Type
DomainSpec(sdh::SubDofHandler, material, fe_values; set=_getcellset(sdh), colors_or_chunks=nothing, user_data=nothing)
DomainSpec(dh::DofHandler, material, fe_values; set=1:getncells(dh), colors=nothing, chunks=nothing, user_data=nothing)

Create a DomainSpec that can be used to set up a domain buffer.

  • sdh/dh: Give the DofHandler for the domain in question, or a SubDofHandler in case there are more than one in DofHandler (See Ferrite.jl's documentation)
  • material: Used for dispatch on the utilized worker's function.
  • fe_values: CellValues or FacetValues depending on the type of domain.
  • set: The items in the domain, the element type determines the type of domain
    • Cell domain: Int
    • Facet domain: FacetIndex
  • colors::Vector{Vector{I}}: used to avoid race conditions when multithreading. For cell domains, I=Int, and for facet domains, I can be either Int (denoting cell numbers) or FacetIndex for actual facets. If I=Int, it will be converted to FacetIndex internally. If colors=nothing and chunks=nothing, Ferrite.jl's default coloring algorithm is used.
  • chunks::Vector{Vector{Vector{I}}}. During multithreading, each task works with items in one chunk::Vector{I} at a time. Items in chunks[k][i] and chunks[k][j] should be independent (i.e. not share dofs). If given, this input takes precedence over colors. For chunks, I must be Int for cell domains and FacetIndex for facet domains.
  • user_data: Can be whatever the user wants to and is passed along by reference everywhere. It is accessible from the ItemBuffer (e.g. CellBuffer) given to the worker's function via the get_user_data function. However, since it is passed by reference, modifying values during work, care must be taken to ensure thread safety. To avoid allocations, caches can be created separately with allocate_cell_cache and allocate_facet_cache.
source
FerriteAssembly.setup_domainbuffer — Function
setup_domainbuffer(domain::DomainSpec; a=nothing, threading=false, autodiffbuffer=false, num_tasks=Threads.nthreads())

Setup a domain buffer for a single grid domain, domain.

  • a::Vector: The global degree of freedom values are used to pass the local element dof values to the create_cell_state function, making it possible to create the initial state dependent on the initial conditions for the field variables.
  • threading: Should a ThreadedDomainBuffer be created to work the grid multithreaded if supported by the used worker?
  • num_tasks: The number of tasks to spawn during threaded assembly. Only applicable for threading = true.
  • autodiffbuffer: Should a custom itembuffer be used to speed up the automatic differentiation (if supported by the itembuffer)

An empty domain.set (e.g. because the given set does not intersect the SubDofHandler's cellset) is supported as a no-op: the resulting buffer is skipped when work!ing.

source
FerriteAssembly.setup_domainbuffers — Function
setup_domainbuffers(domains::Dict{String,DomainSpec}, suppress_warnings = false; kwargs...)

Setup multiple domain buffers, one for each DomainSpec in domains. Set suppress_warnings = true to suppress warnings checking for typical input errors when setting up multiple domains. See setup_domainbuffer for description of the keyword arguments.

Note: All domains in domains must be of the same kind: either all cell domains or all facet (or other non-cell) domains. Mixing cell and facet domains in a single dictionary is not supported; use separate dictionaries (and separate calls to setup_domainbuffers/work!) instead.

source

AbstractDomainBuffer

The domain buffer be a DomainBuffer, ThreadedDomainBuffer, or a Dict{String} with eltype of one of the former. The following functions are defined for these buffers:

FerriteAssembly.get_material — Method
get_material(dbs::Dict{String,AbstractDomainBuffer}, domain::String)
get_material(db::AbstractDomainBuffer)
get_material(sim::Simulation)

Get the material for the domain represented by db or dbs[domain].

Note: This always returns the base material. If db is threaded, work! uses independent copies of this material, so mutating the returned object will not be reflected when doing threaded assembly. However, in most cases the material is immutable, and this is not a problem.

source
FerriteAssembly.get_dofhandler — Method
get_dofhandler(dbs::Dict{String,AbstractDomainBuffer})
get_dofhandler(db::AbstractDomainBuffer)
get_dofhandler(sim::Simulation)

Get the dofhandler stored in db. Note that this is the global dofhandler, and not the SubDofHandler that is local to a specific domain.

source
FerriteAssembly.get_state — Method
get_state(dbs::Dict{String,AbstractDomainBuffer}, domain::String)
get_state(db::Union{AbstractDomainBuffer,Dict{String,AbstractDomainBuffer}})
get_state(sim::Simulation[, domain::String])

Get the states::StateVector{S}, where S is the type of the state for each entity in the domain, stored in the db or dbs[domain]. StateVector{S} <: AbstractDict{Int,S} is indexed by cell number and supports the usual read-only AbstractDict interface (keys, values, pairs, iteration, haskey, get, equality), but is not a Dict. If no domain is given for multiple domains, a Dict{String} is returned with state variables for each domain.

source
FerriteAssembly.get_old_state — Method
get_old_state(dbs::Dict{String,AbstractDomainBuffer}, domain::String)
get_old_state(db::Union{AbstractDomainBuffer,Dict{String,AbstractDomainBuffer}})
get_old_state(sim::Simulation[, domain::String])

Get the states::StateVector{S}, where S is the type of the state for each entity in the domain, stored in the db or dbs[domain]. StateVector{S} <: AbstractDict{Int,S} is indexed by cell number and supports the usual read-only AbstractDict interface (keys, values, pairs, iteration, haskey, get, equality), but is not a Dict. If no domain is given for multiple domains, a Dict{String} is returned with state variables for each domain.

source
FerriteAssembly.getset — Function
getset(dbs::Dict{String,AbstractDomainBuffer}, domain::String)
getset(db::AbstractDomainBuffer)
getset(sim::Simulation[, domain::String])

Get the set of items stored in db or dbs[domain]

source
FerriteAssembly.update_states! — Method
update_states!(db::Dict{String,AbstractDomainBuffer}; mode::Symbol = :copy)
update_states!(db::AbstractDomainBuffer; mode::Symbol = :copy)
update_states!(sim::Simulation; mode::Symbol = :copy)

Update the states such that old_states = states (the just-converged values) for the states stored in db.

mode selects how this is done:

  • mode = :copy (default): copies the values from states into old_states; states itself is left untouched. This means both old_states and states correctly hold the just-converged values directly after the call — safe to read (e.g. for postprocessing) immediately afterwards. If create_cell_state returns a mutable AbstractArray, this reuses that array's own storage (no allocation for the array itself, though copying non-isbits elements into it may still allocate — unless FerriteAssembly.copy_state! is overloaded for the element type, see below), and it must therefore keep the same axes between calls (ArgumentError otherwise). Any other non-isbits cell state must have a FerriteAssembly.copy_state or FerriteAssembly.copy_state! method — otherwise a MethodError is thrown. This is a breaking change from previous releases (which behaved like mode = :flip): a mutable, non-array cell state without a copy_state/copy_state! overload that used to work now throws; use mode = :flip to keep the old behavior for such states.
  • mode = :flip: cheaply swaps the references of old_states and states (no copying, no allocation, and no copy_state requirement — this is the behavior of update_states! in releases prior to this change). After the call, states (the "new" container) holds the stale values from before this step, not the just-converged ones.
    Warning

    Under mode = :flip, states must not be read again until it has been overwritten by the next call to work! — including implicitly, e.g. via a default QuadPointEvaluator reading get_state/s during postprocessing right after update_states!. Reading it earlier silently observes the previous step's data. If you need to read the just-converged state after updating (e.g. for postprocessing), use the default mode = :copy instead.

source
FerriteAssembly.revert_states! — Method
revert_states!(db::Dict{String,AbstractDomainBuffer})
revert_states!(db::AbstractDomainBuffer)
revert_states!(sim::Simulation)

Update the states such that states = old_states for the states stored in db, i.e. the opposite direction of update_states!. This is useful when retrying a time increment after a non-converged solution, when the current (new) state is used as an initial guess (typical in staggered solution schemes). Requires FerriteAssembly.copy_state or FerriteAssembly.copy_state! for non isbits with the same requirements as stated in update_states! with mode = :copy.

source
FerriteAssembly.set_time_increment! — Method
set_time_increment!(db::Dict{String,AbstractDomainBuffer}, Δt)
set_time_increment!(db::AbstractDomainBuffer, Δt)
set_time_increment!(sim::Simulation, Δt)

Update the time increment stored in db, which is passed on to the stored AbstractItemBuffer

source

Updating materials

Material update contract

get_material (above) always returns the base material; a threaded domain buffer's task-local copies, used by threaded work!, are not kept in sync with mutations to that returned object. Use replace_material instead to create a new buffer if this is required.

FerriteAssembly.replace_material — Method
replace_material(db::Dict{String,AbstractDomainBuffer}, replacement_function)
replace_material(db::AbstractDomainBuffer, replacement_function)

Return a new instance of db where as much as possible is copied by reference, and where the stored material, m, is replaced by replacement_function(m).

source
FerriteAssembly.replace_material — Method
replace_material(dbs::Dict{String,AbstractDomainBuffer}, domain::String, replacement_function)

Return a new instance of dbs where as much as possible is copied by reference, and where the material, m, of dbs[domain] is replaced by replacement_function(m). Other domains are copied by reference, unchanged.

source

Coupled simulations

The Simulation type contains an abstract domain buffer, along with (optionally) the global degree of freedom values, which are used to get the local values for each item.

A CoupledSimulations group is built, once, from a set of named Simulations, and gives access to values from other simulations at the item level (e.g. state variables and local dof-values) via get_coupled_buffer. For example, when solving two separate problems in parallel using staggered iterations. See the Phase-field fracture tutorial for an example. Coupling is resolved entirely at group-construction time; work!ing a group member (work!(worker, group.member_name)) never re-discovers or rebuilds the coupling.

Concurrency contract

Coupled buffers reference the partner's actual mutable storage — no copies are made. work! calls that share any of that storage must therefore not run concurrently with each other. This includes: working two members of the same group at the same time; working a member of a group at the same time as its own original (pre-group) source Simulation; working members of two different groups that were built from the same source Simulation(s) (e.g. a group and a later replace_material-built group that still shares some members' storage by reference); and re-entrant work! calls that would reuse the same scratch. Ordinary staggered iteration — working one member, then another, in sequence — is safe; it is simultaneous access to shared scratch that is not.

FerriteAssembly.Simulation — Type
Simulation(db, a = nothing, aold = nothing)

A Simulation is a collection of the simulation domain(s) db, and the global degree of freedom vectors, a and aold.

Note: If a or aold are not provided, the local vectors will have NaN values.

source
FerriteAssembly.CoupledSimulations — Type
CoupledSimulations(primaries::NamedTuple; refs::NamedTuple = NamedTuple())

Build a group of mutually-wired simulations from primaries (members that read partners and are worked via the group) and, optionally, refs (members with no outgoing dependencies, still accessible/workable through the group but never rewired themselves).

Each primary reads every other primary and every ref (excluded: itself). Names must be unique across primaries and refs. Member access is direct/nonrecursive: g.a's view of g.b exposes b's own local values, not b's further coupling.

g = CoupledSimulations((a = sima, b = simb, c = simc))              # mutual
g = CoupledSimulations((a = sima,); refs = (b = simb,))             # one-way: a reads b
g = CoupledSimulations((a = sima, b = simb); refs = (c = simc,))    # mixed

work!(worker_a, g.a)

See the package documentation for the full setup-validation and replacement contract.

source