Skip to content

Commit def4b16

Browse files
committed
add nonlinear version of eas
all commits squashed for an easy rebase
1 parent 2ff5c77 commit def4b16

28 files changed

+648
-82
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ SPDX-License-Identifier: LGPL-3.0-or-later
5656
- Add an About Ikarus page in the documentation ([#291](https://github.com/ikarus-project/ikarus/pull/291))
5757
- Add new class `Vtk::Writer`, which implements some convenience methods over the existing `dune-vtk` module ([#309](https://github.com/ikarus-project/ikarus/pull/309))
5858
- Add `VanishingStrain` material (useful for example for plane strain case), also refactor the constructor of `LinearElastic` to take any linear material law ([#317](https://github.com/ikarus-project/ikarus/pull/317))
59+
- Refactor EAS to handle NonLinearElastic ([#325](https://github.com/ikarus-project/ikarus/pull/325))
60+
- `updateState()` function is added to `mixin.hh`, which can be used to update the internal state variables of the skills.
61+
- `updateStateImpl()` now has to be implemented by every skill.
62+
- For EAS, `updateStateImpl()` is used to update the internal variable `alpha` in a nonlinear analysis.
63+
- Missing functions like `getStress` and `materialTangentFunction` are added to `LinearElastic` and `NonLinearElastic`, respectively.
64+
- A `helperfunctions.hh` file is added that contains the helper functions used by the nonlinear solvers.
65+
- This contains the function `updateStates()` that calls the `updateState()` function for every finite element.
66+
- `updateStates()` is called by nonlinear solvers during every iteration, whenever the correction vector is updated.
67+
- `NonLinearOperator` now has an additional template argument, `Assembler`.
68+
- It stores a shared pointer to the underlying assembler.
69+
- This helps the nonlinear solvers to have access to the underlying assembler,
70+
which in turn provides the finite element container to update the state variables.
5971

6072
## Release v0.4 (Ganymede)
6173

docs/literature.bib

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,29 @@ @book{bonet2008nonlinear
175175
doi = {https://doi.org/10.1017/CBO9780511755446}
176176
}
177177

178+
@article{bischoffShearDeformableShell1997b,
179+
title = {{Shear deformable shell elements for large strains and rotations}},
180+
author = {Bischoff, M. and Ramm, E.},
181+
year = {1997},
182+
journal = {International Journal for Numerical Methods in Engineering},
183+
volume = {40},
184+
number = {23},
185+
pages = {4427--4449},
186+
issn = {1097-0207},
187+
doi = {10.1002/(SICI)1097-0207(19971215)40:23<4427::AID-NME268>3.0.CO;2-9}
188+
}
178189

179-
180-
181-
182-
190+
@article{klinkelGeometricalNonlinearBrick1997,
191+
title = {A Geometrical Non-Linear Brick Element Based on the {{EAS-method}}},
192+
author = {Klinkel, S. and Wagner, W.},
193+
year = {1997},
194+
journal = {International Journal for Numerical Methods in Engineering},
195+
volume = {40},
196+
number = {24},
197+
pages = {4529--4545},
198+
issn = {1097-0207},
199+
doi = {10.1002/(SICI)1097-0207(19971230)40:24<4529::AID-NME271>3.0.CO;2-I}
200+
}
183201

184202
@book{trustregion,
185203
author = {Conn, Andrew R. and Gould, Nicholas I. M. and Toint, Philippe L.},

ikarus/assembler/assemblermanipulatorfuser.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public:
8282
using WrappedAssembler::boundToDBCOption;
8383
using WrappedAssembler::boundToRequirement;
8484
using WrappedAssembler::createFullVector;
85+
using WrappedAssembler::finiteElements;
8586

8687
using WrappedAssembler::affordanceCollection;
8788
using WrappedAssembler::constraintsBelow;
@@ -173,6 +174,7 @@ public:
173174
using WrappedAssembler::boundToDBCOption;
174175
using WrappedAssembler::boundToRequirement;
175176
using WrappedAssembler::createFullVector;
177+
using WrappedAssembler::finiteElements;
176178

177179
using WrappedAssembler::affordanceCollection;
178180
using WrappedAssembler::constraintsBelow;
@@ -283,6 +285,7 @@ public:
283285
using WrappedAssembler::boundToDBCOption;
284286
using WrappedAssembler::boundToRequirement;
285287
using WrappedAssembler::createFullVector;
288+
using WrappedAssembler::finiteElements;
286289

287290
using WrappedAssembler::affordanceCollection;
288291
using WrappedAssembler::constraintsBelow;

ikarus/finiteelements/ferequirements.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ auto scalarAffordance(VectorAffordance affordanceV) {
203203
namespace AffordanceCollections {
204204
inline constexpr AffordanceCollection elastoStatics(ScalarAffordance::mechanicalPotentialEnergy,
205205
VectorAffordance::forces, MatrixAffordance::stiffness);
206-
}
206+
} // namespace AffordanceCollections
207207

208208
namespace Impl {
209209
template <typename T>

ikarus/finiteelements/fetraits.hh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ struct FETraits
8080
using VectorType =
8181
std::conditional_t<useEigenRef, Eigen::Ref<Eigen::VectorX<ScalarType>>, Eigen::VectorX<ScalarType>&>;
8282

83+
/** \brief Type of the vector passed to updateState */
84+
template <typename ScalarType = ctype>
85+
using VectorTypeConst =
86+
std::conditional_t<useEigenRef, const Eigen::Ref<Eigen::VectorX<ScalarType>>, const Eigen::VectorX<ScalarType>&>;
87+
8388
/** \brief Type of the matrix passed to calculateMatrix */
8489
template <typename ScalarType = ctype>
8590
using MatrixType =

ikarus/finiteelements/mechanics/enhancedassumedstrains.hh

Lines changed: 118 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -138,27 +138,73 @@ public:
138138
if (numberOfEASParameters != 0)
139139
easApplicabilityCheck();
140140
easVariant_.setEASType(numberOfEASParameters);
141+
initializeState();
141142
}
142143

144+
/**
145+
* \brief Gets the internal state variable alpha for the EAS element.
146+
*
147+
* \return Internal state variable (alpha).
148+
*/
149+
const auto& alpha() const { return alpha_; }
150+
143151
protected:
144152
void bindImpl() {
145153
assert(underlying().localView().bound());
146154
easVariant_.bind(underlying().localView().element().geometry());
155+
initializeState();
156+
}
157+
158+
/**
159+
* \brief Updates the internal state variable alpha_ at the end of an iteration
160+
* when NonLinearSolverMessages::SOLUTION_UPDATED is notified by the non-linear solver.
161+
* See \cite bischoffShearDeformableShell1997b and \cite klinkelGeometricalNonlinearBrick1997 for implementation
162+
* details.
163+
*
164+
* \param par The Requirement object.
165+
* \param correction The correction in displacement (DeltaD) vector passed based on which the internal state variable
166+
* alpha is to be updated.
167+
*/
168+
void updateStateImpl(const Requirement& par, typename Traits::template VectorTypeConst<> correction) {
169+
using ScalarType = Traits::ctype;
170+
if (isDisplacementBased())
171+
return;
172+
const auto& Rtilde = calculateRtilde<ScalarType>(par);
173+
const auto localdxBlock = Ikarus::FEHelper::localSolutionBlockVector<Traits, Eigen::VectorXd, double>(
174+
correction, underlying().localView());
175+
const auto localdx = Dune::viewAsFlatEigenVector(localdxBlock);
176+
177+
decltype(auto) LMat = [this]() -> decltype(auto) {
178+
if constexpr (std::is_same_v<ScalarType, double>)
179+
return [this]() -> Eigen::MatrixXd& { return L_; }();
180+
else
181+
return Eigen::MatrixX<ScalarType>{};
182+
}();
183+
184+
auto correctAlpha = [&]<typename EAST>(const EAST& easFunction) {
185+
constexpr int enhancedStrainSize = EAST::enhancedStrainSize;
186+
Eigen::Matrix<double, enhancedStrainSize, enhancedStrainSize> D;
187+
calculateDAndLMatrix(easFunction, par, D, LMat);
188+
const decltype(alpha_) updateAlpha = D.inverse() * (Rtilde + (LMat * localdx).eval());
189+
this->alpha_ -= updateAlpha;
190+
};
191+
192+
easVariant_(correctAlpha);
147193
}
148194

149-
public:
150-
protected:
151195
inline void easApplicabilityCheck() const {
152196
const auto& numberOfNodes = underlying().numberOfNodes();
153197
assert(not(not((numberOfNodes == 4 and Traits::mydim == 2) or (numberOfNodes == 8 and Traits::mydim == 3)) and
154198
(not isDisplacementBased())) &&
155-
"EAS only supported for Q1 or H1 elements");
199+
"EAS is only supported for Q1 or H1 elements");
156200
}
157201

158202
template <typename ScalarType>
159203
void calculateMatrixImpl(
160204
const Requirement& par, const MatrixAffordance& affordance, typename Traits::template MatrixType<> K,
161205
const std::optional<std::reference_wrapper<const Eigen::VectorX<ScalarType>>>& dx = std::nullopt) const {
206+
if (affordance != MatrixAffordance::stiffness)
207+
DUNE_THROW(Dune::NotImplemented, "MatrixAffordance not implemented: " + toString(affordance));
162208
using namespace Dune::DerivativeDirections;
163209
using namespace Dune;
164210
easApplicabilityCheck();
@@ -172,10 +218,30 @@ protected:
172218
return [this]() -> Eigen::MatrixXd& { return L_; }();
173219
}();
174220

221+
auto strainFunction = underlying().strainFunction(par, dx);
222+
const auto C = underlying().materialTangentFunction(par);
223+
const auto geo = underlying().localView().element().geometry();
224+
const auto numberOfNodes = underlying().numberOfNodes();
225+
175226
auto calculateMatrixContribution = [&]<typename EAST>(const EAST& easFunction) {
176227
typename EAST::DType D;
177228
calculateDAndLMatrix(easFunction, par, D, LMat, dx);
178229

230+
for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
231+
const auto M = easFunction.calcM(gp.position());
232+
const double intElement = geo.integrationElement(gp.position()) * gp.weight();
233+
const auto EVoigt = strainFunction.evaluate(gpIndex, on(gridElement)).eval();
234+
const auto CEval = C(gpIndex);
235+
auto stresses = (CEval * M * alpha_).eval();
236+
for (size_t i = 0; i < numberOfNodes; ++i) {
237+
for (size_t j = 0; j < numberOfNodes; ++j) {
238+
const auto kgIJ =
239+
strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i, j)), along(stresses), on(gridElement));
240+
K.template block<Traits::mydim, Traits::mydim>(i * Traits::mydim, j * Traits::mydim) += kgIJ * intElement;
241+
}
242+
}
243+
}
244+
179245
K.template triangularView<Eigen::Upper>() -= LMat.transpose() * D.inverse() * LMat;
180246
K.template triangularView<Eigen::StrictlyLower>() = K.transpose();
181247
};
@@ -197,50 +263,59 @@ protected:
197263
void calculateVectorImpl(
198264
const Requirement& par, VectorAffordance affordance, typename Traits::template VectorType<ScalarType> force,
199265
const std::optional<std::reference_wrapper<const Eigen::VectorX<ScalarType>>>& dx = std::nullopt) const {
266+
if (affordance != VectorAffordance::forces)
267+
DUNE_THROW(Dune::NotImplemented, "VectorAffordance not implemented: " + toString(affordance));
200268
easApplicabilityCheck();
269+
if (isDisplacementBased())
270+
return;
201271
using namespace Dune;
202272
using namespace Dune::DerivativeDirections;
203273
const auto uFunction = underlying().displacementFunction(par, dx);
204274
auto strainFunction = underlying().strainFunction(par, dx);
205275
const auto& numberOfNodes = underlying().numberOfNodes();
276+
const auto C = underlying().materialTangentFunction(par);
206277

207278
auto calculateForceContribution = [&]<typename EAST>(const EAST& easFunction) {
208279
typename EAST::DType D;
209280
calculateDAndLMatrix(easFunction, par, D, L_);
210281

211-
decltype(auto) LMat = [this]() -> decltype(auto) {
212-
if constexpr (Concepts::AutodiffScalar<ScalarType>)
213-
return Eigen::MatrixX<ScalarType>{};
214-
else
215-
return [this]() -> Eigen::MatrixXd& { return L_; }();
216-
}();
217-
const auto disp = Dune::viewAsFlatEigenVector(uFunction.coefficientsRef());
218-
const auto alpha = (-D.inverse() * L_ * disp).eval();
219-
const auto geo = underlying().localView().element().geometry();
220-
auto C = underlying().materialTangentFunction(par);
221-
282+
const auto disp = Dune::viewAsFlatEigenVector(uFunction.coefficientsRef());
283+
const auto geo = underlying().localView().element().geometry();
284+
const auto& Rtilde = calculateRtilde(par, dx);
222285
for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
223286
const auto M = easFunction.calcM(gp.position());
224287
const double intElement = geo.integrationElement(gp.position()) * gp.weight();
225288
const auto CEval = C(gpIndex);
226-
auto stresses = (CEval * M * alpha).eval();
289+
auto stresses = (CEval * M * alpha_).eval();
227290
for (size_t i = 0; i < numberOfNodes; ++i) {
228291
const auto bopI = strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i)), on(gridElement));
229292
force.template segment<Traits::worlddim>(Traits::worlddim * i) += bopI.transpose() * stresses * intElement;
230293
}
231294
}
295+
force -= L_.transpose() * D.inverse() * Rtilde;
232296
};
233297
easVariant_(calculateForceContribution);
234298
}
235299

236300
private:
237301
EAS::Impl::EASVariant<Geometry> easVariant_;
238302
mutable Eigen::MatrixXd L_;
303+
Eigen::VectorXd alpha_;
239304

240305
//> CRTP
241306
const auto& underlying() const { return static_cast<const FE&>(*this); }
242307
auto& underlying() { return static_cast<FE&>(*this); }
243308

309+
/**
310+
* \brief Initializes the internal state variable alpha_ based on the number of EAS parameters.
311+
*/
312+
void initializeState() {
313+
if (isDisplacementBased())
314+
return;
315+
alpha_.resize(numberOfEASParameters());
316+
alpha_.setZero();
317+
}
318+
244319
template <typename ScalarType, int enhancedStrainSize>
245320
void calculateDAndLMatrix(
246321
const auto& easFunction, const auto& par, Eigen::Matrix<double, enhancedStrainSize, enhancedStrainSize>& DMat,
@@ -267,6 +342,34 @@ private:
267342
}
268343
}
269344
}
345+
346+
template <typename ScalarType>
347+
Eigen::VectorX<ScalarType> calculateRtilde(
348+
const Requirement& par,
349+
const std::optional<std::reference_wrapper<const Eigen::VectorX<ScalarType>>>& dx = std::nullopt) const {
350+
using namespace Dune;
351+
using namespace Dune::DerivativeDirections;
352+
const auto geo = underlying().localView().element().geometry();
353+
auto strainFunction = underlying().strainFunction(par, dx);
354+
const auto C = underlying().materialTangentFunction(par);
355+
Eigen::VectorX<ScalarType> Rtilde;
356+
Rtilde.setZero(numberOfEASParameters());
357+
358+
auto calculateRtildeContribution = [&]<typename EAST>(const EAST& easFunction) {
359+
for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
360+
const auto M = easFunction.calcM(gp.position());
361+
const double intElement = geo.integrationElement(gp.position()) * gp.weight();
362+
const auto EVoigt = (strainFunction.evaluate(gpIndex, on(gridElement))).eval();
363+
const auto CEval = C(gpIndex);
364+
auto stresses = ((CEval * M * alpha_).template cast<ScalarType>()).eval();
365+
stresses += underlying().getStress(EVoigt).eval();
366+
Rtilde += (M.transpose() * stresses).eval() * intElement;
367+
}
368+
};
369+
370+
easVariant_(calculateRtildeContribution);
371+
return Rtilde;
372+
}
270373
};
271374

