Skip to content

Commit f02aa39

Browse files
Add reduced material routine to compute stress only.
1 parent f500d13 commit f02aa39

2 files changed

Lines changed: 162 additions & 81 deletions

File tree

src/modeling/solid/elements.jl

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ function assemble_element!(Kₑ::AbstractMatrix, uₑ::AbstractVector, geometry_
6464
∇u = function_gradient(cv, qp, uₑ)
6565
F = one(∇u) + ∇u
6666

67-
# Compute stress and tangent
68-
P, ∂P∂F = material_routine(constitutive_model, F, coefficient_cache, internal_cache, geometry_cache, qp, time)
67+
# Compute "tangent only"
68+
_, ∂P∂F = material_routine(constitutive_model, F, coefficient_cache, internal_cache, geometry_cache, qp, time)
6969

7070
# Loop over test functions
7171
for i in 1:ndofs
@@ -97,22 +97,15 @@ function assemble_element!(residualₑ::AbstractVector, uₑ::AbstractVector, ge
9797
∇u = function_gradient(cv, qp, uₑ)
9898
F = one(∇u) + ∇u
9999

100-
# Compute stress and tangent
101-
P, ∂P∂F = material_routine(constitutive_model, F, coefficient_cache, internal_cache, geometry_cache, qp, time)
100+
# Compute stress only
101+
P = reduced_material_routine(constitutive_model, F, coefficient_cache, internal_cache, geometry_cache, qp, time)
102102

103103
# Loop over test functions
104104
for i in 1:ndofs
105105
∇δui = shape_gradient(cv, qp, i)
106106

107107
# Add contribution to the residual from this test function
108108
residualₑ[i] += ∇δui P *
109-
110-
# ∇δui∂P∂F = ∇δui ⊡ ∂P∂F # Hoisted computation
111-
# for j in 1:ndofs
112-
# ∇δuj = shape_gradient(cv, qp, j)
113-
# # Add contribution to the tangent
114-
# Kₑ[i, j] += ( ∇δui∂P∂F ⊡ ∇δuj ) * dΩ
115-
# end
116109
end
117110
end
118111
end

src/modeling/solid/materials.jl

Lines changed: 158 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ function material_routine(material_model::AbstractMaterialModel, F::Tensor{2}, c
2222
return P, ∂P∂F + ∂P∂QdQdF
2323
end
2424

25+
function reduced_material_routine(material_model::AbstractMaterialModel, F::Tensor{2}, coefficient_cache, ::EmptyInternalCache, geometry_cache::Ferrite.CellCache, qp::QuadraturePoint, time)
26+
coefficients = evaluate_coefficient(coefficient_cache, geometry_cache, qp, time)
27+
return stress_function(material_model, F, coefficients, EmptyInternalModel())
28+
end
29+
30+
function reduced_material_routine(material_model::AbstractMaterialModel, F::Tensor{2}, coefficient_cache, state_cache::TrivialCondensationMaterialStateCache, geometry_cache::Ferrite.CellCache, qp::QuadraturePoint, time)
31+
coefficients = evaluate_coefficient(coefficient_cache, geometry_cache, qp, time)
32+
Q = state(state_cache, geometry_cache, qp, time)
33+
return stress_function(material_model, F, coefficients, Q)
34+
end
35+
36+
function reduced_material_routine(material_model::AbstractMaterialModel, F::Tensor{2}, coefficient_cache, state_cache::RateIndependentCondensationMaterialStateCache, geometry_cache::Ferrite.CellCache, qp::QuadraturePoint, time)
37+
coefficients = evaluate_coefficient(coefficient_cache, geometry_cache, qp, time)
38+
Q = solve_local_constraint_state_only(F, coefficients, material_model, state_cache, geometry_cache, qp, time)
39+
P = stress_function(material_model, F, coefficients, Q)
40+
return P, ∂P∂F + ∂P∂QdQdF
41+
end
42+
2543
@doc raw"""
2644
PrestressedMechanicalModel(inner_model, prestress_field)
2745
@@ -92,11 +110,20 @@ default_initial_state!(uq, model::PK1Model) = default_initial_state!(uq, model.i
92110

93111
setup_internal_cache(material_model::PK1Model, qr::QuadratureRule, sdh::SubDofHandler) = setup_internal_cache(material_model.internal_model, qr, sdh)
94112

113+
function stress_function(model::PK1Model, F::Tensor{2}, coefficients, ::EmptyInternalModel)
114+
∂Ψ∂F = Tensors.gradient(
115+
F_ad -> Ψ(F_ad, coefficients, model.material),
116+
F
117+
)
118+
119+
return ∂Ψ∂F
120+
end
121+
95122
function stress_and_tangent(model::PK1Model, F::Tensor{2}, coefficients, ::EmptyInternalModel)
96123
∂²Ψ∂F², ∂Ψ∂F = Tensors.hessian(
97-
F_ad ->
98-
Ψ(F_ad, coefficients, model.material),
99-
F, :all)
124+
F_ad -> Ψ(F_ad, coefficients, model.material),
125+
F, :all
126+
)
100127

101128
return ∂Ψ∂F, ∂²Ψ∂F²
102129
end
@@ -163,6 +190,20 @@ function setup_coefficient_cache(m::ExtendedHillModel, qr::QuadratureRule, sdh::
163190
return setup_coefficient_cache(m.microstructure_model, qr, sdh)
164191
end
165192

193+
function stress_function(model::ExtendedHillModel, F::Tensor{2}, coefficients, cell_state)
194+
# TODO what is a good abstraction here?
195+
Fᵃ = compute_Fᵃ(cell_state, coefficients, model.contraction_model, model.active_deformation_gradient_model)
196+
N = 𝓝(cell_state, F, coefficients, model.contraction_model)
197+
198+
∂Ψ∂F = Tensors.gradient(
199+
F_ad ->
200+
Ψ(F_ad, coefficients, model.passive_spring)
201+
+ N*Ψ(F_ad, Fᵃ, coefficients, model.active_spring),
202+
F, :all)
203+
204+
return ∂Ψ∂F
205+
end
206+
166207
function stress_and_tangent(model::ExtendedHillModel, F::Tensor{2}, coefficients, cell_state)
167208
# TODO what is a good abstraction here?
168209
Fᵃ = compute_Fᵃ(cell_state, coefficients, model.contraction_model, model.active_deformation_gradient_model)
@@ -202,6 +243,17 @@ function setup_coefficient_cache(m::ActiveStressModel, qr::QuadratureRule, sdh::
202243
return setup_coefficient_cache(m.microstructure_model, qr, sdh)
203244
end
204245

246+
function stress_function(model::ActiveStressModel, F::Tensor{2}, coefficients, cell_state)
247+
∂²Ψ∂F², ∂Ψ∂F = Tensors.gradient(
248+
F_ad -> Ψ(F_ad, coefficients, model.material_model),
249+
F
250+
)
251+
252+
P2 = 𝓝(cell_state, F_ad, coefficients, model.contraction_model) * active_stress(model.active_stress_model, F_ad, coefficients)
253+
return ∂Ψ∂F + P2
254+
end
255+
256+
205257
function stress_and_tangent(model::ActiveStressModel, F::Tensor{2}, coefficients, cell_state)
206258
∂²Ψ∂F², ∂Ψ∂F = Tensors.hessian(
207259
F_ad ->
@@ -250,6 +302,49 @@ function _solve_local_sarcomere_dQdF(dQdλ, dλdF, λ, F, coefficients, active_t
250302
return -dQdF
251303
end
252304

305+
# Local solve
306+
function solve_internal_timestep(material::ActiveStressModel, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, λ, dλdt, Q, Qprev, Ca)
307+
@unpack Δt = state_cache
308+
# dsdt = sarcomere_rhs(s,λ,t)
309+
# <=> (sₜ₁ - sₜ₀) / Δt = sarcomere_rhs(sₜ₁,λₜ₁,t1)
310+
311+
function local_residual!(R, Q, λ, dλdt)
312+
dQ = zeros(eltype(Q), length(Q)) # TODO preallocate during setup
313+
sarcomere_rhs!(dQ, Q, λ, dλdt, Ca, time, material_model.contraction_model)
314+
@.. R = (Q - Qprev) / state_cache.Δt - dQ
315+
return nothing
316+
end
317+
318+
function local_residual_jac_wrap!(R, Q)
319+
return local_residual!(R, Q, λ, dλdt)
320+
end
321+
322+
R = state_cache.local_solver_cache.residual
323+
J = state_cache.local_solver_cache.J
324+
rtol = min(state_cache.local_solver_cache.params.tol, state_cache.local_solver_cache.outer_tol)
325+
for newton_iter in 1:state_cache.local_solver_cache.params.max_iters
326+
ForwardDiff.jacobian!(J, local_residual_jac_wrap!, R, Q)
327+
local_residual!(R, Q, λ, dλdt)
328+
ΔQ = J \ R
329+
Q .-= ΔQ
330+
residualnorm = norm(R)
331+
if residualnorm < state_cache.local_solver_cache.params.tol
332+
break
333+
elseif newton_iter == state_cache.local_solver_cache.params.max_iters
334+
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.MaxIters
335+
@debug "Reached maximum local Newton iterations at cell $(cellid(geometry_cache)) qp $(qp.i). Aborting. ||r|| = $(residualnorm)" _group=:nlsolve
336+
return Q, J
337+
elseif isnan(residualnorm)
338+
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.ConvergenceFailure
339+
@debug "Newton-Raphson diverged. Aborting. ||r|| = $residualnorm" _group=:nlsolve
340+
return Q, J
341+
end
342+
end
343+
ForwardDiff.jacobian!(J, local_residual_jac_wrap!, R, Q)
344+
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.Success
345+
return Q, J
346+
end
347+
253348
function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::ActiveStressModel, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
254349
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
255350
# Early out if any of the previous local solves failed
@@ -267,50 +362,7 @@ function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::
267362
dλdt = 0.0 # TODO query
268363
Ca = evaluate_coefficient(state_cache.model_cache.calcium_cache, geometry_cache, qp, time)
269364

270-
# Local solve
271-
function solve_internal_timestep(material::ActiveStressModel, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, λ, dλdt, Q, Qprev)
272-
@unpack Δt = state_cache
273-
# dsdt = sarcomere_rhs(s,λ,t)
274-
# <=> (sₜ₁ - sₜ₀) / Δt = sarcomere_rhs(sₜ₁,λₜ₁,t1)
275-
276-
function local_residual!(R, Q, λ, dλdt)
277-
dQ = zeros(eltype(Q), length(Q)) # TODO preallocate during setup
278-
sarcomere_rhs!(dQ, Q, λ, dλdt, Ca, time, material_model.contraction_model)
279-
@.. R = (Q - Qprev) / state_cache.Δt - dQ
280-
return nothing
281-
end
282-
283-
function local_residual_jac_wrap!(R, Q)
284-
return local_residual!(R, Q, λ, dλdt)
285-
end
286-
287-
R = state_cache.local_solver_cache.residual
288-
J = state_cache.local_solver_cache.J
289-
rtol = min(state_cache.local_solver_cache.params.tol, state_cache.local_solver_cache.outer_tol)
290-
for newton_iter in 1:state_cache.local_solver_cache.params.max_iters
291-
ForwardDiff.jacobian!(J, local_residual_jac_wrap!, R, Q)
292-
local_residual!(R, Q, λ, dλdt)
293-
ΔQ = J \ R
294-
Q .-= ΔQ
295-
residualnorm = norm(R)
296-
if residualnorm < state_cache.local_solver_cache.params.tol
297-
break
298-
elseif newton_iter == state_cache.local_solver_cache.params.max_iters
299-
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.MaxIters
300-
@debug "Reached maximum local Newton iterations at cell $(cellid(geometry_cache)) qp $(qp.i). Aborting. ||r|| = $(residualnorm)" _group=:nlsolve
301-
return Q, J
302-
elseif isnan(residualnorm)
303-
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.ConvergenceFailure
304-
@debug "Newton-Raphson diverged. Aborting. ||r|| = $residualnorm" _group=:nlsolve
305-
return Q, J
306-
end
307-
end
308-
ForwardDiff.jacobian!(J, local_residual_jac_wrap!, R, Q)
309-
state_cache.local_solver_cache.retcode = SciMLBase.ReturnCode.Success
310-
return Q, J
311-
end
312-
313-
Q, J = solve_internal_timestep(material_model, state_cache, λ, dλdt, Qflat, Qprevflat)
365+
Q, J = solve_internal_timestep(material_model, state_cache, λ, dλdt, Qflat, Qprevflat, Ca)
314366
# Abort if local solve failed
315367
if state_cache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
316368
return Qflat, zero(Tensor{4,dim,Float64,4^dim})
@@ -333,6 +385,34 @@ function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::
333385
return Q, _solve_local_sarcomere_dQdF(dQdλ, dλdF, λ, F, coefficients, material_model.active_stress_model, material_model.contraction_model)
334386
end
335387

388+
function solve_local_constraint_state_only(F::Tensor{2,dim}, coefficients, material_model::ActiveStressModel, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
389+
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
390+
# Early out if any of the previous local solves failed
391+
if state_cache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
392+
return Qflat, zero(Tensor{4,dim,Float64,4^dim})
393+
end
394+
395+
function computeλ(F)
396+
f = F coefficients.f
397+
return (f f)
398+
end
399+
400+
# Frozen variables
401+
dλdF, λ = Tensors.gradient(computeλ, F, :all)
402+
dλdt = 0.0 # TODO query
403+
Ca = evaluate_coefficient(state_cache.model_cache.calcium_cache, geometry_cache, qp, time)
404+
405+
Q, J = solve_internal_timestep(material_model, state_cache, λ, dλdt, Qflat, Qprevflat, Ca)
406+
# Abort if local solve failed
407+
if state_cache.local_solver_cache.retcode (SciMLBase.ReturnCode.Default, SciMLBase.ReturnCode.Success)
408+
return Qflat, zero(Tensor{4,dim,Float64,4^dim})
409+
end
410+
Qflat .= Q
411+
_store_local_state!(state_cache, geometry_cache, qp)
412+
413+
return Q
414+
end
415+
336416
# Some debug materials
337417
Base.@kwdef struct LinearMaxwellMaterial{T, sdim} <: AbstractMaterialModel
338418
E₀::T
@@ -386,38 +466,36 @@ function _store_local_state!(state_cache::GenericFirstOrderRateIndependentConden
386466
return nothing
387467
end
388468

389-
function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
390-
# Concept only for now.
391-
function solve_internal_timestep(material::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, ε, εᵛflat, εᵛprevflat)
392-
@unpack Δt = state_cache
393-
εᵛ₁ = SymmetricTensor{2,dim}(εᵛflat)
394-
εᵛ₀ = SymmetricTensor{2,dim}(εᵛprevflat)
395-
# dεᵛdt = E₁/η₁ c : (ε - εᵛ)
396-
# <=> (εᵛ₁ - εᵛ₀) / Δt = E₁/η₁ c : (ε - εᵛ₁) = E₁/η₁ c : ε - E₁/η₁ c : εᵛ₁
397-
# <=> εᵛ₁ / Δt + E₁/η₁ c : εᵛ₁ = εᵛ₀/Δt + E₁/η₁ c : ε
398-
# <=> (𝐈 / Δt + E₁/η₁ c) : εᵛ₁ = εᵛ₀/Δt + E₁/η₁ c : ε
469+
function solve_internal_timestep(material::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, ε, εᵛflat, εᵛprevflat)
470+
@unpack Δt = state_cache
471+
εᵛ₁ = SymmetricTensor{2,dim}(εᵛflat)
472+
εᵛ₀ = SymmetricTensor{2,dim}(εᵛprevflat)
473+
# dεᵛdt = E₁/η₁ c : (ε - εᵛ)
474+
# <=> (εᵛ₁ - εᵛ₀) / Δt = E₁/η₁ c : (ε - εᵛ₁) = E₁/η₁ c : ε - E₁/η₁ c : εᵛ₁
475+
# <=> εᵛ₁ / Δt + E₁/η₁ c : εᵛ₁ = εᵛ₀/Δt + E₁/η₁ c : ε
476+
# <=> (𝐈 / Δt + E₁/η₁ c) : εᵛ₁ = εᵛ₀/Δt + E₁/η₁ c : ε
399477

400-
(; E₀, E₁, μ, η₁, ν) = material
401-
I = one(ε)
402-
c₁ = ν / ((ν + 1)*(1-2ν)) * I I
403-
c₂ = 1 / (1+ν) * one(c₁)
404-
= c₁ + c₂
478+
(; E₀, E₁, μ, η₁, ν) = material
479+
I = one(ε)
480+
c₁ = ν / ((ν + 1)*(1-2ν)) * I I
481+
c₂ = 1 / (1+ν) * one(c₁)
482+
= c₁ + c₂
405483

406-
# FIXME non-allocating version by using state_cache nlsolver
407-
A = tomandel(SMatrix, one(ℂ)/Δt + E₁/η₁ * ℂ)
408-
b = tomandel(SVector, εᵛ₀/Δt + E₁/η₁ * ε)
409-
return frommandel(typeof(ε), A \ b)
410-
end
484+
# FIXME non-allocating version by using state_cache nlsolver
485+
A = tomandel(SMatrix, one(ℂ)/Δt + E₁/η₁ * ℂ)
486+
b = tomandel(SVector, εᵛ₀/Δt + E₁/η₁ * ε)
487+
return frommandel(typeof(ε), A \ b)
488+
end
411489

490+
function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
412491
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
413492
ε = symmetric(F - one(F))
414493
Q = solve_internal_timestep(material_model, state_cache, ε, Qflat, Qprevflat)
415494
Qflat .= Q.data
416495
_store_local_state!(state_cache, geometry_cache, qp)
417496

418497
# Corrector
419-
# Concept only for now.
420-
function solve_internal_timestep_corrector(material::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, ε, εᵛflat, εᵛprevflat)
498+
function solve_internal_timestep_corrector(material::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, ε, εᵛflat, εᵛprevflat, coefficients)
421499
@unpack Δt = state_cache
422500
εᵛ₁ = SymmetricTensor{2,dim}(εᵛflat)
423501
εᵛ₀ = SymmetricTensor{2,dim}(εᵛprevflat)
@@ -436,12 +514,22 @@ function solve_local_constraint(F::Tensor{2,dim}, coefficients, material_model::
436514
B = tomandel(SMatrix, E₁/η₁ * ℂ)
437515
return frommandel(typeof(ℂ), A \ B)
438516
end
439-
dQdF = solve_internal_timestep_corrector(material_model, state_cache, ε, Qflat, Qprevflat)
517+
dQdF = solve_internal_timestep_corrector(material_model, state_cache, ε, Qflat, Qprevflat, coefficients)
440518
∂P∂Q = Tensors.gradient(εᵛ->stress_function(material_model, ε, coefficients, εᵛ), Q)
441519

442520
return Q, ∂P∂Q dQdF
443521
end
444522

523+
function solve_local_constraint_state_only(F::Tensor{2,dim}, coefficients, material_model::LinearMaxwellMaterial, state_cache::GenericFirstOrderRateIndependentCondensationMaterialStateCache, geometry_cache, qp, time) where dim
524+
Qflat, Qprevflat = _query_local_state(state_cache, geometry_cache, qp)
525+
ε = symmetric(F - one(F))
526+
Q = solve_internal_timestep(material_model, state_cache, ε, Qflat, Qprevflat)
527+
Qflat .= Q.data
528+
_store_local_state!(state_cache, geometry_cache, qp)
529+
530+
return Q
531+
end
532+
445533
function stress_function(material::LinearMaxwellMaterial, ε, coefficients, εᵛ)
446534
(; E₀, E₁, μ, η₁, ν) = material
447535
I = one(ε)

0 commit comments

Comments
 (0)