From 695efe8938a123e9520b3024a0d1a6c9976d6709 Mon Sep 17 00:00:00 2001 From: Jonathan Drechsel <jonathan.drechsel@gmx.de> Date: Wed, 24 Jun 2020 11:35:00 +0200 Subject: [PATCH] Implementation of LocalHyperDualStiffness - implementation of hyper-dual numbers is added - LocalHyperDualStiffness is a new alternative to LocalADOLCStiffness that uses hyper-dual numbers instead of AD by ADOL-C - localhyperdualstiffnesstest verifies the results of LocalHyperDualStiffness --- CHANGELOG.md | 1 + dune/elasticity/assemblers/CMakeLists.txt | 1 + .../assemblers/localhyperdualstiffness.hh | 109 +++ dune/elasticity/common/CMakeLists.txt | 1 + dune/elasticity/common/hyperdual.hh | 634 ++++++++++++++++++ test/CMakeLists.txt | 3 + test/localhyperdualstiffnesstest.cc | 114 ++++ 7 files changed, 863 insertions(+) create mode 100644 dune/elasticity/assemblers/localhyperdualstiffness.hh create mode 100644 dune/elasticity/common/hyperdual.hh create mode 100644 test/localhyperdualstiffnesstest.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c3b976..23d7579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Introduce class `LocalDensity` for material-specific implementations - Introduce class `LocalIntegralEnergy` to work with the densities - Local energies and FE assemblers use now `dune-functions` power bases instead of scalar `dune-fufem` bases; the key element is the `LocalView` which contains the information for each element +- Introduce class `LocalHyperDualStiffness` and `hyperdual` to calculate gradient and hessian using hyper-dual numbers ## Deprecations and removals diff --git a/dune/elasticity/assemblers/CMakeLists.txt b/dune/elasticity/assemblers/CMakeLists.txt index fe27d4d..7425e0e 100644 --- a/dune/elasticity/assemblers/CMakeLists.txt +++ b/dune/elasticity/assemblers/CMakeLists.txt @@ -1,6 +1,7 @@ install(FILES feassembler.hh localadolcstiffness.hh + localhyperdualstiffness.hh localenergy.hh localfestiffness.hh neohookefunctionalassembler.hh diff --git a/dune/elasticity/assemblers/localhyperdualstiffness.hh b/dune/elasticity/assemblers/localhyperdualstiffness.hh new file mode 100644 index 0000000..a91b68e --- /dev/null +++ b/dune/elasticity/assemblers/localhyperdualstiffness.hh @@ -0,0 +1,109 @@ +#pragma once + +#include <dune/common/fmatrix.hh> +#include <dune/istl/matrix.hh> + +#include <dune/elasticity/assemblers/localfestiffness.hh> +#include <dune/elasticity/common/hyperdual.hh> + +namespace Dune::Elasticity { + +/** \brief Assembles energy gradient and Hessian using Hyperdual Numbers + */ +template<class LocalView> +class LocalHyperDualStiffness + : public LocalFEStiffness<LocalView,double> +{ + enum {gridDim=LocalView::GridView::dimension}; + +public: + + // accepts hyperdual only + LocalHyperDualStiffness(std::shared_ptr<Dune::Elasticity::LocalEnergy<LocalView, hyperdual>> energy) + : localEnergy_(energy) + {} + + /** \brief Compute the energy at the current configuration */ + virtual double energy (const LocalView& localView, + const std::vector<double>& localConfiguration) const; + + /** \brief Assemble the local stiffness matrix at the current position + * + * This uses the automatic differentiation using hyperdual numbers + */ + virtual void assembleGradientAndHessian(const LocalView& localView, + const std::vector<double>& localConfiguration, + std::vector<double>& localGradient, + Dune::Matrix<double>& localHessian); + + std::shared_ptr<Dune::Elasticity::LocalEnergy<LocalView, hyperdual>> localEnergy_; + +}; + + +template<class LocalView> +double LocalHyperDualStiffness<LocalView>:: +energy(const LocalView& localView, + const std::vector<double>& localConfiguration) const +{ + double pureEnergy; + + std::vector<hyperdual> localHyperDualConfiguration(localConfiguration.size()); + hyperdual energy; + + for (size_t i=0; i<localConfiguration.size(); i++) + localHyperDualConfiguration[i] = hyperdual(localConfiguration[i]); + + energy = localEnergy_->energy(localView,localHyperDualConfiguration); + + pureEnergy = energy.real(); + + return pureEnergy; +} + + +template<class LocalView> +void LocalHyperDualStiffness<LocalView>:: +assembleGradientAndHessian(const LocalView& localView, + const std::vector<double>& localConfiguration, + std::vector<double>& localGradient, + Dune::Matrix<double>& localHessian + ) +{ + size_t nDoubles = localConfiguration.size(); + + localGradient.resize(nDoubles); + + localHessian.setSize(nDoubles,nDoubles); + // Create hyperdual vector localHyperDualConfiguration = double vector localConfiguration + std::vector<hyperdual> localHyperDualConfiguration(nDoubles); + for (size_t i=0; i<nDoubles; i++) + localHyperDualConfiguration[i] = hyperdual(localConfiguration[i]); + + // Compute gradient and hessian (symmetry is used) using hyperdual function evaluation + // localHyperDualConfiguration puts Ones in the eps1 and eps2 component to evaluate the different partial derivatives + for (size_t i=0; i<nDoubles; i++) + { + localHyperDualConfiguration[i].seteps1(1.0); + localHyperDualConfiguration[i].seteps2(1.0); + + hyperdual temp = localEnergy_->energy(localView, localHyperDualConfiguration); + localGradient[i] = temp.eps1(); + localHessian[i][i] = temp.eps1eps2(); + + localHyperDualConfiguration[i].seteps2(0.0); + + for (size_t j=i+1; j<nDoubles; j++) + { + localHyperDualConfiguration[j].seteps2(1.0); + localHessian[i][j] = localEnergy_->energy(localView, localHyperDualConfiguration).eps1eps2(); + localHessian[j][i] = localHessian[i][j]; // Use symmetry of hessian + localHyperDualConfiguration[j].seteps2(0.0); + } + + localHyperDualConfiguration[i].seteps1(0.0); + } + +} + +} // namespace Dune::Elasticity diff --git a/dune/elasticity/common/CMakeLists.txt b/dune/elasticity/common/CMakeLists.txt index a46e7dc..df13ce7 100644 --- a/dune/elasticity/common/CMakeLists.txt +++ b/dune/elasticity/common/CMakeLists.txt @@ -1,6 +1,7 @@ install(FILES directionalspringrobinfunction.hh elasticityhelpers.hh + hyperdual.hh maxnormtrustregion.hh nonlinearelasticityproblem.hh nonlinearelasticityproblem.cc diff --git a/dune/elasticity/common/hyperdual.hh b/dune/elasticity/common/hyperdual.hh new file mode 100644 index 0000000..524b785 --- /dev/null +++ b/dune/elasticity/common/hyperdual.hh @@ -0,0 +1,634 @@ +/* + * Class: hyperdual + * + * Implementation of hyper-dual numbers + * + * Written by: Jeffrey A. Fike + * Stanford University, Department of Aeronautics and Astronautics + * + * Copyright (c) 2006 Jeffrey A. Fike + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#ifndef DUNE_ELASTICITY_COMMON_HYPERDUAL_HH +#define DUNE_ELASTICITY_COMMON_HYPERDUAL_HH + +#include <iostream> +#include <math.h> + +class hyperdual{ + double f0,f1,f2,f12; +public: + //creation operators and function to manually set values + hyperdual(); + hyperdual(double x1,double x2,double x3,double x4); + hyperdual(double x1); + void setvalues(double x1,double x2,double x3,double x4); + void setreal(double x1); + void seteps1(double x2); + void seteps2(double x3); + void seteps12(double x4); + + //examine values + void view(void); + double real(void) const; + double eps1(void) const; + double eps2(void) const; + double eps1eps2(void) const; + friend std::ostream& operator<<(std::ostream& output, const hyperdual& rhs); + + //basic manipulation + hyperdual operator+ () const; + hyperdual operator+ (const hyperdual rhs) const; + friend hyperdual operator+ (const double lhs, const hyperdual rhs); + hyperdual operator- () const; + hyperdual operator- (const hyperdual rhs) const; + friend hyperdual operator- (const double lhs, const hyperdual rhs); + hyperdual operator* (const hyperdual rhs)const; + friend hyperdual operator* (const double lhs, const hyperdual rhs); + friend hyperdual operator/ (const hyperdual lhs, const hyperdual rhs); + friend hyperdual operator/ (const double lhs, const hyperdual rhs); + friend hyperdual operator/ (const hyperdual lhs, const double rhs); + hyperdual& operator+= (hyperdual rhs); + hyperdual& operator-= (hyperdual rhs); + hyperdual& operator*= (hyperdual rhs); + hyperdual& operator*= (double rhs); + hyperdual& operator/= (hyperdual rhs); + hyperdual& operator/= (double rhs); + + //math.h functions + friend hyperdual pow (hyperdual x, double a); + friend hyperdual pow (hyperdual x, hyperdual a); + friend hyperdual exp(hyperdual x); + friend hyperdual log(hyperdual x); + friend hyperdual sin(hyperdual x); + friend hyperdual cos(hyperdual x); + friend hyperdual tan(hyperdual x); + friend hyperdual asin(hyperdual x); + friend hyperdual acos(hyperdual x); + friend hyperdual atan(hyperdual x); + friend hyperdual sqrt(hyperdual x); + friend hyperdual fabs(hyperdual x); + friend hyperdual abs(hyperdual x); + friend hyperdual max(hyperdual x1, hyperdual x2); + friend hyperdual max(hyperdual x1, double x2); + friend hyperdual max(double x1, hyperdual x2); + friend hyperdual min(hyperdual x1, hyperdual x2); + friend hyperdual min(hyperdual x1, double x2); + friend hyperdual min(double x1, hyperdual x2); + + //comparisons + friend bool operator> (hyperdual lhs, hyperdual rhs); + friend bool operator> (double lhs, hyperdual rhs); + friend bool operator> (hyperdual lhs, double rhs); + friend bool operator>= (hyperdual lhs, hyperdual rhs); + friend bool operator>= (double lhs, hyperdual rhs); + friend bool operator>= (hyperdual lhs, double rhs); + friend bool operator< (hyperdual lhs, hyperdual rhs); + friend bool operator< (double lhs, hyperdual rhs); + friend bool operator< (hyperdual lhs, double rhs); + friend bool operator<= (hyperdual lhs, hyperdual rhs); + friend bool operator<= (double lhs, hyperdual rhs); + friend bool operator<= (hyperdual lhs, double rhs); + friend bool operator== (hyperdual lhs, hyperdual rhs); + friend bool operator== (double lhs, hyperdual rhs); + friend bool operator== (hyperdual lhs, double rhs); + friend bool operator!= (hyperdual lhs, hyperdual rhs); + friend bool operator!= (double lhs, hyperdual rhs); + friend bool operator!= (hyperdual lhs, double rhs); +}; + + +hyperdual::hyperdual() +{ + f0 = 0.0; + f1 = 0.0; + f2 = 0.0; + f12 = 0.0; +} +hyperdual::hyperdual(double x1,double x2,double x3,double x4) +{ + f0 = x1; + f1 = x2; + f2 = x3; + f12 = x4; +} +hyperdual::hyperdual(double x1) +{ + f0 = x1; + f1 = 0.0; + f2 = 0.0; + f12 = 0.0; +} +void hyperdual::setvalues(double x1,double x2,double x3,double x4) +{ + f0 = x1; + f1 = x2; + f2 = x3; + f12 = x4; +} +void hyperdual::setreal(double x1) +{ + f0 = x1; +} +void hyperdual::seteps1(double x2) +{ + f1 = x2; +} +void hyperdual::seteps2(double x3) +{ + f2 = x3; +} +void hyperdual::seteps12(double x4) +{ + f12 = x4; +} +void hyperdual::view(void) +{ + printf("%g + %g epsilon1 + %g epsilon2 + %g epsilon1 epsilon2\n",f0,f1,f2,f12); +} +double hyperdual::real(void) const +{ + return f0; +} +double hyperdual::eps1(void) const +{ + return f1; +} +double hyperdual::eps2(void) const +{ + return f2; +} +double hyperdual::eps1eps2(void) const +{ + return f12; +} +std::ostream& operator<<(std::ostream& output, const hyperdual& rhs) +{ + output << "(" << rhs.f0 << ","<< rhs.f1 << ","<< rhs.f2 << ","<< rhs.f12 << ")"; + return output; +} + +hyperdual hyperdual::operator+ () const +{ + return *this; +} +hyperdual hyperdual::operator+ (const hyperdual rhs) const +{ + hyperdual temp; + temp.f0 = f0 + rhs.f0; + temp.f1 = f1 + rhs.f1; + temp.f2 = f2 + rhs.f2; + temp.f12 = f12 + rhs.f12; + return temp; +} +hyperdual operator+ (const double lhs, const hyperdual rhs) +{ + hyperdual temp; + temp.f0 = lhs + rhs.f0; + temp.f1 = rhs.f1; + temp.f2 = rhs.f2; + temp.f12 = rhs.f12; + return temp; +} +hyperdual hyperdual::operator- () const +{ + hyperdual temp; + temp.f0 = -f0; + temp.f1 = -f1; + temp.f2 = -f2; + temp.f12 = -f12; + return temp; +} +hyperdual hyperdual::operator- (const hyperdual rhs) const +{ + hyperdual temp; + temp.f0 = f0 - rhs.f0; + temp.f1 = f1 - rhs.f1; + temp.f2 = f2 - rhs.f2; + temp.f12 = f12 - rhs.f12; + return temp; +} +hyperdual operator- (const double lhs, const hyperdual rhs) +{ + hyperdual temp; + temp.f0 = lhs - rhs.f0; + temp.f1 = -rhs.f1; + temp.f2 = -rhs.f2; + temp.f12 = -rhs.f12; + return temp; +} +hyperdual hyperdual::operator* (const hyperdual rhs) const +{ + hyperdual temp; + temp.f0 = f0*rhs.f0; + temp.f1 = f0*rhs.f1 + f1*rhs.f0; + temp.f2 = f0*rhs.f2 + f2*rhs.f0; + temp.f12 = f0*rhs.f12 + f1*rhs.f2 + f2*rhs.f1 + f12*rhs.f0; + return temp; +} +hyperdual operator* (const double lhs, const hyperdual rhs) +{ + hyperdual temp; + temp.f0 = lhs*rhs.f0; + temp.f1 = lhs*rhs.f1; + temp.f2 = lhs*rhs.f2; + temp.f12 = lhs*rhs.f12; + return temp; +} +hyperdual operator/ (const hyperdual lhs, const hyperdual rhs) +{ + hyperdual inv; + double prod; + + prod = rhs.f0*rhs.f0; + inv.f0 = 1.0/rhs.f0; + inv.f1 = -rhs.f1/prod; + inv.f2 = -rhs.f2/prod; + inv.f12 = 2.0*rhs.f1*rhs.f2/(prod*rhs.f0) - rhs.f12/prod; + return lhs*inv; +} +hyperdual operator/ (const double lhs, const hyperdual rhs) +{ + hyperdual temp,inv; + temp = hyperdual(lhs)/rhs; + return temp; +} +hyperdual operator/ (const hyperdual lhs, const double rhs) +{ + hyperdual temp; + double inv; + inv = 1.0/rhs; + temp.f0 = inv*lhs.f0; + temp.f1 = inv*lhs.f1; + temp.f2 = inv*lhs.f2; + temp.f12 = inv*lhs.f12; + return temp; +} +hyperdual& hyperdual::operator+= (hyperdual rhs) +{ + f0 += rhs.f0; + f1 += rhs.f1; + f2 += rhs.f2; + f12 += rhs.f12; + return *this; +} +hyperdual& hyperdual::operator-= (hyperdual rhs) +{ + f0 -= rhs.f0; + f1 -= rhs.f1; + f2 -= rhs.f2; + f12 -= rhs.f12; + return *this; +} +hyperdual& hyperdual::operator*= (hyperdual rhs) +{ + double tf0,tf1,tf2,tf12; + tf0=f0; + tf1=f1; + tf2=f2; + tf12=f12; + f0 = tf0*rhs.f0; + f1 = tf0*rhs.f1 + tf1*rhs.f0; + f2 = tf0*rhs.f2 + tf2*rhs.f0; + f12 = tf0*rhs.f12 + tf1*rhs.f2 + tf2*rhs.f1 + tf12*rhs.f0; + return *this; +} +hyperdual& hyperdual::operator*= (double rhs) +{ + f0 *= rhs; + f1 *= rhs; + f2 *= rhs; + f12 *= rhs; + return *this; +} +hyperdual& hyperdual::operator/= (hyperdual rhs) +{ + *this = *this / rhs; + return *this; +} +hyperdual& hyperdual::operator/= (double rhs) +{ + f0 /= rhs; + f1 /= rhs; + f2 /= rhs; + f12 /= rhs; + return *this; +} +hyperdual pow (hyperdual x, double a) +{ + hyperdual temp; + double deriv,xval,tol; + xval = x.f0; + tol = 1e-15; + if (fabs(xval) < tol) + { + if (xval >= 0) + xval = tol; + if (xval < 0) + xval = -tol; + } + deriv = a*std::pow(xval,(a-1)); + //temp.f0 = pow(xval,a); + temp.f0 = std::pow(x.f0,a); //Use actual x value, only use tol for derivs + temp.f1 = x.f1*deriv; + temp.f2 = x.f2*deriv; + temp.f12 = x.f12*deriv + a*(a-1)*x.f1*x.f2*std::pow(xval,(a-2)); + + return temp; +} +hyperdual pow (hyperdual x, hyperdual a) +{ + return exp(a*log(x)); +} +hyperdual exp(hyperdual x) +{ + hyperdual temp; + double deriv; + deriv = std::exp(x.f0); + temp.f0 = deriv; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*(x.f12 + x.f1*x.f2); + return temp; +} +hyperdual log(hyperdual x) +{ + hyperdual temp; + double deriv1,deriv2; + deriv1 = x.f1/x.f0; + deriv2 = x.f2/x.f0; + temp.f0 = std::log(x.f0); + temp.f1 = deriv1; + temp.f2 = deriv2; + temp.f12 = x.f12/x.f0 - (deriv1*deriv2); + return temp; +} +hyperdual sin(hyperdual x) +{ + hyperdual temp; + double funval,deriv; + funval = std::sin(x.f0); + deriv = std::cos(x.f0); + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 - funval*x.f1*x.f2; + return temp; +} +hyperdual cos(hyperdual x) +{ + hyperdual temp; + double funval,deriv; + funval = std::cos(x.f0); + deriv = -std::sin(x.f0); + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 - funval*x.f1*x.f2; + return temp; +} +hyperdual tan(hyperdual x) +{ + hyperdual temp; + double funval,deriv; + funval = std::tan(x.f0); + deriv = funval*funval + 1.0; + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 + x.f1*x.f2*(2*funval*deriv); + return temp; +} +hyperdual asin(hyperdual x) +{ + hyperdual temp; + double funval,deriv1,deriv; + funval = std::asin(x.f0); + deriv1 = 1.0-x.f0*x.f0; + deriv = 1.0/std::sqrt(deriv1); + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 + x.f1*x.f2*(x.f0*std::pow(deriv1,-1.5)); + return temp; +} +hyperdual acos(hyperdual x) +{ + hyperdual temp; + double funval,deriv1,deriv; + funval = std::acos(x.f0); + deriv1 = 1.0-x.f0*x.f0; + deriv = -1.0/std::sqrt(deriv1); + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 + x.f1*x.f2*(-x.f0*std::pow(deriv1,-1.5)); + return temp; +} +hyperdual atan(hyperdual x) +{ + hyperdual temp; + double funval,deriv1,deriv; + funval = std::atan(x.f0); + deriv1 = 1.0+x.f0*x.f0; + deriv = 1.0/deriv1; + temp.f0 = funval; + temp.f1 = deriv*x.f1; + temp.f2 = deriv*x.f2; + temp.f12 = deriv*x.f12 + x.f1*x.f2*(-2*x.f0/(deriv1*deriv1)); + return temp; +} +hyperdual sqrt(hyperdual x) +{ + return pow(x,0.5); +} +hyperdual fabs(hyperdual x) +{ + hyperdual temp; + if (x < 0.0) + temp = -x; + else + temp = x; + return temp; +} + +hyperdual abs(hyperdual x) +{ + return fabs(x); +} +hyperdual max(hyperdual x1, hyperdual x2) +{ + hyperdual temp; + if (x1>x2) + temp = x1; + else + temp = x2; + return temp; +} +hyperdual max(hyperdual x1, double x2) +{ + hyperdual temp; + if (x1>x2) + temp = x1; + else + temp = x2; + return temp; +} +hyperdual max(double x1, hyperdual x2) +{ + hyperdual temp; + if (x1>x2) + temp = x1; + else + temp = x2; + return temp; +} +hyperdual min(hyperdual x1, hyperdual x2) +{ + hyperdual temp; + if (x1<x2) + temp = x1; + else + temp = x2; + return temp; +} +hyperdual min(hyperdual x1, double x2) +{ + hyperdual temp; + if (x1<x2) + temp = x1; + else + temp = x2; + return temp; +} +hyperdual min(double x1, hyperdual x2) +{ + hyperdual temp; + if (x1<x2) + temp = x1; + else + temp = x2; + return temp; +} + +bool operator> (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 > rhs.f0); +} +bool operator> (double lhs, hyperdual rhs) +{ + return (lhs > rhs.f0); +} +bool operator> (hyperdual lhs, double rhs) +{ + return (lhs.f0 > rhs); +} +bool operator>= (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 >= rhs.f0); +} +bool operator>= (double lhs, hyperdual rhs) +{ + return (lhs >= rhs.f0); +} +bool operator>= (hyperdual lhs, double rhs) +{ + return (lhs.f0 >= rhs); +} +bool operator< (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 < rhs.f0); +} +bool operator< (double lhs, hyperdual rhs) +{ + return (lhs < rhs.f0); +} +bool operator< (hyperdual lhs, double rhs) +{ + return (lhs.f0 < rhs); +} +bool operator<= (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 <= rhs.f0); +} +bool operator<= (double lhs, hyperdual rhs) +{ + return (lhs <= rhs.f0); +} +bool operator<= (hyperdual lhs, double rhs) +{ + return (lhs.f0 <= rhs); +} +bool operator== (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 == rhs.f0); +} +bool operator== (double lhs, hyperdual rhs) +{ + return (lhs == rhs.f0); +} +bool operator== (hyperdual lhs, double rhs) +{ + return (lhs.f0 == rhs); +} +bool operator!= (hyperdual lhs, hyperdual rhs) +{ + return (lhs.f0 != rhs.f0); +} +bool operator!= (double lhs, hyperdual rhs) +{ + return (lhs != rhs.f0); +} +bool operator!= (hyperdual lhs, double rhs) +{ + return (lhs.f0 != rhs); +} + + +// Dune related definitions with hyperdual numbers +namespace Dune +{ + template<> + struct IsNumber<hyperdual> + : std::true_type + {}; + + /* Specializations of the PromotionTraits class from dune-common for the hyperdual type. + * This is needed, e.g., to be able to use the FieldVector implementation of dot products + * between a FieldVector<double> and a FieldVector<hyperdual>. + */ + template <> + struct PromotionTraits<double,hyperdual> + { + using PromotedType = hyperdual; + }; + + template <> + struct PromotionTraits<hyperdual,double> + { + using PromotedType = hyperdual; + }; +} + + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f39aa17..6503516 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,3 +7,6 @@ dune_add_test(SOURCES materialtest CMAKE_GUARD ADOLC_FOUND) add_dune_ug_flags(materialtest) add_dune_adolc_flags(materialtest) + +dune_add_test(SOURCES localhyperdualstiffnesstest + CMAKE_GUARD ADOLC_FOUND) diff --git a/test/localhyperdualstiffnesstest.cc b/test/localhyperdualstiffnesstest.cc new file mode 100644 index 0000000..f0c8887 --- /dev/null +++ b/test/localhyperdualstiffnesstest.cc @@ -0,0 +1,114 @@ + +#include <config.h> + +#include <iostream> + +#include <dune/common/test/testsuite.hh> +#include <dune/common/parametertree.hh> +#include <dune/common/parametertreeparser.hh> +#include <dune/common/timer.hh> + +#include <dune/grid/uggrid.hh> +#include <dune/grid/utility/structuredgridfactory.hh> + +#include <dune/istl/bvector.hh> +#include <dune/istl/bcrsmatrix.hh> + +#include <dune/functions/functionspacebases/lagrangebasis.hh> +#include <dune/functions/functionspacebases/powerbasis.hh> + +#include <dune/elasticity/assemblers/feassembler.hh> +#include <dune/elasticity/assemblers/localadolcstiffness.hh> +#include <dune/elasticity/assemblers/localhyperdualstiffness.hh> +#include <dune/elasticity/materials/stvenantkirchhoffdensity.hh> +#include <dune/elasticity/materials/localintegralenergy.hh> + +using namespace Dune; + +template<int dim> +TestSuite hyperdualEqualsADOL_C() +{ + TestSuite t; + + using Vector = BlockVector<FieldVector<double, dim>>; + using Matrix = BCRSMatrix<FieldMatrix<double,dim,dim>>; + + using GridType = UGGrid<dim>; + + std::shared_ptr<GridType> grid; + + FieldVector<double,dim> lower(0), upper(1); + + std::array<unsigned int,dim> elements; + elements.fill(10); + + grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); + + using GridView = typename GridType::LeafGridView; + GridView gridView = grid->leafGridView(); + + using namespace Dune::Functions::BasisFactory; + auto basis = makeBasis( + gridView, + power<dim>( + lagrange<1>() + )); + + auto localView = basis.localView(); + using LocalView = decltype(localView); + using Basis = decltype(basis); + + std::shared_ptr<Elasticity::LocalFEStiffness<LocalView,double>> localStiffness; + + std::string parameterString = "mu = 2.7191e+6\nlambda = 4.4364e+6"; + std::istringstream stream(parameterString); + ParameterTree parameterSet; + ParameterTreeParser::readINITree(stream, parameterSet); + + auto hyperdualElasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,hyperdual>>(parameterSet); + auto adolcElasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,adouble>>(parameterSet); + + auto hyperdualElasticEnergy = std::make_shared<Elasticity::LocalIntegralEnergy<LocalView, hyperdual>>(hyperdualElasticDensity); + auto adolcElasticEnergy = std::make_shared<Elasticity::LocalIntegralEnergy<LocalView, adouble>>(adolcElasticDensity); + + auto hyperdualLocalStiffness = std::make_shared<Elasticity::LocalHyperDualStiffness<LocalView>>(hyperdualElasticEnergy); + auto adolcLocalStiffness = std::make_shared<Elasticity::LocalADOLCStiffness<LocalView>>(adolcElasticEnergy); + + Elasticity::FEAssembler<Basis,Vector> hyperdualAssembler(basis, hyperdualLocalStiffness); + Elasticity::FEAssembler<Basis,Vector> adolcAssembler(basis, adolcLocalStiffness); + + Matrix hyperdualHessian, adolcHessian; + Vector hyperdualGradient, adolcGradient; + + Vector x(basis.size()); + x = 42.; + + Timer timer; + hyperdualAssembler.assembleGradientAndHessian(x,hyperdualGradient,hyperdualHessian); + std::cout << dim <<"D: hyperdual assembler took " << timer.elapsed() << " sec." << std::endl; + timer.reset(); + adolcAssembler.assembleGradientAndHessian(x,adolcGradient,adolcHessian); + std::cout << dim << "D: ADOL-C assembler took " << timer.elapsed() << " sec." << std::endl; + + + hyperdualHessian -= adolcHessian; + hyperdualGradient -= adolcGradient; + + // check the relative error + t.check(hyperdualHessian.frobenius_norm()/adolcHessian.frobenius_norm() < 1e-12 && hyperdualGradient.two_norm()/adolcGradient.two_norm() < 1e-12); + + return t; + +} + +int main(int argc, char **argv) +{ + Dune::MPIHelper::instance(argc, argv); + + TestSuite t; + + t.subTest(hyperdualEqualsADOL_C<2>()); + t.subTest(hyperdualEqualsADOL_C<3>()); + + return t.exit(); +} -- GitLab