272375
/**

ikarus/finiteelements/mechanics/kirchhoffloveshell.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ protected:
234234
return kin;
235235
}
236236

237+
void updateStateImpl(const Requirement& /* par */, typename Traits::template VectorTypeConst<> /* correction */) {}
238+
237239
template <typename ST>
238240
void calculateMatrixImpl(
239241
const Requirement& par, const MatrixAffordance& affordance, typename Traits::template MatrixType<ST> K,

ikarus/finiteelements/mechanics/linearelastic.hh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,24 @@ public:
154154
return [&]([[maybe_unused]] auto gp) { return materialTangent(); };
155155
}
156156

157+
/**
158+
* \brief Get the stress for the given strain.
159+
*
160+
* \tparam ScalarType The scalar type for the material and strain.
161+
* \tparam strainDim The dimension of the strain vector.
162+
* \tparam voigt A boolean indicating whether to use the Voigt notation for stress.
163+
* \param strain The strain vector.
164+
* \return The stress vector calculated using the material's stresses function.
165+
*/
166+
template <typename ScalarType, int strainDim, bool voigt = true>
167+
auto getStress(const Eigen::Vector<ScalarType, strainDim>& strain) const {
168+
return (materialTangent() * strain).eval();
169+
}
170+
157171
const Geometry& geometry() const { return *geo_; }
158172
[[nodiscard]] size_t numberOfNodes() const { return numberOfNodes_; }
159173
[[nodiscard]] int order() const { return order_; }
160174

