// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /* This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. OPM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . Consult the COPYING file in the top-level source directory of this module for the precise wording of the license and the list of copyright holders. */ /*! * \file * * \brief A number of commonly used algebraic functions for the localized OPM automatic * differentiation (AD) framework. * * This file provides AD variants of the the most commonly used functions of the * header file. */ #ifndef OPM_LOCAL_AD_MATH_HPP #define OPM_LOCAL_AD_MATH_HPP #include "Evaluation.hpp" #include namespace Opm { namespace DenseAd { // forward declaration of the Evaluation template class template class Evaluation; // provide some algebraic functions template Evaluation abs(const Evaluation& x) { return (x > 0.0)?x:-x; } template Evaluation min(const Evaluation& x1, const Evaluation& x2) { return (x1 < x2)?x1:x2; } template Evaluation min(const Arg1ValueType& x1, const Evaluation& x2) { if (x1 < x2) { Evaluation ret(x2); ret = x1; return ret; } else return x2; } template Evaluation min(const Evaluation& x1, const Arg2ValueType& x2) { return min(x2, x1); } template Evaluation max(const Evaluation& x1, const Evaluation& x2) { return (x1 > x2)?x1:x2; } template Evaluation max(const Arg1ValueType& x1, const Evaluation& x2) { if (x1 > x2) { Evaluation ret(x2); ret = x1; return ret; } else return x2; } template Evaluation max(const Evaluation& x1, const Arg2ValueType& x2) { return max(x2, x1); } template Evaluation tan(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); const ValueType& tmp = ValueTypeToolbox::tan(x.value()); result.setValue(tmp); // derivatives use the chain rule const ValueType& df_dx = 1 + tmp*tmp; for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation atan(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::atan(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1/(1 + x.value()*x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation atan2(const Evaluation& x, const Evaluation& y) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::atan2(x.value(), y.value())); // derivatives use the chain rule const ValueType& alpha = 1/(1 + (x.value()*x.value())/(y.value()*y.value())); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) { result.setDerivative(curVarIdx, alpha/(y.value()*y.value()) *(x.derivative(curVarIdx)*y.value() - x.value()*y.derivative(curVarIdx))); } return result; } template Evaluation atan2(const Evaluation& x, const ValueType& y) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::atan2(x.value(), y)); // derivatives use the chain rule const ValueType& alpha = 1/(1 + (x.value()*x.value())/(y*y)); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) { result.setDerivative(curVarIdx, alpha/(y*y) *(x.derivative(curVarIdx)*y)); } return result; } template Evaluation atan2(const ValueType& x, const Evaluation& y) { typedef MathToolbox ValueTypeToolbox; Evaluation result(y); result.setValue(ValueTypeToolbox::atan2(x, y.value())); // derivatives use the chain rule const ValueType& alpha = 1/(1 + (x.value()*x.value())/(y.value()*y.value())); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) { result.setDerivative(curVarIdx, alpha/(y.value()*y.value()) *x*y.derivative(curVarIdx)); } return result; } template Evaluation sin(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::sin(x.value())); // derivatives use the chain rule const ValueType& df_dx = ValueTypeToolbox::cos(x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation asin(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::asin(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1.0/ValueTypeToolbox::sqrt(1 - x.value()*x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation sinh(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::sinh(x.value())); // derivatives use the chain rule const ValueType& df_dx = ValueTypeToolbox::cosh(x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation asinh(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::asinh(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1.0/ValueTypeToolbox::sqrt(x.value()*x.value() + 1); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation cos(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::cos(x.value())); // derivatives use the chain rule const ValueType& df_dx = -ValueTypeToolbox::sin(x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation acos(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::acos(x.value())); // derivatives use the chain rule const ValueType& df_dx = - 1.0/ValueTypeToolbox::sqrt(1 - x.value()*x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation cosh(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::cosh(x.value())); // derivatives use the chain rule const ValueType& df_dx = ValueTypeToolbox::sinh(x.value()); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation acosh(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::acosh(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1.0/ValueTypeToolbox::sqrt(x.value()*x.value() - 1); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation sqrt(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); const ValueType& sqrt_x = ValueTypeToolbox::sqrt(x.value()); result.setValue(sqrt_x); // derivatives use the chain rule ValueType df_dx = 0.5/sqrt_x; for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) { result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); } return result; } template Evaluation exp(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); const ValueType& exp_x = ValueTypeToolbox::exp(x.value()); result.setValue(exp_x); // derivatives use the chain rule const ValueType& df_dx = exp_x; for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } // exponentiation of arbitrary base with a fixed constant template Evaluation pow(const Evaluation& base, const ExpType& exp) { typedef MathToolbox ValueTypeToolbox; Evaluation result(base); const ValueType& pow_x = ValueTypeToolbox::pow(base.value(), exp); result.setValue(pow_x); if (base == 0.0) { // we special case the base 0 case because 0.0 is in the valid range of the // base but the generic code leads to NaNs. result = 0.0; } else { // derivatives use the chain rule const ValueType& df_dx = pow_x/base.value()*exp; for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*base.derivative(curVarIdx)); } return result; } // exponentiation of constant base with an arbitrary exponent template Evaluation pow(const BaseType& base, const Evaluation& exp) { typedef MathToolbox ValueTypeToolbox; Evaluation result(exp); if (base == 0.0) { // we special case the base 0 case because 0.0 is in the valid range of the // base but the generic code leads to NaNs. result = 0.0; } else { const ValueType& lnBase = ValueTypeToolbox::log(base); result.setValue(ValueTypeToolbox::exp(lnBase*exp.value())); // derivatives use the chain rule const ValueType& df_dx = lnBase*result.value(); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*exp.derivative(curVarIdx)); } return result; } // this is the most expensive power function. Computationally it is pretty expensive, so // one of the above two variants above should be preferred if possible. template Evaluation pow(const Evaluation& base, const Evaluation& exp) { typedef MathToolbox ValueTypeToolbox; Evaluation result(base); if (base == 0.0) { // we special case the base 0 case because 0.0 is in the valid range of the // base but the generic code leads to NaNs. result = 0.0; } else { ValueType valuePow = ValueTypeToolbox::pow(base.value(), exp.value()); result.setValue(valuePow); // use the chain rule for the derivatives. since both, the base and the exponent can // potentially depend on the variable set, calculating these is quite elaborate... const ValueType& f = base.value(); const ValueType& g = exp.value(); const ValueType& logF = ValueTypeToolbox::log(f); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) { const ValueType& fPrime = base.derivative(curVarIdx); const ValueType& gPrime = exp.derivative(curVarIdx); result.setDerivative(curVarIdx, (g*fPrime/f + logF*gPrime) * valuePow); } } return result; } template Evaluation log(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::log(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1/x.value(); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } template Evaluation log10(const Evaluation& x) { typedef MathToolbox ValueTypeToolbox; Evaluation result(x); result.setValue(ValueTypeToolbox::log10(x.value())); // derivatives use the chain rule const ValueType& df_dx = 1/x.value() * ValueTypeToolbox::log10(ValueTypeToolbox::exp(1.0)); for (int curVarIdx = 0; curVarIdx < result.size(); ++curVarIdx) result.setDerivative(curVarIdx, df_dx*x.derivative(curVarIdx)); return result; } } // namespace DenseAd // a kind of traits class for the automatic differentiation case. (The toolbox for the // scalar case is provided by the MathToolbox.hpp header file.) template struct MathToolbox > { private: public: typedef ValueT ValueType; typedef MathToolbox InnerToolbox; typedef typename InnerToolbox::Scalar Scalar; typedef DenseAd::Evaluation Evaluation; static ValueType value(const Evaluation& eval) { return eval.value(); } static decltype(InnerToolbox::scalarValue(0.0)) scalarValue(const Evaluation& eval) { return InnerToolbox::scalarValue(eval.value()); } static Evaluation createBlank(const Evaluation& x) { return Evaluation::createBlank(x); } static Evaluation createConstantZero(const Evaluation& x) { return Evaluation::createConstantZero(x); } static Evaluation createConstantOne(const Evaluation& x) { return Evaluation::createConstantOne(x); } static Evaluation createConstant(ValueType value) { return Evaluation::createConstant(value); } static Evaluation createConstant(unsigned numDeriv, const ValueType value) { return Evaluation::createConstant(numDeriv, value); } static Evaluation createConstant(const Evaluation& x, const ValueType value) { return Evaluation::createConstant(x, value); } static Evaluation createVariable(ValueType value, int varIdx) { return Evaluation::createVariable(value, varIdx); } template static typename std::enable_if::value, LhsEval>::type decay(const Evaluation& eval) { return eval; } template static typename std::enable_if::value, LhsEval>::type decay(const Evaluation&& eval) { return eval; } template static typename std::enable_if::value, LhsEval>::type decay(const Evaluation& eval) { return eval.value(); } // comparison static bool isSame(const Evaluation& a, const Evaluation& b, Scalar tolerance) { typedef MathToolbox ValueTypeToolbox; // make sure that the value of the evaluation is identical if (!ValueTypeToolbox::isSame(a.value(), b.value(), tolerance)) return false; // make sure that the derivatives are identical for (int curVarIdx = 0; curVarIdx < numVars; ++curVarIdx) if (!ValueTypeToolbox::isSame(a.derivative(curVarIdx), b.derivative(curVarIdx), tolerance)) return false; return true; } // arithmetic functions template static Evaluation max(const Arg1Eval& arg1, const Arg2Eval& arg2) { return DenseAd::max(arg1, arg2); } template static Evaluation min(const Arg1Eval& arg1, const Arg2Eval& arg2) { return DenseAd::min(arg1, arg2); } static Evaluation abs(const Evaluation& arg) { return DenseAd::abs(arg); } static Evaluation tan(const Evaluation& arg) { return DenseAd::tan(arg); } static Evaluation atan(const Evaluation& arg) { return DenseAd::atan(arg); } static Evaluation atan2(const Evaluation& arg1, const Evaluation& arg2) { return DenseAd::atan2(arg1, arg2); } template static Evaluation atan2(const Evaluation& arg1, const Eval2& arg2) { return DenseAd::atan2(arg1, arg2); } template static Evaluation atan2(const Eval1& arg1, const Evaluation& arg2) { return DenseAd::atan2(arg1, arg2); } static Evaluation sin(const Evaluation& arg) { return DenseAd::sin(arg); } static Evaluation asin(const Evaluation& arg) { return DenseAd::asin(arg); } static Evaluation cos(const Evaluation& arg) { return DenseAd::cos(arg); } static Evaluation acos(const Evaluation& arg) { return DenseAd::acos(arg); } static Evaluation sqrt(const Evaluation& arg) { return DenseAd::sqrt(arg); } static Evaluation exp(const Evaluation& arg) { return DenseAd::exp(arg); } static Evaluation log(const Evaluation& arg) { return DenseAd::log(arg); } static Evaluation log10(const Evaluation& arg) { return DenseAd::log10(arg); } template static Evaluation pow(const Evaluation& arg1, const RhsValueType& arg2) { return DenseAd::pow(arg1, arg2); } template static Evaluation pow(const RhsValueType& arg1, const Evaluation& arg2) { return DenseAd::pow(arg1, arg2); } static Evaluation pow(const Evaluation& arg1, const Evaluation& arg2) { return DenseAd::pow(arg1, arg2); } static bool isfinite(const Evaluation& arg) { if (!InnerToolbox::isfinite(arg.value())) return false; for (int i = 0; i < numVars; ++i) if (!InnerToolbox::isfinite(arg.derivative(i))) return false; return true; } static bool isnan(const Evaluation& arg) { if (InnerToolbox::isnan(arg.value())) return true; for (int i = 0; i < numVars; ++i) if (InnerToolbox::isnan(arg.derivative(i))) return true; return false; } }; } #endif