diff --git a/opm/material/localad/Evaluation.hpp b/opm/material/localad/Evaluation.hpp
new file mode 100644
index 000000000..664f2f7f7
--- /dev/null
+++ b/opm/material/localad/Evaluation.hpp
@@ -0,0 +1,467 @@
+/*
+ Copyright (C) 2015 by Andreas Lauser
+
+ 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 .
+*/
+/*!
+ * \file
+ *
+ * \brief Representation of an evaluation of a function and its derivatives w.r.t. a set
+ * of variables in the localized OPM automatic differentiation (AD) framework.
+ */
+#ifndef OPM_LOCAL_AD_EVALUATION_HPP
+#define OPM_LOCAL_AD_EVALUATION_HPP
+
+#include
+#include
+#include
+#include
+
+namespace Opm {
+namespace LocalAd {
+/*!
+ * \brief Represents a function evaluation and its derivatives w.r.t. a fixed set of
+ * variables.
+ */
+template
+class Evaluation
+{
+public:
+ typedef ScalarT Scalar;
+
+ enum { size = numVars };
+
+ Evaluation()
+ {};
+
+ // copy other function evaluation
+ Evaluation(const Evaluation& other)
+ {
+ // copy evaluated function value and derivatives
+ value = other.value;
+ std::copy(other.derivatives.begin(), other.derivatives.end(), derivatives.begin());
+ };
+
+ // create an evaluation which represents a constant function
+ //
+ // i.e., f(x) = c. this implies an evaluation with the given value and all
+ // derivatives being zero.
+ Evaluation(Scalar c)
+ {
+ value = c;
+ std::fill(derivatives.begin(), derivatives.end(), 0.0);
+ };
+
+ // create a function evaluation for a "naked" depending variable (i.e., f(x) = x)
+ static Evaluation createVariable(Scalar value, int varPos)
+ {
+ // The variable position must be in represented by the given variable descriptor
+ assert(0 <= varPos && varPos < size);
+
+ Evaluation result;
+
+ // copy function value and set all derivatives to 0, except for the variable
+ // which is represented by the value (which is set to 1.0)
+ result.value = value;
+ std::fill(result.derivatives.begin(), result.derivatives.end(), 0.0);
+ result.derivatives[varPos] = 1.0;
+
+ return result;
+ }
+
+ // "evaluate" a constant function (i.e. a function that does not depend on the set of
+ // relevant variables, f(x) = c).
+ static Evaluation createConstant(Scalar value)
+ {
+ Evaluation result;
+ result.value = value;
+ std::fill(result.derivatives.begin(), result.derivatives.end(), 0.0);
+ Valgrind::CheckDefined(result.value);
+ Valgrind::CheckDefined(result.derivatives);
+ return result;
+ }
+
+ // print the value and the derivatives of the function evaluation
+ void print(std::ostream& os = std::cout) const
+ {
+ os << "v: " << value << " / d:";
+ for (int varIdx = 0; varIdx < derivatives.size(); ++varIdx)
+ os << " " << derivatives[varIdx];
+ }
+
+
+ Evaluation& operator+=(const Evaluation& other)
+ {
+ // value and derivatives are added
+ this->value += other.value;
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ this->derivatives[varIdx] += other.derivatives[varIdx];
+
+ return *this;
+ }
+
+ Evaluation& operator+=(Scalar other)
+ {
+ // value is added, derivatives stay the same
+ this->value += other;
+
+ return *this;
+ }
+
+ Evaluation& operator-=(const Evaluation& other)
+ {
+ // value and derivatives are subtracted
+ this->value -= other.value;
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ this->derivatives[varIdx] -= other.derivatives[varIdx];
+
+ return *this;
+ }
+
+ Evaluation& operator-=(Scalar other)
+ {
+ // for constants, values are subtracted, derivatives stay the same
+ this->value -= other;
+
+ return *this;
+ }
+
+ Evaluation& operator*=(const Evaluation& other)
+ {
+ // while the values are multiplied, the derivatives follow the product rule,
+ // i.e., (u*v)' = (v'u + u'v).
+ Scalar u = this->value;
+ Scalar v = other.value;
+ this->value *= v;
+ for (int varIdx= 0; varIdx < size; ++varIdx) {
+ Scalar uPrime = this->derivatives[varIdx];
+ Scalar vPrime = other.derivatives[varIdx];
+
+ this->derivatives[varIdx] = (v*uPrime + u*vPrime);
+ }
+
+ return *this;
+ }
+
+ Evaluation& operator*=(Scalar other)
+ {
+ // values and derivatives are multiplied
+ this->value *= other;
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ this->derivatives[varIdx] *= other;
+
+ return *this;
+ }
+
+ Evaluation& operator/=(const Evaluation& other)
+ {
+ // values are divided, derivatives follow the rule for division, i.e., (u/v)' = (v'u -
+ // u'v)/v^2.
+ Scalar u = this->value;
+ Scalar v = other.value;
+ this->value /= v;
+ for (int varIdx= 0; varIdx < size; ++varIdx) {
+ Scalar uPrime = this->derivatives[varIdx];
+ Scalar vPrime = other.derivatives[varIdx];
+
+ this->derivatives[varIdx] = (v*uPrime - u*vPrime)/(v*v);
+ }
+
+ return *this;
+ }
+
+ Evaluation& operator/=(Scalar other)
+ {
+ // values and derivatives are divided
+ other = 1.0/other;
+ this->value *= other;
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ this->derivatives[varIdx] *= other;
+
+ return *this;
+ }
+
+ Evaluation operator+(const Evaluation& other) const
+ {
+ Evaluation result(*this);
+ result += other;
+ return result;
+ }
+
+ Evaluation operator+(Scalar other) const
+ {
+ Evaluation result(*this);
+ result += other;
+ return result;
+ }
+
+ Evaluation operator-(const Evaluation& other) const
+ {
+ Evaluation result(*this);
+ result -= other;
+ return result;
+ }
+
+ Evaluation operator-(Scalar other) const
+ {
+ Evaluation result(*this);
+ result -= other;
+ return result;
+ }
+
+ // negation (unary minus) operator
+ Evaluation operator-() const
+ {
+ Evaluation result;
+ result.value = -this->value;
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ result.derivatives[varIdx] = - this->derivatives[varIdx];
+
+ return result;
+ }
+
+ Evaluation operator*(const Evaluation& other) const
+ {
+ Evaluation result(*this);
+ result *= other;
+ return result;
+ }
+
+ Evaluation operator*(Scalar other) const
+ {
+ Evaluation result(*this);
+ result *= other;
+ return result;
+ }
+
+ Evaluation operator/(const Evaluation& other) const
+ {
+ Evaluation result(*this);
+ result /= other;
+ return result;
+ }
+
+ Evaluation operator/(Scalar other) const
+ {
+ Evaluation result(*this);
+ result /= other;
+ return result;
+ }
+
+ Evaluation& operator=(Scalar other)
+ {
+ this->value = other;
+ std::fill(this->derivatives.begin(), this->derivatives.end(), 0.0);
+ return *this;
+ }
+
+ // copy assignment from evaluation
+ Evaluation& operator=(const Evaluation& other)
+ {
+ this->value = other.value;
+ std::copy(other.derivatives.begin(), other.derivatives.end(), this->derivatives.begin());
+ return *this;
+ }
+
+ bool operator==(Scalar other) const
+ { return this->value == other; }
+
+ bool operator==(const Evaluation& other) const
+ {
+ if (this->value != other.value)
+ return false;
+
+ for (int varIdx= 0; varIdx < size; ++varIdx)
+ if (this->derivatives[varIdx] != other.derivatives[varIdx])
+ return false;
+
+ return true;
+ }
+
+ bool isSame(const Evaluation& other, Scalar tolerance) const
+ {
+ Scalar tmp = other.value - other.value;
+ if (std::abs(tmp) > tolerance && std::abs(tmp)/tolerance > 1.0)
+ return false;
+
+ for (int varIdx= 0; varIdx < size; ++varIdx) {
+ Scalar tmp = other.derivatives[varIdx] - this->derivatives[varIdx];
+ if (std::abs(tmp) > tolerance && std::abs(tmp)/tolerance > 1.0)
+ return false;
+ }
+
+ return true;
+ }
+
+ bool operator!=(const Evaluation& other) const
+ { return !operator==(other); }
+
+ bool operator>(Scalar other) const
+ { return this->value > other; }
+
+ bool operator>(const Evaluation& other) const
+ { return this->value > other.value; }
+
+ bool operator<(Scalar other) const
+ { return this->value < other; }
+
+ bool operator<(const Evaluation& other) const
+ { return this->value < other.value; }
+
+ bool operator>=(Scalar other) const
+ { return this->value >= other; }
+
+ bool operator>=(const Evaluation& other) const
+ { return this->value >= other.value; }
+
+ bool operator<=(Scalar other) const
+ { return this->value <= other; }
+
+ bool operator<=(const Evaluation& other) const
+ { return this->value <= other.value; }
+
+ // maybe this should be made 'private'...
+ Scalar value;
+ std::array derivatives;
+};
+
+template
+bool operator<(const ScalarA& a, const Evaluation &b)
+{ return b > a; }
+
+template
+bool operator>(const ScalarA& a, const Evaluation &b)
+{ return b < a; }
+
+template
+bool operator<=(const ScalarA& a, const Evaluation &b)
+{ return b >= a; }
+
+template
+bool operator>=(const ScalarA& a, const Evaluation &b)
+{ return b <= a; }
+
+template
+bool operator!=(const ScalarA& a, const Evaluation &b)
+{ return a != b.value; }
+
+template
+Evaluation operator+(const ScalarA& a, const Evaluation &b)
+{
+ Evaluation result(b);
+
+ result += a;
+
+ return result;
+}
+
+template
+Evaluation operator-(const ScalarA& a, const Evaluation &b)
+{
+ Evaluation result;
+
+ result.value = a - b.value;
+ for (int varIdx= 0; varIdx < numVars; ++varIdx)
+ result.derivatives[varIdx] = - b.derivatives[varIdx];
+
+ return result;
+}
+
+template
+Evaluation operator/(const ScalarA& a, const Evaluation &b)
+{
+ Evaluation result;
+
+ result.value = a/b.value;
+
+ // outer derivative
+ Scalar df_dg = - a/(b.value*b.value);
+ for (int varIdx= 0; varIdx < numVars; ++varIdx)
+ result.derivatives[varIdx] = df_dg*b.derivatives[varIdx];
+
+ return result;
+}
+
+template
+Evaluation operator*(const ScalarA& a, const Evaluation &b)
+{
+ Evaluation result;
+
+ result.value = a*b.value;
+ for (int varIdx= 0; varIdx < numVars; ++varIdx)
+ result.derivatives[varIdx] = a*b.derivatives[varIdx];
+
+ return result;
+}
+
+template
+std::ostream& operator<<(std::ostream& os, const Evaluation& eval)
+{
+ os << eval.value;
+ return os;
+}
+
+} // namespace LocalAd
+} // namespace Opm
+
+// make the Dune matrix/vector classes happy. Obviously, this is not very elegant...
+
+#ifdef DUNE_DENSEMATRIX_HH
+// for those who are wondering: in C++ function templates cannot be partially
+// specialized, and function argument overloads must be known _before_ they are used. The
+// latter is what we do for the 'Dune::fvmeta::absreal()' function.
+//
+// consider the following test program:
+//
+// double foo(double i)
+// { return i; }
+//
+// void bar()
+// { std::cout << foo(0) << "\n"; }
+//
+// int foo(int i)
+// { return i + 1; }
+//
+// void foobar()
+// { std::cout << foo(0) << "\n"; }
+//
+// this will print '0' for bar() and '1' for foobar()...
+//
+#error "Due to some C++ peculiarity regarding function template specialization,"
+#error "the 'evaluation.hh' header must be included before Dune's 'densematrix.hh'!"
+#endif
+
+#include
+
+namespace Dune {
+template
+struct FieldTraits >
+{
+public:
+ typedef Opm::LocalAd::Evaluation field_type;
+ typedef Scalar real_type;
+};
+
+namespace fvmeta {
+template
+inline Scalar absreal(const Opm::LocalAd::Evaluation& k)
+{
+ return std::abs(k.value);
+}
+
+}} // namespace fvmeta, Dune
+
+#endif
diff --git a/opm/material/localad/Math.hpp b/opm/material/localad/Math.hpp
new file mode 100644
index 000000000..1246b2f61
--- /dev/null
+++ b/opm/material/localad/Math.hpp
@@ -0,0 +1,383 @@
+/*
+ Copyright (C) 2015 by Andreas Lauser
+
+ 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 .
+*/
+/*!
+ * \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"
+
+namespace Opm {
+namespace LocalAd {
+// provide some algebraic functions
+template
+Evaluation abs(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::abs(x.value);
+
+ // derivatives use the chain rule
+ if (x.value < 0.0) {
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = -x.derivatives[curVarIdx];
+ }
+ else {
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = x.derivatives[curVarIdx];
+ }
+
+ return result;
+}
+
+template
+Evaluation min(const Evaluation& x1,
+ const Evaluation& x2)
+{
+ Evaluation result;
+
+ if (x1.value < x2.value) {
+ result.value = x1.value;
+
+ std::copy(x1.derivatives.begin(),
+ x1.derivatives.end(),
+ result.derivatives.begin());
+ }
+ else {
+ result.value = x2.value;
+
+ std::copy(x2.derivatives.begin(),
+ x2.derivatives.end(),
+ result.derivatives.begin());
+ }
+
+ return result;
+}
+
+template
+Evaluation min(ScalarA x1,
+ const Evaluation& x2)
+{
+ Evaluation result;
+
+ if (x1 < x2.value) {
+ result.value = x1;
+
+ std::fill(result.derivatives.begin(),
+ result.derivatives.end(),
+ 0.0);
+ }
+ else {
+ result.value = x2.value;
+
+ std::copy(x2.derivatives.begin(),
+ x2.derivatives.end(),
+ result.derivatives.begin());
+ }
+
+ return result;
+}
+
+template
+Evaluation min(const Evaluation& x2,
+ ScalarB x1)
+{ return min(x1, x2); }
+
+template
+Evaluation max(const Evaluation& x1,
+ const Evaluation& x2)
+{
+ Evaluation result;
+
+ if (x1.value > x2.value) {
+ result.value = x1.value;
+
+ std::copy(x1.derivatives.begin(),
+ x1.derivatives.end(),
+ result.derivatives.begin());
+ }
+ else {
+ result.value = x2.value;
+
+ std::copy(x2.derivatives.begin(),
+ x2.derivatives.end(),
+ result.derivatives.begin());
+ }
+
+ return result;
+}
+
+template
+Evaluation max(ScalarA x1,
+ const Evaluation& x2)
+{
+ Evaluation result;
+
+ if (x1 > x2.value) {
+ result.value = x1;
+
+ std::fill(result.derivatives.begin(),
+ result.derivatives.end(),
+ 0.0);
+ }
+ else {
+ result.value = x2.value;
+
+ std::copy(x2.derivatives.begin(),
+ x2.derivatives.end(),
+ result.derivatives.begin());
+ }
+
+ return result;
+}
+
+template
+Evaluation max(const Evaluation& x2,
+ ScalarB x1)
+{ return max(x1, x2); }
+
+template
+Evaluation tan(const Evaluation& x)
+{
+ Evaluation result;
+
+ Scalar tmp = std::tan(x.value);
+ result.value = tmp;
+
+ // derivatives use the chain rule
+ Scalar df_dx = 1 + tmp*tmp;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation atan(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::atan(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = 1/(1 + x.value*x.value);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation atan2(const Evaluation& x,
+ const Evaluation& y)
+{
+ Evaluation result;
+
+ result.value = std::atan2(x.value, y.value);
+
+ // derivatives use the chain rule
+ Scalar alpha = 1/(1 + (x.value*x.value)/(y.value*y.value));
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx) {
+ result.derivatives[curVarIdx] =
+ alpha
+ /(y.value*y.value)
+ *(x.derivatives[curVarIdx]*y.value - x.value*y.derivatives[curVarIdx]);
+ }
+
+ return result;
+}
+
+template
+Evaluation sin(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::sin(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = std::cos(x.value);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation asin(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::asin(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = 1.0/std::sqrt(1 - x.value*x.value);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation cos(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::cos(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = -std::sin(x.value);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation acos(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::acos(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = - 1.0/std::sqrt(1 - x.value*x.value);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+template
+Evaluation sqrt(const Evaluation& x)
+{
+ Evaluation result;
+
+ Scalar sqrt_x = std::sqrt(x.value);
+ result.value = sqrt_x;
+
+ // derivatives use the chain rule
+ Scalar df_dx = 0.5/sqrt_x;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx) {
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+ }
+
+ return result;
+}
+
+template
+Evaluation exp(const Evaluation& x)
+{
+ Evaluation result;
+
+ Scalar exp_x = std::exp(x.value);
+ result.value = exp_x;
+
+ // derivatives use the chain rule
+ Scalar df_dx = exp_x;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+// exponentiation of arbitrary base with a fixed constant
+template
+Evaluation pow(const Evaluation& base, Scalar exp)
+{
+ Evaluation result;
+
+ Scalar pow_x = std::pow(base.value, exp);
+ result.value = pow_x;
+
+ // derivatives use the chain rule
+ Scalar df_dx = pow_x/base.value*exp;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*base.derivatives[curVarIdx];
+
+ return result;
+}
+
+// exponentiation of constant base with an arbitrary exponent
+template
+Evaluation pow(Scalar base, const Evaluation& exp)
+{
+ Evaluation result;
+
+ Scalar lnBase = std::log(base);
+ result.value = std::exp(lnBase*exp.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = lnBase*result.value;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*exp.derivatives[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)
+{
+ Evaluation result;
+
+ Scalar valuePow = std::pow(base.value, exp.value);
+ result.value = 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...
+ Scalar f = base.value;
+ Scalar g = exp.value;
+ Scalar logF = std::log(f);
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx) {
+ Scalar fPrime = base.derivatives[curVarIdx];
+ Scalar gPrime = exp.derivatives[curVarIdx];
+ result.derivatives[curVarIdx] = (g*fPrime/f + logF*gPrime) * valuePow;
+ }
+
+ return result;
+}
+
+template
+Evaluation log(const Evaluation& x)
+{
+ Evaluation result;
+
+ result.value = std::log(x.value);
+
+ // derivatives use the chain rule
+ Scalar df_dx = 1/x.value;
+ for (unsigned curVarIdx = 0; curVarIdx < result.derivatives.size(); ++curVarIdx)
+ result.derivatives[curVarIdx] = df_dx*x.derivatives[curVarIdx];
+
+ return result;
+}
+
+} // namespace LocalAd
+
+}
+
+#endif
diff --git a/tests/test_localad.cpp b/tests/test_localad.cpp
new file mode 100644
index 000000000..033bb9f46
--- /dev/null
+++ b/tests/test_localad.cpp
@@ -0,0 +1,544 @@
+/*
+ Copyright (C) 2015 by Andreas Lauser
+
+ 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 .
+*/
+/*!
+ * \file
+ *
+ * \brief Low-level tests for the localized automatic differentiation (AD) framework.
+ */
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+struct TestVariables
+{
+ static const int size = 3;
+
+ static const int temperatureIdx = 0;
+ static const int pressureIdx = 1;
+ static const int saturationIdx = 2;
+};
+
+template
+void testOperators()
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ // test the constructors of the Opm::LocalAd::Evaluation class
+ const Scalar c = 1.234;
+ const Scalar x = 4.567;
+ const Scalar y = 8.910;
+ const Eval cEval = Eval::createConstant(c);
+ const Eval OPM_UNUSED c2Eval = c;
+ const Eval xEval = Eval::createVariable(x, 0);
+ const Eval yEval = Eval::createVariable(y, 1);
+
+ // test the non-inplace operators
+ {
+ Eval a = xEval + yEval;
+ if (a.value != x + y)
+ throw std::logic_error("oops: operator+");
+
+ Eval b = xEval + c;
+ if (b.value != x + c)
+ throw std::logic_error("oops: operator+");
+
+ Eval d = xEval + cEval;
+ if (d.value != x + c)
+ throw std::logic_error("oops: operator+");
+ }
+
+ {
+ Eval a = xEval - yEval;
+ if (a.value != x - y)
+ throw std::logic_error("oops: operator-");
+
+ Eval b = xEval - c;
+ if (b.value != x - c)
+ throw std::logic_error("oops: operator-");
+
+ Eval d = xEval - cEval;
+ if (d.value != x - c)
+ throw std::logic_error("oops: operator-");
+ }
+
+ {
+ Eval a = xEval*yEval;
+ if (a.value != x*y)
+ throw std::logic_error("oops: operator*");
+
+ Eval b = xEval*c;
+ if (b.value != x*c)
+ throw std::logic_error("oops: operator*");
+
+ Eval d = xEval*cEval;
+ if (d.value != x*c)
+ throw std::logic_error("oops: operator*");
+ }
+
+ {
+ Eval a = xEval/yEval;
+ if (a.value != x/y)
+ throw std::logic_error("oops: operator/");
+
+ Eval b = xEval/c;
+ if (b.value != x/c)
+ throw std::logic_error("oops: operator/");
+
+ Eval d = xEval/cEval;
+ if (d.value != x/c)
+ throw std::logic_error("oops: operator/");
+ }
+
+ // test the inplace operators
+ {
+ Eval a = xEval;
+ a += yEval;
+ if (a.value != x + y)
+ throw std::logic_error("oops: operator+");
+
+ Eval b = xEval;
+ b += c;
+ if (b.value != x + c)
+ throw std::logic_error("oops: operator+");
+
+ Eval d = xEval;
+ d += cEval;
+ if (d.value != x + c)
+ throw std::logic_error("oops: operator+");
+ }
+
+ {
+ Eval a = xEval;
+ a -= yEval;
+ if (a.value != x - y)
+ throw std::logic_error("oops: operator-");
+
+ Eval b = xEval;
+ b -= c;
+ if (b.value != x - c)
+ throw std::logic_error("oops: operator-");
+
+ Eval d = xEval;
+ d -= cEval;
+ if (d.value != x - c)
+ throw std::logic_error("oops: operator-");
+ }
+
+ {
+ Eval a = xEval;
+ a *= yEval;
+ if (a.value != x*y)
+ throw std::logic_error("oops: operator*");
+
+ Eval b = xEval;
+ b *= c;
+ if (b.value != x*c)
+ throw std::logic_error("oops: operator*");
+
+ Eval d = xEval;
+ d *= cEval;
+ if (d.value != x*c)
+ throw std::logic_error("oops: operator*");
+ }
+
+ {
+ Eval a = xEval;
+ a /= yEval;
+ if (a.value != x/y)
+ throw std::logic_error("oops: operator/");
+
+ Eval b = xEval;
+ b /= c;
+ if (b.value != x/c)
+ throw std::logic_error("oops: operator/");
+
+ Eval d = xEval;
+ d /= cEval;
+ if (d.value != x/c)
+ throw std::logic_error("oops: operator/");
+ }
+
+ {
+ Eval a = 1.0;
+ Eval b = 2.0;
+ if (a >= b)
+ throw std::logic_error("oops: operator>=");
+ if (a >= 2.0)
+ throw std::logic_error("oops: operator>=");
+
+ if (!(a >= a))
+ throw std::logic_error("oops: operator>=");
+ if (!(a >= 1.0))
+ throw std::logic_error("oops: operator>=");
+ if (!(1.0 <= a))
+ throw std::logic_error("oops: operator<=");
+
+ if (b <= a)
+ throw std::logic_error("oops: operator<=");
+ if (b <= 1.0)
+ throw std::logic_error("oops: operator<=");
+
+ if (!(b <= b))
+ throw std::logic_error("oops: operator<=");
+ if (!(b <= 2.0))
+ throw std::logic_error("oops: operator<=");
+ if (!(2.0 >= b))
+ throw std::logic_error("oops: operator>=");
+
+ if (a != a)
+ throw std::logic_error("oops: operator!=");
+ if (a != 1.0)
+ throw std::logic_error("oops: operator!=");
+ if (1.0 != a)
+ throw std::logic_error("oops: operator!=");
+ }
+}
+
+template
+void test1DFunction(AdFn* adFn, ClassicFn* classicFn, Scalar xMin = 1e-6, Scalar xMax = 1000)
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ int n = 100*1000;
+ for (int i = 0; i < n; ++ i) {
+ Scalar x = Scalar(i)/(n - 1)*(xMax - xMin) + xMin;
+
+ const auto& xEval = Eval::createVariable(x, 0);
+ const Eval& yEval = adFn(xEval);
+
+ const Scalar eps = 1e-10;
+ Scalar y = classicFn(x);
+ Scalar yStar1 = classicFn(x - eps);
+ Scalar yStar2 = classicFn(x + eps);
+ Scalar yPrime = (yStar2 - yStar1)/(2*eps);
+
+ if (y != yEval.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(yPrime - yEval.derivatives[0]);
+ Scalar deltaRel = std::abs(deltaAbs/yPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @"+std::to_string((long double) x)+": "
+ + std::to_string((long double) yPrime) + " vs "
+ + std::to_string((long double) yEval.derivatives[0])
+ + " delta: " + std::to_string((long double) std::abs(yPrime - yEval.derivatives[0])));
+ }
+}
+
+template
+void test2DFunction1(AdFn* adFn, ClassicFn* classicFn, Scalar xMin, Scalar xMax, Scalar y)
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ int n = 100*1000;
+ for (int i = 0; i < n; ++ i) {
+ Scalar x = Scalar(i)/(n - 1)*(xMax - xMin) + xMin;
+
+ const auto& xEval = Eval::createVariable(x, 0);
+ const auto& yEval = Eval::createConstant(y);
+ const Eval& zEval = adFn(xEval, yEval);
+
+ const Scalar eps = 1e-10;
+ Scalar z = classicFn(x, y);
+ Scalar zStar1 = classicFn(x - eps, y);
+ Scalar zStar2 = classicFn(x + eps, y);
+ Scalar zPrime = (zStar2 - zStar1)/(2*eps);
+
+ if (z != zEval.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(zPrime - zEval.derivatives[0]);
+ Scalar deltaRel = std::abs(deltaAbs/zPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @"+std::to_string((long double) x)+": "
+ + std::to_string((long double) zPrime) + " vs "
+ + std::to_string((long double) zEval.derivatives[0])
+ + " delta: " + std::to_string((long double) std::abs(zPrime - zEval.derivatives[0])));
+ }
+}
+
+template
+void test2DFunction2(AdFn* adFn, ClassicFn* classicFn, Scalar x, Scalar yMin, Scalar yMax)
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ int n = 100*1000;
+ for (int i = 0; i < n; ++ i) {
+ Scalar y = Scalar(i)/(n - 1)*(yMax - yMin) + yMin;
+
+ const auto& xEval = Eval::createConstant(x);
+ const auto& yEval = Eval::createVariable(y, 1);
+ const Eval& zEval = adFn(xEval, yEval);
+
+ const Scalar eps = 1e-10;
+ Scalar z = classicFn(x, y);
+ Scalar zStar1 = classicFn(x, y - eps);
+ Scalar zStar2 = classicFn(x, y + eps);
+ Scalar zPrime = (zStar2 - zStar1)/(2*eps);
+
+ if (z != zEval.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(zPrime - zEval.derivatives[1]);
+ Scalar deltaRel = std::abs(deltaAbs/zPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @"+std::to_string((long double) x)+": "
+ + std::to_string((long double) zPrime) + " vs "
+ + std::to_string((long double) zEval.derivatives[1])
+ + " delta: " + std::to_string((long double) std::abs(zPrime - zEval.derivatives[1])));
+ }
+}
+
+template
+void testPowBase(Scalar baseMin = 1e-2, Scalar baseMax = 100)
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ Scalar exp = 1.234;
+ const auto& expEval = Eval::createConstant(exp);
+
+ int n = 100*1000;
+ for (int i = 0; i < n; ++ i) {
+ Scalar base = Scalar(i)/(n - 1)*(baseMax - baseMin) + baseMin;
+
+ const auto& baseEval = Eval::createVariable(base, 0);
+ const Eval& zEval1 = pow(baseEval, exp);
+ const Eval& zEval2 = pow(baseEval, expEval);
+
+ const Scalar eps = 1e-5;
+ Scalar z = pow(base, exp);
+ Scalar zStar1 = pow(base - eps, exp);
+ Scalar zStar2 = pow(base + eps, exp);
+ Scalar zPrime = (zStar2 - zStar1)/(2*eps);
+
+ if (z != zEval2.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(zPrime - zEval1.derivatives[0]);
+ Scalar deltaRel = std::abs(deltaAbs/zPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @"+std::to_string((long double) base)+": "
+ + std::to_string((long double) zPrime) + " vs "
+ + std::to_string((long double) zEval1.derivatives[0])
+ + " delta: " + std::to_string((long double) std::abs(zPrime - zEval1.derivatives[0])));
+
+ if (!zEval1.isSame(zEval2, /*tolerance=*/1e-9))
+ throw std::logic_error("oops: pow(Eval, Scalar) != pow(Eval, Eval)");
+ }
+}
+
+template
+void testPowExp(Scalar expMin = -100, Scalar expMax = 100)
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ Scalar base = 1.234;
+ const auto& baseEval = Eval::createConstant(base);
+
+ int n = 100*1000;
+ for (int i = 0; i < n; ++ i) {
+ Scalar exp = Scalar(i)/(n - 1)*(expMax - expMin) + expMin;
+ const auto& expEval = Eval::createVariable(exp, 1);
+
+ const Eval& zEval1 = pow(base, expEval);
+ const Eval& zEval2 = pow(baseEval, expEval);
+
+ const Scalar eps = 1e-8;
+ Scalar z = pow(base, exp);
+ Scalar zStar1 = pow(base, exp - eps);
+ Scalar zStar2 = pow(base, exp + eps);
+ Scalar zPrime = (zStar2 - zStar1)/(2*eps);
+
+ if (z != zEval2.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(zPrime - zEval1.derivatives[1]);
+ Scalar deltaRel = std::abs(deltaAbs/zPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @"+std::to_string((long double) base)+": "
+ + std::to_string((long double) zPrime) + " vs "
+ + std::to_string((long double) zEval1.derivatives[1])
+ + " delta: " + std::to_string((long double) std::abs(zPrime - zEval1.derivatives[1])));
+
+ if (!zEval1.isSame(zEval2, /*tolerance=*/1e-5))
+ throw std::logic_error("oops: pow(Eval, Scalar) != pow(Eval, Eval)");
+ }
+}
+
+template
+void testAtan2()
+{
+ typedef Opm::LocalAd::Evaluation Eval;
+
+ int n = 1000;
+ Scalar maxVal = 10.0;
+ for (int i = 1; i < n; ++ i) {
+ Scalar x = 2*maxVal*Scalar(i)/n - maxVal;
+ if (- 0.05 < x && x < 0.05)
+ // avoid numerical problems
+ continue;
+
+ const Eval& xEval = Eval::createVariable(x, 0);
+
+ for (int j = 1; j < n; ++ j) {
+ Scalar y = 2*maxVal*Scalar(j)/n - maxVal;
+
+ if (- 0.05 < y && y < 0.05)
+ // avoid numerical problems
+ continue;
+
+ const Eval& yEval = Eval::createVariable(y, 0);
+ const Eval& zEval = atan2(xEval, yEval);
+
+ const Scalar eps = 1e-8;
+ Scalar z = atan2(x, y);
+ Scalar zStar1 = atan2(x - eps, y - eps);
+ Scalar zStar2 = atan2(x + eps, y + eps);
+ Scalar zPrime = (zStar2 - zStar1)/(2*eps);
+
+ if (z != zEval.value)
+ throw std::logic_error("oops: value");
+
+ Scalar deltaAbs = std::abs(zPrime - zEval.derivatives[0]);
+ Scalar deltaRel = std::abs(deltaAbs/zPrime);
+ if (deltaAbs > 1e-3 && deltaRel > 1e-3)
+ throw std::logic_error("oops: derivative @("+std::to_string((long double) x)+","+std::to_string((long double) y)+"): "
+ + std::to_string((long double) zPrime) + " vs "
+ + std::to_string((long double) zEval.derivatives[0])
+ + " delta: " + std::to_string((long double) std::abs(zPrime - zEval.derivatives[0])));
+
+ }
+ }
+}
+
+double myScalarMin(double a, double b)
+{ return std::min(a, b); }
+
+double myScalarMax(double a, double b)
+{ return std::max(a, b); }
+
+int main()
+{
+ typedef double Scalar;
+
+ typedef TestVariables VarsDescriptor;
+
+ // the following is commented out because it is supposed to produce a compiler
+ // error. This is the case since the function does not calculate the derivatives
+ // w.r.t. Pressure but they have been requested...
+ //const auto& result2 = Opm::LocalAd::sqrt(TemperatureEval::createVariable(4.0));
+
+ std::cout << "testing operators and constructors\n";
+ testOperators();
+
+ std::cout << "testing min()\n";
+ test2DFunction1(Opm::LocalAd::min,
+ myScalarMin,
+ -1000, 1000,
+ /*p=*/1.234);
+
+ test2DFunction2(Opm::LocalAd::min,
+ myScalarMin,
+ /*T=*/1.234,
+ -1000, 1000);
+
+ std::cout << "testing max()\n";
+ test2DFunction1(Opm::LocalAd::max,
+ myScalarMax,
+ -1000, 1000,
+ /*p=*/1.234);
+
+ test2DFunction2(Opm::LocalAd::max,
+ myScalarMax,
+ /*T=*/1.234,
+ -1000, 1000);
+
+ std::cout << "testing pow()\n";
+ testPowBase();
+ testPowExp();
+
+ std::cout << "testing abs()\n";
+ test1DFunction(Opm::LocalAd::abs,
+ static_cast(std::abs));
+
+ std::cout << "testing sqrt()\n";
+ test1DFunction(Opm::LocalAd::sqrt,
+ static_cast(std::sqrt));
+
+ std::cout << "testing sin()\n";
+ test1DFunction(Opm::LocalAd::sin,
+ static_cast(std::sin),
+ 0, 2*M_PI);
+
+ std::cout << "testing asin()\n";
+ test1DFunction(Opm::LocalAd::asin,
+ static_cast(std::asin),
+ -1.0, 1.0);
+
+ std::cout << "testing cos()\n";
+ test1DFunction(Opm::LocalAd::cos,
+ static_cast(std::cos),
+ 0, 2*M_PI);
+
+ std::cout << "testing acos()\n";
+ test1DFunction(Opm::LocalAd::acos,
+ static_cast(std::acos),
+ -1.0, 1.0);
+
+ std::cout << "testing tan()\n";
+ test1DFunction(Opm::LocalAd::tan,
+ static_cast(std::tan),
+ -M_PI / 2 * 0.95, M_PI / 2 * 0.95);
+
+ std::cout << "testing atan()\n";
+ test1DFunction(Opm::LocalAd::atan,
+ static_cast(std::atan),
+ -10*1000.0, 10*1000.0);
+
+ std::cout << "testing atan2()\n";
+ testAtan2();
+
+ std::cout << "testing exp()\n";
+ test1DFunction(Opm::LocalAd::exp,
+ static_cast(std::exp),
+ -100, 100);
+
+ std::cout << "testing log()\n";
+ test1DFunction(Opm::LocalAd::log,
+ static_cast(std::log),
+ 1e-6, 1e9);
+
+ return 0;
+}