161-
public:
162175
/**
163176
* \brief Calculates a requested result at a specific local position.
164177
*
@@ -195,6 +208,8 @@ private:
195208
int order_{};
196209

197210
protected:
211+
void updateStateImpl(const Requirement& /* par */, typename Traits::template VectorTypeConst<> /* correction */) {}
212+
198213
template <typename ScalarType>
199214
void calculateMatrixImpl(
200215
const Requirement& par, const MatrixAffordance& affordance, typename Traits::template MatrixType<> K,

ikarus/finiteelements/mechanics/loads/traction.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ protected:
143143
const Requirement&, MatrixAffordance, typename Traits::template MatrixType<>,
144144
const std::optional<std::reference_wrapper<const Eigen::VectorX<ST>>>& = std::nullopt) const {}
145145

146+
void updateStateImpl(const Requirement& /* par */, typename Traits::template VectorTypeConst<> /* correction */) {}
147+
146148
private:
147149
std::function<Eigen::Vector<double, worldDim>(const Dune::FieldVector<double, worldDim>&, const double&)>
148150
neumannBoundaryLoad_;

ikarus/finiteelements/mechanics/loads/volume.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ protected:
111111
const Requirement& par, MatrixAffordance, typename Traits::template MatrixType<> K,
112112
const std::optional<std::reference_wrapper<const Eigen::VectorX<ST>>>& dx = std::nullopt) const {}
113113

114+
void updateStateImpl(const Requirement& /* par */, typename Traits::template VectorTypeConst<> /* correction */) {}
115+
114116
private:
115117
std::function<Eigen::Vector<double, worldDim>(const Dune::FieldVector<double, worldDim>&, const double&)> volumeLoad_;
116118
//> CRTP

ikarus/finiteelements/mechanics/membranestrains.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
#pragma once
10-
#include <ranges>
1110

1211
#include <dune/common/fvector.hh>
1312

0 commit comments

Comments
 (0)