Skip to content

Commit 81eae4d

Browse files
committed
add nonlinear version of eas
1 parent 8e2263e commit 81eae4d

File tree

5 files changed

+266
-16
lines changed

5 files changed

+266
-16
lines changed

ikarus/finiteelements/mechanics/enhancedassumedstrains.hh

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,27 +137,74 @@ public:
137137
if (numberOfEASParameters != 0)
138138
easApplicabilityCheck();
139139
easVariant_.setEASType(numberOfEASParameters);
140+
initializeState();
140141
}
141142

143+
/**
144+
* \brief Initializes the internal parameter alpha_ based on the number of EAS parameters.
145+
*/
146+
void initializeState() {
147+
if (isDisplacementBased())
148+
return;
149+
alpha_.resize(numberOfEASParameters());
150+
alpha_.setZero();
151+
}
152+
153+
/**
154+
* \brief Updates the internal parameter alpha_ at the end of an iteration
155+
* when NonLinearSolverMessages::SOLUTION_UPDATED is notified by the non-linear solver.
156+
*
157+
* \param par The Requirement object.
158+
*/
159+
template <typename ScalarType = double>
160+
void updateState(const Requirement& par, const Eigen::VectorXd& correction_) {
161+
if (isDisplacementBased())
162+
return;
163+
const auto& Rtilde = calculateRtilde<ScalarType>(par);
164+
const auto localdxBlock = Ikarus::FEHelper::localSolutionBlockVector<Traits, Eigen::VectorXd, double>(
165+
correction_, underlying().localView());
166+
const auto localdx = Dune::viewAsFlatEigenVector(localdxBlock);
167+
168+
decltype(auto) LMat = [this]() -> decltype(auto) {
169+
if constexpr (std::is_same_v<ScalarType, double>)
170+
return [this]() -> Eigen::MatrixXd& { return L_; }();
171+
else
172+
return Eigen::MatrixX<ScalarType>{};
173+
}();
174+
175+
auto correctAlpha = [&]<typename EAST>(const EAST& easFunction) {
176+
constexpr int enhancedStrainSize = EAST::enhancedStrainSize;
177+
Eigen::Matrix<double, enhancedStrainSize, enhancedStrainSize> D;
178+
calculateDAndLMatrix(easFunction, par, D, LMat);
179+
const decltype(alpha_) updateAlpha = D.inverse() * (Rtilde + (LMat * localdx).eval());
180+
this->alpha_ -= updateAlpha;
181+
};
182+
183+
easVariant_(correctAlpha);
184+
}
185+
186+
const auto& alpha() const { return alpha_; }
187+
142188
protected:
143189
void bindImpl() {
144190
assert(underlying().localView().bound());
145191
easVariant_.bind(underlying().localView().element().geometry());
192+
initializeState();
146193
}
147194

148-
public:
149-
protected:
150195
inline void easApplicabilityCheck() const {
151196
const auto& numberOfNodes = underlying().numberOfNodes();
152197
assert(not(not((numberOfNodes == 4 and Traits::mydim == 2) or (numberOfNodes == 8 and Traits::mydim == 3)) and
153198
(not isDisplacementBased())) &&
154-
"EAS only supported for Q1 or H1 elements");
199+
"EAS is only supported for Q1 or H1 elements");
155200
}
156201

157202
template <typename ScalarType>
158203
void calculateMatrixImpl(
159204
const Requirement& par, const MatrixAffordance& affordance, typename Traits::template MatrixType<> K,
160205
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));
161208
using namespace Dune::DerivativeDirections;
162209
using namespace Dune;
163210
easApplicabilityCheck();
@@ -171,10 +218,30 @@ protected:
171218
return Eigen::MatrixX<ScalarType>{};
172219
}();
173220

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+
174226
auto calculateMatrixContribution = [&]<typename EAST>(const EAST& easFunction) {
175227
typename EAST::DType D;
176228
calculateDAndLMatrix(easFunction, par, D, LMat, dx);
177229

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+
178245
K.template triangularView<Eigen::Upper>() -= LMat.transpose() * D.inverse() * LMat;
179246
K.template triangularView<Eigen::StrictlyLower>() = K.transpose();
180247
};
@@ -196,45 +263,44 @@ protected:
196263
void calculateVectorImpl(
197264
const Requirement& par, VectorAffordance affordance, typename Traits::template VectorType<ScalarType> force,
198265
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));
199268
easApplicabilityCheck();
269+
if (isDisplacementBased())
270+
return;
200271
using namespace Dune;
201272
using namespace Dune::DerivativeDirections;
202273
const auto uFunction = underlying().displacementFunction(par, dx);
203274
auto strainFunction = underlying().strainFunction(par, dx);
204275
const auto& numberOfNodes = underlying().numberOfNodes();
276+
const auto C = underlying().materialTangentFunction(par);
205277

