Skip to content

Commit 8be3777

Browse files
Fix multi-material support when combining models with different internal variables
1 parent d4fbacf commit 8be3777

9 files changed

Lines changed: 138 additions & 55 deletions

File tree

src/discretization/operator.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ function AssembledNonlinearOperator(integrator::NonlinearIntegrator, dh::Abstrac
161161
)
162162
end
163163

164+
function Base.show(io::IO, cache::AssembledNonlinearOperator)
165+
println(io, "AssembledNonlinearOperator:")
166+
Base.show(io, typeof(cache.integrator))
167+
Base.show(io, MIME"text/plain"(), cache.dh)
168+
end
169+
164170
getJ(op::AssembledNonlinearOperator) = op.J
165171

166172
function update_linearization!(op::AssembledNonlinearOperator, u::AbstractVector, time)

src/ferrite-addons/quadrature_iterator.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ A helper to loop over the quadrature points in some rule or cache with type [`Qu
1919
struct QuadratureIterator{QR<:QuadratureRule}
2020
qr::QR
2121
end
22-
QuadratureIterator(fqr::FacetQuadratureRule, local_face_idx::Int) = QuadratureIterator(fqr.facet_rules[local_face_idx])
22+
QuadratureIterator(fqr::FacetQuadratureRule, local_face_idx::Int) = QuadratureIterator(fqr.face_rules[local_face_idx])
2323
QuadratureIterator(cv::CellValues) = QuadratureIterator(cv.qr)
24-
QuadratureIterator(fv::FacetValues) = QuadratureIterator(fv.fqr.facet_rules[fv.current_facet[]])
24+
QuadratureIterator(fv::FacetValues) = QuadratureIterator(fv.fqr.face_rules[fv.current_facet[]])
2525

2626
function Base.iterate(iterator::QuadratureIterator, i = 1)
2727
i > getnquadpoints(iterator.qr) && return nothing

src/modeling/functions.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ end
9393

9494
solution_size(f::QuasiStaticFunction) = ndofs(f.dh)+ndofs(f.lvh)
9595
function local_function_size(f::QuasiStaticFunction)
96-
length(f.lvh.dh.subdofhandlers) == 0 && return 0
97-
return sum(Ferrite.n_components.(f.lvh.dh.subdofhandlers[1].field_interpolations); init=0)
96+
error("Local function size of QuasiStaticFunction can vary!")
9897
end
9998
function default_initial_condition!(u::AbstractVector, f::QuasiStaticFunction)
10099
fill!(u, 0.0)
@@ -119,3 +118,17 @@ end
119118

120119
gather_internal_variable_infos(model::QuasiStaticModel) = gather_internal_variable_infos(model.material_model)
121120
gather_internal_variable_infos(model::AbstractMaterialModel) = InternalVariableInfo[]
121+
122+
@unroll function __get_material_model_multi(materials, domains, sdh)
123+
idx = 1
124+
@unroll for material materials
125+
if first(domains[idx]) sdh.cellset
126+
return material
127+
end
128+
idx += 1
129+
end
130+
error("MultiDomainIntegrator is broken: Requested to construct an internal cache for a SubDofHandler which is not associated with the integrator.")
131+
end
132+
__get_material_model(model::MultiMaterialModel, sdh) = __get_material_model_multi(model.materials, model.domains, sdh)
133+
__get_material_model(model::AbstractMaterialModel, sdh) = model
134+
get_material_model(f::QuasiStaticFunction, sdh) = __get_material_model(f.integrator.volume_model.material_model, sdh)

src/modeling/solid/elements.jl

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function assemble_element!(residualₑ::AbstractVector, uₑ::AbstractVector, ge
117117
end
118118
end
119119

120-
struct MultiMaterialModel{MaterialTuple}
120+
struct MultiMaterialModel{MaterialTuple} <: AbstractMaterialModel
121121
materials::MaterialTuple
122122
domains::Vector{OrderedSet{Int}} # These must match the subdofhandler sets and hence be disjoint
123123
domain_names::Vector{String} # These must match the subdofhandler sets and hence be disjoint
@@ -174,17 +174,3 @@ end
174174
function setup_internal_cache(model::MultiMaterialModel, qr::QuadratureRule, sdh::SubDofHandler)
175175
return setup_internal_cache_multi(model.materials, model.domains, qr, sdh)
176176
end
177-
178-
@unroll function __get_material_model_multi(materials, domains, sdh)
179-
idx = 1
180-
@unroll for material materials
181-
if first(domains[idx]) sdh.cellset
182-
return material
183-
end
184-
idx += 1
185-
end
186-
error("MultiDomainIntegrator is broken: Requested to construct an internal cache for a SubDofHandler which is not associated with the integrator.")
187-
end
188-
__get_material_model(model::MultiMaterialModel, sdh) = __get_material_model_multi(model.materials, model.domains, sdh)
189-
__get_material_model(model::AbstractMaterialModel, sdh) = model
190-
get_material_model(f::QuasiStaticFunction, sdh) = __get_material_model(f.integrator.volume_model.material_model, sdh)

src/modeling/solid/materials.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,9 @@ function _solve_local_sarcomere_dQdF(dQdλ, dλdF, λ, F, coefficients, active_t
251251
end
252252

253253
function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::ActiveStressModel, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
254+
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
254255
# Early out if any of the previous local solves failed
255256
if state_cache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
256-
Qflat, _ = _query_local_state(state_cache, geometry_cache, qp)
257257
return Qflat, zero(Tensor{4,dim,Float64,4^dim})
258258
end
259259

@@ -310,11 +310,9 @@ function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::
310310
return Q, J
311311
end
312312

313-
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
314313
Q, J = solve_internal_timestep(material_model, state_cache, λ, dλdt, Qflat, Qprevflat)
315314
# Abort if local solve failed
316315
if state_cache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
317-
Qflat, _ = _query_local_state(state_cache, geometry_cache, qp)
318316
return Qflat, zero(Tensor{4,dim,Float64,4^dim})
319317
end
320318
Qflat .= Q
@@ -336,13 +334,14 @@ function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::
336334
end
337335

338336
# Some debug materials
339-
Base.@kwdef struct LinearMaxwellMaterial{T} <: AbstractMaterialModel
337+
Base.@kwdef struct LinearMaxwellMaterial{T, sdim} <: AbstractMaterialModel
340338
E₀::T
341339
E₁::T
342340
μ::T
343341
η₁::T
344342
ν::T
345343
end
344+
LinearMaxwellMaterial(E₀::T, Eₗ::T, μ::T, η₁::T, ν::T) where T = LinearMaxwellMaterial{T,3}(E₀, Eₗ, μ, η₁, ν)
346345

347346
local_function_size(model::QuasiStaticModel) = local_function_size(model.material_model)
348347
function local_function_size(model::AbstractMaterialModel)
@@ -452,7 +451,7 @@ function stress_function(material::LinearMaxwellMaterial, ε, coefficients, ε
452451
return E₀ * ε + E₁ *- εᵛ)
453452
end
454453

455-
function stress_and_tangent(material_model::LinearMaxwellMaterial, F::Tensor{2}, coefficients, εᵛ)
454+
function stress_and_tangent(material_model::LinearMaxwellMaterial, F::Tensor{2}, coefficients, εᵛ::SymmetricTensor{2})
456455
ε = symmetric(F - one(F))
457456
∂σ∂ε, σ = Tensors.gradient->stress_function(material_model, ε, coefficients, εᵛ), ε, :all)
458457
return σ, ∂σ∂ε
@@ -463,9 +462,13 @@ function setup_coefficient_cache(m::LinearMaxwellMaterial, qr::QuadratureRule, s
463462
end
464463

465464
function setup_internal_cache(material_model::LinearMaxwellMaterial, qr::QuadratureRule, sdh::SubDofHandler)
466-
return nothing # FIXME what should we do here? :)
465+
return EmptyRateIndependentCondensationMaterialStateCache()
467466
end
468467

469-
function gather_internal_variable_infos(model::LinearMaxwellMaterial)
470-
return InternalVariableInfo(:εᵛ, 6) # TODO dimension info
468+
function gather_internal_variable_infos(model::LinearMaxwellMaterial{T, sdim}) where {T, sdim}
469+
if sdim == 3
470+
return InternalVariableInfo(:εᵛ, 6)
471+
else
472+
return InternalVariableInfo(:εᵛ, 4)
473+
end
471474
end

src/solver/nonlinear/multilevel_newton_raphson.jl

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@ Base.@kwdef mutable struct GenericLocalNonlinearSolverCache{JacobianType, Residu
1212
retcode::SciMLBase.ReturnCode.T = SciMLBase.ReturnCode.Default
1313
end
1414

15+
function Base.show(io::IO, cache::GenericLocalNonlinearSolverCache)
16+
println(io, "NewtonRaphsonSolverCache:")
17+
Base.show(io, cache.params)
18+
println(io, "J=$(typeof(cache.J)) with size $(size(cache.J))")
19+
println(io, "R=$(typeof(cache.residual)) with size $(size(cache.residual))")
20+
println(io, "C=$(typeof(cache.rhs_corrector)) with size $(size(cache.rhs_corrector))")
21+
println(io, "outer_tol=$(cache.outer_tol)")
22+
println(io, "status=$(cache.retcode)")
23+
end
24+
25+
function check_local_solve_covergence(local_solver_cache::GenericLocalNonlinearSolverCache)
26+
return local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
27+
end
28+
function check_local_solve_covergence(local_solver_cache::Tuple)
29+
return any(check_local_solve_covergence.(local_solver_cache))
30+
end
31+
32+
function set_local_solver_tol(local_solver_cache::GenericLocalNonlinearSolverCache, tol)
33+
local_solver_cache.outer_tol = tol
34+
end
35+
function set_local_solver_tol(local_solver_cache::Tuple, tol)
36+
set_local_solver_tol.(local_solver_cache, tol)
37+
end
38+
1539
"""
1640
MultilevelNewtonRaphsonSolver{T}
1741
@@ -29,6 +53,18 @@ struct MultiLevelNewtonRaphsonSolverCache{gCacheType, lCacheType} <: AbstractNon
2953
local_solver_cache::lCacheType
3054
end
3155

56+
function Base.show(io::IO, cache::MultiLevelNewtonRaphsonSolverCache)
57+
println(io, "MultiLevelNewtonRaphsonSolverCache:")
58+
Base.show(io, cache.global_solver_cache)
59+
if cache.local_solver_cache isa Tuple
60+
for local_solver_cache in cache.local_solver_cache
61+
Base.show(io, local_solver_cache)
62+
end
63+
else
64+
Base.show(io, cache.local_solver_cache)
65+
end
66+
end
67+
3268
function nlsolve!(u::AbstractVector, f::AbstractSemidiscreteFunction, mlcache::MultiLevelNewtonRaphsonSolverCache, t)
3369
cache = mlcache.global_solver_cache
3470

@@ -39,21 +75,21 @@ function nlsolve!(u::AbstractVector, f::AbstractSemidiscreteFunction, mlcache::M
3975
residualnormprev = 0.0
4076
Θ1prev = length(Θks) > 0 ? first(Θks) : 0.0
4177
resize!(Θks, 0)
42-
mlcache.local_solver_cache.outer_tol = Inf
78+
set_local_solver_tol(mlcache.local_solver_cache, Inf)
4379
while true
4480
cache.iter += 1
4581
residual .= 0.0
4682
@timeit_debug "update operator" update_linearization!(op, residual, u, t)
4783
# Check if local solve failed
48-
if mlcache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
84+
if check_local_solve_covergence(mlcache.local_solver_cache)
4985
@debug "Some local newton did not converge. Aborting. ||r|| = $residualnorm" _group=:nlsolve
5086
return false
5187
end
5288
@timeit_debug "elimination" eliminate_constraints_from_linearization!(cache, f)
5389
linear_solver_cache.isfresh = true # Notify linear solver that we touched the system matrix
5490

5591
residualnorm = residual_norm(cache, f)
56-
mlcache.local_solver_cache.outer_tol = residualnorm
92+
set_local_solver_tol(mlcache.local_solver_cache, residualnorm^2)
5793
if residualnorm < cache.parameters.tol && cache.iter > 1 # Do at least two iterations to get a sane convergence estimate
5894
break
5995
elseif cache.iter > cache.parameters.max_iter

src/solver/nonlinear/newton_raphson.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ mutable struct NewtonRaphsonSolverCache{OpType, ResidualType, T, NewtonType <: N
2727
iter::Int
2828
end
2929

30+
function Base.show(io::IO, cache::NewtonRaphsonSolverCache)
31+
println(io, "NewtonRaphsonSolverCache:")
32+
Base.show(io, cache.parameters)
33+
Base.show(io, cache.op)
34+
end
35+
3036
function setup_solver_cache(f::AbstractSemidiscreteFunction, solver::NewtonRaphsonSolver{T}) where {T}
3137
@unpack inner_solver = solver
3238
op = setup_operator(f, solver)

src/solver/time/euler.jl

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,12 @@ end
180180
function setup_solver_cache(wrapper::BackwardEulerStageAnnotation, solver::AbstractNonlinearSolver)
181181
_setup_solver_cache(wrapper, wrapper.f, solver)
182182
end
183-
@inline function _setup_solver_cache(wrapper::BackwardEulerStageAnnotation, f::QuasiStaticFunction, solver::MultiLevelNewtonRaphsonSolver)
184-
@unpack integrator, dh = f
185-
@unpack volume_model, face_model = integrator
186-
@unpack local_solver, newton = solver
187-
188-
# TODO add an abstraction layer to autoamte the steps below
189-
singleQsize = local_function_size(f)
190-
local_solver_cache = GenericLocalNonlinearSolverCache(
183+
function _setup_local_solver_cache(local_solver::GenericLocalNonlinearSolver, material_model::AbstractMaterialModel)
184+
singleQsize = local_function_size(material_model)
185+
@debug "Setting up local nonlinear solver with size(Q)=$(singleQsize) for material $(material_model)" _group=:nlsolve
186+
return GenericLocalNonlinearSolverCache(
191187
# Solver parameters
192-
solver.local_solver,
188+
local_solver,
193189
# Buffers
194190
zeros(singleQsize, singleQsize),
195191
zeros(singleQsize),
@@ -199,6 +195,17 @@ end
199195
# Local convergence
200196
SciMLBase.ReturnCode.Default,
201197
)
198+
end
199+
function _setup_local_solver_cache(local_solver::GenericLocalNonlinearSolver, material_models::MultiMaterialModel)
200+
return map(material_model -> _setup_local_solver_cache(local_solver, material_model), material_models.materials)
201+
end
202+
@inline function _setup_solver_cache(wrapper::BackwardEulerStageAnnotation, f::QuasiStaticFunction, solver::MultiLevelNewtonRaphsonSolver)
203+
@unpack integrator, dh = f
204+
@unpack volume_model, face_model = integrator
205+
@unpack local_solver, newton = solver
206+
207+
# TODO add an abstraction layer to autoamte the steps below
208+
local_solver_cache = _setup_local_solver_cache(solver.local_solver, f.integrator.volume_model.material_model)
202209

203210
# Extract condensable parts
204211
Q = @view wrapper.u[(ndofs(dh)+1):end]
@@ -237,10 +244,13 @@ end
237244

238245
newton_cache = NewtonRaphsonSolverCache(op, residual, newton, inner_cache, T[], 0)
239246

240-
return MultiLevelNewtonRaphsonSolverCache(
247+
cache = MultiLevelNewtonRaphsonSolverCache(
241248
newton_cache, # setup_solver_cache(G, solver.newton),
242249
local_solver_cache, #setup_solver_cache(L, solver.local_newton), # FIXME pass
243250
)
251+
@debug "Setting up Multi-Level Newton-Raphson solver." _group=:nlsolve
252+
@debug cache _group=:nlsolve
253+
return cache
244254
end
245255

246256
# TODO Refactor the setup into generic parts and use multiple dispatch for the specifics.
@@ -347,7 +357,7 @@ end
347357
n_ivs_per_qp = local_function_size(material_model)
348358
return GenericFirstOrderRateIndependentCondensationMaterialStateCache(
349359
# Pass the model
350-
wrapper.f,
360+
material_model,
351361
# And some cache to speed up evaluation of f and associated coefficients
352362
internal_cache,
353363
# Pass global solution info
@@ -356,7 +366,7 @@ end
356366
# Current time step length
357367
wrapper.Δt,
358368
# Local nonlinear solver cache
359-
wrapper.local_solver_cache,
369+
wrapper.local_solver_cache[idx],
360370
# This one holds information about the local dofs inside u and uprev
361371
wrapper.lvh,
362372
# Buffer for Q and Qprev
@@ -367,13 +377,12 @@ end
367377
idx += 1
368378
end
369379
error("MultiDomainIntegrator is broken: Requested to construct an element cache for a SubDofHandler which is not associated with the integrator.")
370-
371380
end
372-
function setup_internal_cache_backward_euler_unwrap(wrapper::BackwardEulerStageFunctionWrapper{<:QuasiStaticModel}, material_model::AbstractMaterialModel internal_cache::Union{RateIndependentCondensationMaterialStateCache, RateDependentCondensationMaterialStateCache}, qr::QuadratureRule, sdh::SubDofHandler)
373-
n_ivs_per_qp = local_function_size(wrapper.f.material_model)
381+
function setup_internal_cache_backward_euler_unwrap(wrapper::BackwardEulerStageFunctionWrapper{<:QuasiStaticModel}, material_model::AbstractMaterialModel, internal_cache::Union{RateIndependentCondensationMaterialStateCache, RateDependentCondensationMaterialStateCache}, qr::QuadratureRule, sdh::SubDofHandler)
382+
n_ivs_per_qp = local_function_size(material_model)
374383
return GenericFirstOrderRateIndependentCondensationMaterialStateCache(
375384
# Pass the model
376-
wrapper.f,
385+
material_model,
377386
# And some cache to speed up evaluation of f and associated coefficients
378387
internal_cache,
379388
# Pass global solution info
@@ -391,7 +400,7 @@ function setup_internal_cache_backward_euler_unwrap(wrapper::BackwardEulerStageF
391400
)
392401
end
393402
function setup_internal_cache(wrapper::BackwardEulerStageFunctionWrapper{<:QuasiStaticModel}, qr::QuadratureRule, sdh::SubDofHandler)
394-
return setup_internal_cache_backward_euler_unwrap(wrapper, setup_internal_cache(wrapper.f.material_model, qr, sdh), qr, sdh)
403+
return setup_internal_cache_backward_euler_unwrap(wrapper, wrapper.f.material_model, setup_internal_cache(wrapper.f.material_model, qr, sdh), qr, sdh)
395404
end
396405

397406
function setup_boundary_cache(wrapper::BackwardEulerStageFunctionWrapper, fqr, sdh)

0 commit comments

Comments
 (0)