version 0.4.1
assumedstress.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2021-2025 The Ikarus Developers ikarus@ibb.uni-stuttgart.de
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
10#pragma once
11
12#if HAVE_DUNE_LOCALFEFUNCTIONS
13 #include <dune/localfefunctions/derivativetransformators.hh>
14 #include <dune/localfefunctions/linearAlgebraHelper.hh>
15 #include <dune/localfefunctions/meta.hh>
16
25
26namespace Ikarus {
27
28template <typename PreFE, typename FE, typename ASType>
29class AssumedStress;
30
35template <typename ASType>
37{
39
40 template <typename PreFE, typename FE>
42};
43
55template <typename PreFE, typename FE, typename ASF>
57 : public std::conditional_t<std::same_as<ASF, PS::LinearStress>,
58 ResultTypeBase<ResultTypes::linearStress, ResultTypes::linearStressFull>,
59 ResultTypeBase<ResultTypes::PK2Stress, ResultTypes::PK2StressFull>>
60
61{
62public:
65 using LocalView = typename Traits::LocalView;
66 using Geometry = typename Traits::Geometry;
67 using GridView = typename Traits::GridView;
70
71 template <typename ST>
72 using VectorXOptRef = std::optional<std::reference_wrapper<const Eigen::VectorX<ST>>>;
73
74 template <template <typename, int, int> class RT>
76
77 static constexpr int myDim = Traits::mydim;
78 static constexpr int strainDim = myDim * (myDim + 1) / 2;
79 using StrainVector = Eigen::Vector<double, strainDim>;
80 using MaterialMatrix = Eigen::Matrix<double, strainDim, strainDim>;
81
86 explicit AssumedStress(const Pre& pre) {
88 "PS method is only implemented for the total Lagrangian setting.");
89 static_assert(not(FE::strainType == StrainTags::linear) or (std::same_as<ASF, PS::LinearStress>),
90 "If FE::strainType is linear, then the assumed stress must also be linear.");
92 }
93
99 const auto& asVariant() const { return asVariant_; }
100
106 auto numberOfInternalVariables() const { return asVariant_.numberOfInternalVariables(); }
107
121 template <template <typename, int, int> class RT>
122 requires(AssumedStress::template supportsResultType<RT>())
124 Dune::PriorityTag<2>) const {
125 if constexpr (isSameResultType<RT, ResultTypes::linearStress> or isSameResultType<RT, ResultTypes::PK2Stress> or
126 isSameResultType<RT, ResultTypes::linearStressFull> or
127 isSameResultType<RT, ResultTypes::PK2StressFull>) {
128 const auto geo = underlying().localView().element().geometry();
129 const auto ufunc = underlying().displacementFunction(req);
130 auto disp = Dune::viewAsFlatEigenVector(ufunc.coefficientsRef());
131
132 RTWrapperType<RT> resultWrapper{};
133 auto calculateAtContribution = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
134 Eigen::VectorXd beta;
135 beta.setZero(numberOfInternalVariables());
136 const auto& Rtilde = calculateRtilde<double>(req);
137 typename AssumedStressT::HType H;
138 calculateHAndGMatrix(asFunction, req, H, G_);
139 beta = H.inverse() * (G_ * disp);
140
141 const auto SVoigt = AssumedStressFunction::value(geo, local, asFunction, beta);
142
143 if constexpr ((isSameResultType<RT, ResultTypes::PK2StressFull> or
144 isSameResultType<RT, ResultTypes::linearStressFull>) and
145 requires { underlying().material().underlying(); }) {
146 resultWrapper = enlargeStressAnsatz(SVoigt);
147 } else {
148 resultWrapper = SVoigt;
149 }
150 };
151 asVariant_(calculateAtContribution);
152 return resultWrapper;
153 }
154 DUNE_THROW(Dune::NotImplemented, "The requested result type is not supported");
155 }
156
165 initializeState();
166 }
167
173 const auto& internalVariable() const { return beta_; }
174
175protected:
176 void bindImpl() {
177 assert(underlying().localView().bound());
178 asVariant_.bind(underlying().localView().element().geometry());
179 initializeState();
180 }
181
192 const std::remove_reference_t<typename Traits::template VectorType<>>& correction) {
193 using ScalarType = Traits::ctype;
195 auto correctbeta = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
196 const auto localdxBlock = Ikarus::FEHelper::localSolutionBlockVector<Traits, Eigen::VectorXd, double>(
197 correction, underlying().localView());
198 const auto localdx = Dune::viewAsFlatEigenVector(localdxBlock);
199
200 typename AssumedStressT::HType H;
201 calculateHAndGMatrix(asFunction, par, H, G_);
202 const auto& Rtilde = calculateRtilde<ScalarType>(par);
203 this->beta_ += H.inverse() * (Rtilde + (G_ * localdx));
204 };
205
206 asVariant_(correctbeta);
207 calculateMaterialInversion();
208 }
209
210 inline void asApplicabilityCheck() const {
211 const auto& numberOfNodes = underlying().numberOfNodes();
212 if (not((numberOfNodes == 4 and Traits::mydim == 2) or (numberOfNodes == 8 and Traits::mydim == 3)))
213 DUNE_THROW(Dune::NotImplemented, "AssumedStress is only supported for Q1 and H1 elements" +
214 std::to_string(numberOfNodes) + std::to_string(Traits::mydim));
215 }
216
217 template <typename ScalarType>
218 void calculateMatrixImpl(const Requirement& par, const MatrixAffordance& affordance,
219 typename Traits::template MatrixType<> K,
220 const VectorXOptRef<ScalarType>& dx = std::nullopt) const {
221 using namespace Dune;
222 using namespace Dune::DerivativeDirections;
223
224 if (affordance != MatrixAffordance::stiffness)
225 DUNE_THROW(Dune::NotImplemented, "MatrixAffordance not implemented: " + toString(affordance));
227
228 const auto geo = underlying().localView().element().geometry();
229 const auto& strainFunction = underlying().strainFunction(par, dx);
230 const auto& kGFunction = underlying().template geometricStiffnessMatrixFunction<ScalarType>(par, K, dx);
231 const auto numberOfNodes = underlying().numberOfNodes();
232 const auto& localBasis = underlying().localBasis();
233
234 auto calculateMatrixContribution = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
235 for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
236 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
237
238 for (size_t i = 0; i < numberOfNodes; ++i)
239 for (size_t j = 0; j < numberOfNodes; ++j) {
240 const auto E_dd =
241 strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i, j)), along(SVoigt), on(gridElement));
242 kGFunction(E_dd, i, j, gp);
243 }
244 }
245
246 typename AssumedStressT::HType H;
247 calculateHAndGMatrix(asFunction, par, H, G_, dx);
248
249 K.template triangularView<Eigen::Upper>() += G_.transpose() * H.inverse() * G_;
250 K.template triangularView<Eigen::StrictlyLower>() = K.transpose();
251 };
252 asVariant_(calculateMatrixContribution);
253 }
254
255 template <typename ScalarType>
256 inline ScalarType calculateScalarImpl(const Requirement& par, ScalarAffordance affordance,
257 const VectorXOptRef<ScalarType>& dx = std::nullopt) const {
258 using namespace Dune;
259 using namespace Dune::DerivativeDirections;
261 ScalarType energy = 0.0;
262
263 // This will not work in the context of autodiff because `AutoDiffFE` class doesn't include static condensation of
264 // the internal variable
265 if constexpr (not Concepts::AutodiffScalar<ScalarType>) {
266 const auto geo = underlying().localView().element().geometry();
267 const auto strainFunction = underlying().strainFunction(par, dx);
268
269 auto calculateScalarContribution = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
270 for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
271 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
272 const auto& Es = strainStates_[gpIndex];
273 const auto EVoigt = strainFunction.evaluate(gp.position(), on(gridElement));
274
275 energy += ((SVoigt.transpose() * EVoigt - ScalarType(0.5) * (SVoigt.transpose() * Es)).eval() *
276 geo.integrationElement(gp.position()) * gp.weight())
277 .eval()[0];
278 }
279 };
280
281 asVariant_(calculateScalarContribution);
282 } else {
283 DUNE_THROW(Dune::NotImplemented,
284 "AssumedStress element does not support scalar calculations for autodiff scalars");
285 }
286 return energy;
287 }
288
289 template <typename ScalarType>
291 typename Traits::template VectorType<ScalarType> force,
292 const VectorXOptRef<ScalarType>& dx = std::nullopt) const {
293 using namespace Dune::DerivativeDirections;
294 using namespace Dune;
295
296 if (affordance != VectorAffordance::forces)
297 DUNE_THROW(Dune::NotImplemented, "VectorAffordance not implemented: " + toString(affordance));
299
300 const auto geo = underlying().localView().element().geometry();
301 const auto& uFunction = underlying().displacementFunction(par, dx);
302 const auto& strainFunction = underlying().strainFunction(par, dx);
303 const auto numberOfNodes = underlying().numberOfNodes();
304 const auto& localBasis = underlying().localBasis();
305 const auto fIntFunction = underlying().template internalForcesFunction<ScalarType>(par, force, dx);
306
307 auto calculateForceContribution = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
308 for (const auto& [gpIndex, gp] : uFunction.viewOverIntegrationPoints()) {
309 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
310
311 for (size_t i = 0; i < numberOfNodes; ++i) {
312 const auto E_dI = strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i)), on(gridElement));
313 fIntFunction(SVoigt, E_dI, i, gp);
314 }
315 }
316 typename AssumedStressT::HType H;
317 calculateHAndGMatrix(asFunction, par, H, G_);
318 const auto Rtilde = calculateRtilde(par, dx);
319 force += G_.transpose() * H.inverse() * Rtilde;
320 };
321 asVariant_(calculateForceContribution);
322 }
323
324 template <typename MT, typename BC>
325 void subscribeToImpl(BC& bc) {
326 if constexpr (std::same_as<MT, NonLinearSolverMessages>) {
327 using NLSState = typename BC::State;
328 underlying().subscribe(bc, [&](NonLinearSolverMessages message, const NLSState& state) {
330 this->updateStateImpl(state.domain, state.correction);
331 });
332 }
333 }
334
335private:
337 mutable Eigen::MatrixXd G_;
338 Eigen::VectorXd beta_;
339 std::vector<StrainVector> strainStates_;
340 std::vector<MaterialMatrix> invertedMaterialStates_;
341
342 //> CRTP
343 const auto& underlying() const { return static_cast<const FE&>(*this); }
344 auto& underlying() { return static_cast<FE&>(*this); }
345
349 void initializeState() {
350 beta_.resize(numberOfInternalVariables());
351 beta_.setZero();
352 strainStates_.resize(underlying().localBasis().integrationPointSize(), StrainVector::Zero());
353 invertedMaterialStates_.resize(underlying().localBasis().integrationPointSize(), MaterialMatrix::Zero());
354 calculateMaterialInversion();
355 }
356
357 template <typename ScalarType, int assumedStressSize>
358 void calculateHAndGMatrix(const auto& asFunction, const auto& par,
359 Eigen::Matrix<double, assumedStressSize, assumedStressSize>& H,
360 Eigen::MatrixX<ScalarType>& G, const VectorXOptRef<ScalarType>& dx = std::nullopt) const {
361 using namespace Dune::DerivativeDirections;
362 using namespace Dune;
363
364 const auto& uFunction = underlying().displacementFunction(par);
365 const auto& strainFunction = underlying().strainFunction(par, dx);
366 const auto geo = underlying().localView().element().geometry();
367 const auto numberOfNodes = underlying().numberOfNodes();
368 const auto& localBasis = underlying().localBasis();
369 H.setZero();
370 G.setZero(assumedStressSize, underlying().localView().size());
371 for (const auto& [gpIndex, gp] : uFunction.viewOverIntegrationPoints()) {
372 const double intElement = geo.integrationElement(gp.position()) * gp.weight();
373
374 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
375 const auto& D = invertedMaterialStates_[gpIndex];
376
377 const auto S_b = AssumedStressFunction::template firstDerivative(geo, uFunction, localBasis, gpIndex,
378 gp.position(), asFunction, beta_);
379 const auto S_bb = AssumedStressFunction::template secondDerivative(geo, uFunction, localBasis, gpIndex,
380 gp.position(), SVoigt, asFunction, beta_);
381
382 H += (S_b.transpose() * D * S_b + S_bb) * intElement;
383
384 for (size_t i = 0U; i < numberOfNodes; ++i) {
385 const auto E_dI = strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i)), on(gridElement));
386
387 G.template block<assumedStressSize, Traits::worlddim>(0, myDim * i) += S_b.transpose() * E_dI * intElement;
388 }
389 }
390 }
391
392 template <typename ScalarType>
393 Eigen::VectorX<ScalarType> calculateRtilde(const Requirement& par,
394 const VectorXOptRef<ScalarType>& dx = std::nullopt) const {
395 using namespace Dune;
396 using namespace Dune::DerivativeDirections;
397
398 const auto geo = underlying().localView().element().geometry();
399 const auto& uFunction = underlying().displacementFunction(par, dx);
400 const auto strainFunction = underlying().strainFunction(par, dx);
401 const auto& localBasis = underlying().localBasis();
402
403 Eigen::VectorX<ScalarType> Rtilde;
404 Rtilde.setZero(numberOfInternalVariables());
405
406 auto calculateRtildeContribution = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
407 for (const auto& [gpIndex, gp] : uFunction.viewOverIntegrationPoints()) {
408 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
409 const auto S_b = AssumedStressFunction::template firstDerivative(geo, uFunction, localBasis, gpIndex,
410 gp.position(), asFunction, beta_);
411
412 const auto& Es = strainStates_[gpIndex];
413 const auto EVoigt = strainFunction.evaluate(gp.position(), on(gridElement));
414
415 const double intElement = geo.integrationElement(gp.position()) * gp.weight();
416 Rtilde += (S_b.transpose() * (EVoigt - Es)).eval() * intElement;
417 }
418 };
419
420 asVariant_(calculateRtildeContribution);
421 return Rtilde;
422 }
423
424 void calculateMaterialInversion() {
425 auto saveMaterialState = [&]<typename AssumedStressT>(const AssumedStressT& asFunction) {
426 const auto geo = underlying().localView().element().geometry();
427
428 for (const auto& [gpIndex, gp] : underlying().localBasis().viewOverIntegrationPoints()) {
429 const auto SVoigt = AssumedStressFunction::value(geo, gp.position(), asFunction, beta_);
430 const auto& EsOld = strainStates_[gpIndex];
431 std::tie(invertedMaterialStates_[gpIndex], strainStates_[gpIndex]) =
432 underlying().material().template materialInversion<FE::strainType, true>(SVoigt, EsOld);
433 }
434 };
435 asVariant_(saveMaterialState);
436 }
437
438 auto enlargeStressAnsatz(const StrainVector& SVoigt, size_t qpIndexForIteration = 0) const
439 requires(decltype(underlying().material())::isReduced)
440 {
441 auto [_, Es] = underlying().material().template materialInversion<FE::strainType, true>(
442 SVoigt, strainStates_[qpIndexForIteration]);
443 auto Esfull = enlargeIfReduced<decltype(underlying().material())>(Es).eval();
444 return underlying().material().underlying().template stresses<FE::strainType, true>(Esfull);
445 }
446};
447
453template <typename ASType = PS::LinearStress>
454auto assumedStress(int numberOfInternalVariables) {
455 return AssumedStressPre<ASType>(numberOfInternalVariables);
456}
457
458} // namespace Ikarus
459
460#else
461 #error AssumedStress depends on dune-localfefunctions, which is not included
462#endif
Helper for the autodiff library.
Definition of several material related enums.
Definition of the AssumedStress variants.
Header file for various assumed stress functions.
Definition of the LinearElastic class for finite element mechanics computations.
Enums for observer messages.
NonLinearSolverMessages
Enum class defining non-linear solver-related messages.
Definition: broadcastermessages.hh:22
decltype(auto) enlargeIfReduced(const Eigen::MatrixBase< Derived > &E)
Enlarges a matrix if it reduced in the context of material laws, i.e., VanishingStress If the materia...
Definition: linearalgebrahelper.hh:593
auto viewAsFlatEigenVector(Dune::BlockVector< ValueType > &blockedVector)
View Dune::BlockVector as an Eigen::Vector.
Definition: linearalgebrahelper.hh:57
Definition: assemblermanipulatorbuildingblocks.hh:22
MatrixAffordance
A strongly typed enum class representing the matrix affordance.
Definition: ferequirements.hh:64
VectorAffordance
A strongly typed enum class representing the vector affordance.
Definition: ferequirements.hh:49
auto assumedStress(int numberOfInternalVariables)
A helper function to create an assumed stress pre finite element.
Definition: assumedstress.hh:454
constexpr std::string toString(DBCOption _e)
Definition: dirichletbcenforcement.hh:8
ScalarAffordance
A strongly typed enum class representing the scalar affordance.
Definition: ferequirements.hh:38
Definition: utils/dirichletvalues.hh:30
FE class is a base class for all finite elements.
Definition: febase.hh:79
FETraits< BH, useEigenRef, useFlat > Traits
Definition: febase.hh:38
Class representing the requirements for finite element calculations.
Definition: ferequirements.hh:223
Container that is used for FE Results. It gives access to the stored value, but can also be used to a...
Definition: feresulttypes.hh:164
Traits for handling finite elements.
Definition: fetraits.hh:25
typename Basis::LocalView LocalView
Type of the local view.
Definition: fetraits.hh:42
typename Element::Geometry Geometry
Type of the element geometry.
Definition: fetraits.hh:51
double ctype
Type used for coordinates.
Definition: fetraits.hh:57
typename Basis::GridView GridView
Type of the grid view.
Definition: fetraits.hh:45
static constexpr int mydim
Dimension of the geometry.
Definition: fetraits.hh:63
Wrapper class for using Assumed Stress with displacement based elements.
Definition: assumedstress.hh:61
const auto & internalVariable() const
Gets the internal state variable beta for the AssumedStress element.
Definition: assumedstress.hh:173
void calculateMatrixImpl(const Requirement &par, const MatrixAffordance &affordance, typename Traits::template MatrixType<> K, const VectorXOptRef< ScalarType > &dx=std::nullopt) const
Definition: assumedstress.hh:218
void subscribeToImpl(BC &bc)
Definition: assumedstress.hh:325
typename Traits::LocalView LocalView
Definition: assumedstress.hh:65
AssumedStress(const Pre &pre)
Constructor for Assunmed Stress elements.
Definition: assumedstress.hh:86
static constexpr int myDim
Definition: assumedstress.hh:77
void setAssumedStressType(int numberOfInternalVariables)
Sets the AssumedStress type for 2D elements.
Definition: assumedstress.hh:162
typename Traits::Geometry Geometry
Definition: assumedstress.hh:66
auto calculateAtImpl(const Requirement &req, const Dune::FieldVector< double, Traits::mydim > &local, Dune::PriorityTag< 2 >) const
Calculates a requested result at a specific local position using the Assumed Stress Method.
Definition: assumedstress.hh:123
void updateStateImpl(const Requirement &par, const std::remove_reference_t< typename Traits::template VectorType<> > &correction)
Updates the internal state variable beta_ at the end of an iteration before the update of the displac...
Definition: assumedstress.hh:191
typename Traits::GridView GridView
Definition: assumedstress.hh:67
void calculateVectorImpl(const Requirement &par, VectorAffordance affordance, typename Traits::template VectorType< ScalarType > force, const VectorXOptRef< ScalarType > &dx=std::nullopt) const
Definition: assumedstress.hh:290
Eigen::Matrix< double, strainDim, strainDim > MaterialMatrix
Definition: assumedstress.hh:80
FERequirements< FESolutions::displacement, FEParameter::loadfactor > Requirement
Definition: assumedstress.hh:64
void bindImpl()
Definition: assumedstress.hh:176
ASF AssumedStressFunction
Definition: assumedstress.hh:69
ScalarType calculateScalarImpl(const Requirement &par, ScalarAffordance affordance, const VectorXOptRef< ScalarType > &dx=std::nullopt) const
Definition: assumedstress.hh:256
std::optional< std::reference_wrapper< const Eigen::VectorX< ST > > > VectorXOptRef
Definition: assumedstress.hh:72
static constexpr int strainDim
Definition: assumedstress.hh:78
auto numberOfInternalVariables() const
Gets the number of AssumedStress parameters based on the current AssumedStress type.
Definition: assumedstress.hh:106
const auto & asVariant() const
Gets the variant representing the type of Assumed Stress.
Definition: assumedstress.hh:99
Eigen::Vector< double, strainDim > StrainVector
Definition: assumedstress.hh:79
void asApplicabilityCheck() const
Definition: assumedstress.hh:210
A PreFE struct for Assumed Stress.
Definition: assumedstress.hh:37
int numberOfParameters
Definition: assumedstress.hh:38
auto numberOfInternalVariables() const
A helper function to get the number of AssumedStress parameters.
Definition: asvariants.hh:63
void bind(const GEO &geometry)
Definition: asvariants.hh:73
void setAssumedStressType(int numberOfInternalVariables)
Definition: asvariants.hh:68
Definition: utils/dirichletvalues.hh:32
Concept to check if the underlying scalar type is a dual type.
Definition: utils/concepts.hh:622
Concept to check if the underlying strain and stress tag correspond to a total Lagrangian formulation...
Definition: utils/concepts.hh:670
Several concepts.