206278
auto calculateForceContribution = [&]<typename EAST>(const EAST& easFunction) {
207279
typename EAST::DType D;
208280
calculateDAndLMatrix(easFunction, par, D, L_);
209281

210-
decltype(auto) LMat = [this]() -> decltype(auto) {
211-
if constexpr (std::is_same_v<ScalarType, double>)
212-
return [this]() -> Eigen::MatrixXd& { return L_; }();
213-
else
214-
return Eigen::MatrixX<ScalarType>{};
215-
}();
216-
const auto disp = Dune::viewAsFlatEigenVector(uFunction.coefficientsRef());
217-
const auto alpha = (-D.inverse() * L_ * disp).eval();
218-
const auto geo = underlying().localView().element().geometry();
219-
auto C = underlying().materialTangentFunction(par);
220-
282+
const auto disp = Dune::viewAsFlatEigenVector(uFunction.coefficientsRef());
283+
const auto geo = underlying().localView().element().geometry();
284+
const auto& Rtilde = calculateRtilde(par, dx);
221285
for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
222286
const auto M = easFunction.calcM(gp.position());
223287
const double intElement = geo.integrationElement(gp.position()) * gp.weight();
224288
const auto CEval = C(gpIndex);
225-
auto stresses = (CEval * M * alpha).eval();
289+
auto stresses = (CEval * M * alpha_).eval();
226290
for (size_t i = 0; i < numberOfNodes; ++i) {
227291
const auto bopI = strainFunction.evaluateDerivative(gpIndex, wrt(coeff(i)), on(gridElement));
228292
force.template segment<Traits::worlddim>(Traits::worlddim * i) += bopI.transpose() * stresses * intElement;
229293
}
230294
}
295+
force -= L_.transpose() * D.inverse() * Rtilde;
231296
};
232297
easVariant_(calculateForceContribution);
233298
}
234299

235300
private:
236301
EAS::Impl::EASVariant<Geometry> easVariant_;
237302
mutable Eigen::MatrixXd L_;
303+
Eigen::VectorXd alpha_;
238304

239305
//> CRTP
240306
const auto& underlying() const { return static_cast<const FE&>(*this); }
@@ -266,6 +332,34 @@ private:
266332
}
267333
}
268334
}
335+
336+
template <typename ScalarType>
337+
Eigen::VectorX<ScalarType> calculateRtilde(
338+
const Requirement& par,
339+
const std::optional<std::reference_wrapper<const Eigen::VectorX<ScalarType>>>& dx = std::nullopt) const {
340+
using namespace Dune;
341+
using namespace Dune::DerivativeDirections;
342+
const auto geo = underlying().localView().element().geometry();
343+
auto strainFunction = underlying().strainFunction(par, dx);
344+
const auto C = underlying().materialTangentFunction(par);
345+
Eigen::VectorX<ScalarType> Rtilde;
346+
Rtilde.setZero(numberOfEASParameters());
347+
348+
auto calculateRtildeContribution = [&]<typename EAST>(const EAST& easFunction) {
349+
for (const auto& [gpIndex, gp] : strainFunction.viewOverIntegrationPoints()) {
350+
const auto M = easFunction.calcM(gp.position());
351+
const double intElement = geo.integrationElement(gp.position()) * gp.weight();
352+
const auto EVoigt = (strainFunction.evaluate(gpIndex, on(gridElement))).eval();
353+
const auto CEval = C(gpIndex);
354+
auto stresses = ((CEval * M * alpha_).template cast<ScalarType>()).eval();
355+
stresses += underlying().getStress(EVoigt).eval();
356+
Rtilde += (M.transpose() * stresses).eval() * intElement;
357+
}
358+
};
359+
360+
easVariant_(calculateRtildeContribution);
361+
return Rtilde;
362+
}
269363
};
270364

