version 0.4.1
autodifffe.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2021-2024 The Ikarus Developers mueller@ibb.uni-stuttgart.de
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
9#pragma once
10
11#include <autodiff/forward/dual/dual.hpp>
12#include <autodiff/forward/dual/eigen.hpp>
13
16
17namespace Ikarus {
18
26template <typename FEImpl, bool forceAutoDiff = false>
27class AutoDiffFE : public FEImpl
28{
29public:
30 using RealFE = FEImpl;
31 using BasisHandler = typename RealFE::BasisHandler;
32 using Traits = typename RealFE::Traits;
33 using LocalView = typename Traits::LocalView;
34 using Element = typename Traits::Element;
35 using Requirement = typename RealFE::Requirement;
36private:
37 using Mixin = FEImpl::Mixin;
38
39public:
48 friend void calculateMatrix(const AutoDiffFE& self, const Requirement& par, const MatrixAffordance& affordance,
49 typename Traits::template MatrixType<> h) {
50 self.calculateMatrix(par, affordance, h);
51 }
52
61 friend void calculateVector(const AutoDiffFE& self, const Requirement& par, VectorAffordance affordance,
62 typename Traits::template VectorType<double> g) {
63 self.calculateVector(par, affordance, g);
64 }
65
76 friend void calculateLocalSystem(const AutoDiffFE& self, const Requirement& par, const MatrixAffordance& affordanceM,
77 VectorAffordance affordanceV, typename Traits::template MatrixType<> h,
78 typename Traits::template VectorType<> g) {
79 self.calculateLocalSystem(par, affordanceM, affordanceV, h, g);
80 }
81
90 friend auto calculateScalar(const AutoDiffFE& self, const Requirement& par, ScalarAffordance affordance) {
91 return self.calculateScalar(par, affordance);
92 }
93
99 const RealFE& realFE() const { return *this; }
100
108 template <typename... Args>
109 explicit AutoDiffFE(Args&&... args)
110 : RealFE{std::forward<Args>(args)...} {}
111
112private:
113 void calculateMatrix(const Requirement& req, const MatrixAffordance& affordance,
114 typename Traits::template MatrixType<> h) const {
115 // real element implements calculateMatrix by itself, then we simply forward the call
116
117 if constexpr (requires(Eigen::VectorXd v) {
118 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
119 .template calculateMatrixImpl<double>(req, affordance, h, v);
120 } and not forceAutoDiff) {
121 return Mixin::template calculateMatrixImpl<double>(req, affordance, h);
122 } else if constexpr (requires(Eigen::VectorXdual v) {
123 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
124 .template calculateVectorImpl<autodiff::dual>(
125 req, vectorAffordance(affordance),
126 std::declval<typename Traits::template VectorType<autodiff::dual>>(), v);
127 }) {
128 // real element implements calculateVector by itself, therefore we only need first order derivatives
129 Eigen::VectorXdual dx(this->localView().size());
130 Eigen::VectorXdual g(this->localView().size());
131 dx.setZero();
132 auto f = [this, &req, affordance, &g](auto& x) -> auto& {
133 // Since req is const as a function argument, we can not make this lambda capture by mutable reference
134 // But we have to do this since for efficiency reason we reuse the g vector
135 // therefore, the only remaining option is to cast the const away from g
136 Eigen::VectorXdual& gref = const_cast<Eigen::VectorXdual&>(g);
137 gref.setZero();
138 Mixin::template calculateVectorImpl<autodiff::dual>(req, vectorAffordance(affordance), gref, x);
139 return g;
140 };
141 jacobian(f, autodiff::wrt(dx), at(dx), g, h);
142 } else if constexpr (requires(typename Traits::template VectorType<autodiff::dual2nd> v) {
143 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
144 .template calculateScalarImpl<autodiff::dual2nd>(req, scalarAffordance(affordance), v);
145 }) {
146 // real element implements calculateScalar by itself, therefore we need second order derivatives
147 Eigen::VectorXdual2nd dx(this->localView().size());
148 Eigen::VectorXd g;
149 autodiff::dual2nd e;
150 dx.setZero();
151 auto f = [this, &req, affordance](auto& x) {
152 return Mixin::template calculateScalarImpl<autodiff::dual2nd>(req, scalarAffordance(affordance), x);
153 };
154 hessian(f, autodiff::wrt(dx), at(dx), e, g, h);
155 } else
156 static_assert(Dune::AlwaysFalse<AutoDiffFE>::value,
157 "Appropriate calculateScalarImpl or calculateVectorImpl functions are not implemented for the "
158 "chosen element.");
159 }
160
161 void calculateVector(const Requirement& req, VectorAffordance affordance,
162 typename Traits::template VectorType<> g) const {
163 // real element implements calculateVector by itself, then we simply forward the call
164 if constexpr (requires {
165 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
166 .template calculateVectorImpl<double>(
167 req, affordance, std::declval<typename Traits::template VectorType<double>>(),
168 std::declval<const Eigen::VectorXd&>());
169 } and not forceAutoDiff) {
170 return Mixin::template calculateVectorImpl<double>(req, affordance, g);
171 } else if constexpr (requires {
172 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
173 .template calculateScalarImpl<autodiff::dual>(req, scalarAffordance(affordance),
174 std::declval<const Eigen::VectorXdual&>());
175 }) {
176 // real element implements calculateScalar by itself but no calculateVectorImpl, therefore we need first order
177 // derivatives
178 Eigen::VectorXdual dx(this->localView().size());
179 dx.setZero();
180 autodiff::dual e;
181 auto f = [this, &req, affordance](auto& x) {
182 return Mixin::template calculateScalarImpl<autodiff::dual>(req, scalarAffordance(affordance), x);
183 };
184 gradient(f, autodiff::wrt(dx), at(dx), e, g);
185 } else
186 static_assert(Dune::AlwaysFalse<AutoDiffFE>::value,
187 "Appropriate calculateScalarImpl function is not implemented for the "
188 "chosen element.");
189 }
190
191 [[nodiscard]] double calculateScalar(const Requirement& par, ScalarAffordance affordance) const {
192 // real element implements calculateScalar by itself, then we simply forward the call
193 if constexpr (requires {
194 static_cast<const Mixin&>(std::declval<AutoDiffFE>())
195 .template calculateScalarImpl<double>(par, affordance);
196 }) {
197 // real element only implements the protected calculateScalarImpl by itself, thus we call that one.
198 return Mixin::template calculateScalarImpl<double>(par, affordance);
199 } else {
200 static_assert(Dune::AlwaysFalse<AutoDiffFE>::value,
201 "Appropriate calculateScalar and calculateScalarImpl functions are not implemented for the "
202 "chosen element.");
203 }
204 }
205
206 void calculateLocalSystem(const Requirement& req, const MatrixAffordance& affordanceM, VectorAffordance affordanceV,
207 typename Traits::template MatrixType<> h, typename Traits::template VectorType<> g) const {
208 assert(scalarAffordance(affordanceM) == scalarAffordance(affordanceV));
209 Eigen::VectorXdual2nd dx(this->localView().size());
210 dx.setZero();
211 auto f = [&](auto& x) { return Mixin::calculateScalarImpl(req, scalarAffordance(affordanceV), x); };
212 hessian(f, autodiff::wrt(dx), at(dx), g, h);
213 }
214};
215} // namespace Ikarus
Contains stl-like type traits.
Definition of the LinearElastic class for finite element mechanics computations.
STL namespace.
Definition: assemblermanipulatorbuildingblocks.hh:22
auto vectorAffordance(MatrixAffordance affordanceM)
Definition: ferequirements.hh:176
MatrixAffordance
A strongly typed enum class representing the matrix affordance.
Definition: ferequirements.hh:63
auto scalarAffordance(MatrixAffordance affordanceM)
Definition: ferequirements.hh:185
VectorAffordance
A strongly typed enum class representing the vector affordance.
Definition: ferequirements.hh:48
ScalarAffordance
A strongly typed enum class representing the scalar affordance.
Definition: ferequirements.hh:37
AutoDiffFE class, an automatic differentiation wrapper for finite elements.
Definition: autodifffe.hh:28
AutoDiffFE(Args &&... args)
Constructor for the AutoDiffFE class. Forward the construction to the underlying element.
Definition: autodifffe.hh:109
typename RealFE::Traits Traits
Type traits for local view.
Definition: autodifffe.hh:32
friend void calculateVector(const AutoDiffFE &self, const Requirement &par, VectorAffordance affordance, typename Traits::template VectorType< double > g)
Calculate the vector associated with the finite element.
Definition: autodifffe.hh:61
friend auto calculateScalar(const AutoDiffFE &self, const Requirement &par, ScalarAffordance affordance)
Calculate the scalar value associated with the finite element.
Definition: autodifffe.hh:90
const RealFE & realFE() const
Get the reference to the base finite element.
Definition: autodifffe.hh:99
friend void calculateMatrix(const AutoDiffFE &self, const Requirement &par, const MatrixAffordance &affordance, typename Traits::template MatrixType<> h)
Calculate the matrix associated with the finite element.
Definition: autodifffe.hh:48
friend void calculateLocalSystem(const AutoDiffFE &self, const Requirement &par, const MatrixAffordance &affordanceM, VectorAffordance affordanceV, typename Traits::template MatrixType<> h, typename Traits::template VectorType<> g)
Calculate the local system associated with the finite element.
Definition: autodifffe.hh:76
FEImpl RealFE
Type of the base finite element.
Definition: autodifffe.hh:30
typename RealFE::BasisHandler BasisHandler
Type of the basis handler.
Definition: autodifffe.hh:31
typename Traits::LocalView LocalView
Type of the local view.
Definition: autodifffe.hh:33
typename Traits::Element Element
Type of the element.
Definition: autodifffe.hh:34
typename RealFE::Requirement Requirement
Type of the Finite Element Requirements.
Definition: autodifffe.hh:35