1212
1313A struct that saves all derivative information using a `Matrix{T}` for each derivative,
1414where `T=get_params_eltype(m)`. The dimensions are obtained from `get_num_tensorcomponents`,
15- `get_num_statevars`, and `get_num_params`. The values should be updated in `differentiate_material!`
16- by direct access of the fields, where `σ` is the stress, `ϵ` the strain, `s` and `ⁿs` are the current
15+ `get_num_statevars`, and `get_num_params`. `m` must support `tovector` and `fromvector`, while
16+ the output of `initial_material_state` must support `tovector`, and in addition the element type
17+ of `tovector(initial_material_state(m))` must respect the element type in `tovector(m)` for any `m`.
18+
19+ The values should be updated in `differentiate_material!` by direct access of the fields,
20+ where `σ` is the stress, `ϵ` the strain, `s` and `ⁿs` are the current
1721and old state variables, and `p` the material parameter vector.
1822
1923* `dσdϵ`
@@ -29,13 +33,14 @@ function MaterialDerivatives(m::AbstractMaterial)
2933 n_tensor = get_num_tensorcomponents (m)
3034 n_state = get_num_statevars (m)
3135 n_params = get_num_params (m)
36+ dsdp = ForwardDiff. jacobian (p -> tovector (initial_material_state (fromvector (p, m))), tovector (m))
3237 return MaterialDerivatives (
3338 zeros (T, n_tensor, n_tensor), # dσdϵ
3439 zeros (T, n_tensor, n_state), # dσdⁿs
3540 zeros (T, n_tensor, n_params), # dσdp
3641 zeros (T, n_state, n_tensor), # dsdϵ
3742 zeros (T, n_state, n_state), # dsdⁿs
38- zeros (T, n_state, n_params) # dsdp
43+ dsdp
3944 )
4045end
4146
@@ -66,4 +71,83 @@ allocate_differentiation_output(::AbstractMaterial) = NoExtraOutput()
6671Calculate the derivatives and save them in `diff`, see
6772[`MaterialDerivatives`](@ref) for a description of the fields in `diff`.
6873"""
69- function differentiate_material! end
74+ function differentiate_material! end
75+
76+ struct StressStateDerivatives{T}
77+ mderiv:: MaterialDerivatives{T}
78+ dϵdp:: Matrix{T}
79+ dσdp:: Matrix{T}
80+ ϵindex:: SMatrix{3, 3, Int} # To allow indexing by (i, j) into only
81+ σindex:: SMatrix{3, 3, Int} # saved values to avoid storing unused rows.
82+ # TODO : Reduce the dimensions, for now all entries (even those that are zero) are stored.
83+ end
84+
85+ """
86+ differentiate_material!(ssd::StressStateDerivatives, stress_state, m, args...)
87+
88+ For material models implementing `material_response(m, args...)` and `differentiate_material!(::MaterialDerivatives, m, args...)`,
89+ this method will work automatically by
90+ 1) Calling `σ, dσdϵ, state = material_response(stress_state, m, args...)` (except that `dσdϵ::FourthOrderTensor{dim = 3}` is extracted)
91+ 2) Calling `differentiate_material!(ssd.mderiv::MaterialDerivatives, m, args..., dσdϵ::FourthOrderTensor{3})`
92+ 3) Updating `ssd` according to the constraints imposed by the `stress_state`.
93+
94+ For material models that directly implement `material_response(stress_state, m, args...)`, this function should be overloaded directly
95+ to calculate the derivatives in `ssd`. Here the user has full control and no modifications occur automatically, however, typically the
96+ (total) derivatives `ssd.dσdp`, `ssd.dϵdp`, and `ssd.mderiv.dsdp` should be updated.
97+ """
98+ function differentiate_material! (ssd:: StressStateDerivatives , stress_state:: AbstractStressState , m:: AbstractMaterial , args:: Vararg{Any,N} ) where {N}
99+ σ_full, dσdϵ_full, state, ϵ_full = stress_state_material_response (stress_state, m, args... )
100+ differentiate_material! (ssd. mderiv, m, args... , dσdϵ_full)
101+ dσᶠdϵᶠ_inv = inv (get_unknowns (stress_state, dσdϵ_full)) # f: unknown strain components solved for during stress iterations
102+ ssd. dϵdp[sc, :] .= .- dσᶠdϵᶠ_inv * ssd. mderiv. dσdp[sc, :]
103+ ssd. dσdp[ec, :] .= ssd. mderiv. dσdp .+ ssd. mderiv. dσdϵ[ec, sc] * ssd. dϵdp[sc, :]
104+ ssd. mderiv. dsdp .+ = ssd. mderiv. dsdϵ[:, sc] * ssd. dϵdp[sc, :]
105+ return return reduce_tensordim (stress_state, σ_full), reduce_stiffness (stress_state, dσdϵ_full), state, ϵ_full
106+ end
107+
108+ """
109+ stress_controlled_indices(stress_state::AbstractStressState, ::AbstractTensor)::SVector{N, Int}
110+
111+ Get the `N` indices that are stress-controlled in `stress_state`. The tensor input is used to
112+ determine if a symmetric or full tensor is used.
113+ """
114+ function stress_controlled_indices end
115+
116+ """
117+ strain_controlled_indices(stress_state::AbstractStressState, ::AbstractTensor)::SVector{N, Int}
118+
119+ Get the `N` indices that are strain-controlled in `stress_state`. The tensor input is used to
120+ determine if a symmetric or full tensor is used.
121+ """
122+ function strain_controlled_indices end
123+
124+ # NoIterationState
125+ stress_controlled_indices (:: NoIterationState , :: AbstractTensor ) = SVector {0,Int} ()
126+ strain_controlled_indices (:: NoIterationState , :: SymmetricTensor ) = @SVector ([1 , 2 , 3 , 4 , 5 , 6 ])
127+ strain_controlled_indices (:: NoIterationState , :: Tensor ) = @SVector ([1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])
128+
129+ # UniaxialStress
130+ stress_controlled_indices (:: UniaxialStress , :: SymmetricTensor ) = @SVector ([2 , 3 , 4 , 5 , 6 ])
131+ stress_controlled_indices (:: UniaxialStress , :: Tensor ) = @SVector ([2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])
132+ strain_controlled_indices (:: UniaxialStress , :: AbstractTensor ) = @SVector ([1 ])
133+
134+ # UniaxialNormalStress
135+ stress_controlled_indices (:: UniaxialNormalStress , :: AbstractTensor ) = @SVector ([2 ,3 ])
136+ strain_controlled_indices (:: UniaxialNormalStress , :: SymmetricTensor ) = @SVector ([1 , 4 , 5 , 6 ])
137+ strain_controlled_indices (:: UniaxialNormalStress , :: Tensor ) = @SVector ([1 , 4 , 5 , 6 , 7 , 8 , 9 ])
138+
139+ # PlaneStress 12 -> 6, 21 -> 9
140+ stress_controlled_indices (:: PlaneStress , :: SymmetricTensor ) = @SVector ([3 , 4 , 5 ])
141+ stress_controlled_indices (:: PlaneStress , :: Tensor ) = @SVector ([3 , 4 , 5 , 7 , 8 ])
142+ strain_controlled_indices (:: PlaneStress , :: SymmetricTensor ) = @SVector ([1 , 2 , 6 ])
143+ strain_controlled_indices (:: PlaneStress , :: Tensor ) = @SVector ([1 , 2 , 6 , 9 ])
144+
145+ # GeneralStressState
146+ stress_controlled_indices (ss:: GeneralStressState{Nσ} , :: AbstractTensor ) where Nσ = controlled_indices_from_tensor (ss. σ_ctrl, true , Val (Nσ))
147+ function strain_controlled_indices (ss:: GeneralStressState{Nσ,TT} , :: AbstractTensor ) where {Nσ,TT}
148+ N = Tensors. n_components (Tensors. get_base (TT)) - Nσ
149+ return controlled_indices_from_tensor (ss. σ_ctrl, false , Val (N))
150+ end
151+ function controlled_indices_from_tensor (ctrl:: AbstractTensor , return_if, :: Val{N} ) where N
152+ return SVector {N} (i for (i, v) in pairs (tovoigt (SVector, ctrl)) if v == return_if)
153+ end
0 commit comments