271365
/**

ikarus/finiteelements/mechanics/linearelastic.hh

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

157+
template <typename ScalarType, int strainDim, bool voigt = true>
158+
auto getStress(const Eigen::Vector<ScalarType, strainDim>& strain) const {
159+
return (materialTangent() * strain).eval();
160+
}
161+
157162
const Geometry& geometry() const { return *geo_; }
158163
[[nodiscard]] size_t numberOfNodes() const { return numberOfNodes_; }
159164
[[nodiscard]] int order() const { return order_; }
160165

161-
public:
162166
/**
163167
* \brief Calculates a requested result at a specific local position.
164168
*

ikarus/finiteelements/mechanics/nonlinearelastic.hh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,22 @@ public:
160160
}
161161
}
162162

163+
/**
164+
* \brief Gets the material tangent function for the given Requirement.
165+
*
166+
* \param par The Requirement object.
167+
* \return The material tangent function.
168+
*/
169+
auto materialTangentFunction([[maybe_unused]] const Requirement& par) const {
170+
return [&]([[maybe_unused]] auto gpIndex) {
171+
using namespace Dune::DerivativeDirections;
172+
using namespace Dune;
173+
const auto eps = strainFunction(par);
174+
const auto EVoigt = (eps.evaluate(gpIndex, on(gridElement))).eval();
175+
return materialTangent(EVoigt);
176+
};
177+
}
178+
163179
/**
164180
* \brief Get the internal energy for the given strain.
165181
*

tests/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ set(programSourceFiles
2727
testlinearelasticity.cpp
2828
testmanifolds.cpp
2929
testmaterial.cpp
30+
testnonlineareas.cpp
3031
testnonlinearelasticityneohooke.cpp
3132
testnonlinearelasticitysvk.cpp
3233
testnonlinearoperator.cpp

tests/src/testnonlineareas.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-FileCopyrightText: 2021-2024 The Ikarus Developers [email protected]
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#include <config.h>
5+
6+
#include "testcommon.hh"
7+
8+
#include <dune/common/test/testsuite.hh>
9+
#include <dune/fufem/boundarypatch.hh>
10+
#include <dune/functions/functionspacebases/lagrangebasis.hh>
11+
#include <dune/functions/functionspacebases/powerbasis.hh>
12+
13+
#include <ikarus/finiteelements/autodifffe.hh>
14+
#include <ikarus/finiteelements/fefactory.hh>
15+
#include <ikarus/finiteelements/mechanics/enhancedassumedstrains.hh>
16+
#include <ikarus/finiteelements/mechanics/materials.hh>
17+
#include <ikarus/finiteelements/mechanics/nonlinearelastic.hh>
18+
#include <ikarus/utils/basis.hh>
19+
#include <ikarus/utils/init.hh>
20+
21+
using namespace Ikarus;
22+
using namespace Dune::Indices;
23+
using Dune::TestSuite;
24+
25+
template <int gridDim, typename GV, typename MAT, typename BH, typename VectorType>
26+
auto nonlinearEASAutoDiffTest(const GV& gridView, const MAT& mat, const BH& basis, VectorType& d,
27+
const int numberOfEASParameters) {
28+
TestSuite t("Nonlinear EAS AutoDiff with gridDim = " + std::to_string(gridDim) +
29+
" and numberOfEASParameters = " + std::to_string(numberOfEASParameters));
30+
double lambda = 7.3;
31+
auto sk = skills(nonLinearElastic(mat), eas(numberOfEASParameters));
32+
auto fe = Ikarus::makeFE(basis, sk);
33+
using FE = decltype(fe);
34+
auto req = typename FE::Requirement();
35+
req.insertGlobalSolution(d).insertParameter(lambda);
36+
37+
auto affordance = AffordanceCollections::elastoStatics;
38+
39+
for (auto element : elements(gridView)) {
40+
auto localView = basis.flat().localView();
41+
localView.bind(element);
42+
auto nDOF = localView.size();
43+
const double tol = 1e-10;
44+
45+
fe.bind(element);
46+
fe.updateState(req, d); // here d = correction vector (DeltaD)
47+
48+
const std::string feClassName = Dune::className(fe);
49+
50+
using AutoDiffBasedFE = Ikarus::AutoDiffFE<decltype(fe), true>;
51+
AutoDiffBasedFE feAutoDiff(fe);
52+
53+
Eigen::MatrixXd K, KAutoDiff;
54+
K.setZero(nDOF, nDOF);
55+
KAutoDiff.setZero(nDOF, nDOF);
56+
57+
calculateMatrix(fe, req, affordance.matrixAffordance(), K);
58+
calculateMatrix(feAutoDiff, req, affordance.matrixAffordance(), KAutoDiff);
59+
60+
t.check(fe.numberOfEASParameters() == feAutoDiff.realFE().numberOfEASParameters())
61+
<< "Number of EAS parameters for FE(" << fe.numberOfEASParameters()
62+
<< ") and number of EAS parameters for AutodiffFE(" << feAutoDiff.realFE().numberOfEASParameters()
63+
<< ") are not equal";
64+
65+
t.check(fe.alpha().isApprox(feAutoDiff.alpha(), tol), "Mismatch in alpha.")
66+
<< "alpha is\n"
67+
<< fe.alpha().transpose() << "\n alphaAutoDiff is\n"
68+
<< feAutoDiff.alpha();
69+
70+
t.check(K.isApprox(KAutoDiff, tol),
71+
"Mismatch between the stiffness matrices obtained from explicit implementation and the one based on "
72+
"automatic differentiation.")
73+
<< "K is \n"
74+
<< K << "\nKAutoDiff is \n"
75+
<< KAutoDiff << "\nThe difference is\n"
76+
<< (K - KAutoDiff);
77+
78+
if (numberOfEASParameters != 0) {
79+
t.checkThrow<Dune::NotImplemented>(
80+
[&]() {
81+
Eigen::VectorXd RAutoDiff;
82+
RAutoDiff.setZero(nDOF);
83+
calculateVector(feAutoDiff, req, affordance.vectorAffordance(), RAutoDiff);
84+
},
85+
"calculateVector with AutoDiff should throw a Dune::NotImplemented");
86+
87+
t.checkThrow<Dune::NotImplemented>(
88+
[&]() { auto energyAutoDiff = calculateScalar(feAutoDiff, req, affordance.scalarAffordance()); },
89+
"calculateScalar with AutoDiff should throw a Dune::NotImplemented");
90+
}
91+
}
92+
return t;
93+
}
94+
95+
template <int gridDim, typename TestSuitType>
96+
void easAutoDiffTest(const TestSuitType& t) {
97+
std::array<int, (gridDim == 2) ? 4 : 3> easParameters;
98+
if constexpr (gridDim == 2)
99+
easParameters = {0, 4, 5, 7};
100+
else
101+
easParameters = {0, 9, 21};
102+
103+
auto grid = createUGGridFromCorners<gridDim>(CornerDistortionFlag::randomlyDistorted);
104+
grid->globalRefine(2);
105+
auto gridView = grid->leafGridView();
106+
auto matParameter = toLamesFirstParameterAndShearModulus({.emodul = 100.0, .nu = 0.3});
107+
StVenantKirchhoff matSVK(matParameter);
108+
auto reducedMat = planeStress(matSVK);
109+
110+
using namespace Dune::Functions::BasisFactory;
111+
auto basis = Ikarus::makeBasis(gridView, power<gridDim>(lagrange<1>()));
112+
Eigen::VectorXd d;
113+
114+
auto autoDiffTestFunctor = [&](int numberOfEASParameters) {
115+
if constexpr (gridDim == 2)
116+
nonlinearEASAutoDiffTest<gridDim>(gridView, reducedMat, basis, d, numberOfEASParameters);
117+
else
118+
nonlinearEASAutoDiffTest<gridDim>(gridView, matSVK, basis, d, numberOfEASParameters);
119+
};
120+
121+
for (const int numberOfEASParameters : easParameters) {
122+
d.setZero(basis.flat().size());
123+
autoDiffTestFunctor(numberOfEASParameters);
124+
d.setRandom(basis.flat().size());
125+
autoDiffTestFunctor(numberOfEASParameters);
126+
}
127+
}
128+
129+
int main(int argc, char** argv) {
130+
Ikarus::init(argc, argv);
131+
Dune::TestSuite t("Nonlinear EAS Test");
132+
easAutoDiffTest<2>(t);
133+
easAutoDiffTest<3>(t);
134+
return t.exit();
135+
}

0 commit comments

Comments
 (0)