From: Jean-Paul Pelteret Date: Fri, 22 Mar 2019 09:09:11 +0000 (+0100) Subject: Add headers for tests common to all supported AD frameworks X-Git-Tag: v9.1.0-rc1~137^2~3 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=99e83e1d7c87fb7b8f8a4b5037482e34b2070acd;p=dealii.git Add headers for tests common to all supported AD frameworks --- diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_01.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_01.h new file mode 100644 index 0000000000..2383e542dd --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_01.h @@ -0,0 +1,228 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (scalar + scalar components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestScalarScalarCoupled +{ + static NumberType + psi(const NumberType &s1, const NumberType &s2) + { + return 2.0 * std::pow(s1, 4) * std::pow(s2, 3); + }; + + static NumberType + dpsi_ds1(const NumberType &s1, const NumberType &s2) + { + return 8.0 * std::pow(s1, 3) * std::pow(s2, 3); + }; + + static NumberType + dpsi_ds2(const NumberType &s1, const NumberType &s2) + { + return 6.0 * std::pow(s1, 4) * std::pow(s2, 2); + }; + + static NumberType + d2psi_ds1_ds1(const NumberType &s1, const NumberType &s2) + { + return 24.0 * std::pow(s1, 2) * std::pow(s2, 3); + }; + + static NumberType + d2psi_ds2_ds1(const NumberType &s1, const NumberType &s2) + { + return 24.0 * std::pow(s1, 3) * std::pow(s2, 2); + }; + + static NumberType + d2psi_ds1_ds2(const NumberType &s1, const NumberType &s2) + { + return d2psi_ds2_ds1(s1, s2); + }; + + static NumberType + d2psi_ds2_ds2(const NumberType &s1, const NumberType &s2) + { + return 12.0 * std::pow(s1, 4) * std::pow(s2, 1); + }; +}; + +template +void +test_scalar_scalar_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Scalar + Scalar (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestScalarScalarCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Scalar s1_dof(0); + const FEValuesExtractors::Scalar s2_dof(1); + const unsigned int n_AD_components = 2; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + ScalarNumberType s1 = 3.1; + ScalarNumberType s2 = 5.9; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(s1, s1_dof); + ad_helper.register_independent_variable(s2, s2_dof); + + const ADNumberType s1_ad = ad_helper.get_sensitive_variables(s1_dof); + const ADNumberType s2_ad = ad_helper.get_sensitive_variables(s2_dof); + + const ADNumberType psi(func_ad::psi(s1_ad, s2_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s1_ad: " << s1_ad << std::endl; + std::cout << "s2_ad: " << s2_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s1 = 4.9; + s2 = 0.87; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variable(s1, s1_dof); + ad_helper.set_independent_variable(s2, s2_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const ScalarNumberType dpsi_ds1 = + ad_helper.extract_gradient_component(Dpsi, s1_dof); + const ScalarNumberType dpsi_ds2 = + ad_helper.extract_gradient_component(Dpsi, s2_dof); + std::cout << "extracted Dpsi (s1): " << dpsi_ds1 << "\n" + << "extracted Dpsi (s2): " << dpsi_ds2 << "\n"; + + // Verify the result + typedef FunctionsTestScalarScalarCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + Assert(std::abs(psi - func::psi(s1, s2)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs(dpsi_ds1 - func::dpsi_ds1(s1, s2)) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs(dpsi_ds2 - func::dpsi_ds2(s1, s2)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const ScalarNumberType d2psi_ds1_ds1 = + ad_helper.extract_hessian_component(D2psi, s1_dof, s1_dof); + const ScalarNumberType d2psi_ds2_ds1 = + ad_helper.extract_hessian_component(D2psi, s1_dof, s2_dof); + const ScalarNumberType d2psi_ds1_ds2 = + ad_helper.extract_hessian_component(D2psi, s2_dof, s1_dof); + const ScalarNumberType d2psi_ds2_ds2 = + ad_helper.extract_hessian_component(D2psi, s2_dof, s2_dof); + std::cout << "extracted D2psi (s1,s1): " << d2psi_ds1_ds1 << "\n" + << "extracted D2psi (s1,s2): " << d2psi_ds2_ds1 << "\n" + << "extracted D2psi (s2,s1): " << d2psi_ds1_ds2 << "\n" + << "extracted D2psi (s2,s2): " << d2psi_ds2_ds2 << "\n" + << std::endl; + Assert(std::abs(d2psi_ds1_ds1 - func::d2psi_ds1_ds1(s1, s2)) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds2_ds1 - func::d2psi_ds2_ds1(s1, s2)) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds1_ds2 - func::d2psi_ds1_ds2(s1, s2)) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds2_ds2 - func::d2psi_ds2_ds2(s1, s2)) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_02.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_02.h new file mode 100644 index 0000000000..530e3c95e5 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_02.h @@ -0,0 +1,233 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (vector + scalar components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestVectorScalarCoupled +{ + static NumberType + psi(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return (v * v) * std::pow(s, 3); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return 2.0 * v * std::pow(s, 3); + }; + + static NumberType + dpsi_ds(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return 3.0 * (v * v) * std::pow(s, 2); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + static const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return 2.0 * I * std::pow(s, 3); + }; + + static Tensor<1, dim, NumberType> + d2psi_ds_dv(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return 6.0 * v * std::pow(s, 2); + }; + + static Tensor<1, dim, NumberType> + d2psi_dv_ds(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return d2psi_ds_dv(v, s); + }; + + static NumberType + d2psi_ds_ds(const Tensor<1, dim, NumberType> &v, const NumberType &s) + { + return 6.0 * (v * v) * std::pow(s, 1); + }; +}; + +template +void +test_vector_scalar_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Vector + Scalar (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestVectorScalarCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Vector v_dof(0); + const FEValuesExtractors::Scalar s_dof( + Tensor<1, dim>::n_independent_components); + const unsigned int n_AD_components = dim + 1; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + ScalarNumberType s = 3.0; + Tensor<1, dim, ScalarNumberType> v; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 1.0 + i; + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(v, v_dof); + ad_helper.register_independent_variable(s, s_dof); + + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + const ADNumberType s_ad = ad_helper.get_sensitive_variables(s_dof); + + const ADNumberType psi(func_ad::psi(v_ad, s_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "v_ad: " << v_ad << std::endl; + std::cout << "s_ad: " << s_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + s = 2.75; + v *= 1.3; + ad_helper.set_independent_variable(v, v_dof); + ad_helper.set_independent_variable(s, s_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + const ScalarNumberType dpsi_ds = + ad_helper.extract_gradient_component(Dpsi, s_dof); + std::cout << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n"; + + // Verify the result + typedef FunctionsTestVectorScalarCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + Assert(std::abs(psi - func::psi(v, s)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dv - func::dpsi_dv(v, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs(dpsi_ds - func::dpsi_ds(v, s)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_ds_dv = + ad_helper.extract_hessian_component(D2psi, s_dof, v_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_dv_ds = + ad_helper.extract_hessian_component(D2psi, v_dof, s_dof); + const ScalarNumberType d2psi_ds_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, s_dof); + std::cout << "extracted D2psi (v,v): " << d2psi_dv_dv << "\n" + << "extracted D2psi (v,s): " << d2psi_ds_dv << "\n" + << "extracted D2psi (s,v): " << d2psi_dv_ds << "\n" + << "extracted D2psi (s,s): " << d2psi_ds_ds << "\n" + << std::endl; + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dv - func::d2psi_ds_dv(v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_ds - func::d2psi_dv_ds(v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds_ds - func::d2psi_ds_ds(v, s)) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_03.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_03.h new file mode 100644 index 0000000000..a563b785bd --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_03.h @@ -0,0 +1,244 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (tensor + scalar components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestTensorScalarCoupled +{ + static NumberType + psi(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return double_contract(t, t) * std::pow(s, 3); + }; + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return 2.0 * t * std::pow(s, 3); + }; + + static NumberType + dpsi_ds(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return 3.0 * double_contract(t, t) * std::pow(s, 2); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + // Non-symmetric fourth order identity tensor + static const SymmetricTensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + Tensor<4, dim, NumberType> II; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + II[i][j][k][l] = I[i][k] * I[j][l]; + + return 2.0 * II * std::pow(s, 3); + }; + + static Tensor<2, dim, NumberType> + d2psi_ds_dt(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return 6.0 * t * std::pow(s, 2); + }; + + static Tensor<2, dim, NumberType> + d2psi_dt_ds(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return d2psi_ds_dt(t, s); + }; + + static NumberType + d2psi_ds_ds(const Tensor<2, dim, NumberType> &t, const NumberType &s) + { + return 6.0 * double_contract(t, t) * std::pow(s, 1); + }; +}; + +template +void +test_tensor_scalar_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Tensor + Scalar (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensorScalarCoupled func_ad; + + const FEValuesExtractors::Tensor<2> t_dof(0); + const FEValuesExtractors::Scalar s_dof( + Tensor<2, dim>::n_independent_components); + const unsigned int n_AD_components = + Tensor<2, dim>::n_independent_components + 1; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + ScalarNumberType s = 7.5; + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.18 * (i + 0.12); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + ad_helper.register_independent_variable(s, s_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + const ADNumberType s_ad = ad_helper.get_sensitive_variables(s_dof); + + const ADNumberType psi(func_ad::psi(t_ad, s_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "s_ad: " << s_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + s = 1.2; + t *= 1.75; + ad_helper.set_independent_variable(t, t_dof); + ad_helper.set_independent_variable(s, s_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + const ScalarNumberType dpsi_ds = + ad_helper.extract_gradient_component(Dpsi, s_dof); + std::cout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n"; + ; + + // Verify the result + typedef FunctionsTestTensorScalarCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + + Assert(std::abs(psi - func::psi(t, s)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt - func::dpsi_dt(t, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs(dpsi_ds - func::dpsi_ds(t, s)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_ds_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, s_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dt_ds = + ad_helper.extract_hessian_component(D2psi, t_dof, s_dof); + const ScalarNumberType d2psi_ds_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, s_dof); + std::cout << "extracted D2psi (t,t): " << d2psi_dt_dt << "\n" + << "extracted D2psi (t,s): " << d2psi_ds_dt << "\n" + << "extracted D2psi (s,t): " << d2psi_dt_ds << "\n" + << "extracted D2psi (s,s): " << d2psi_ds_ds << "\n" + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dt - func::d2psi_ds_dt(t, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_ds - func::d2psi_dt_ds(t, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds_ds - func::d2psi_ds_ds(t, s)) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_04.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_04.h new file mode 100644 index 0000000000..f69b51f8ea --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_04.h @@ -0,0 +1,260 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (vector + vector components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestVectorVectorCoupled +{ + static NumberType + psi(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + return std::pow(v1 * v1, 2) * std::pow(v2 * v2, 3); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv1(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + return 2.0 * std::pow(v1 * v1, 1) * 2.0 * v1 * std::pow(v2 * v2, 3); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv2(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + return std::pow(v1 * v1, 2) * 3.0 * std::pow(v2 * v2, 2) * 2.0 * v2; + }; + + static Tensor<2, dim, NumberType> + d2psi_dv1_dv1(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return 2.0 * 2.0 * std::pow(v2 * v2, 3) * + (pow(v1 * v1, 0) * 2.0 * outer_product(v1, v1) + + std::pow(v1 * v1, 1) * I); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv2_dv1(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + // Note: This is not symmetric, and this is why we + // don't set the return type for hessian extractor (vector,vector) + // operations as SymmetricTensor. + return (2.0 * std::pow(v1 * v1, 1) * 2.0) * + (3.0 * std::pow(v2 * v2, 2) * 2.0) * outer_product(v1, v2); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv1_dv2(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + return (2.0 * std::pow(v1 * v1, 1) * 2.0) * + (3.0 * std::pow(v2 * v2, 2) * 2.0) * outer_product(v2, v1); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv2_dv2(const Tensor<1, dim, NumberType> &v1, + const Tensor<1, dim, NumberType> &v2) + { + const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + // return std::pow(v1*v1,2)*3.0* + // ( 2.0*std::pow(v2*v2,1)*2.0*outer_product(v2,v2) + + // std::pow(v2*v2,2)*2.0*I); + return std::pow(v1 * v1, 2) * 3.0 * 2.0 * + (2.0 * std::pow(v2 * v2, 1) * 2.0 * outer_product(v2, v2) + + std::pow(v2 * v2, 2) * I); + }; +}; + +template +void +test_vector_vector_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Vector + Vector (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestVectorVectorCoupled func_ad; + + const FEValuesExtractors::Vector v1_dof(0); + const FEValuesExtractors::Vector v2_dof( + Tensor<1, dim>::n_independent_components); + const unsigned int n_AD_components = dim + dim; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<1, dim, ScalarNumberType> v1, v2; + for (unsigned int i = 0; i < dim; ++i) + { + v1[i] = 0.2 * (1.0 + i); + v2[i] = 0.1 * (2.0 + i * 1.7); + } + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(v1, v1_dof); + ad_helper.register_independent_variable(v2, v2_dof); + + const Tensor<1, dim, ADNumberType> v1_ad = + ad_helper.get_sensitive_variables(v1_dof); + const Tensor<1, dim, ADNumberType> v2_ad = + ad_helper.get_sensitive_variables(v2_dof); + + const ADNumberType psi(func_ad::psi(v1_ad, v2_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "v1_ad: " << v1_ad << std::endl; + std::cout << "v2_ad: " << v2_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + v1 *= 1.7; + v2 *= 0.25; + ad_helper.set_independent_variable(v1, v1_dof); + ad_helper.set_independent_variable(v2, v2_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<1, dim, ScalarNumberType> dpsi_dv1 = + ad_helper.extract_gradient_component(Dpsi, v1_dof); + const Tensor<1, dim, ScalarNumberType> dpsi_dv2 = + ad_helper.extract_gradient_component(Dpsi, v2_dof); + std::cout << "extracted Dpsi (v1): " << dpsi_dv1 << "\n" + << "extracted Dpsi (v2): " << dpsi_dv2 << "\n"; + + // Verify the result + typedef FunctionsTestVectorVectorCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + + Assert(std::abs(psi - func::psi(v1, v2)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dv1 - func::dpsi_dv1(v1, v2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dv2 - func::dpsi_dv2(v1, v2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<2, dim, ScalarNumberType> d2psi_dv1_dv1 = + ad_helper.extract_hessian_component(D2psi, v1_dof, v1_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv2_dv1 = + ad_helper.extract_hessian_component(D2psi, v1_dof, v2_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv1_dv2 = + ad_helper.extract_hessian_component(D2psi, v2_dof, v1_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv2_dv2 = + ad_helper.extract_hessian_component(D2psi, v2_dof, v2_dof); + std::cout << "extracted D2psi (v1,v1): " << d2psi_dv1_dv1 << "\n" + << "extracted D2psi (v1,v2): " << d2psi_dv2_dv1 << "\n" + << "extracted D2psi (v2,v1): " << d2psi_dv1_dv2 << "\n" + << "extracted D2psi (v2,v2): " << d2psi_dv2_dv2 << "\n" + << std::endl; + Assert(std::abs((d2psi_dv1_dv1 - func::d2psi_dv1_dv1(v1, v2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv2_dv1 - func::d2psi_dv2_dv1(v1, v2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv1_dv2 - func::d2psi_dv1_dv2(v1, v2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv2_dv2 - func::d2psi_dv2_dv2(v1, v2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_05.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_05.h new file mode 100644 index 0000000000..bb9fdf2e41 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_05.h @@ -0,0 +1,332 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (tensor + vector components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestTensorVectorCoupled +{ + static NumberType + det_t(const Tensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static Tensor<2, dim, NumberType> + ddet_t_dt(const Tensor<2, dim, NumberType> &t) + { + return det_t(t) * transpose(invert(t)); + } + + static Tensor<4, dim, NumberType> + d2det_t_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + Tensor<4, dim, NumberType> dt_inv_trans_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + dt_inv_trans_dt[i][j][k][l] = -t_inv[l][i] * t_inv[j][k]; + + return det_t(t) * outer_product(transpose(t_inv), transpose(t_inv)) + + det_t(t) * dt_inv_trans_dt; + } + + static NumberType + v_squ(const Tensor<1, dim, NumberType> &v) + { + return v * v; + } + + static Tensor<1, dim, NumberType> + dv_squ_dv(const Tensor<1, dim, NumberType> &v) + { + return 2.0 * v; + } + + static Tensor<2, dim, NumberType> + d2v_squ_dv_dv(const Tensor<1, dim, NumberType> &v) + { + static const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return 2.0 * I; + } + + // -------- + + static NumberType + psi(const Tensor<2, dim, NumberType> &t, const Tensor<1, dim, NumberType> &v) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3); + }; + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return 2.0 * std::pow(v_squ(v), 3) * + (pow(det_t(t), 0) * outer_product(ddet_t_dt(t), ddet_t_dt(t)) + + std::pow(det_t(t), 1) * d2det_t_dt_dt(t)); + }; + + static Tensor<3, dim, NumberType> + d2psi_dv_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(ddet_t_dt(t), dv_squ_dv(v)); + }; + + static Tensor<3, dim, NumberType> + d2psi_dt_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(dv_squ_dv(v), ddet_t_dt(t)); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v) + { + return std::pow(det_t(t), 2) * 3.0 * + (2.0 * std::pow(v_squ(v), 1) * + outer_product(dv_squ_dv(v), dv_squ_dv(v)) + + std::pow(v_squ(v), 2) * d2v_squ_dv_dv(v)); + }; +}; + +template +void +test_tensor_vector_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Tensor + Vector (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensorVectorCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Tensor<2> t_dof(0); + const FEValuesExtractors::Vector v_dof( + Tensor<2, dim>::n_independent_components); + const unsigned int n_AD_components = + Tensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.21 * (i + 0.045); + Tensor<1, dim, ScalarNumberType> v; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 0.275 * (1.0 + i); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + ad_helper.register_independent_variable(v, v_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + + const ADNumberType psi(func_ad::psi(t_ad, v_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "v_ad: " << v_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 0.9; + v *= 0.63; + ad_helper.set_independent_variable(t, t_dof); + ad_helper.set_independent_variable(v, v_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + std::cout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n"; + + // Verify the result + typedef FunctionsTestTensorVectorCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t,v): " << func::dpsi_dt(t, v) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt - func::dpsi_dt(t, v)).norm()) + << std::endl; + std::cout << "dpsi_dv: " << dpsi_dv << std::endl; + std::cout << "func::dpsi_dv(t,v): " << func::dpsi_dv(t, v) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dv - func::dpsi_dv(t, v)).norm()) + << std::endl; + Assert(std::abs(psi - func::psi(t, v)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt - func::dpsi_dt(t, v)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dv - func::dpsi_dv(t, v)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dv_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, v_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dt_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, t_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + std::cout << "extracted D2psi (t,t): " << d2psi_dt_dt << "\n" + << "extracted D2psi (t,v): " << d2psi_dv_dt << "\n" + << "extracted D2psi (v,t): " << d2psi_dt_dv << "\n" + << "extracted D2psi (v,v): " << d2psi_dv_dv << "\n" + << std::endl; + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t,v): " << func::d2psi_dt_dt(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dv_dt: " << d2psi_dv_dt << std::endl; + std::cout << "func::d2psi_dv_dt(t,v): " << func::d2psi_dv_dt(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dt_dv: " << d2psi_dt_dv << std::endl; + std::cout << "func::d2psi_dt_dv(t,v): " << func::d2psi_dt_dv(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dv_dv: " << d2psi_dv_dv << std::endl; + std::cout << "func::d2psi_dv_dv(t,v): " << func::d2psi_dv_dv(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v)).norm()) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_06.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_06.h new file mode 100644 index 0000000000..f255145682 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_06.h @@ -0,0 +1,342 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (symmetric tensor + vector components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSymmetricTensorVectorCoupled +{ + static NumberType + det_t(const SymmetricTensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static SymmetricTensor<2, dim, NumberType> + ddet_t_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + return det_t(t) * t_inv; + } + + static SymmetricTensor<4, dim, NumberType> + d2det_t_dt_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + SymmetricTensor<4, dim, NumberType> dt_inv_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + dt_inv_dt[i][j][k][l] = + -0.5 * (t_inv[i][k] * t_inv[j][l] + t_inv[i][l] * t_inv[j][k]); + + return det_t(t) * outer_product(t_inv, t_inv) + det_t(t) * dt_inv_dt; + } + + static NumberType + v_squ(const Tensor<1, dim, NumberType> &v) + { + return v * v; + } + + static Tensor<1, dim, NumberType> + dv_squ_dv(const Tensor<1, dim, NumberType> &v) + { + return 2.0 * v; + } + + static SymmetricTensor<2, dim, NumberType> + d2v_squ_dv_dv(const Tensor<1, dim, NumberType> &v) + { + static const SymmetricTensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return SymmetricTensor<2, dim, NumberType>(2.0 * I); + } + + // ------- + + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3); + }; + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return SymmetricTensor<2, dim, NumberType>( + 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3)); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt_dt(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return SymmetricTensor<4, dim, NumberType>( + 2.0 * std::pow(v_squ(v), 3) * + (pow(det_t(t), 0) * outer_product(ddet_t_dt(t), ddet_t_dt(t)) + + std::pow(det_t(t), 1) * d2det_t_dt_dt(t))); + }; + + static Tensor<3, dim, NumberType> + d2psi_dv_dt(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(static_cast>(ddet_t_dt(t)), + dv_squ_dv(v)); + }; + + static Tensor<3, dim, NumberType> + d2psi_dt_dv(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(dv_squ_dv(v), + static_cast>(ddet_t_dt(t))); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const SymmetricTensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> & v) + { + return std::pow(det_t(t), 2) * 3.0 * + (2.0 * std::pow(v_squ(v), 1) * + outer_product(dv_squ_dv(v), dv_squ_dv(v)) + + std::pow(v_squ(v), 2) * d2v_squ_dv_dv(v)); + }; +}; + +template +void +test_symmetric_tensor_vector_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: SymmetricTensor + Vector (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestSymmetricTensorVectorCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> t_dof(0); + const FEValuesExtractors::Vector v_dof( + SymmetricTensor<2, dim>::n_independent_components); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.165 * (i + 0.1); + Tensor<1, dim, ScalarNumberType> v; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 1.3 + 0.5 * i; + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + ad_helper.register_independent_variable(v, v_dof); + + const SymmetricTensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + + const ADNumberType psi(func_ad::psi(t_ad, v_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "v_ad: " << v_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 0.9; + v *= 0.63; + ad_helper.set_independent_variable(t, t_dof); + ad_helper.set_independent_variable(v, v_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + std::cout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n"; + + // Verify the result + typedef FunctionsTestSymmetricTensorVectorCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t,v): " << func::dpsi_dt(t, v) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt - func::dpsi_dt(t, v)).norm()) + << std::endl; + std::cout << "dpsi_dv: " << dpsi_dv << std::endl; + std::cout << "func::dpsi_dv(t,v): " << func::dpsi_dv(t, v) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dv - func::dpsi_dv(t, v)).norm()) + << std::endl; + Assert(std::abs(psi - func::psi(t, v)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt - func::dpsi_dt(t, v)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dv - func::dpsi_dv(t, v)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dv_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, v_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dt_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, t_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + std::cout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted D2psi (t,t): " << d2psi_dt_dt << "\n" + << "extracted D2psi (t,v): " << d2psi_dv_dt << "\n" + << "extracted D2psi (v,t): " << d2psi_dt_dv << "\n" + << "extracted D2psi (v,v): " << d2psi_dv_dv << "\n" + << std::endl; + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t,v): " << func::d2psi_dt_dt(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dv_dt: " << d2psi_dv_dt << std::endl; + std::cout << "func::d2psi_dv_dt(t,v): " << func::d2psi_dv_dt(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dt_dv: " << d2psi_dt_dv << std::endl; + std::cout << "func::d2psi_dt_dv(t,v): " << func::d2psi_dt_dv(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v)).norm()) + << std::endl; + std::cout << "d2psi_dv_dv: " << d2psi_dv_dv << std::endl; + std::cout << "func::d2psi_dv_dv(t,v): " << func::d2psi_dv_dv(t, v) + << std::endl; + std::cout << "diff: " + << std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v)).norm()) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_07.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_07.h new file mode 100644 index 0000000000..8ce5d57c56 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_07.h @@ -0,0 +1,361 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (symmetric tensor + tensor components) +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSymmetricTensorTensorCoupled +{ + static NumberType + det_t(const SymmetricTensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static SymmetricTensor<2, dim, NumberType> + ddet_t_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + return det_t(t) * t_inv; + } + + static SymmetricTensor<4, dim, NumberType> + d2det_t_dt_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + SymmetricTensor<4, dim, NumberType> dt_inv_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + dt_inv_dt[i][j][k][l] = + -0.5 * (t_inv[i][k] * t_inv[j][l] + t_inv[i][l] * t_inv[j][k]); + + return det_t(t) * outer_product(t_inv, t_inv) + det_t(t) * dt_inv_dt; + } + + // ------- + + static NumberType + det_t(const Tensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static Tensor<2, dim, NumberType> + ddet_t_dt(const Tensor<2, dim, NumberType> &t) + { + return det_t(t) * transpose(invert(t)); + } + + static Tensor<4, dim, NumberType> + d2det_t_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + Tensor<4, dim, NumberType> dt_inv_trans_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + dt_inv_trans_dt[i][j][k][l] = -t_inv[l][i] * t_inv[j][k]; + + return det_t(t) * outer_product(transpose(t_inv), transpose(t_inv)) + + det_t(t) * dt_inv_trans_dt; + } + + // ------- + + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return std::pow(det_t(t1), 2) * std::pow(det_t(t2), 3); + }; + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return SymmetricTensor<2, dim, NumberType>( + 2.0 * std::pow(det_t(t1), 1) * ddet_t_dt(t1) * std::pow(det_t(t2), 3)); + }; + + static Tensor<2, dim, NumberType> + dpsi_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return std::pow(det_t(t1), 2) * 3.0 * std::pow(det_t(t2), 2) * + ddet_t_dt(t2); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt1_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return SymmetricTensor<4, dim, NumberType>( + 2.0 * std::pow(det_t(t2), 3) * + (pow(det_t(t1), 0) * outer_product(ddet_t_dt(t1), ddet_t_dt(t1)) + + std::pow(det_t(t1), 1) * d2det_t_dt_dt(t1))); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt2_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return 2.0 * std::pow(det_t(t1), 1) * 3.0 * std::pow(det_t(t2), 2) * + outer_product(static_cast>(ddet_t_dt(t1)), + ddet_t_dt(t2)); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt1_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return 2.0 * std::pow(det_t(t1), 1) * 3.0 * std::pow(det_t(t2), 2) * + outer_product(ddet_t_dt(t2), + static_cast>( + ddet_t_dt(t1))); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt2_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const Tensor<2, dim, NumberType> & t2) + { + return std::pow(det_t(t1), 2) * 3.0 * + (2.0 * std::pow(det_t(t2), 1) * + outer_product(ddet_t_dt(t2), ddet_t_dt(t2)) + + std::pow(det_t(t2), 2) * d2det_t_dt_dt(t2)); + }; +}; + +template +void +test_symmetric_tensor_tensor_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: SymmetricTensor + Tensor (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestSymmetricTensorTensorCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> t1_dof(0); + const FEValuesExtractors::Tensor<2> t2_dof( + SymmetricTensor<2, dim>::n_independent_components); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components + + Tensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> t1 = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t1.n_independent_components; ++i) + t1[t1.unrolled_to_component_indices(i)] += 0.125 * (i + 0.15); + Tensor<2, dim, ScalarNumberType> t2 = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t2.n_independent_components; ++i) + t2[t2.unrolled_to_component_indices(i)] += 0.24 * (i + 0.05); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t1, t1_dof); + ad_helper.register_independent_variable(t2, t2_dof); + + const SymmetricTensor<2, dim, ADNumberType> t1_ad = + ad_helper.get_sensitive_variables(t1_dof); + const Tensor<2, dim, ADNumberType> t2_ad = + ad_helper.get_sensitive_variables(t2_dof); + + const ADNumberType psi(func_ad::psi(t1_ad, t2_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t1_ad: " << t1_ad << std::endl; + std::cout << "t2_ad: " << t2_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t1 *= 1.17; + t2 *= 0.92; + ad_helper.set_independent_variable(t1, t1_dof); + ad_helper.set_independent_variable(t2, t2_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt1 = + ad_helper.extract_gradient_component(Dpsi, t1_dof); + const Tensor<2, dim, ScalarNumberType> dpsi_dt2 = + ad_helper.extract_gradient_component(Dpsi, t2_dof); + std::cout << "extracted Dpsi (t1): " << dpsi_dt1 << "\n" + << "extracted Dpsi (t2): " << dpsi_dt2 << "\n"; + + // Verify the result + typedef FunctionsTestSymmetricTensorTensorCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "dpsi_dt1: " << dpsi_dt1 << std::endl; + std::cout << "func::dpsi_dt1(t1,t2): " << func::dpsi_dt1(t1, t2) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt1 - func::dpsi_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "dpsi_dt2: " << dpsi_dt2 << std::endl; + std::cout << "func::dpsi_dt2(t1,t2): " << func::dpsi_dt2(t1, t2) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt2 - func::dpsi_dt2(t1, t2)).norm()) + << std::endl; + Assert(std::abs(psi - func::psi(t1, t2)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt1 - func::dpsi_dt1(t1, t2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dt2 - func::dpsi_dt2(t1, t2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt1_dt1 = + ad_helper.extract_hessian_component(D2psi, t1_dof, t1_dof); + const Tensor<4, dim, ScalarNumberType> d2psi_dt2_dt1 = + ad_helper.extract_hessian_component(D2psi, t1_dof, t2_dof); + const Tensor<4, dim, ScalarNumberType> d2psi_dt1_dt2 = + ad_helper.extract_hessian_component(D2psi, t2_dof, t1_dof); + const Tensor<4, dim, ScalarNumberType> d2psi_dt2_dt2 = + ad_helper.extract_hessian_component(D2psi, t2_dof, t2_dof); + std::cout << "extracted D2psi (t1,t1): " << d2psi_dt1_dt1 << "\n" + << "extracted D2psi (t1,t2): " << d2psi_dt2_dt1 << "\n" + << "extracted D2psi (t2,t1): " << d2psi_dt1_dt2 << "\n" + << "extracted D2psi (t2,t2): " << d2psi_dt2_dt2 << "\n" + << std::endl; + std::cout << "d2psi_dt1_dt1: " << d2psi_dt1_dt1 << std::endl; + std::cout << "func::d2psi_dt1_dt1(t1,t2): " << func::d2psi_dt1_dt1(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt1_dt1 - func::d2psi_dt1_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt2_dt1: " << d2psi_dt2_dt1 << std::endl; + std::cout << "func::d2psi_dt2_dt1(t1,t2): " << func::d2psi_dt2_dt1(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt2_dt1 - func::d2psi_dt2_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt1_dt2: " << d2psi_dt1_dt2 << std::endl; + std::cout << "func::d2psi_dt1_dt2(t1,t2): " << func::d2psi_dt1_dt2(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt1_dt2 - func::d2psi_dt1_dt2(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt2_dt2: " << d2psi_dt2_dt2 << std::endl; + std::cout << "func::d2psi_dt2_dt2(t1,t2): " << func::d2psi_dt2_dt2(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt2_dt2 - func::d2psi_dt2_dt2(t1, t2)).norm()) + << std::endl; + Assert(std::abs((d2psi_dt1_dt1 - func::d2psi_dt1_dt1(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt2_dt1 - func::d2psi_dt2_dt1(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt1_dt2 - func::d2psi_dt1_dt2(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt2_dt2 - func::d2psi_dt2_dt2(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_2_components_08.h b/tests/ad_common_tests/helper_scalar_coupled_2_components_08.h new file mode 100644 index 0000000000..971885de84 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_2_components_08.h @@ -0,0 +1,337 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (symmetric tensor + symmetric tensor +// components) using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSymmetricTensorSymmetricTensorCoupled +{ + static NumberType + det_t(const SymmetricTensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static SymmetricTensor<2, dim, NumberType> + ddet_t_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + return det_t(t) * t_inv; + } + + static SymmetricTensor<4, dim, NumberType> + d2det_t_dt_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + SymmetricTensor<4, dim, NumberType> dt_inv_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + dt_inv_dt[i][j][k][l] = + -0.5 * (t_inv[i][k] * t_inv[j][l] + t_inv[i][l] * t_inv[j][k]); + + return det_t(t) * outer_product(t_inv, t_inv) + det_t(t) * dt_inv_dt; + } + + // ------- + + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return std::pow(det_t(t1), 2) * std::pow(det_t(t2), 3); + }; + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<2, dim, NumberType>( + 2.0 * std::pow(det_t(t1), 1) * ddet_t_dt(t1) * std::pow(det_t(t2), 3)); + }; + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<2, dim, NumberType>( + pow(det_t(t1), 2) * 3.0 * std::pow(det_t(t2), 2) * ddet_t_dt(t2)); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt1_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<4, dim, NumberType>( + 2.0 * std::pow(det_t(t2), 3) * + (pow(det_t(t1), 0) * outer_product(ddet_t_dt(t1), ddet_t_dt(t1)) + + std::pow(det_t(t1), 1) * d2det_t_dt_dt(t1))); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt2_dt1(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<4, dim, NumberType>( + 2.0 * std::pow(det_t(t1), 1) * 3.0 * std::pow(det_t(t2), 2) * + outer_product(ddet_t_dt(t1), ddet_t_dt(t2))); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt1_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<4, dim, NumberType>( + 2.0 * std::pow(det_t(t1), 1) * 3.0 * std::pow(det_t(t2), 2) * + outer_product(ddet_t_dt(t2), ddet_t_dt(t1))); + }; + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt2_dt2(const SymmetricTensor<2, dim, NumberType> &t1, + const SymmetricTensor<2, dim, NumberType> &t2) + { + return SymmetricTensor<4, dim, NumberType>( + pow(det_t(t1), 2) * 3.0 * + (2.0 * std::pow(det_t(t2), 1) * + outer_product(ddet_t_dt(t2), ddet_t_dt(t2)) + + std::pow(det_t(t2), 2) * d2det_t_dt_dt(t2))); + }; +}; + +template +void +test_symmetric_tensor_symmetric_tensor_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout + << "*** Test variables: SymmetricTensor + SymmetricTensor (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestSymmetricTensorSymmetricTensorCoupled + func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> t1_dof(0); + const FEValuesExtractors::SymmetricTensor<2> t2_dof( + SymmetricTensor<2, dim>::n_independent_components); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components + + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> t1 = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t1.n_independent_components; ++i) + t1[t1.unrolled_to_component_indices(i)] += 0.135 * (i + 0.195); + SymmetricTensor<2, dim, ScalarNumberType> t2 = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t2.n_independent_components; ++i) + t2[t2.unrolled_to_component_indices(i)] += 0.18 * (i + 0.09); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t1, t1_dof); + ad_helper.register_independent_variable(t2, t2_dof); + + const SymmetricTensor<2, dim, ADNumberType> t1_ad = + ad_helper.get_sensitive_variables(t1_dof); + const SymmetricTensor<2, dim, ADNumberType> t2_ad = + ad_helper.get_sensitive_variables(t2_dof); + + const ADNumberType psi(func_ad::psi(t1_ad, t2_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t1_ad: " << t1_ad << std::endl; + std::cout << "t2_ad: " << t2_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t1 *= 1.17; + t2 *= 0.92; + ad_helper.set_independent_variable(t1, t1_dof); + ad_helper.set_independent_variable(t2, t2_dof); + } + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt1 = + ad_helper.extract_gradient_component(Dpsi, t1_dof); + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt2 = + ad_helper.extract_gradient_component(Dpsi, t2_dof); + std::cout << "extracted Dpsi (t1): " << dpsi_dt1 << "\n" + << "extracted Dpsi (t2): " << dpsi_dt2 << "\n"; + + // Verify the result + typedef FunctionsTestSymmetricTensorSymmetricTensorCoupled + func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "dpsi_dt1: " << dpsi_dt1 << std::endl; + std::cout << "func::dpsi_dt1(t1,t2): " << func::dpsi_dt1(t1, t2) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt1 - func::dpsi_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "dpsi_dt2: " << dpsi_dt2 << std::endl; + std::cout << "func::dpsi_dt2(t1,t2): " << func::dpsi_dt2(t1, t2) << std::endl; + std::cout << "diff: " << std::abs((dpsi_dt2 - func::dpsi_dt2(t1, t2)).norm()) + << std::endl; + Assert(std::abs(psi - func::psi(t1, t2)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt1 - func::dpsi_dt1(t1, t2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dt2 - func::dpsi_dt2(t1, t2)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt1_dt1 = + ad_helper.extract_hessian_component(D2psi, t1_dof, t1_dof); + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt2_dt1 = + ad_helper.extract_hessian_component(D2psi, t1_dof, t2_dof); + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt1_dt2 = + ad_helper.extract_hessian_component(D2psi, t2_dof, t1_dof); + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt2_dt2 = + ad_helper.extract_hessian_component(D2psi, t2_dof, t2_dof); + std::cout << "extracted Dpsi (t1): " << dpsi_dt1 << "\n" + << "extracted Dpsi (t2): " << dpsi_dt2 << "\n" + << "extracted D2psi (t1,t1): " << d2psi_dt1_dt1 << "\n" + << "extracted D2psi (t1,t2): " << d2psi_dt2_dt1 << "\n" + << "extracted D2psi (t2,t1): " << d2psi_dt1_dt2 << "\n" + << "extracted D2psi (t2,t2): " << d2psi_dt2_dt2 << "\n" + << std::endl; + std::cout << "d2psi_dt1_dt1: " << d2psi_dt1_dt1 << std::endl; + std::cout << "func::d2psi_dt1_dt1(t1,t2): " << func::d2psi_dt1_dt1(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt1_dt1 - func::d2psi_dt1_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt2_dt1: " << d2psi_dt2_dt1 << std::endl; + std::cout << "func::d2psi_dt2_dt1(t1,t2): " << func::d2psi_dt2_dt1(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt2_dt1 - func::d2psi_dt2_dt1(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt1_dt2: " << d2psi_dt1_dt2 << std::endl; + std::cout << "func::d2psi_dt1_dt2(t1,t2): " << func::d2psi_dt1_dt2(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt1_dt2 - func::d2psi_dt1_dt2(t1, t2)).norm()) + << std::endl; + std::cout << "d2psi_dt2_dt2: " << d2psi_dt2_dt2 << std::endl; + std::cout << "func::d2psi_dt2_dt2(t1,t2): " << func::d2psi_dt2_dt2(t1, t2) + << std::endl; + std::cout << "diff: " + << std::abs( + (d2psi_dt2_dt2 - func::d2psi_dt2_dt2(t1, t2)).norm()) + << std::endl; + Assert(std::abs((d2psi_dt1_dt1 - func::d2psi_dt1_dt1(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt2_dt1 - func::d2psi_dt2_dt1(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt1_dt2 - func::d2psi_dt1_dt2(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt2_dt2 - func::d2psi_dt2_dt2(t1, t2)).norm()) < + tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_3_components_01.h b/tests/ad_common_tests/helper_scalar_coupled_3_components_01.h new file mode 100644 index 0000000000..b12facfc44 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_3_components_01.h @@ -0,0 +1,421 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (tensor + vector + scalar components) +// using a helper class + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestTensorVectorScalarCoupled +{ + static NumberType + det_t(const Tensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static Tensor<2, dim, NumberType> + ddet_t_dt(const Tensor<2, dim, NumberType> &t) + { + return det_t(t) * transpose(invert(t)); + } + + static Tensor<4, dim, NumberType> + d2det_t_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + Tensor<4, dim, NumberType> dt_inv_trans_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + dt_inv_trans_dt[i][j][k][l] = -t_inv[l][i] * t_inv[j][k]; + + return det_t(t) * outer_product(transpose(t_inv), transpose(t_inv)) + + det_t(t) * dt_inv_trans_dt; + } + + static NumberType + v_squ(const Tensor<1, dim, NumberType> &v) + { + return v * v; + } + + static Tensor<1, dim, NumberType> + dv_squ_dv(const Tensor<1, dim, NumberType> &v) + { + return 2.0 * v; + } + + static Tensor<2, dim, NumberType> + d2v_squ_dv_dv(const Tensor<1, dim, NumberType> &v) + { + static const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return 2.0 * I; + } + + // -------- + + static const double sf; + + static NumberType + psi(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + std::pow(s, sf); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + std::pow(s, sf); + }; + + static NumberType + dpsi_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * sf * + std::pow(s, sf - 1.0); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(v_squ(v), 3) * + (pow(det_t(t), 0) * outer_product(ddet_t_dt(t), ddet_t_dt(t)) + + std::pow(det_t(t), 1) * d2det_t_dt_dt(t)) * + std::pow(s, sf); + }; + + static Tensor<3, dim, NumberType> + d2psi_dv_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(ddet_t_dt(t), dv_squ_dv(v)) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + d2psi_ds_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<3, dim, NumberType> + d2psi_dt_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(dv_squ_dv(v), ddet_t_dt(t)) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * + (2.0 * std::pow(v_squ(v), 1) * + outer_product(dv_squ_dv(v), dv_squ_dv(v)) + + std::pow(v_squ(v), 2) * d2v_squ_dv_dv(v)) * + std::pow(s, sf); + }; + + static Tensor<1, dim, NumberType> + d2psi_ds_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<2, dim, NumberType> + d2psi_dt_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<1, dim, NumberType> + d2psi_dv_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + sf * std::pow(s, sf - 1.0); + }; + + static NumberType + d2psi_ds_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * sf * (sf - 1.0) * + std::pow(s, sf - 2.0); + }; +}; + +template +const double FunctionsTestTensorVectorScalarCoupled::sf = 2.2; + +template +void +test_tensor_vector_scalar_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + const unsigned int this_mpi_process = + Utilities::MPI::this_mpi_process(MPI_COMM_WORLD); + ConditionalOStream pcout(deallog.get_console(), this_mpi_process == 0); + + pcout << "*** Test variables: Tensor + Vector + Scalar (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensorVectorScalarCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Tensor<2> t_dof(0); + const FEValuesExtractors::Vector v_dof( + Tensor<2, dim>::n_independent_components); + const FEValuesExtractors::Scalar s_dof( + Tensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components); + const unsigned int n_AD_components = + Tensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components + 1; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.11 * (i + 0.125); + Tensor<1, dim, ScalarNumberType> v; + ScalarNumberType s = 0.57; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 0.275 * (1.0 + i); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + ad_helper.register_independent_variable(v, v_dof); + ad_helper.register_independent_variable(s, s_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + ADNumberType s_ad = ad_helper.get_sensitive_variables(s_dof); + + const ADNumberType psi(func_ad::psi(t_ad, v_ad, s_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + pcout << "Recorded data..." << std::endl; + pcout << "independent variable values: " << std::flush; + if (this_mpi_process == 0) + ad_helper.print_values(pcout.get_stream()); + pcout << "t_ad: " << t_ad << std::endl; + pcout << "v_ad: " << v_ad << std::endl; + pcout << "s_ad: " << s_ad << std::endl; + pcout << "psi: " << psi << std::endl; + pcout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + pcout << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 0.9; + v *= 0.63; + s *= 1.21; + ad_helper.set_independent_variable(t, t_dof); + ad_helper.set_independent_variable(v, v_dof); + ad_helper.set_independent_variable(s, s_dof); + } + + pcout << "independent variable values: " << std::flush; + if (this_mpi_process == 0) + ad_helper.print_values(pcout.get_stream()); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + pcout << "psi: " << psi << std::endl; + pcout << "Dpsi: \n"; + if (this_mpi_process == 0) + Dpsi.print(pcout.get_stream()); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + pcout << "D2psi: \n"; + if (this_mpi_process == 0) + D2psi.print_formatted(pcout.get_stream(), 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + const Tensor<0, dim, ScalarNumberType> dpsi_ds = + ad_helper.extract_gradient_component(Dpsi, s_dof); + pcout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n"; + + // Verify the result + typedef FunctionsTestTensorVectorScalarCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + Assert(std::abs(psi - func::psi(t, v, s)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt - func::dpsi_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dv - func::dpsi_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs(dpsi_ds - func::dpsi_ds(t, v, s)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dv_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, v_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_ds_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, s_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dt_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, t_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_ds_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, s_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dt_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, t_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_dv_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, v_dof); + const Tensor<0, dim, ScalarNumberType> d2psi_ds_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, s_dof); + pcout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n" + << "extracted D2psi (t,t): " << d2psi_dt_dt << "\n" + << "extracted D2psi (t,v): " << d2psi_dv_dt << "\n" + << "extracted D2psi (t,s): " << d2psi_ds_dt << "\n" + << "extracted D2psi (v,t): " << d2psi_dt_dv << "\n" + << "extracted D2psi (v,v): " << d2psi_dv_dv << "\n" + << "extracted D2psi (v,s): " << d2psi_ds_dv << "\n" + << "extracted D2psi (s,t): " << d2psi_dt_ds << "\n" + << "extracted D2psi (s,v): " << d2psi_dv_ds << "\n" + << "extracted D2psi (s,s): " << d2psi_ds_ds << "\n" + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dt - func::d2psi_ds_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dv - func::d2psi_ds_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_ds - func::d2psi_dt_ds(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_ds - func::d2psi_dv_ds(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds_ds - func::d2psi_ds_ds(t, v, s)) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_coupled_3_components_01_tapeless_only.h b/tests/ad_common_tests/helper_scalar_coupled_3_components_01_tapeless_only.h new file mode 100644 index 0000000000..fe265efa0b --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_coupled_3_components_01_tapeless_only.h @@ -0,0 +1,400 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a coupled system (tensor + vector + scalar components) +// using a helper class. +// This test is based off of helper_scalar_coupled_3_components_01.h, and checks +// that everything still works in tapeless only mode (i.e. when the +// start_recording_operations and stop_recording_operations calls are +// removed). + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestTensorVectorScalarCoupled +{ + static NumberType + det_t(const Tensor<2, dim, NumberType> &t) + { + return determinant(t); + } + + static Tensor<2, dim, NumberType> + ddet_t_dt(const Tensor<2, dim, NumberType> &t) + { + return det_t(t) * transpose(invert(t)); + } + + static Tensor<4, dim, NumberType> + d2det_t_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + Tensor<4, dim, NumberType> dt_inv_trans_dt; + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + dt_inv_trans_dt[i][j][k][l] = -t_inv[l][i] * t_inv[j][k]; + + return det_t(t) * outer_product(transpose(t_inv), transpose(t_inv)) + + det_t(t) * dt_inv_trans_dt; + } + + static NumberType + v_squ(const Tensor<1, dim, NumberType> &v) + { + return v * v; + } + + static Tensor<1, dim, NumberType> + dv_squ_dv(const Tensor<1, dim, NumberType> &v) + { + return 2.0 * v; + } + + static Tensor<2, dim, NumberType> + d2v_squ_dv_dv(const Tensor<1, dim, NumberType> &v) + { + static const Tensor<2, dim, NumberType> I( + unit_symmetric_tensor()); + return 2.0 * I; + } + + // -------- + + static const double sf; + + static NumberType + psi(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + std::pow(s, sf); + }; + + static Tensor<1, dim, NumberType> + dpsi_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + std::pow(s, sf); + }; + + static NumberType + dpsi_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * sf * + std::pow(s, sf - 1.0); + }; + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(v_squ(v), 3) * + (pow(det_t(t), 0) * outer_product(ddet_t_dt(t), ddet_t_dt(t)) + + std::pow(det_t(t), 1) * d2det_t_dt_dt(t)) * + std::pow(s, sf); + }; + + static Tensor<3, dim, NumberType> + d2psi_dv_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(ddet_t_dt(t), dv_squ_dv(v)) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + d2psi_ds_dt(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<3, dim, NumberType> + d2psi_dt_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * 3.0 * std::pow(v_squ(v), 2) * + outer_product(dv_squ_dv(v), ddet_t_dt(t)) * std::pow(s, sf); + }; + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * + (2.0 * std::pow(v_squ(v), 1) * + outer_product(dv_squ_dv(v), dv_squ_dv(v)) + + std::pow(v_squ(v), 2) * d2v_squ_dv_dv(v)) * + std::pow(s, sf); + }; + + static Tensor<1, dim, NumberType> + d2psi_ds_dv(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<2, dim, NumberType> + d2psi_dt_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return 2.0 * std::pow(det_t(t), 1) * ddet_t_dt(t) * std::pow(v_squ(v), 3) * + sf * std::pow(s, sf - 1.0); + }; + + static Tensor<1, dim, NumberType> + d2psi_dv_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * 3.0 * std::pow(v_squ(v), 2) * dv_squ_dv(v) * + sf * std::pow(s, sf - 1.0); + }; + + static NumberType + d2psi_ds_ds(const Tensor<2, dim, NumberType> &t, + const Tensor<1, dim, NumberType> &v, + const NumberType & s) + { + return std::pow(det_t(t), 2) * std::pow(v_squ(v), 3) * sf * (sf - 1.0) * + std::pow(s, sf - 2.0); + }; +}; + +template +const double FunctionsTestTensorVectorScalarCoupled::sf = 2.2; + +template +void +test_tensor_vector_scalar_coupled() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + const unsigned int this_mpi_process = + Utilities::MPI::this_mpi_process(MPI_COMM_WORLD); + ConditionalOStream pcout(deallog.get_console(), this_mpi_process == 0); + + pcout << "*** Test variables: Tensor + Vector + Scalar (coupled), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensorVectorScalarCoupled func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Tensor<2> t_dof(0); + const FEValuesExtractors::Vector v_dof( + Tensor<2, dim>::n_independent_components); + const FEValuesExtractors::Scalar s_dof( + Tensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components); + const unsigned int n_AD_components = + Tensor<2, dim>::n_independent_components + + Tensor<1, dim>::n_independent_components + 1; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.11 * (i + 0.125); + Tensor<1, dim, ScalarNumberType> v; + ScalarNumberType s = 0.57; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 0.275 * (1.0 + i); + + // Perform the differentiation operations + { + ad_helper.register_independent_variable(t, t_dof); + ad_helper.register_independent_variable(v, v_dof); + ad_helper.register_independent_variable(s, s_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + ADNumberType s_ad = ad_helper.get_sensitive_variables(s_dof); + + const ADNumberType psi(func_ad::psi(t_ad, v_ad, s_ad)); + + ad_helper.register_dependent_variable(psi); + + pcout << "Recorded data..." << std::endl; + pcout << "independent variable values: " << std::flush; + if (this_mpi_process == 0) + ad_helper.print_values(pcout.get_stream()); + pcout << "t_ad: " << t_ad << std::endl; + pcout << "v_ad: " << v_ad << std::endl; + pcout << "s_ad: " << s_ad << std::endl; + pcout << "psi: " << psi << std::endl; + pcout << std::endl; + } + + pcout << "independent variable values: " << std::flush; + if (this_mpi_process == 0) + ad_helper.print_values(pcout.get_stream()); + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + pcout << "psi: " << psi << std::endl; + pcout << "Dpsi: \n"; + if (this_mpi_process == 0) + Dpsi.print(pcout.get_stream()); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + pcout << "D2psi: \n"; + if (this_mpi_process == 0) + D2psi.print_formatted(pcout.get_stream(), 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + const Tensor<0, dim, ScalarNumberType> dpsi_ds = + ad_helper.extract_gradient_component(Dpsi, s_dof); + pcout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n"; + + // Verify the result + typedef FunctionsTestTensorVectorScalarCoupled func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + Assert(std::abs(psi - func::psi(t, v, s)) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs((dpsi_dt - func::dpsi_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs((dpsi_dv - func::dpsi_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for first derivative.")); + Assert(std::abs(dpsi_ds - func::dpsi_ds(t, v, s)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dv_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, v_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_ds_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, s_dof); + const Tensor<3, dim, ScalarNumberType> d2psi_dt_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, t_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_ds_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, s_dof); + const Tensor<2, dim, ScalarNumberType> d2psi_dt_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, t_dof); + const Tensor<1, dim, ScalarNumberType> d2psi_dv_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, v_dof); + const Tensor<0, dim, ScalarNumberType> d2psi_ds_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, s_dof); + pcout << "extracted Dpsi (t): " << dpsi_dt << "\n" + << "extracted Dpsi (v): " << dpsi_dv << "\n" + << "extracted Dpsi (s): " << dpsi_ds << "\n" + << "extracted D2psi (t,t): " << d2psi_dt_dt << "\n" + << "extracted D2psi (t,v): " << d2psi_dv_dt << "\n" + << "extracted D2psi (t,s): " << d2psi_ds_dt << "\n" + << "extracted D2psi (v,t): " << d2psi_dt_dv << "\n" + << "extracted D2psi (v,v): " << d2psi_dv_dv << "\n" + << "extracted D2psi (v,s): " << d2psi_ds_dv << "\n" + << "extracted D2psi (s,t): " << d2psi_dt_ds << "\n" + << "extracted D2psi (s,v): " << d2psi_dv_ds << "\n" + << "extracted D2psi (s,s): " << d2psi_ds_ds << "\n" + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dt - func::d2psi_dv_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dt - func::d2psi_ds_dt(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_dv - func::d2psi_dt_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_ds_dv - func::d2psi_ds_dv(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dt_ds - func::d2psi_dt_ds(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs((d2psi_dv_ds - func::d2psi_dv_ds(t, v, s)).norm()) < tol, + ExcMessage("No match for second derivative.")); + Assert(std::abs(d2psi_ds_ds - func::d2psi_ds_ds(t, v, s)) < tol, + ExcMessage("No match for second derivative.")); + } +} diff --git a/tests/ad_common_tests/helper_scalar_single_component_01.h b/tests/ad_common_tests/helper_scalar_single_component_01.h new file mode 100644 index 0000000000..c6e0ab3fe5 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_single_component_01.h @@ -0,0 +1,179 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a single component (scalar) system using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestScalar +{ + static NumberType + psi(const NumberType &s) + { + return 4.0 * std::pow(s, 4); + } + + static NumberType + dpsi_ds(const NumberType &s) + { + return 16.0 * std::pow(s, 3); + } + + static NumberType + d2psi_ds_ds(const NumberType &s) + { + return 48.0 * std::pow(s, 2); + } +}; + +template +void +test_scalar() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Scalar dof, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestScalar func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Scalar s_dof(0); + const unsigned int n_AD_components = 1; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + ScalarNumberType s = 1.2; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(s, s_dof); + + const ADNumberType s_ad = ad_helper.get_sensitive_variables(s_dof); + + const ADNumberType psi(func_ad::psi(s_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s_ad: " << s_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s = 1.5; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variable(s, s_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + ad_helper.compute_hessian(D2psi); + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const ScalarNumberType dpsi_ds = + ad_helper.extract_gradient_component(Dpsi, s_dof); + + // Verify the result + typedef FunctionsTestScalar func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(s): " << func::psi(s) << std::endl; + Assert(std::abs(psi - func::psi(s)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_ds: " << dpsi_ds << std::endl; + std::cout << "func::dpsi_ds(s): " << func::dpsi_ds(s) << std::endl; + Assert(std::abs(dpsi_ds - func::dpsi_ds(s)) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const ScalarNumberType d2psi_ds_ds = + ad_helper.extract_hessian_component(D2psi, s_dof, s_dof); + std::cout << "d2psi_ds_ds: " << d2psi_ds_ds << std::endl; + std::cout << "func::d2psi_ds_ds(s): " << func::d2psi_ds_ds(s) + << std::endl; + Assert(std::abs(d2psi_ds_ds - func::d2psi_ds_ds(s)) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} diff --git a/tests/ad_common_tests/helper_scalar_single_component_02.h b/tests/ad_common_tests/helper_scalar_single_component_02.h new file mode 100644 index 0000000000..e9b4e40f9c --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_single_component_02.h @@ -0,0 +1,190 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a single component (vector) system using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestVector +{ + static NumberType + psi(const Tensor<1, dim, NumberType> &v) + { + // Potential memory corruption in adtl::adouble... + // Probably need to zero some temporary value + // in Tensor::operator* or Tensor::contract() + return 2.0 * (v * v); + // return 2.0*contract(v,v); + // return 2.0*scalar_product(v,v); + } + + static Tensor<1, dim, NumberType> + dpsi_dv(const Tensor<1, dim, NumberType> &v) + { + return 4.0 * v; + } + + static Tensor<2, dim, NumberType> + d2psi_dv_dv(const Tensor<1, dim, NumberType> &v) + { + const SymmetricTensor<2, dim, NumberType> I(unit_symmetric_tensor()); + const Tensor<2, dim, NumberType> I_ns(I); + return 4.0 * I_ns; + } +}; + +template +void +test_vector() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Vector dof, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestVector func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Vector v_dof(0); + const unsigned int n_AD_components = dim; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<1, dim, ScalarNumberType> v; + for (unsigned int i = 0; i < dim; ++i) + v[i] = 1.0 + i; + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(v, v_dof); + + const Tensor<1, dim, ADNumberType> v_ad = + ad_helper.get_sensitive_variables(v_dof); + + const ADNumberType psi = func_ad::psi(v_ad); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "v_ad: " << v_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + v *= 2.2; + ad_helper.set_independent_variable(v, v_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<1, dim, ScalarNumberType> dpsi_dv = + ad_helper.extract_gradient_component(Dpsi, v_dof); + + // Verify the result + typedef FunctionsTestVector func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(v): " << func::psi(v) << std::endl; + Assert(std::abs(psi - func::psi(v)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dv: " << dpsi_dv << std::endl; + std::cout << "func::dpsi_dv(v): " << func::dpsi_dv(v) << std::endl; + Assert(std::abs((dpsi_dv - func::dpsi_dv(v)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<2, dim, ScalarNumberType> d2psi_dv_dv = + ad_helper.extract_hessian_component(D2psi, v_dof, v_dof); + std::cout << "d2psi_dv_dv: " << d2psi_dv_dv << std::endl; + std::cout << "func::d2psi_dv_dv(v): " << func::d2psi_dv_dv(v) + << std::endl; + Assert(std::abs((d2psi_dv_dv - func::d2psi_dv_dv(v)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} diff --git a/tests/ad_common_tests/helper_scalar_single_component_03.h b/tests/ad_common_tests/helper_scalar_single_component_03.h new file mode 100644 index 0000000000..50309e7622 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_single_component_03.h @@ -0,0 +1,201 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a single component (tensor) system using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestTensor +{ + static NumberType + psi(const Tensor<2, dim, NumberType> &t) + { + return 3.0 * determinant(t); + } + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t) + { + // Wriggers2008 p521 + const NumberType det_t = determinant(t); + // std::cout << "determinant(t): " << determinant(t) << std::endl; + // std::cout << "transpose(invert(t)): " << transpose(invert(t)) << + // std::endl; + return 3.0 * (det_t * transpose(invert(t))); + } + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const NumberType det_t = determinant(t); + const Tensor<2, dim, NumberType> t_inv = invert(t); + Tensor<4, dim, NumberType> dt_inv_trans_dt; + + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = 0; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + dt_inv_trans_dt[i][j][k][l] = -t_inv[l][i] * t_inv[j][k]; + + return 3.0 * (det_t * outer_product(transpose(t_inv), transpose(t_inv)) + + det_t * dt_inv_trans_dt); + } +}; + +template +void +test_tensor() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Tensor dof, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensor func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Tensor<2> t_dof(0); + const unsigned int n_AD_components = Tensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.14 * (i + 0.07); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + + const ADNumberType psi(func_ad::psi(t_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 1.15; + ad_helper.set_independent_variable(t, t_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + + // Verify the result + typedef FunctionsTestTensor func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(t): " << func::psi(t) << std::endl; + Assert(std::abs(psi - func::psi(t)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t): " << func::dpsi_dt(t) << std::endl; + Assert(std::abs((dpsi_dt - func::dpsi_dt(t)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t): " << func::d2psi_dt_dt(t) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} diff --git a/tests/ad_common_tests/helper_scalar_single_component_04.h b/tests/ad_common_tests/helper_scalar_single_component_04.h new file mode 100644 index 0000000000..75efb243f6 --- /dev/null +++ b/tests/ad_common_tests/helper_scalar_single_component_04.h @@ -0,0 +1,205 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a single component (symmetric tensor) system using a helper +// class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSymmetricTensor +{ + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &t) + { + return 2.0 * determinant(t); + } + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + // Wriggers2008 p521 + const NumberType det_t = determinant(t); + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + return SymmetricTensor<2, dim, NumberType>(2.0 * (det_t * t_inv)); + } + + static SymmetricTensor<4, dim, NumberType> + d2psi_dt_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const NumberType det_t = determinant(t); + const SymmetricTensor<2, dim, NumberType> t_inv = + symmetrize(invert(static_cast>(t))); + SymmetricTensor<4, dim, NumberType> dt_inv_dt; + + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_determinant_of_a_tensor + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + dt_inv_dt[i][j][k][l] = + -0.5 * (t_inv[i][k] * t_inv[j][l] + t_inv[i][l] * t_inv[j][k]); + + return SymmetricTensor<4, dim, NumberType>( + 2.0 * (det_t * outer_product(t_inv, t_inv) + det_t * dt_inv_dt)); + } +}; + +template +void +test_symmetric_tensor() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: SymmetricTensor dof, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestSymmetricTensor func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> t_dof(0); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + + const SymmetricTensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + + const ADNumberType psi(func_ad::psi(t_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 4.0; + ad_helper.set_independent_variable(t, t_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + + // Verify the result + typedef FunctionsTestSymmetricTensor func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(t): " << func::psi(t) << std::endl; + Assert(std::abs(psi - func::psi(t)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t): " << func::dpsi_dt(t) << std::endl; + Assert(std::abs((dpsi_dt - func::dpsi_dt(t)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t): " << func::d2psi_dt_dt(t) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} diff --git a/tests/ad_common_tests/helper_vector_1_indep_1_dep_vars_01.h b/tests/ad_common_tests/helper_vector_1_indep_1_dep_vars_01.h new file mode 100644 index 0000000000..86939769c8 --- /dev/null +++ b/tests/ad_common_tests/helper_vector_1_indep_1_dep_vars_01.h @@ -0,0 +1,158 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a single component (scalar) system using a helper class +// This is the equivalent of helper_scalar_single_component_01, but for +// the other (vector) helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestScalar +{ + static NumberType + f0(const NumberType &s0) + { + return 4.0 * std::pow(s0, 4); + } + + static NumberType + df0_ds0(const NumberType &s0) + { + return 16.0 * std::pow(s0, 3); + } +}; + +template +void +test_AD_vector_jacobian() +{ + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Variables: 1 independent, 1 dependent, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Function and its derivatives + typedef FunctionsTestScalar func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const unsigned int n_vars_indep = 1; + const unsigned int n_vars_dep = 1; + ADHelper ad_helper(n_vars_indep, n_vars_dep); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + std::vector s(n_vars_indep); + s[0] = 1.2; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variables(s); + + const std::vector s_ad = + ad_helper.get_sensitive_variables(); + + // ADNumberType f0 (s_ad*s_ad*s_ad); + std::vector f_ad(n_vars_dep, ADNumberType(0.0)); + f_ad[0] = func_ad::f0(s_ad[0]); + + ad_helper.register_dependent_variables(f_ad); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s_ad: "; + for (unsigned int i = 0; i < n_vars_indep; ++i) + std::cout << s_ad[i] << (i < (n_vars_indep - 1) ? "," : ""); + std::cout << std::endl; + std::cout << "f_ad: "; + for (unsigned int i = 0; i < n_vars_dep; ++i) + std::cout << f_ad[i] << (i < (n_vars_dep - 1) ? "," : ""); + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s[0] = 1.5; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variables(s); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function values and their jacobian for the new evaluation point + Vector funcs(n_vars_dep); + FullMatrix Dfuncs(n_vars_dep, n_vars_indep); + ad_helper.compute_values(funcs); + ad_helper.compute_jacobian(Dfuncs); + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "funcs: \n"; + funcs.print(std::cout); + std::cout << "Dfuncs: \n"; + Dfuncs.print_formatted(std::cout, 3, true, 0, "0.0"); + + // Verify the result + typedef FunctionsTestScalar func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "funcs[0]: " << funcs[0] + << "\t func::f0(s[0])): " << func::f0(s[0]) << std::endl; + std::cout << "Dfuncs[0][0]: " << Dfuncs[0][0] + << "\t func::df0_ds0(s[0])): " << func::df0_ds0(s[0]) << std::endl; + Assert(std::abs(funcs[0] - func::f0(s[0])) < tol, + ExcMessage("No match for function value.")); + Assert(std::abs(Dfuncs[0][0] - func::df0_ds0(s[0])) < tol, + ExcMessage("No match for first derivative.")); +} diff --git a/tests/ad_common_tests/helper_vector_1_indep_2_dep_vars_01.h b/tests/ad_common_tests/helper_vector_1_indep_2_dep_vars_01.h new file mode 100644 index 0000000000..e7a2ee1594 --- /dev/null +++ b/tests/ad_common_tests/helper_vector_1_indep_2_dep_vars_01.h @@ -0,0 +1,178 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a vector of 2 dependent and 1 independent variables +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSquare +{ + static NumberType + f0(const NumberType &s0) + { + return 2.0 * std::pow(s0, 4); + }; + + static NumberType + df0_ds0(const NumberType &s0) + { + return 8.0 * std::pow(s0, 3); + }; + + static NumberType + f1(const NumberType &s0) + { + return 3.0 * std::pow(s0, 2); + }; + + static NumberType + df1_ds0(const NumberType &s0) + { + return 6.0 * std::pow(s0, 1); + }; +}; + +template +void +test_AD_vector_jacobian() +{ + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Variables: 1 independent, 2 dependent, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Function and its derivatives + typedef FunctionsTestSquare func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const unsigned int n_vars_indep = 1; + const unsigned int n_vars_dep = 2; + ADHelper ad_helper(n_vars_indep, n_vars_dep); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + std::vector s(n_vars_indep); + s[0] = 3.1; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variables(s); + + const std::vector s_ad = + ad_helper.get_sensitive_variables(); + + std::vector f_ad(n_vars_dep, ADNumberType(0.0)); + f_ad[0] = func_ad::f0(s_ad[0]); + f_ad[1] = func_ad::f1(s_ad[0]); + + ad_helper.register_dependent_variables(f_ad); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s_ad: "; + for (unsigned int i = 0; i < n_vars_indep; ++i) + std::cout << s_ad[i] << (i < (n_vars_indep - 1) ? "," : ""); + std::cout << std::endl; + std::cout << "f_ad: "; + for (unsigned int i = 0; i < n_vars_dep; ++i) + std::cout << f_ad[i] << (i < (n_vars_dep - 1) ? "," : ""); + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s[0] = 4.9; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variables(s); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function values and their jacobian for the new evaluation point + Vector funcs(n_vars_dep); + FullMatrix Dfuncs(n_vars_dep, n_vars_indep); + ad_helper.compute_values(funcs); + ad_helper.compute_jacobian(Dfuncs); + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "funcs: \n"; + funcs.print(std::cout); + std::cout << "Dfuncs: \n"; + Dfuncs.print_formatted(std::cout, 3, true, 0, "0.0"); + + // Verify the result + typedef FunctionsTestSquare func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "funcs[0]: " << funcs[0] + << "\t func::f0(s[0])): " << func::f0(s[0]) << std::endl; + std::cout << "funcs[1]: " << funcs[1] + << "\t func::f1(s[0])): " << func::f1(s[0]) << std::endl; + std::cout << "Dfuncs[0][0]: " << Dfuncs[0][0] + << "\t func::df0_ds0(s[0])): " << func::df0_ds0(s[0]) << std::endl; + std::cout << "Dfuncs[1][0]: " << Dfuncs[1][0] + << "\t func::df1_ds0(s[0])): " << func::df1_ds0(s[0]) << std::endl; + Assert(std::abs(funcs[0] - func::f0(s[0])) < tol, + ExcMessage("No match for function 1 value.")); + Assert(std::abs(funcs[1] - func::f1(s[0])) < tol, + ExcMessage("No match for function 2 value.")); + Assert(std::abs(Dfuncs[0][0] - func::df0_ds0(s[0])) < tol, + ExcMessage("No match for function 1 first derivative..")); + Assert(std::abs(Dfuncs[1][0] - func::df1_ds0(s[0])) < tol, + ExcMessage("No match for function 2 first derivative..")); +} diff --git a/tests/ad_common_tests/helper_vector_2_indep_1_dep_vars_01.h b/tests/ad_common_tests/helper_vector_2_indep_1_dep_vars_01.h new file mode 100644 index 0000000000..e140a7e69e --- /dev/null +++ b/tests/ad_common_tests/helper_vector_2_indep_1_dep_vars_01.h @@ -0,0 +1,171 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a vector of 1 dependent and 2 independent variables +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSquare +{ + static NumberType + f0(const NumberType &s0, const NumberType &s1) + { + return 2.0 * std::pow(s0, 4) * std::pow(s1, 3); + }; + + static NumberType + df0_ds0(const NumberType &s0, const NumberType &s1) + { + return 8.0 * std::pow(s0, 3) * std::pow(s1, 3); + }; + + static NumberType + df0_ds1(const NumberType &s0, const NumberType &s1) + { + return 6.0 * std::pow(s0, 4) * std::pow(s1, 2); + }; +}; + +template +void +test_AD_vector_jacobian() +{ + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Variables: 2 independent, 1 dependent, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Function and its derivatives + typedef FunctionsTestSquare func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const unsigned int n_vars_indep = 2; + const unsigned int n_vars_dep = 1; + ADHelper ad_helper(n_vars_indep, n_vars_dep); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + std::vector s(n_vars_indep); + s[0] = 3.1; + s[1] = 5.9; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variables(s); + + const std::vector s_ad = + ad_helper.get_sensitive_variables(); + + std::vector f_ad(n_vars_dep, ADNumberType(0.0)); + f_ad[0] = func_ad::f0(s_ad[0], s_ad[1]); + + ad_helper.register_dependent_variables(f_ad); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s_ad: "; + for (unsigned int i = 0; i < n_vars_indep; ++i) + std::cout << s_ad[i] << (i < (n_vars_indep - 1) ? "," : ""); + std::cout << std::endl; + std::cout << "f_ad: "; + for (unsigned int i = 0; i < n_vars_dep; ++i) + std::cout << f_ad[i] << (i < (n_vars_dep - 1) ? "," : ""); + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s[0] = 4.9; + s[1] = 0.87; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variables(s); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function values and their jacobian for the new evaluation point + Vector funcs(n_vars_dep); + FullMatrix Dfuncs(n_vars_dep, n_vars_indep); + ad_helper.compute_values(funcs); + ad_helper.compute_jacobian(Dfuncs); + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "funcs: \n"; + funcs.print(std::cout); + std::cout << "Dfuncs: \n"; + Dfuncs.print_formatted(std::cout, 3, true, 0, "0.0"); + + // Verify the result + typedef FunctionsTestSquare func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "funcs[0]: " << funcs[0] + << "\t func::f0(s[0],s[1])): " << func::f0(s[0], s[1]) << std::endl; + std::cout << "Dfuncs[0][1]: " << Dfuncs[0][0] + << "\t func::df0_ds0(s[0],s[1])): " << func::df0_ds0(s[0], s[1]) + << std::endl; + std::cout << "Dfuncs[0][1]: " << Dfuncs[0][1] + << "\t func::df0_ds1(s[0],s[1])): " << func::df0_ds1(s[0], s[1]) + << std::endl; + Assert(std::abs(funcs[0] - func::f0(s[0], s[1])) < tol, + ExcMessage("No match for function 1 value.")); + Assert(std::abs(Dfuncs[0][0] - func::df0_ds0(s[0], s[1])) < tol, + ExcMessage("No match for function 1 first derivative..")); + Assert(std::abs(Dfuncs[0][1] - func::df0_ds1(s[0], s[1])) < tol, + ExcMessage("No match for function 1 first derivative..")); +} diff --git a/tests/ad_common_tests/helper_vector_2_indep_2_dep_vars_01.h b/tests/ad_common_tests/helper_vector_2_indep_2_dep_vars_01.h new file mode 100644 index 0000000000..79a4030db5 --- /dev/null +++ b/tests/ad_common_tests/helper_vector_2_indep_2_dep_vars_01.h @@ -0,0 +1,204 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Evaluation of a vector of 2 dependent and 2 independent variables +// using a helper class + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +// Function and its derivatives +template +struct FunctionsTestSquare +{ + static NumberType + f0(const NumberType &s0, const NumberType &s1) + { + return 2.0 * std::pow(s0, 4) * std::pow(s1, 3); + }; + + static NumberType + df0_ds0(const NumberType &s0, const NumberType &s1) + { + return 8.0 * std::pow(s0, 3) * std::pow(s1, 3); + }; + + static NumberType + df0_ds1(const NumberType &s0, const NumberType &s1) + { + return 6.0 * std::pow(s0, 4) * std::pow(s1, 2); + }; + + static NumberType + f1(const NumberType &s0, const NumberType &s1) + { + return 3.0 * std::pow(s0, 2) * std::pow(s1, 4); + }; + + static NumberType + df1_ds0(const NumberType &s0, const NumberType &s1) + { + return 6.0 * std::pow(s0, 1) * std::pow(s1, 4); + }; + + static NumberType + df1_ds1(const NumberType &s0, const NumberType &s1) + { + return 12.0 * std::pow(s0, 2) * std::pow(s1, 3); + }; +}; + +template +void +test_AD_vector_jacobian() +{ + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Test variables: Variables: 2 independent, 2 dependent, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Function and its derivatives + typedef FunctionsTestSquare func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const unsigned int n_vars_indep = 2; + const unsigned int n_vars_dep = 2; + ADHelper ad_helper(n_vars_indep, n_vars_dep); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + std::vector s(n_vars_indep); + s[0] = 3.1; + s[1] = 5.9; + + // Configure tape + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variables(s); + + const std::vector s_ad = + ad_helper.get_sensitive_variables(); + + std::vector f_ad(n_vars_dep, ADNumberType(0.0)); + f_ad[0] = func_ad::f0(s_ad[0], s_ad[1]); + f_ad[1] = func_ad::f1(s_ad[0], s_ad[1]); + + ad_helper.register_dependent_variables(f_ad); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Taped data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "s_ad: "; + for (unsigned int i = 0; i < n_vars_indep; ++i) + std::cout << s_ad[i] << (i < (n_vars_indep - 1) ? "," : ""); + std::cout << std::endl; + std::cout << "f_ad: "; + for (unsigned int i = 0; i < n_vars_dep; ++i) + std::cout << f_ad[i] << (i < (n_vars_dep - 1) ? "," : ""); + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + s[0] = 4.9; + s[1] = 0.87; + ad_helper.activate_recorded_tape(tape_no); + ad_helper.set_independent_variables(s); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function values and their jacobian for the new evaluation point + Vector funcs(n_vars_dep); + FullMatrix Dfuncs(n_vars_dep, n_vars_indep); + ad_helper.compute_values(funcs); + ad_helper.compute_jacobian(Dfuncs); + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "funcs: \n"; + funcs.print(std::cout); + std::cout << "Dfuncs: \n"; + Dfuncs.print_formatted(std::cout, 3, true, 0, "0.0"); + + // Verify the result + typedef FunctionsTestSquare func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "funcs[0]: " << funcs[0] + << "\t func::f0(s[0],s[1])): " << func::f0(s[0], s[1]) << std::endl; + std::cout << "funcs[1]: " << funcs[1] + << "\t func::f1(s[0],s[1])): " << func::f1(s[0], s[1]) << std::endl; + std::cout << "Dfuncs[0][0]: " << Dfuncs[0][0] + << "\t func::df0_ds0(s[0],s[1])): " << func::df0_ds0(s[0], s[1]) + << std::endl; + std::cout << "Dfuncs[0][1]: " << Dfuncs[0][1] + << "\t func::df0_ds1(s[0],s[1])): " << func::df0_ds1(s[0], s[1]) + << std::endl; + std::cout << "Dfuncs[1][0]: " << Dfuncs[1][0] + << "\t func::df1_ds0(s[0],s[1])): " << func::df1_ds0(s[0], s[1]) + << std::endl; + std::cout << "Dfuncs[1][1]: " << Dfuncs[1][1] + << "\t func::df1_ds1(s[0],s[1])): " << func::df1_ds1(s[0], s[1]) + << std::endl; + Assert(std::abs(funcs[0] - func::f0(s[0], s[1])) < tol, + ExcMessage("No match for function 1 value.")); + Assert(std::abs(funcs[1] - func::f1(s[0], s[1])) < tol, + ExcMessage("No match for function 2 value.")); + Assert(std::abs(Dfuncs[0][0] - func::df0_ds0(s[0], s[1])) < tol, + ExcMessage("No match for function 1 first derivative..")); + Assert(std::abs(Dfuncs[0][1] - func::df0_ds1(s[0], s[1])) < tol, + ExcMessage("No match for function 1 first derivative..")); + Assert(std::abs(Dfuncs[1][0] - func::df1_ds0(s[0], s[1])) < tol, + ExcMessage("No match for function 2 first derivative..")); + Assert(std::abs(Dfuncs[1][1] - func::df1_ds1(s[0], s[1])) < tol, + ExcMessage("No match for function 2 first derivative..")); +} diff --git a/tests/ad_common_tests/step-44-helper_scalar_01.h b/tests/ad_common_tests/step-44-helper_scalar_01.h new file mode 100644 index 0000000000..4e80e4372d --- /dev/null +++ b/tests/ad_common_tests/step-44-helper_scalar_01.h @@ -0,0 +1,2278 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + +// Header file: +// This is a modified version of step-44, which tests the implementation of +// QP-level auto-differentiation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../tests.h" +namespace Step44 +{ + using namespace dealii; + namespace AD = dealii::Differentiation::AD; + namespace Parameters + { + struct FESystem + { + unsigned int poly_degree; + unsigned int quad_order; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + FESystem::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Finite element system"); + { + prm.declare_entry("Polynomial degree", + "2", + Patterns::Integer(0), + "Displacement system polynomial order"); + prm.declare_entry("Quadrature order", + "3", + Patterns::Integer(0), + "Gauss quadrature order"); + } + prm.leave_subsection(); + } + void + FESystem::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Finite element system"); + { + poly_degree = prm.get_integer("Polynomial degree"); + quad_order = prm.get_integer("Quadrature order"); + } + prm.leave_subsection(); + } + struct Geometry + { + unsigned int global_refinement; + double scale; + double p_p0; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Geometry::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Geometry"); + { + prm.declare_entry("Global refinement", + "2", + Patterns::Integer(0), + "Global refinement level"); + prm.declare_entry("Grid scale", + "1e-3", + Patterns::Double(0.0), + "Global grid scaling factor"); + prm.declare_entry("Pressure ratio p/p0", + "100", + Patterns::Selection("20|40|60|80|100"), + "Ratio of applied pressure to reference pressure"); + } + prm.leave_subsection(); + } + void + Geometry::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Geometry"); + { + global_refinement = prm.get_integer("Global refinement"); + scale = prm.get_double("Grid scale"); + p_p0 = prm.get_double("Pressure ratio p/p0"); + } + prm.leave_subsection(); + } + struct Materials + { + double nu; + double mu; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Materials::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Material properties"); + { + prm.declare_entry("Poisson's ratio", + "0.4999", + Patterns::Double(-1.0, 0.5), + "Poisson's ratio"); + prm.declare_entry("Shear modulus", + "80.194e6", + Patterns::Double(), + "Shear modulus"); + } + prm.leave_subsection(); + } + void + Materials::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Material properties"); + { + nu = prm.get_double("Poisson's ratio"); + mu = prm.get_double("Shear modulus"); + } + prm.leave_subsection(); + } + struct LinearSolver + { + std::string type_lin; + double tol_lin; + double max_iterations_lin; + bool use_static_condensation; + std::string preconditioner_type; + double preconditioner_relaxation; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + LinearSolver::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Linear solver"); + { + prm.declare_entry("Solver type", + "CG", + Patterns::Selection("CG|Direct"), + "Type of solver used to solve the linear system"); + prm.declare_entry("Residual", + "1e-6", + Patterns::Double(0.0), + "Linear solver residual (scaled by residual norm)"); + prm.declare_entry( + "Max iteration multiplier", + "1", + Patterns::Double(0.0), + "Linear solver iterations (multiples of the system matrix size)"); + prm.declare_entry("Use static condensation", + "true", + Patterns::Bool(), + "Solve the full block system or a reduced problem"); + prm.declare_entry("Preconditioner type", + "ssor", + Patterns::Selection("jacobi|ssor"), + "Type of preconditioner"); + prm.declare_entry("Preconditioner relaxation", + "0.65", + Patterns::Double(0.0), + "Preconditioner relaxation value"); + } + prm.leave_subsection(); + } + void + LinearSolver::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Linear solver"); + { + type_lin = prm.get("Solver type"); + tol_lin = prm.get_double("Residual"); + max_iterations_lin = prm.get_double("Max iteration multiplier"); + use_static_condensation = prm.get_bool("Use static condensation"); + preconditioner_type = prm.get("Preconditioner type"); + preconditioner_relaxation = prm.get_double("Preconditioner relaxation"); + } + prm.leave_subsection(); + } + struct NonlinearSolver + { + unsigned int max_iterations_NR; + double tol_f; + double tol_u; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + NonlinearSolver::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Nonlinear solver"); + { + prm.declare_entry("Max iterations Newton-Raphson", + "10", + Patterns::Integer(0), + "Number of Newton-Raphson iterations allowed"); + prm.declare_entry("Tolerance force", + "1.0e-9", + Patterns::Double(0.0), + "Force residual tolerance"); + prm.declare_entry("Tolerance displacement", + "1.0e-6", + Patterns::Double(0.0), + "Displacement error tolerance"); + } + prm.leave_subsection(); + } + void + NonlinearSolver::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Nonlinear solver"); + { + max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson"); + tol_f = prm.get_double("Tolerance force"); + tol_u = prm.get_double("Tolerance displacement"); + } + prm.leave_subsection(); + } + struct Time + { + double delta_t; + double end_time; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Time::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Time"); + { + prm.declare_entry("End time", "1", Patterns::Double(), "End time"); + prm.declare_entry("Time step size", + "0.1", + Patterns::Double(), + "Time step size"); + } + prm.leave_subsection(); + } + void + Time::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Time"); + { + end_time = prm.get_double("End time"); + delta_t = prm.get_double("Time step size"); + } + prm.leave_subsection(); + } + struct AllParameters : public FESystem, + public Geometry, + public Materials, + public LinearSolver, + public NonlinearSolver, + public Time + { + AllParameters(const std::string &input_file); + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + AllParameters::AllParameters(const std::string &input_file) + { + ParameterHandler prm; + declare_parameters(prm); + prm.parse_input(input_file); + parse_parameters(prm); + } + void + AllParameters::declare_parameters(ParameterHandler &prm) + { + FESystem::declare_parameters(prm); + Geometry::declare_parameters(prm); + Materials::declare_parameters(prm); + LinearSolver::declare_parameters(prm); + NonlinearSolver::declare_parameters(prm); + Time::declare_parameters(prm); + } + void + AllParameters::parse_parameters(ParameterHandler &prm) + { + FESystem::parse_parameters(prm); + Geometry::parse_parameters(prm); + Materials::parse_parameters(prm); + LinearSolver::parse_parameters(prm); + NonlinearSolver::parse_parameters(prm); + Time::parse_parameters(prm); + } + } // namespace Parameters + template + class StandardTensors + { + public: + static const SymmetricTensor<2, dim> I; + static const SymmetricTensor<4, dim> IxI; + static const SymmetricTensor<4, dim> II; + static const SymmetricTensor<4, dim> dev_P; + }; + template + const SymmetricTensor<2, dim> + StandardTensors::I = unit_symmetric_tensor(); + template + const SymmetricTensor<4, dim> StandardTensors::IxI = outer_product(I, I); + template + const SymmetricTensor<4, dim> + StandardTensors::II = identity_tensor(); + template + const SymmetricTensor<4, dim> + StandardTensors::dev_P = deviator_tensor(); + class Time + { + public: + Time(const double time_end, const double delta_t) + : timestep(0) + , time_current(0.0) + , time_end(time_end) + , delta_t(delta_t) + {} + virtual ~Time() + {} + double + current() const + { + return time_current; + } + double + end() const + { + return time_end; + } + double + get_delta_t() const + { + return delta_t; + } + unsigned int + get_timestep() const + { + return timestep; + } + void + increment() + { + time_current += delta_t; + ++timestep; + } + + private: + unsigned int timestep; + double time_current; + const double time_end; + const double delta_t; + }; + template + class Material_Compressible_Neo_Hook_Three_Field + { + public: + Material_Compressible_Neo_Hook_Three_Field(const double mu, const double nu) + : kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))) + , c_1(mu / 2.0) + , det_F(1.0) + , p_tilde(0.0) + , J_tilde(1.0) + , b_bar(StandardTensors::I) + { + Assert(kappa > 0, ExcInternalError()); + } + ~Material_Compressible_Neo_Hook_Three_Field() + {} + void + update_material_data(const Tensor<2, dim> &F, + const double p_tilde_in, + const double J_tilde_in) + { + det_F = determinant(F); + b_bar = std::pow(det_F, -2.0 / dim) * symmetrize(F * transpose(F)); + p_tilde = p_tilde_in; + J_tilde = J_tilde_in; + Assert(det_F > 0, ExcInternalError()); + } + template + NumberType + get_Psi_iso(const SymmetricTensor<2, dim, NumberType> &C_bar) + { + return c_1 * (trace(C_bar) - dim); + } + template + NumberType + get_Psi_vol(const NumberType &J_tilde) + { + return (kappa / 4.0) * (J_tilde * J_tilde - 1.0 - 2.0 * log(J_tilde)); + } + + // === OLD FUNCTIONS REMAIN FOR TESTING === + + SymmetricTensor<2, dim> + get_tau() + { + return get_tau_iso() + get_tau_vol(); + } + SymmetricTensor<4, dim> + get_Jc() const + { + return get_Jc_vol() + get_Jc_iso(); + } + double + get_dPsi_vol_dJ() const + { + return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde); + } + double + get_d2Psi_vol_dJ2() const + { + return ((kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde))); + } + double + get_det_F() const + { + return det_F; + } + double + get_p_tilde() const + { + return p_tilde; + } + double + get_J_tilde() const + { + return J_tilde; + } + + protected: + const double kappa; + const double c_1; + double det_F; + double p_tilde; + double J_tilde; + SymmetricTensor<2, dim> b_bar; + SymmetricTensor<2, dim> + get_tau_vol() const + { + return p_tilde * det_F * StandardTensors::I; + } + SymmetricTensor<2, dim> + get_tau_iso() const + { + return StandardTensors::dev_P * get_tau_bar(); + } + SymmetricTensor<2, dim> + get_tau_bar() const + { + return 2.0 * c_1 * b_bar; + } + SymmetricTensor<4, dim> + get_Jc_vol() const + { + return p_tilde * det_F * + (StandardTensors::IxI - (2.0 * StandardTensors::II)); + } + SymmetricTensor<4, dim> + get_Jc_iso() const + { + const SymmetricTensor<2, dim> tau_bar = get_tau_bar(); + const SymmetricTensor<2, dim> tau_iso = get_tau_iso(); + const SymmetricTensor<4, dim> tau_iso_x_I = + outer_product(tau_iso, StandardTensors::I); + const SymmetricTensor<4, dim> I_x_tau_iso = + outer_product(StandardTensors::I, tau_iso); + const SymmetricTensor<4, dim> c_bar = get_c_bar(); + return (2.0 / dim) * trace(tau_bar) * StandardTensors::dev_P - + (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso) + + StandardTensors::dev_P * c_bar * StandardTensors::dev_P; + } + SymmetricTensor<4, dim> + get_c_bar() const + { + return SymmetricTensor<4, dim>(); + } + }; + template + class PointHistory + { + public: + PointHistory() + : F_inv(StandardTensors::I) + , tau(SymmetricTensor<2, dim>()) + , d2Psi_vol_dJ2(0.0) + , dPsi_vol_dJ(0.0) + , Jc(SymmetricTensor<4, dim>()) + {} + virtual ~PointHistory() + {} + void + setup_lqp(const Parameters::AllParameters ¶meters) + { + material.reset( + new Material_Compressible_Neo_Hook_Three_Field(parameters.mu, + parameters.nu)); + update_values(Tensor<2, dim>(), 0.0, 1.0); + } + void + update_values(const Tensor<2, dim> &Grad_u_n, + const double p_tilde, + const double J_tilde) + { + const Tensor<2, dim> F = + (Tensor<2, dim>(StandardTensors::I) + Grad_u_n); + material->update_material_data(F, p_tilde, J_tilde); + F_inv = invert(F); + + // Step 1: Update stress and material tangent + { + const FEValuesExtractors::SymmetricTensor<2> C_dofs(0); + const FEValuesExtractors::Scalar p_dofs( + dealii::SymmetricTensor<2, dim>::n_independent_components); + const FEValuesExtractors::Scalar J_dofs( + dealii::SymmetricTensor<2, dim>::n_independent_components + 1); + const unsigned int n_independent_variables = + SymmetricTensor<2, dim>::n_independent_components + 1 + 1; + + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + ADHelper ad_helper(n_independent_variables); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no, // material_id + true, // overwrite_tape + true); // keep + + const SymmetricTensor<2, dim> C = symmetrize(transpose(F) * F); + + if (is_recording == true) + { + ad_helper.register_independent_variable(C, C_dofs); + ad_helper.register_independent_variable(p_tilde, p_dofs); + ad_helper.register_independent_variable(J_tilde, J_dofs); + + const SymmetricTensor<2, dim, ADNumberType> C_AD = + ad_helper.get_sensitive_variables(C_dofs); + const ADNumberType p_tilde_AD = + ad_helper.get_sensitive_variables(p_dofs); + const ADNumberType J_tilde_AD = + ad_helper.get_sensitive_variables(J_dofs); + + const ADNumberType det_F_AD = sqrt(determinant(C_AD)); + SymmetricTensor<2, dim, ADNumberType> C_bar_AD(C_AD); + C_bar_AD *= std::pow(det_F_AD, -2.0 / dim); + + ADNumberType psi_CpJ = material->get_Psi_iso(C_bar_AD); + psi_CpJ += p_tilde_AD * (det_F_AD - J_tilde_AD); + + ad_helper.register_dependent_variable(psi_CpJ); + ad_helper.stop_recording_operations(false); // write_tapes_to_file + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // ad_helper.activate_recorded_tape(tape_no); + // ad_helper.set_independent_variable(C, C_dofs); + // ad_helper.set_independent_variable(p_tilde, p_dofs); + // ad_helper.set_independent_variable(J_tilde, J_dofs); + + const double psi = ad_helper.compute_value(); + Vector Dpsi(n_independent_variables); + ad_helper.compute_gradient(Dpsi); + FullMatrix D2psi(n_independent_variables, + n_independent_variables); + ad_helper.compute_hessian(D2psi); + + const SymmetricTensor<2, dim> S = + 2.0 * ad_helper.extract_gradient_component(Dpsi, C_dofs); + const SymmetricTensor<4, dim> H = + 4.0 * ad_helper.extract_hessian_component(D2psi, C_dofs, C_dofs); + + tau = 0.0; + Jc = 0.0; + // Naive push forwards: Super slow! + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + { + for (unsigned int I = 0; I < dim; ++I) + for (unsigned int J = 0; J < dim; ++J) + tau[i][j] += F[i][I] * S[I][J] * F[j][J]; + + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + for (unsigned int I = 0; I < dim; ++I) + for (unsigned int J = 0; J < dim; ++J) + for (unsigned int K = 0; K < dim; ++K) + for (unsigned int L = 0; L < dim; ++L) + Jc[i][j][k][l] += F[i][I] * F[j][J] * H[I][J][K][L] * + F[k][K] * F[l][L]; + } + } + + // Step 2: Update volumetric penalty terms + { + const FEValuesExtractors::Scalar J_dofs(0); + const unsigned int n_independent_variables = 1; + + typedef typename AD::ScalarFunction::ad_type + ADNumberType; + AD::ScalarFunction ad_helper( + n_independent_variables); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no, // material_id + true, // overwrite_tape + true); // keep + if (is_recording == true) + { + ad_helper.register_independent_variable(J_tilde, J_dofs); + + const ADNumberType J_tilde_AD = + ad_helper.get_sensitive_variables(J_dofs); + + ADNumberType psi_vol = material->get_Psi_vol(J_tilde_AD); + + ad_helper.register_dependent_variable(psi_vol); + ad_helper.stop_recording_operations(false); // write_tapes_to_file + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // ad_helper.activate_recorded_tape(tape_no); + // ad_helper.set_independent_variable(J_tilde, J_dofs); + + const double psi = ad_helper.compute_value(); + Vector Dpsi(n_independent_variables); + FullMatrix D2psi(n_independent_variables, + n_independent_variables); + ad_helper.compute_gradient(Dpsi); + ad_helper.compute_hessian(D2psi); + + dPsi_vol_dJ = ad_helper.extract_gradient_component(Dpsi, J_dofs); + d2Psi_vol_dJ2 = + ad_helper.extract_hessian_component(D2psi, J_dofs, J_dofs); + } + + static const double tol = + 1e-3; // Minor computation error due to order of operations + Assert((tau - material->get_tau()).norm() < tol, + ExcMessage("AD computed stress is incorrect.")); + Assert((Jc - material->get_Jc()).norm() < tol, + ExcMessage("AD computed tangent is incorrect.")); + Assert(std::abs(dPsi_vol_dJ - material->get_dPsi_vol_dJ()) < tol, + ExcMessage("AD computed dPsi_vol_dJ is incorrect.")); + Assert(std::abs(d2Psi_vol_dJ2 - material->get_d2Psi_vol_dJ2()) < tol, + ExcMessage("AD computed d2Psi_vol_dJ2 is incorrect.")); + } + double + get_J_tilde() const + { + return material->get_J_tilde(); + } + double + get_det_F() const + { + return material->get_det_F(); + } + const Tensor<2, dim> & + get_F_inv() const + { + return F_inv; + } + double + get_p_tilde() const + { + return material->get_p_tilde(); + } + const SymmetricTensor<2, dim> & + get_tau() const + { + return tau; + } + double + get_dPsi_vol_dJ() const + { + return dPsi_vol_dJ; + } + double + get_d2Psi_vol_dJ2() const + { + return d2Psi_vol_dJ2; + } + const SymmetricTensor<4, dim> & + get_Jc() const + { + return Jc; + } + + private: + std::shared_ptr> material; + Tensor<2, dim> F_inv; + SymmetricTensor<2, dim> tau; + double d2Psi_vol_dJ2; + double dPsi_vol_dJ; + SymmetricTensor<4, dim> Jc; + }; + template + class Solid + { + public: + Solid(const std::string &input_file); + virtual ~Solid(); + void + run(); + + private: + struct PerTaskData_K; + struct ScratchData_K; + struct PerTaskData_RHS; + struct ScratchData_RHS; + struct PerTaskData_SC; + struct ScratchData_SC; + struct PerTaskData_UQPH; + struct ScratchData_UQPH; + void + make_grid(); + void + system_setup(); + void + determine_component_extractors(); + void + assemble_system_tangent(); + void + assemble_system_tangent_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_K & scratch, + PerTaskData_K & data) const; + void + copy_local_to_global_K(const PerTaskData_K &data); + void + assemble_system_rhs(); + void + assemble_system_rhs_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_RHS & scratch, + PerTaskData_RHS & data) const; + void + copy_local_to_global_rhs(const PerTaskData_RHS &data); + void + assemble_sc(); + void + assemble_sc_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC & scratch, + PerTaskData_SC & data); + void + copy_local_to_global_sc(const PerTaskData_SC &data); + void + make_constraints(const int &it_nr); + void + setup_qph(); + void + update_qph_incremental(const BlockVector &solution_delta); + void + update_qph_incremental_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_UQPH & scratch, + PerTaskData_UQPH & data); + void + copy_local_to_global_UQPH(const PerTaskData_UQPH & /*data*/) + {} + void + solve_nonlinear_timestep(BlockVector &solution_delta); + std::pair + solve_linear_system(BlockVector &newton_update); + BlockVector + get_total_solution(const BlockVector &solution_delta) const; + void + output_results() const; + Parameters::AllParameters parameters; + double vol_reference; + Triangulation triangulation; + Time time; + mutable TimerOutput timer; + CellDataStorage::cell_iterator, + PointHistory> + quadrature_point_history; + const unsigned int degree; + const FESystem fe; + DoFHandler dof_handler_ref; + const unsigned int dofs_per_cell; + const FEValuesExtractors::Vector u_fe; + const FEValuesExtractors::Scalar p_fe; + const FEValuesExtractors::Scalar J_fe; + static const unsigned int n_blocks = 3; + static const unsigned int n_components = dim + 2; + static const unsigned int first_u_component = 0; + static const unsigned int p_component = dim; + static const unsigned int J_component = dim + 1; + enum + { + u_dof = 0, + p_dof = 1, + J_dof = 2 + }; + std::vector dofs_per_block; + std::vector element_indices_u; + std::vector element_indices_p; + std::vector element_indices_J; + const QGauss qf_cell; + const QGauss qf_face; + const unsigned int n_q_points; + const unsigned int n_q_points_f; + ConstraintMatrix constraints; + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix tangent_matrix; + BlockVector system_rhs; + BlockVector solution_n; + struct Errors + { + Errors() + : norm(1.0) + , u(1.0) + , p(1.0) + , J(1.0) + {} + void + reset() + { + norm = 1.0; + u = 1.0; + p = 1.0; + J = 1.0; + } + void + normalise(const Errors &rhs) + { + if (rhs.norm != 0.0) + norm /= rhs.norm; + if (rhs.u != 0.0) + u /= rhs.u; + if (rhs.p != 0.0) + p /= rhs.p; + if (rhs.J != 0.0) + J /= rhs.J; + } + double norm, u, p, J; + }; + Errors error_residual, error_residual_0, error_residual_norm, error_update, + error_update_0, error_update_norm; + void + get_error_residual(Errors &error_residual); + void + get_error_update(const BlockVector &newton_update, + Errors & error_update); + std::pair + get_error_dilation() const; + double + compute_vol_current() const; + static void + print_conv_header(); + void + print_conv_footer(); + }; + template + Solid::Solid(const std::string &input_file) + : parameters(input_file) + , triangulation(Triangulation::maximum_smoothing) + , time(parameters.end_time, parameters.delta_t) + , timer(std::cout, TimerOutput::never, TimerOutput::wall_times) + , degree(parameters.poly_degree) + , fe(FE_Q(parameters.poly_degree), + dim, // displacement + FE_DGPMonomial(parameters.poly_degree - 1), + 1, // pressure + FE_DGPMonomial(parameters.poly_degree - 1), + 1) + , // dilatation + dof_handler_ref(triangulation) + , dofs_per_cell(fe.dofs_per_cell) + , u_fe(first_u_component) + , p_fe(p_component) + , J_fe(J_component) + , dofs_per_block(n_blocks) + , qf_cell(parameters.quad_order) + , qf_face(parameters.quad_order) + , n_q_points(qf_cell.size()) + , n_q_points_f(qf_face.size()) + { + Assert(dim == 2 || dim == 3, + ExcMessage("This problem only works in 2 or 3 space dimensions.")); + determine_component_extractors(); + } + template + Solid::~Solid() + { + dof_handler_ref.clear(); + } + template + void + Solid::run() + { + make_grid(); + system_setup(); + { + ConstraintMatrix constraints; + constraints.close(); + const ComponentSelectFunction J_mask(J_component, n_components); + VectorTools::project(dof_handler_ref, + constraints, + QGauss(degree + 2), + J_mask, + solution_n); + } + // output_results(); + time.increment(); + BlockVector solution_delta(dofs_per_block); + while (time.current() < time.end()) + { + solution_delta = 0.0; + solve_nonlinear_timestep(solution_delta); + solution_n += solution_delta; + // output_results(); + // Output displacement at centre of traction surface + { + const Point soln_pt( + dim == 3 ? Point(0.0, 1.0, 0.0) * parameters.scale : + Point(0.0, 1.0) * parameters.scale); + typename DoFHandler::active_cell_iterator + cell = dof_handler_ref.begin_active(), + endc = dof_handler_ref.end(); + for (; cell != endc; ++cell) + for (unsigned int v = 0; v < GeometryInfo::vertices_per_cell; + ++v) + if (cell->vertex(v).distance(soln_pt) < 1e-6 * parameters.scale) + { + Tensor<1, dim> soln; + for (unsigned int d = 0; d < dim; ++d) + soln[d] = solution_n(cell->vertex_dof_index(v, u_dof + d)); + deallog << "Timestep " << time.get_timestep() << ": " << soln + << std::endl; + } + } + time.increment(); + } + } + template + struct Solid::PerTaskData_K + { + FullMatrix cell_matrix; + std::vector local_dof_indices; + PerTaskData_K(const unsigned int dofs_per_cell) + : cell_matrix(dofs_per_cell, dofs_per_cell) + , local_dof_indices(dofs_per_cell) + {} + void + reset() + { + cell_matrix = 0.0; + } + }; + template + struct Solid::ScratchData_K + { + FEValues fe_values_ref; + std::vector> Nx; + std::vector>> grad_Nx; + std::vector>> symm_grad_Nx; + ScratchData_K(const FiniteElement &fe_cell, + const QGauss & qf_cell, + const UpdateFlags uf_cell) + : fe_values_ref(fe_cell, qf_cell, uf_cell) + , Nx(qf_cell.size(), std::vector(fe_cell.dofs_per_cell)) + , grad_Nx(qf_cell.size(), + std::vector>(fe_cell.dofs_per_cell)) + , symm_grad_Nx(qf_cell.size(), + std::vector>( + fe_cell.dofs_per_cell)) + {} + ScratchData_K(const ScratchData_K &rhs) + : fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()) + , Nx(rhs.Nx) + , grad_Nx(rhs.grad_Nx) + , symm_grad_Nx(rhs.symm_grad_Nx) + {} + void + reset() + { + const unsigned int n_q_points = Nx.size(); + const unsigned int n_dofs_per_cell = Nx[0].size(); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + Assert(Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); + Assert(grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + Assert(symm_grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + for (unsigned int k = 0; k < n_dofs_per_cell; ++k) + { + Nx[q_point][k] = 0.0; + grad_Nx[q_point][k] = 0.0; + symm_grad_Nx[q_point][k] = 0.0; + } + } + } + }; + template + struct Solid::PerTaskData_RHS + { + Vector cell_rhs; + std::vector local_dof_indices; + PerTaskData_RHS(const unsigned int dofs_per_cell) + : cell_rhs(dofs_per_cell) + , local_dof_indices(dofs_per_cell) + {} + void + reset() + { + cell_rhs = 0.0; + } + }; + template + struct Solid::ScratchData_RHS + { + FEValues fe_values_ref; + FEFaceValues fe_face_values_ref; + std::vector> Nx; + std::vector>> symm_grad_Nx; + ScratchData_RHS(const FiniteElement &fe_cell, + const QGauss & qf_cell, + const UpdateFlags uf_cell, + const QGauss & qf_face, + const UpdateFlags uf_face) + : fe_values_ref(fe_cell, qf_cell, uf_cell) + , fe_face_values_ref(fe_cell, qf_face, uf_face) + , Nx(qf_cell.size(), std::vector(fe_cell.dofs_per_cell)) + , symm_grad_Nx(qf_cell.size(), + std::vector>( + fe_cell.dofs_per_cell)) + {} + ScratchData_RHS(const ScratchData_RHS &rhs) + : fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()) + , fe_face_values_ref(rhs.fe_face_values_ref.get_fe(), + rhs.fe_face_values_ref.get_quadrature(), + rhs.fe_face_values_ref.get_update_flags()) + , Nx(rhs.Nx) + , symm_grad_Nx(rhs.symm_grad_Nx) + {} + void + reset() + { + const unsigned int n_q_points = Nx.size(); + const unsigned int n_dofs_per_cell = Nx[0].size(); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + Assert(Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); + Assert(symm_grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + for (unsigned int k = 0; k < n_dofs_per_cell; ++k) + { + Nx[q_point][k] = 0.0; + symm_grad_Nx[q_point][k] = 0.0; + } + } + } + }; + template + struct Solid::PerTaskData_SC + { + FullMatrix cell_matrix; + std::vector local_dof_indices; + FullMatrix k_orig; + FullMatrix k_pu; + FullMatrix k_pJ; + FullMatrix k_JJ; + FullMatrix k_pJ_inv; + FullMatrix k_bbar; + FullMatrix A; + FullMatrix B; + FullMatrix C; + PerTaskData_SC(const unsigned int dofs_per_cell, + const unsigned int n_u, + const unsigned int n_p, + const unsigned int n_J) + : cell_matrix(dofs_per_cell, dofs_per_cell) + , local_dof_indices(dofs_per_cell) + , k_orig(dofs_per_cell, dofs_per_cell) + , k_pu(n_p, n_u) + , k_pJ(n_p, n_J) + , k_JJ(n_J, n_J) + , k_pJ_inv(n_p, n_J) + , k_bbar(n_u, n_u) + , A(n_J, n_u) + , B(n_J, n_u) + , C(n_p, n_u) + {} + void + reset() + {} + }; + template + struct Solid::ScratchData_SC + { + void + reset() + {} + }; + template + struct Solid::PerTaskData_UQPH + { + void + reset() + {} + }; + template + struct Solid::ScratchData_UQPH + { + const BlockVector & solution_total; + std::vector> solution_grads_u_total; + std::vector solution_values_p_total; + std::vector solution_values_J_total; + FEValues fe_values_ref; + ScratchData_UQPH(const FiniteElement & fe_cell, + const QGauss & qf_cell, + const UpdateFlags uf_cell, + const BlockVector &solution_total) + : solution_total(solution_total) + , solution_grads_u_total(qf_cell.size()) + , solution_values_p_total(qf_cell.size()) + , solution_values_J_total(qf_cell.size()) + , fe_values_ref(fe_cell, qf_cell, uf_cell) + {} + ScratchData_UQPH(const ScratchData_UQPH &rhs) + : solution_total(rhs.solution_total) + , solution_grads_u_total(rhs.solution_grads_u_total) + , solution_values_p_total(rhs.solution_values_p_total) + , solution_values_J_total(rhs.solution_values_J_total) + , fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()) + {} + void + reset() + { + const unsigned int n_q_points = solution_grads_u_total.size(); + for (unsigned int q = 0; q < n_q_points; ++q) + { + solution_grads_u_total[q] = 0.0; + solution_values_p_total[q] = 0.0; + solution_values_J_total[q] = 0.0; + } + } + }; + template + void + Solid::make_grid() + { + GridGenerator::hyper_rectangle( + triangulation, + (dim == 3 ? Point(0.0, 0.0, 0.0) : Point(0.0, 0.0)), + (dim == 3 ? Point(1.0, 1.0, 1.0) : Point(1.0, 1.0)), + true); + GridTools::scale(parameters.scale, triangulation); + triangulation.refine_global(std::max(1U, parameters.global_refinement)); + vol_reference = GridTools::volume(triangulation); + std::cout << "Grid:\n\t Reference volume: " << vol_reference << std::endl; + typename Triangulation::active_cell_iterator cell = triangulation + .begin_active(), + endc = + triangulation.end(); + for (; cell != endc; ++cell) + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; + ++face) + { + if (cell->face(face)->at_boundary() == true && + cell->face(face)->center()[1] == 1.0 * parameters.scale) + { + if (dim == 3) + { + if (cell->face(face)->center()[0] < 0.5 * parameters.scale && + cell->face(face)->center()[2] < 0.5 * parameters.scale) + cell->face(face)->set_boundary_id(6); + } + else + { + if (cell->face(face)->center()[0] < 0.5 * parameters.scale) + cell->face(face)->set_boundary_id(6); + } + } + } + } + template + void + Solid::system_setup() + { + timer.enter_subsection("Setup system"); + std::vector block_component(n_components, + u_dof); // Displacement + block_component[p_component] = p_dof; // Pressure + block_component[J_component] = J_dof; // Dilatation + dof_handler_ref.distribute_dofs(fe); + DoFRenumbering::Cuthill_McKee(dof_handler_ref); + DoFRenumbering::component_wise(dof_handler_ref, block_component); + DoFTools::count_dofs_per_block(dof_handler_ref, + dofs_per_block, + block_component); + std::cout << "Triangulation:" + << "\n\t Number of active cells: " + << triangulation.n_active_cells() + << "\n\t Number of degrees of freedom: " + << dof_handler_ref.n_dofs() << std::endl; + tangent_matrix.clear(); + { + const types::global_dof_index n_dofs_u = dofs_per_block[u_dof]; + const types::global_dof_index n_dofs_p = dofs_per_block[p_dof]; + const types::global_dof_index n_dofs_J = dofs_per_block[J_dof]; + BlockDynamicSparsityPattern dsp(n_blocks, n_blocks); + dsp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u); + dsp.block(u_dof, p_dof).reinit(n_dofs_u, n_dofs_p); + dsp.block(u_dof, J_dof).reinit(n_dofs_u, n_dofs_J); + dsp.block(p_dof, u_dof).reinit(n_dofs_p, n_dofs_u); + dsp.block(p_dof, p_dof).reinit(n_dofs_p, n_dofs_p); + dsp.block(p_dof, J_dof).reinit(n_dofs_p, n_dofs_J); + dsp.block(J_dof, u_dof).reinit(n_dofs_J, n_dofs_u); + dsp.block(J_dof, p_dof).reinit(n_dofs_J, n_dofs_p); + dsp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J); + dsp.collect_sizes(); + Table<2, DoFTools::Coupling> coupling(n_components, n_components); + for (unsigned int ii = 0; ii < n_components; ++ii) + for (unsigned int jj = 0; jj < n_components; ++jj) + if (((ii < p_component) && (jj == J_component)) || + ((ii == J_component) && (jj < p_component)) || + ((ii == p_component) && (jj == p_component))) + coupling[ii][jj] = DoFTools::none; + else + coupling[ii][jj] = DoFTools::always; + DoFTools::make_sparsity_pattern( + dof_handler_ref, coupling, dsp, constraints, false); + sparsity_pattern.copy_from(dsp); + } + tangent_matrix.reinit(sparsity_pattern); + system_rhs.reinit(dofs_per_block); + system_rhs.collect_sizes(); + solution_n.reinit(dofs_per_block); + solution_n.collect_sizes(); + setup_qph(); + timer.leave_subsection(); + } + template + void + Solid::determine_component_extractors() + { + element_indices_u.clear(); + element_indices_p.clear(); + element_indices_J.clear(); + for (unsigned int k = 0; k < fe.dofs_per_cell; ++k) + { + const unsigned int k_group = fe.system_to_base_index(k).first.first; + if (k_group == u_dof) + element_indices_u.push_back(k); + else if (k_group == p_dof) + element_indices_p.push_back(k); + else if (k_group == J_dof) + element_indices_J.push_back(k); + else + { + Assert(k_group <= J_dof, ExcInternalError()); + } + } + } + template + void + Solid::setup_qph() + { + std::cout << " Setting up quadrature point data..." << std::endl; + quadrature_point_history.initialize(triangulation.begin_active(), + triangulation.end(), + n_q_points); + for (typename Triangulation::active_cell_iterator cell = + triangulation.begin_active(); + cell != triangulation.end(); + ++cell) + { + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + lqph[q_point]->setup_lqp(parameters); + } + } + template + void + Solid::update_qph_incremental( + const BlockVector &solution_delta) + { + timer.enter_subsection("Update QPH data"); + std::cout << " UQPH " << std::flush; + const BlockVector solution_total( + get_total_solution(solution_delta)); + const UpdateFlags uf_UQPH(update_values | update_gradients); + PerTaskData_UQPH per_task_data_UQPH; + ScratchData_UQPH scratch_data_UQPH(fe, qf_cell, uf_UQPH, solution_total); + // ADOL-C is incompatible with TBB + // WorkStream::run(dof_handler_ref.begin_active(), + // dof_handler_ref.end(), + // *this, + // &Solid::update_qph_incremental_one_cell, + // &Solid::copy_local_to_global_UQPH, + // scratch_data_UQPH, + // per_task_data_UQPH); + for (typename DoFHandler::active_cell_iterator cell = + dof_handler_ref.begin_active(); + cell != dof_handler_ref.end(); + ++cell) + { + update_qph_incremental_one_cell(cell, + scratch_data_UQPH, + per_task_data_UQPH); + copy_local_to_global_UQPH(per_task_data_UQPH); + } + timer.leave_subsection(); + } + template + void + Solid::update_qph_incremental_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_UQPH & scratch, + PerTaskData_UQPH & /*data*/) + { + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + Assert(scratch.solution_grads_u_total.size() == n_q_points, + ExcInternalError()); + Assert(scratch.solution_values_p_total.size() == n_q_points, + ExcInternalError()); + Assert(scratch.solution_values_J_total.size() == n_q_points, + ExcInternalError()); + scratch.reset(); + scratch.fe_values_ref.reinit(cell); + scratch.fe_values_ref[u_fe].get_function_gradients( + scratch.solution_total, scratch.solution_grads_u_total); + scratch.fe_values_ref[p_fe].get_function_values( + scratch.solution_total, scratch.solution_values_p_total); + scratch.fe_values_ref[J_fe].get_function_values( + scratch.solution_total, scratch.solution_values_J_total); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + lqph[q_point]->update_values(scratch.solution_grads_u_total[q_point], + scratch.solution_values_p_total[q_point], + scratch.solution_values_J_total[q_point]); + } + template + void + Solid::solve_nonlinear_timestep( + BlockVector &solution_delta) + { + std::cout << std::endl + << "Timestep " << time.get_timestep() << " @ " << time.current() + << "s" << std::endl; + BlockVector newton_update(dofs_per_block); + error_residual.reset(); + error_residual_0.reset(); + error_residual_norm.reset(); + error_update.reset(); + error_update_0.reset(); + error_update_norm.reset(); + print_conv_header(); + unsigned int newton_iteration = 0; + for (; newton_iteration < parameters.max_iterations_NR; ++newton_iteration) + { + std::cout << " " << std::setw(2) << newton_iteration << " " + << std::flush; + tangent_matrix = 0.0; + system_rhs = 0.0; + assemble_system_rhs(); + get_error_residual(error_residual); + if (newton_iteration == 0) + error_residual_0 = error_residual; + error_residual_norm = error_residual; + error_residual_norm.normalise(error_residual_0); + if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u && + error_residual_norm.u <= parameters.tol_f) + { + std::cout << " CONVERGED! " << std::endl; + print_conv_footer(); + break; + } + assemble_system_tangent(); + make_constraints(newton_iteration); + constraints.condense(tangent_matrix, system_rhs); + const std::pair lin_solver_output = + solve_linear_system(newton_update); + get_error_update(newton_update, error_update); + if (newton_iteration == 0) + error_update_0 = error_update; + error_update_norm = error_update; + error_update_norm.normalise(error_update_0); + solution_delta += newton_update; + update_qph_incremental(solution_delta); + std::cout << " | " << std::fixed << std::setprecision(3) << std::setw(7) + << std::scientific << lin_solver_output.first << " " + << lin_solver_output.second << " " + << error_residual_norm.norm << " " << error_residual_norm.u + << " " << error_residual_norm.p << " " + << error_residual_norm.J << " " << error_update_norm.norm + << " " << error_update_norm.u << " " << error_update_norm.p + << " " << error_update_norm.J << " " << std::endl; + } + AssertThrow(newton_iteration <= parameters.max_iterations_NR, + ExcMessage("No convergence in nonlinear solver!")); + } + template + void + Solid::print_conv_header() + { + static const unsigned int l_width = 155; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + std::cout << " SOLVER STEP " + << " | LIN_IT LIN_RES RES_NORM " + << " RES_U RES_P RES_J NU_NORM " + << " NU_U NU_P NU_J " << std::endl; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + } + template + void + Solid::print_conv_footer() + { + static const unsigned int l_width = 155; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + const std::pair error_dil = get_error_dilation(); + std::cout << "Relative errors:" << std::endl + << "Displacement:\t" << error_update.u / error_update_0.u + << std::endl + << "Force: \t\t" << error_residual.u / error_residual_0.u + << std::endl + << "Dilatation:\t" << error_dil.first << std::endl + << "v / V_0:\t" << error_dil.second * vol_reference << " / " + << vol_reference << " = " << error_dil.second << std::endl; + } + template + double + Solid::compute_vol_current() const + { + double vol_current = 0.0; + FEValues fe_values_ref(fe, qf_cell, update_JxW_values); + for (typename Triangulation::active_cell_iterator cell = + triangulation.begin_active(); + cell != triangulation.end(); + ++cell) + { + fe_values_ref.reinit(cell); + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const double det_F_qp = lqph[q_point]->get_det_F(); + const double JxW = fe_values_ref.JxW(q_point); + vol_current += det_F_qp * JxW; + } + } + Assert(vol_current > 0.0, ExcInternalError()); + return vol_current; + } + template + std::pair + Solid::get_error_dilation() const + { + double dil_L2_error = 0.0; + FEValues fe_values_ref(fe, qf_cell, update_JxW_values); + for (typename Triangulation::active_cell_iterator cell = + triangulation.begin_active(); + cell != triangulation.end(); + ++cell) + { + fe_values_ref.reinit(cell); + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const double det_F_qp = lqph[q_point]->get_det_F(); + const double J_tilde_qp = lqph[q_point]->get_J_tilde(); + const double the_error_qp_squared = + std::pow((det_F_qp - J_tilde_qp), 2); + const double JxW = fe_values_ref.JxW(q_point); + dil_L2_error += the_error_qp_squared * JxW; + } + } + return std::make_pair(std::sqrt(dil_L2_error), + compute_vol_current() / vol_reference); + } + template + void + Solid::get_error_residual(Errors &error_residual) + { + BlockVector error_res(dofs_per_block); + for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i) + if (!constraints.is_constrained(i)) + error_res(i) = system_rhs(i); + error_residual.norm = error_res.l2_norm(); + error_residual.u = error_res.block(u_dof).l2_norm(); + error_residual.p = error_res.block(p_dof).l2_norm(); + error_residual.J = error_res.block(J_dof).l2_norm(); + } + template + void + Solid::get_error_update( + const BlockVector &newton_update, + Errors & error_update) + { + BlockVector error_ud(dofs_per_block); + for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i) + if (!constraints.is_constrained(i)) + error_ud(i) = newton_update(i); + error_update.norm = error_ud.l2_norm(); + error_update.u = error_ud.block(u_dof).l2_norm(); + error_update.p = error_ud.block(p_dof).l2_norm(); + error_update.J = error_ud.block(J_dof).l2_norm(); + } + template + BlockVector + Solid::get_total_solution( + const BlockVector &solution_delta) const + { + BlockVector solution_total(solution_n); + solution_total += solution_delta; + return solution_total; + } + template + void + Solid::assemble_system_tangent() + { + timer.enter_subsection("Assemble tangent matrix"); + std::cout << " ASM_K " << std::flush; + tangent_matrix = 0.0; + const UpdateFlags uf_cell(update_values | update_gradients | + update_JxW_values); + PerTaskData_K per_task_data(dofs_per_cell); + ScratchData_K scratch_data(fe, qf_cell, uf_cell); + WorkStream::run( + dof_handler_ref.begin_active(), + dof_handler_ref.end(), + std::bind( + &Solid::assemble_system_tangent_one_cell, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + std::bind(&Solid::copy_local_to_global_K, + this, + std::placeholders::_1), + scratch_data, + per_task_data); + timer.leave_subsection(); + } + template + void + Solid::copy_local_to_global_K( + const PerTaskData_K &data) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + for (unsigned int j = 0; j < dofs_per_cell; ++j) + tangent_matrix.add(data.local_dof_indices[i], + data.local_dof_indices[j], + data.cell_matrix(i, j)); + } + template + void + Solid::assemble_system_tangent_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_K & scratch, + PerTaskData_K & data) const + { + data.reset(); + scratch.reset(); + scratch.fe_values_ref.reinit(cell); + cell->get_dof_indices(data.local_dof_indices); + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv(); + for (unsigned int k = 0; k < dofs_per_cell; ++k) + { + const unsigned int k_group = fe.system_to_base_index(k).first.first; + if (k_group == u_dof) + { + scratch.grad_Nx[q_point][k] = + scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv; + scratch.symm_grad_Nx[q_point][k] = + symmetrize(scratch.grad_Nx[q_point][k]); + } + else if (k_group == p_dof) + scratch.Nx[q_point][k] = + scratch.fe_values_ref[p_fe].value(k, q_point); + else if (k_group == J_dof) + scratch.Nx[q_point][k] = + scratch.fe_values_ref[J_fe].value(k, q_point); + else + Assert(k_group <= J_dof, ExcInternalError()); + } + } + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const Tensor<2, dim> tau = lqph[q_point]->get_tau(); + const SymmetricTensor<4, dim> Jc = lqph[q_point]->get_Jc(); + const double d2Psi_vol_dJ2 = lqph[q_point]->get_d2Psi_vol_dJ2(); + const double det_F = lqph[q_point]->get_det_F(); + const std::vector & N = scratch.Nx[q_point]; + const std::vector> &symm_grad_Nx = + scratch.symm_grad_Nx[q_point]; + const std::vector> &grad_Nx = scratch.grad_Nx[q_point]; + const double JxW = scratch.fe_values_ref.JxW(q_point); + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + const unsigned int component_i = + fe.system_to_component_index(i).first; + const unsigned int i_group = fe.system_to_base_index(i).first.first; + for (unsigned int j = 0; j <= i; ++j) + { + const unsigned int component_j = + fe.system_to_component_index(j).first; + const unsigned int j_group = + fe.system_to_base_index(j).first.first; + if ((i_group == j_group) && (i_group == u_dof)) + { + data.cell_matrix(i, j) += symm_grad_Nx[i] * + Jc // The material contribution: + * symm_grad_Nx[j] * JxW; + if (component_i == + component_j) // geometrical stress contribution + data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau * + grad_Nx[j][component_j] * JxW; + } + else if ((i_group == p_dof) && (j_group == u_dof)) + { + data.cell_matrix(i, j) += + N[i] * det_F * + (symm_grad_Nx[j] * StandardTensors::I) * JxW; + } + else if ((i_group == J_dof) && (j_group == p_dof)) + data.cell_matrix(i, j) -= N[i] * N[j] * JxW; + else if ((i_group == j_group) && (i_group == J_dof)) + data.cell_matrix(i, j) += N[i] * d2Psi_vol_dJ2 * N[j] * JxW; + else + Assert((i_group <= J_dof) && (j_group <= J_dof), + ExcInternalError()); + } + } + } + for (unsigned int i = 0; i < dofs_per_cell; ++i) + for (unsigned int j = i + 1; j < dofs_per_cell; ++j) + data.cell_matrix(i, j) = data.cell_matrix(j, i); + } + template + void + Solid::assemble_system_rhs() + { + timer.enter_subsection("Assemble system right-hand side"); + std::cout << " ASM_R " << std::flush; + system_rhs = 0.0; + const UpdateFlags uf_cell(update_values | update_gradients | + update_JxW_values); + const UpdateFlags uf_face(update_values | update_normal_vectors | + update_JxW_values); + PerTaskData_RHS per_task_data(dofs_per_cell); + ScratchData_RHS scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face); + WorkStream::run( + dof_handler_ref.begin_active(), + dof_handler_ref.end(), + std::bind( + &Solid::assemble_system_rhs_one_cell, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + std::bind(&Solid::copy_local_to_global_rhs, + this, + std::placeholders::_1), + scratch_data, + per_task_data); + timer.leave_subsection(); + } + template + void + Solid::copy_local_to_global_rhs( + const PerTaskData_RHS &data) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + system_rhs(data.local_dof_indices[i]) += data.cell_rhs(i); + } + template + void + Solid::assemble_system_rhs_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_RHS & scratch, + PerTaskData_RHS & data) const + { + data.reset(); + scratch.reset(); + scratch.fe_values_ref.reinit(cell); + cell->get_dof_indices(data.local_dof_indices); + const std::vector< + std::shared_ptr>> + lqph = quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const Tensor<2, dim> F_inv = lqph[q_point]->get_F_inv(); + for (unsigned int k = 0; k < dofs_per_cell; ++k) + { + const unsigned int k_group = fe.system_to_base_index(k).first.first; + if (k_group == u_dof) + scratch.symm_grad_Nx[q_point][k] = symmetrize( + scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv); + else if (k_group == p_dof) + scratch.Nx[q_point][k] = + scratch.fe_values_ref[p_fe].value(k, q_point); + else if (k_group == J_dof) + scratch.Nx[q_point][k] = + scratch.fe_values_ref[J_fe].value(k, q_point); + else + Assert(k_group <= J_dof, ExcInternalError()); + } + } + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const SymmetricTensor<2, dim> tau = lqph[q_point]->get_tau(); + const double det_F = lqph[q_point]->get_det_F(); + const double J_tilde = lqph[q_point]->get_J_tilde(); + const double p_tilde = lqph[q_point]->get_p_tilde(); + const double dPsi_vol_dJ = lqph[q_point]->get_dPsi_vol_dJ(); + const std::vector & N = scratch.Nx[q_point]; + const std::vector> &symm_grad_Nx = + scratch.symm_grad_Nx[q_point]; + const double JxW = scratch.fe_values_ref.JxW(q_point); + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + const unsigned int i_group = fe.system_to_base_index(i).first.first; + if (i_group == u_dof) + data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW; + else if (i_group == p_dof) + data.cell_rhs(i) -= N[i] * (det_F - J_tilde) * JxW; + else if (i_group == J_dof) + data.cell_rhs(i) -= N[i] * (dPsi_vol_dJ - p_tilde) * JxW; + else + Assert(i_group <= J_dof, ExcInternalError()); + } + } + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; + ++face) + if (cell->face(face)->at_boundary() == true && + cell->face(face)->boundary_id() == 6) + { + scratch.fe_face_values_ref.reinit(cell, face); + for (unsigned int f_q_point = 0; f_q_point < n_q_points_f; + ++f_q_point) + { + const Tensor<1, dim> &N = + scratch.fe_face_values_ref.normal_vector(f_q_point); + static const double p0 = + -4.0 / (parameters.scale * parameters.scale); + const double time_ramp = (time.current() / time.end()); + const double pressure = p0 * parameters.p_p0 * time_ramp; + const Tensor<1, dim> traction = pressure * N; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + const unsigned int i_group = + fe.system_to_base_index(i).first.first; + if (i_group == u_dof) + { + const unsigned int component_i = + fe.system_to_component_index(i).first; + const double Ni = + scratch.fe_face_values_ref.shape_value(i, f_q_point); + const double JxW = + scratch.fe_face_values_ref.JxW(f_q_point); + data.cell_rhs(i) += (Ni * traction[component_i]) * JxW; + } + } + } + } + } + template + void + Solid::make_constraints(const int &it_nr) + { + std::cout << " CST " << std::flush; + if (it_nr > 1) + return; + constraints.clear(); + const bool apply_dirichlet_bc = (it_nr == 0); + const FEValuesExtractors::Scalar x_displacement(0); + const FEValuesExtractors::Scalar y_displacement(1); + { + const int boundary_id = 0; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(x_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(x_displacement)); + } + { + const int boundary_id = 2; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(y_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(y_displacement)); + } + if (dim == 3) + { + const FEValuesExtractors::Scalar z_displacement(2); + { + const int boundary_id = 3; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + } + { + const int boundary_id = 4; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(z_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(z_displacement)); + } + { + const int boundary_id = 6; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + } + } + else + { + { + const int boundary_id = 3; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + } + { + const int boundary_id = 6; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + } + } + constraints.close(); + } + template + void + Solid::assemble_sc() + { + timer.enter_subsection("Perform static condensation"); + std::cout << " ASM_SC " << std::flush; + PerTaskData_SC per_task_data(dofs_per_cell, + element_indices_u.size(), + element_indices_p.size(), + element_indices_J.size()); + ScratchData_SC scratch_data; + WorkStream::run(dof_handler_ref.begin_active(), + dof_handler_ref.end(), + *this, + &Solid::assemble_sc_one_cell, + &Solid::copy_local_to_global_sc, + scratch_data, + per_task_data); + timer.leave_subsection(); + } + template + void + Solid::copy_local_to_global_sc( + const PerTaskData_SC &data) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + for (unsigned int j = 0; j < dofs_per_cell; ++j) + tangent_matrix.add(data.local_dof_indices[i], + data.local_dof_indices[j], + data.cell_matrix(i, j)); + } + template + void + Solid::assemble_sc_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC & scratch, + PerTaskData_SC & data) + { + data.reset(); + scratch.reset(); + cell->get_dof_indices(data.local_dof_indices); + data.k_orig.extract_submatrix_from(tangent_matrix, + data.local_dof_indices, + data.local_dof_indices); + data.k_pu.extract_submatrix_from(data.k_orig, + element_indices_p, + element_indices_u); + data.k_pJ.extract_submatrix_from(data.k_orig, + element_indices_p, + element_indices_J); + data.k_JJ.extract_submatrix_from(data.k_orig, + element_indices_J, + element_indices_J); + data.k_pJ_inv.invert(data.k_pJ); + data.k_pJ_inv.mmult(data.A, data.k_pu); + data.k_JJ.mmult(data.B, data.A); + data.k_pJ_inv.Tmmult(data.C, data.B); + data.k_pu.Tmmult(data.k_bbar, data.C); + data.k_bbar.scatter_matrix_to(element_indices_u, + element_indices_u, + data.cell_matrix); + data.k_pJ_inv.add(-1.0, data.k_pJ); + data.k_pJ_inv.scatter_matrix_to(element_indices_p, + element_indices_J, + data.cell_matrix); + } + template + std::pair + Solid::solve_linear_system( + BlockVector &newton_update) + { + unsigned int lin_it = 0; + double lin_res = 0.0; + if (parameters.use_static_condensation == true) + { + BlockVector A(dofs_per_block); + BlockVector B(dofs_per_block); + { + assemble_sc(); + tangent_matrix.block(p_dof, J_dof) + .vmult(A.block(J_dof), system_rhs.block(p_dof)); + tangent_matrix.block(J_dof, J_dof) + .vmult(B.block(J_dof), A.block(J_dof)); + A.block(J_dof) = system_rhs.block(J_dof); + A.block(J_dof) -= B.block(J_dof); + tangent_matrix.block(p_dof, J_dof) + .Tvmult(A.block(p_dof), A.block(J_dof)); + tangent_matrix.block(u_dof, p_dof) + .vmult(A.block(u_dof), A.block(p_dof)); + system_rhs.block(u_dof) -= A.block(u_dof); + timer.enter_subsection("Linear solver"); + std::cout << " SLV " << std::flush; + if (parameters.type_lin == "CG") + { + const int solver_its = tangent_matrix.block(u_dof, u_dof).m() * + parameters.max_iterations_lin; + const double tol_sol = + parameters.tol_lin * system_rhs.block(u_dof).l2_norm(); + SolverControl solver_control(solver_its, tol_sol, false, false); + GrowingVectorMemory> GVM; + SolverCG> solver_CG(solver_control, GVM); + PreconditionSelector, Vector> + preconditioner(parameters.preconditioner_type, + parameters.preconditioner_relaxation); + preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof)); + solver_CG.solve(tangent_matrix.block(u_dof, u_dof), + newton_update.block(u_dof), + system_rhs.block(u_dof), + preconditioner); + lin_it = solver_control.last_step(); + lin_res = solver_control.last_value(); + } + else if (parameters.type_lin == "Direct") + { + SparseDirectUMFPACK A_direct; + A_direct.initialize(tangent_matrix.block(u_dof, u_dof)); + A_direct.vmult(newton_update.block(u_dof), + system_rhs.block(u_dof)); + lin_it = 1; + lin_res = 0.0; + } + else + Assert(false, ExcMessage("Linear solver type not implemented")); + timer.leave_subsection(); + } + constraints.distribute(newton_update); + timer.enter_subsection("Linear solver postprocessing"); + std::cout << " PP " << std::flush; + { + tangent_matrix.block(p_dof, u_dof) + .vmult(A.block(p_dof), newton_update.block(u_dof)); + A.block(p_dof) *= -1.0; + A.block(p_dof) += system_rhs.block(p_dof); + tangent_matrix.block(p_dof, J_dof) + .vmult(newton_update.block(J_dof), A.block(p_dof)); + } + constraints.distribute(newton_update); + { + tangent_matrix.block(J_dof, J_dof) + .vmult(A.block(J_dof), newton_update.block(J_dof)); + A.block(J_dof) *= -1.0; + A.block(J_dof) += system_rhs.block(J_dof); + tangent_matrix.block(p_dof, J_dof) + .Tvmult(newton_update.block(p_dof), A.block(J_dof)); + } + constraints.distribute(newton_update); + timer.leave_subsection(); + } + else + { + std::cout << " ------ " << std::flush; + timer.enter_subsection("Linear solver"); + std::cout << " SLV " << std::flush; + if (parameters.type_lin == "CG") + { + const Vector &f_u = system_rhs.block(u_dof); + const Vector &f_p = system_rhs.block(p_dof); + const Vector &f_J = system_rhs.block(J_dof); + Vector & d_u = newton_update.block(u_dof); + Vector & d_p = newton_update.block(p_dof); + Vector & d_J = newton_update.block(J_dof); + const auto K_uu = + linear_operator(tangent_matrix.block(u_dof, u_dof)); + const auto K_up = + linear_operator(tangent_matrix.block(u_dof, p_dof)); + const auto K_pu = + linear_operator(tangent_matrix.block(p_dof, u_dof)); + const auto K_Jp = + linear_operator(tangent_matrix.block(J_dof, p_dof)); + const auto K_JJ = + linear_operator(tangent_matrix.block(J_dof, J_dof)); + PreconditionSelector, Vector> + preconditioner_K_Jp_inv("jacobi"); + preconditioner_K_Jp_inv.use_matrix( + tangent_matrix.block(J_dof, p_dof)); + ReductionControl solver_control_K_Jp_inv( + tangent_matrix.block(J_dof, p_dof).m() * + parameters.max_iterations_lin, + 1.0e-30, + parameters.tol_lin); + SolverSelector> solver_K_Jp_inv; + solver_K_Jp_inv.select("cg"); + solver_K_Jp_inv.set_control(solver_control_K_Jp_inv); + const auto K_Jp_inv = + inverse_operator(K_Jp, solver_K_Jp_inv, preconditioner_K_Jp_inv); + const auto K_pJ_inv = transpose_operator(K_Jp_inv); + const auto K_pp_bar = K_Jp_inv * K_JJ * K_pJ_inv; + const auto K_uu_bar_bar = K_up * K_pp_bar * K_pu; + const auto K_uu_con = K_uu + K_uu_bar_bar; + PreconditionSelector, Vector> + preconditioner_K_con_inv(parameters.preconditioner_type, + parameters.preconditioner_relaxation); + preconditioner_K_con_inv.use_matrix( + tangent_matrix.block(u_dof, u_dof)); + ReductionControl solver_control_K_con_inv( + tangent_matrix.block(u_dof, u_dof).m() * + parameters.max_iterations_lin, + 1.0e-30, + parameters.tol_lin); + SolverSelector> solver_K_con_inv; + solver_K_con_inv.select("cg"); + solver_K_con_inv.set_control(solver_control_K_con_inv); + const auto K_uu_con_inv = + inverse_operator(K_uu_con, + solver_K_con_inv, + preconditioner_K_con_inv); + d_u = + K_uu_con_inv * (f_u - K_up * (K_Jp_inv * f_J - K_pp_bar * f_p)); + timer.leave_subsection(); + timer.enter_subsection("Linear solver postprocessing"); + std::cout << " PP " << std::flush; + d_J = K_pJ_inv * (f_p - K_pu * d_u); + d_p = K_Jp_inv * (f_J - K_JJ * d_J); + lin_it = solver_control_K_con_inv.last_step(); + lin_res = solver_control_K_con_inv.last_value(); + } + else if (parameters.type_lin == "Direct") + { + SparseDirectUMFPACK A_direct; + A_direct.initialize(tangent_matrix); + A_direct.vmult(newton_update, system_rhs); + lin_it = 1; + lin_res = 0.0; + std::cout << " -- " << std::flush; + } + else + Assert(false, ExcMessage("Linear solver type not implemented")); + timer.leave_subsection(); + constraints.distribute(newton_update); + } + return std::make_pair(lin_it, lin_res); + } + template + void + Solid::output_results() const + { + DataOut data_out; + std::vector + data_component_interpretation( + dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation.push_back( + DataComponentInterpretation::component_is_scalar); + data_component_interpretation.push_back( + DataComponentInterpretation::component_is_scalar); + std::vector solution_name(dim, "displacement"); + solution_name.push_back("pressure"); + solution_name.push_back("dilatation"); + data_out.attach_dof_handler(dof_handler_ref); + data_out.add_data_vector(solution_n, + solution_name, + DataOut::type_dof_data, + data_component_interpretation); + Vector soln(solution_n.size()); + for (unsigned int i = 0; i < soln.size(); ++i) + soln(i) = solution_n(i); + MappingQEulerian q_mapping(degree, dof_handler_ref, soln); + data_out.build_patches(q_mapping, degree); + std::ostringstream filename; + filename << "solution-" << dim << "d-" << time.get_timestep() << ".vtk"; + std::ofstream output(filename.str().c_str()); + data_out.write_vtk(output); + } +} // namespace Step44 diff --git a/tests/ad_common_tests/step-44-helper_vector_01.h b/tests/ad_common_tests/step-44-helper_vector_01.h new file mode 100644 index 0000000000..a011e3145f --- /dev/null +++ b/tests/ad_common_tests/step-44-helper_vector_01.h @@ -0,0 +1,1765 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + +// Header file: +// This is a modified version of step-44, which tests the implementation of +// cell-level auto-differentiation (linearisation of a residual vector). + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../tests.h" +namespace Step44 +{ + using namespace dealii; + namespace AD = dealii::Differentiation::AD; + namespace Parameters + { + struct FESystem + { + unsigned int poly_degree; + unsigned int quad_order; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + FESystem::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Finite element system"); + { + prm.declare_entry("Polynomial degree", + "2", + Patterns::Integer(0), + "Displacement system polynomial order"); + prm.declare_entry("Quadrature order", + "3", + Patterns::Integer(0), + "Gauss quadrature order"); + } + prm.leave_subsection(); + } + void + FESystem::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Finite element system"); + { + poly_degree = prm.get_integer("Polynomial degree"); + quad_order = prm.get_integer("Quadrature order"); + } + prm.leave_subsection(); + } + struct Geometry + { + unsigned int global_refinement; + double scale; + double p_p0; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Geometry::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Geometry"); + { + prm.declare_entry("Global refinement", + "2", + Patterns::Integer(0), + "Global refinement level"); + prm.declare_entry("Grid scale", + "1e-3", + Patterns::Double(0.0), + "Global grid scaling factor"); + prm.declare_entry("Pressure ratio p/p0", + "100", + Patterns::Selection("20|40|60|80|100"), + "Ratio of applied pressure to reference pressure"); + } + prm.leave_subsection(); + } + void + Geometry::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Geometry"); + { + global_refinement = prm.get_integer("Global refinement"); + scale = prm.get_double("Grid scale"); + p_p0 = prm.get_double("Pressure ratio p/p0"); + } + prm.leave_subsection(); + } + struct Materials + { + double nu; + double mu; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Materials::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Material properties"); + { + prm.declare_entry("Poisson's ratio", + "0.4999", + Patterns::Double(-1.0, 0.5), + "Poisson's ratio"); + prm.declare_entry("Shear modulus", + "80.194e6", + Patterns::Double(), + "Shear modulus"); + } + prm.leave_subsection(); + } + void + Materials::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Material properties"); + { + nu = prm.get_double("Poisson's ratio"); + mu = prm.get_double("Shear modulus"); + } + prm.leave_subsection(); + } + struct LinearSolver + { + std::string type_lin; + double tol_lin; + double max_iterations_lin; + bool use_static_condensation; + std::string preconditioner_type; + double preconditioner_relaxation; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + LinearSolver::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Linear solver"); + { + prm.declare_entry("Solver type", + "CG", + Patterns::Selection("CG|Direct"), + "Type of solver used to solve the linear system"); + prm.declare_entry("Residual", + "1e-6", + Patterns::Double(0.0), + "Linear solver residual (scaled by residual norm)"); + prm.declare_entry( + "Max iteration multiplier", + "1", + Patterns::Double(0.0), + "Linear solver iterations (multiples of the system matrix size)"); + prm.declare_entry("Use static condensation", + "true", + Patterns::Bool(), + "Solve the full block system or a reduced problem"); + prm.declare_entry("Preconditioner type", + "ssor", + Patterns::Selection("jacobi|ssor"), + "Type of preconditioner"); + prm.declare_entry("Preconditioner relaxation", + "0.65", + Patterns::Double(0.0), + "Preconditioner relaxation value"); + } + prm.leave_subsection(); + } + void + LinearSolver::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Linear solver"); + { + type_lin = prm.get("Solver type"); + tol_lin = prm.get_double("Residual"); + max_iterations_lin = prm.get_double("Max iteration multiplier"); + use_static_condensation = prm.get_bool("Use static condensation"); + preconditioner_type = prm.get("Preconditioner type"); + preconditioner_relaxation = prm.get_double("Preconditioner relaxation"); + } + prm.leave_subsection(); + } + struct NonlinearSolver + { + unsigned int max_iterations_NR; + double tol_f; + double tol_u; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + NonlinearSolver::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Nonlinear solver"); + { + prm.declare_entry("Max iterations Newton-Raphson", + "10", + Patterns::Integer(0), + "Number of Newton-Raphson iterations allowed"); + prm.declare_entry("Tolerance force", + "1.0e-9", + Patterns::Double(0.0), + "Force residual tolerance"); + prm.declare_entry("Tolerance displacement", + "1.0e-6", + Patterns::Double(0.0), + "Displacement error tolerance"); + } + prm.leave_subsection(); + } + void + NonlinearSolver::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Nonlinear solver"); + { + max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson"); + tol_f = prm.get_double("Tolerance force"); + tol_u = prm.get_double("Tolerance displacement"); + } + prm.leave_subsection(); + } + struct Time + { + double delta_t; + double end_time; + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + void + Time::declare_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Time"); + { + prm.declare_entry("End time", "1", Patterns::Double(), "End time"); + prm.declare_entry("Time step size", + "0.1", + Patterns::Double(), + "Time step size"); + } + prm.leave_subsection(); + } + void + Time::parse_parameters(ParameterHandler &prm) + { + prm.enter_subsection("Time"); + { + end_time = prm.get_double("End time"); + delta_t = prm.get_double("Time step size"); + } + prm.leave_subsection(); + } + struct AllParameters : public FESystem, + public Geometry, + public Materials, + public LinearSolver, + public NonlinearSolver, + public Time + { + AllParameters(const std::string &input_file); + static void + declare_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); + }; + AllParameters::AllParameters(const std::string &input_file) + { + ParameterHandler prm; + declare_parameters(prm); + prm.parse_input(input_file); + parse_parameters(prm); + } + void + AllParameters::declare_parameters(ParameterHandler &prm) + { + FESystem::declare_parameters(prm); + Geometry::declare_parameters(prm); + Materials::declare_parameters(prm); + LinearSolver::declare_parameters(prm); + NonlinearSolver::declare_parameters(prm); + Time::declare_parameters(prm); + } + void + AllParameters::parse_parameters(ParameterHandler &prm) + { + FESystem::parse_parameters(prm); + Geometry::parse_parameters(prm); + Materials::parse_parameters(prm); + LinearSolver::parse_parameters(prm); + NonlinearSolver::parse_parameters(prm); + Time::parse_parameters(prm); + } + } // namespace Parameters + template + class StandardTensors + { + public: + static const SymmetricTensor<2, dim> I; + static const SymmetricTensor<4, dim> IxI; + static const SymmetricTensor<4, dim> II; + static const SymmetricTensor<4, dim> dev_P; + }; + template + const SymmetricTensor<2, dim> + StandardTensors::I = unit_symmetric_tensor(); + template + const SymmetricTensor<4, dim> StandardTensors::IxI = outer_product(I, I); + template + const SymmetricTensor<4, dim> + StandardTensors::II = identity_tensor(); + template + const SymmetricTensor<4, dim> + StandardTensors::dev_P = deviator_tensor(); + class Time + { + public: + Time(const double time_end, const double delta_t) + : timestep(0) + , time_current(0.0) + , time_end(time_end) + , delta_t(delta_t) + {} + virtual ~Time() + {} + double + current() const + { + return time_current; + } + double + end() const + { + return time_end; + } + double + get_delta_t() const + { + return delta_t; + } + unsigned int + get_timestep() const + { + return timestep; + } + void + increment() + { + time_current += delta_t; + ++timestep; + } + + private: + unsigned int timestep; + double time_current; + const double time_end; + const double delta_t; + }; + template + class Material_Compressible_Neo_Hook_Three_Field + { + public: + Material_Compressible_Neo_Hook_Three_Field(const double mu, const double nu) + : kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))) + , c_1(mu / 2.0) + { + Assert(kappa > 0, ExcInternalError()); + } + ~Material_Compressible_Neo_Hook_Three_Field() + {} + + template + SymmetricTensor<2, dim, NumberType> + get_tau(const Tensor<2, dim, NumberType> &F, + const NumberType & p_tilde) const + { + return get_tau_iso(F) + get_tau_vol(F, p_tilde); + } + template + NumberType + get_dPsi_vol_dJ(const NumberType &J_tilde) const + { + return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde); + } + + protected: + const double kappa; + const double c_1; + + template + SymmetricTensor<2, dim, NumberType> + get_tau_vol(const Tensor<2, dim, NumberType> &F, + const NumberType & p_tilde) const + { + const NumberType det_F = determinant(F); + // return p_tilde * det_F * StandardTensors::I; + SymmetricTensor<2, dim, NumberType> tau_vol(StandardTensors::I); + tau_vol *= p_tilde * det_F; + return tau_vol; + } + template + SymmetricTensor<2, dim, NumberType> + get_tau_iso(const Tensor<2, dim, NumberType> &F) const + { + // return StandardTensors::dev_P * get_tau_bar(F); + + const SymmetricTensor<2, dim, NumberType> tau_bar = get_tau_bar(F); + SymmetricTensor<2, dim, NumberType> tau_iso; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = 0; l < dim; ++l) + tau_iso[i][j] += + StandardTensors::dev_P[i][j][k][l] * tau_bar[k][l]; + + return tau_iso; + } + template + SymmetricTensor<2, dim, NumberType> + get_tau_bar(const Tensor<2, dim, NumberType> &F) const + { + const NumberType det_F = determinant(F); + SymmetricTensor<2, dim, NumberType> b_bar = symmetrize(F * transpose(F)); + b_bar *= std::pow(det_F, -2.0 / dim); + return 2.0 * c_1 * b_bar; + } + }; + template + class PointHistory + { + public: + PointHistory() + {} + virtual ~PointHistory() + {} + void + setup_lqp(const Parameters::AllParameters ¶meters) + { + material.reset( + new Material_Compressible_Neo_Hook_Three_Field(parameters.mu, + parameters.nu)); + } + + template + SymmetricTensor<2, dim, NumberType> + get_tau(const Tensor<2, dim, NumberType> &F, + const NumberType & p_tilde) const + { + return material->get_tau(F, p_tilde); + } + template + NumberType + get_dPsi_vol_dJ(const NumberType &J_tilde) const + { + return material->get_dPsi_vol_dJ(J_tilde); + } + + private: + std::shared_ptr> material; + }; + template + class Solid + { + public: + Solid(const std::string &input_file); + virtual ~Solid(); + void + run(); + + private: + struct PerTaskData_ASM; + struct ScratchData_ASM; + struct PerTaskData_SC; + struct ScratchData_SC; + void + make_grid(); + void + system_setup(); + void + determine_component_extractors(); + void + assemble_system(const BlockVector &solution_delta); + void + assemble_system_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_ASM & scratch, + PerTaskData_ASM & data) const; + void + copy_local_to_global_system(const PerTaskData_ASM &data); + void + assemble_sc(); + void + assemble_sc_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC & scratch, + PerTaskData_SC & data); + void + copy_local_to_global_sc(const PerTaskData_SC &data); + void + make_constraints(const int &it_nr); + void + setup_qph(); + void + solve_nonlinear_timestep(BlockVector &solution_delta); + std::pair + solve_linear_system(BlockVector &newton_update); + BlockVector + get_total_solution(const BlockVector &solution_delta) const; + void + output_results() const; + Parameters::AllParameters parameters; + double vol_reference; + Triangulation triangulation; + Time time; + mutable TimerOutput timer; + CellDataStorage::cell_iterator, + PointHistory> + quadrature_point_history; + const unsigned int degree; + const FESystem fe; + DoFHandler dof_handler_ref; + const unsigned int dofs_per_cell; + const FEValuesExtractors::Vector u_fe; + const FEValuesExtractors::Scalar p_fe; + const FEValuesExtractors::Scalar J_fe; + static const unsigned int n_blocks = 3; + static const unsigned int n_components = dim + 2; + static const unsigned int first_u_component = 0; + static const unsigned int p_component = dim; + static const unsigned int J_component = dim + 1; + enum + { + u_dof = 0, + p_dof = 1, + J_dof = 2 + }; + std::vector dofs_per_block; + std::vector element_indices_u; + std::vector element_indices_p; + std::vector element_indices_J; + const QGauss qf_cell; + const QGauss qf_face; + const unsigned int n_q_points; + const unsigned int n_q_points_f; + ConstraintMatrix constraints; + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix tangent_matrix; + BlockVector system_rhs; + BlockVector solution_n; + struct Errors + { + Errors() + : norm(1.0) + , u(1.0) + , p(1.0) + , J(1.0) + {} + void + reset() + { + norm = 1.0; + u = 1.0; + p = 1.0; + J = 1.0; + } + void + normalise(const Errors &rhs) + { + if (rhs.norm != 0.0) + norm /= rhs.norm; + if (rhs.u != 0.0) + u /= rhs.u; + if (rhs.p != 0.0) + p /= rhs.p; + if (rhs.J != 0.0) + J /= rhs.J; + } + double norm, u, p, J; + }; + Errors error_residual, error_residual_0, error_residual_norm, error_update, + error_update_0, error_update_norm; + void + get_error_residual(Errors &error_residual); + void + get_error_update(const BlockVector &newton_update, + Errors & error_update); + std::pair + get_error_dilation(const BlockVector &solution_total) const; + void + print_conv_header(); + void + print_conv_footer(const BlockVector &solution_delta); + }; + template + Solid::Solid(const std::string &input_file) + : parameters(input_file) + , triangulation(Triangulation::maximum_smoothing) + , time(parameters.end_time, parameters.delta_t) + , timer(std::cout, TimerOutput::never, TimerOutput::wall_times) + , degree(parameters.poly_degree) + , fe(FE_Q(parameters.poly_degree), + dim, // displacement + FE_DGPMonomial(parameters.poly_degree - 1), + 1, // pressure + FE_DGPMonomial(parameters.poly_degree - 1), + 1) + , // dilatation + dof_handler_ref(triangulation) + , dofs_per_cell(fe.dofs_per_cell) + , u_fe(first_u_component) + , p_fe(p_component) + , J_fe(J_component) + , dofs_per_block(n_blocks) + , qf_cell(parameters.quad_order) + , qf_face(parameters.quad_order) + , n_q_points(qf_cell.size()) + , n_q_points_f(qf_face.size()) + { + Assert(dim == 2 || dim == 3, + ExcMessage("This problem only works in 2 or 3 space dimensions.")); + determine_component_extractors(); + } + template + Solid::~Solid() + { + dof_handler_ref.clear(); + } + template + void + Solid::run() + { + make_grid(); + system_setup(); + { + ConstraintMatrix constraints; + constraints.close(); + const ComponentSelectFunction J_mask(J_component, n_components); + VectorTools::project(dof_handler_ref, + constraints, + QGauss(degree + 2), + J_mask, + solution_n); + } + // output_results(); + time.increment(); + BlockVector solution_delta(dofs_per_block); + while (time.current() < time.end()) + { + solution_delta = 0.0; + solve_nonlinear_timestep(solution_delta); + solution_n += solution_delta; + // output_results(); + // Output displacement at centre of traction surface + { + const Point soln_pt( + dim == 3 ? Point(0.0, 1.0, 0.0) * parameters.scale : + Point(0.0, 1.0) * parameters.scale); + typename DoFHandler::active_cell_iterator + cell = dof_handler_ref.begin_active(), + endc = dof_handler_ref.end(); + for (; cell != endc; ++cell) + for (unsigned int v = 0; v < GeometryInfo::vertices_per_cell; + ++v) + if (cell->vertex(v).distance(soln_pt) < 1e-6 * parameters.scale) + { + Tensor<1, dim> soln; + for (unsigned int d = 0; d < dim; ++d) + soln[d] = solution_n(cell->vertex_dof_index(v, u_dof + d)); + deallog << "Timestep " << time.get_timestep() << ": " << soln + << std::endl; + } + } + time.increment(); + } + } + template + struct Solid::PerTaskData_ASM + { + FullMatrix cell_matrix; + Vector cell_rhs; + std::vector local_dof_indices; + PerTaskData_ASM(const unsigned int dofs_per_cell) + : cell_matrix(dofs_per_cell, dofs_per_cell) + , cell_rhs(dofs_per_cell) + , local_dof_indices(dofs_per_cell) + {} + void + reset() + { + cell_matrix = 0.0; + cell_rhs = 0.0; + } + }; + template + struct Solid::ScratchData_ASM + { + const BlockVector &solution_total; + FEValues fe_values_ref; + FEFaceValues fe_face_values_ref; + ScratchData_ASM(const FiniteElement & fe_cell, + const QGauss & qf_cell, + const UpdateFlags uf_cell, + const QGauss & qf_face, + const UpdateFlags uf_face, + const BlockVector &solution_total) + : solution_total(solution_total) + , fe_values_ref(fe_cell, qf_cell, uf_cell) + , fe_face_values_ref(fe_cell, qf_face, uf_face) + {} + ScratchData_ASM(const ScratchData_ASM &rhs) + : solution_total(rhs.solution_total) + , fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()) + , fe_face_values_ref(rhs.fe_face_values_ref.get_fe(), + rhs.fe_face_values_ref.get_quadrature(), + rhs.fe_face_values_ref.get_update_flags()) + {} + void + reset() + {} + }; + template + struct Solid::PerTaskData_SC + { + FullMatrix cell_matrix; + std::vector local_dof_indices; + FullMatrix k_orig; + FullMatrix k_pu; + FullMatrix k_pJ; + FullMatrix k_JJ; + FullMatrix k_pJ_inv; + FullMatrix k_bbar; + FullMatrix A; + FullMatrix B; + FullMatrix C; + PerTaskData_SC(const unsigned int dofs_per_cell, + const unsigned int n_u, + const unsigned int n_p, + const unsigned int n_J) + : cell_matrix(dofs_per_cell, dofs_per_cell) + , local_dof_indices(dofs_per_cell) + , k_orig(dofs_per_cell, dofs_per_cell) + , k_pu(n_p, n_u) + , k_pJ(n_p, n_J) + , k_JJ(n_J, n_J) + , k_pJ_inv(n_p, n_J) + , k_bbar(n_u, n_u) + , A(n_J, n_u) + , B(n_J, n_u) + , C(n_p, n_u) + {} + void + reset() + {} + }; + template + struct Solid::ScratchData_SC + { + void + reset() + {} + }; + template + void + Solid::make_grid() + { + GridGenerator::hyper_rectangle( + triangulation, + (dim == 3 ? Point(0.0, 0.0, 0.0) : Point(0.0, 0.0)), + (dim == 3 ? Point(1.0, 1.0, 1.0) : Point(1.0, 1.0)), + true); + GridTools::scale(parameters.scale, triangulation); + triangulation.refine_global(std::max(1U, parameters.global_refinement)); + vol_reference = GridTools::volume(triangulation); + std::cout << "Grid:\n\t Reference volume: " << vol_reference << std::endl; + typename Triangulation::active_cell_iterator cell = triangulation + .begin_active(), + endc = + triangulation.end(); + for (; cell != endc; ++cell) + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; + ++face) + { + if (cell->face(face)->at_boundary() == true && + cell->face(face)->center()[1] == 1.0 * parameters.scale) + { + if (dim == 3) + { + if (cell->face(face)->center()[0] < 0.5 * parameters.scale && + cell->face(face)->center()[2] < 0.5 * parameters.scale) + cell->face(face)->set_boundary_id(6); + } + else + { + if (cell->face(face)->center()[0] < 0.5 * parameters.scale) + cell->face(face)->set_boundary_id(6); + } + } + } + } + template + void + Solid::system_setup() + { + timer.enter_subsection("Setup system"); + std::vector block_component(n_components, + u_dof); // Displacement + block_component[p_component] = p_dof; // Pressure + block_component[J_component] = J_dof; // Dilatation + dof_handler_ref.distribute_dofs(fe); + DoFRenumbering::Cuthill_McKee(dof_handler_ref); + DoFRenumbering::component_wise(dof_handler_ref, block_component); + DoFTools::count_dofs_per_block(dof_handler_ref, + dofs_per_block, + block_component); + std::cout << "Triangulation:" + << "\n\t Number of active cells: " + << triangulation.n_active_cells() + << "\n\t Number of degrees of freedom: " + << dof_handler_ref.n_dofs() << std::endl; + tangent_matrix.clear(); + { + const types::global_dof_index n_dofs_u = dofs_per_block[u_dof]; + const types::global_dof_index n_dofs_p = dofs_per_block[p_dof]; + const types::global_dof_index n_dofs_J = dofs_per_block[J_dof]; + BlockDynamicSparsityPattern dsp(n_blocks, n_blocks); + dsp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u); + dsp.block(u_dof, p_dof).reinit(n_dofs_u, n_dofs_p); + dsp.block(u_dof, J_dof).reinit(n_dofs_u, n_dofs_J); + dsp.block(p_dof, u_dof).reinit(n_dofs_p, n_dofs_u); + dsp.block(p_dof, p_dof).reinit(n_dofs_p, n_dofs_p); + dsp.block(p_dof, J_dof).reinit(n_dofs_p, n_dofs_J); + dsp.block(J_dof, u_dof).reinit(n_dofs_J, n_dofs_u); + dsp.block(J_dof, p_dof).reinit(n_dofs_J, n_dofs_p); + dsp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J); + dsp.collect_sizes(); + Table<2, DoFTools::Coupling> coupling(n_components, n_components); + for (unsigned int ii = 0; ii < n_components; ++ii) + for (unsigned int jj = 0; jj < n_components; ++jj) + if (((ii < p_component) && (jj == J_component)) || + ((ii == J_component) && (jj < p_component)) || + ((ii == p_component) && (jj == p_component))) + coupling[ii][jj] = DoFTools::none; + else + coupling[ii][jj] = DoFTools::always; + DoFTools::make_sparsity_pattern( + dof_handler_ref, coupling, dsp, constraints, false); + sparsity_pattern.copy_from(dsp); + } + tangent_matrix.reinit(sparsity_pattern); + system_rhs.reinit(dofs_per_block); + system_rhs.collect_sizes(); + solution_n.reinit(dofs_per_block); + solution_n.collect_sizes(); + setup_qph(); + timer.leave_subsection(); + } + template + void + Solid::determine_component_extractors() + { + element_indices_u.clear(); + element_indices_p.clear(); + element_indices_J.clear(); + for (unsigned int k = 0; k < fe.dofs_per_cell; ++k) + { + const unsigned int k_group = fe.system_to_base_index(k).first.first; + if (k_group == u_dof) + element_indices_u.push_back(k); + else if (k_group == p_dof) + element_indices_p.push_back(k); + else if (k_group == J_dof) + element_indices_J.push_back(k); + else + { + Assert(k_group <= J_dof, ExcInternalError()); + } + } + } + template + void + Solid::setup_qph() + { + std::cout << " Setting up quadrature point data..." << std::endl; + quadrature_point_history.initialize(triangulation.begin_active(), + triangulation.end(), + n_q_points); + for (typename Triangulation::active_cell_iterator cell = + triangulation.begin_active(); + cell != triangulation.end(); + ++cell) + { + const std::vector>> lqph = + quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + lqph[q_point]->setup_lqp(parameters); + } + } + template + void + Solid::solve_nonlinear_timestep( + BlockVector &solution_delta) + { + std::cout << std::endl + << "Timestep " << time.get_timestep() << " @ " << time.current() + << "s" << std::endl; + BlockVector newton_update(dofs_per_block); + error_residual.reset(); + error_residual_0.reset(); + error_residual_norm.reset(); + error_update.reset(); + error_update_0.reset(); + error_update_norm.reset(); + print_conv_header(); + unsigned int newton_iteration = 0; + for (; newton_iteration < parameters.max_iterations_NR; ++newton_iteration) + { + std::cout << " " << std::setw(2) << newton_iteration << " " + << std::flush; + tangent_matrix = 0.0; + system_rhs = 0.0; + make_constraints(newton_iteration); + assemble_system(solution_delta); + get_error_residual(error_residual); + if (newton_iteration == 0) + error_residual_0 = error_residual; + error_residual_norm = error_residual; + error_residual_norm.normalise(error_residual_0); + if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u && + error_residual_norm.u <= parameters.tol_f) + { + std::cout << " CONVERGED! " << std::endl; + print_conv_footer(solution_delta); + break; + } + const std::pair lin_solver_output = + solve_linear_system(newton_update); + get_error_update(newton_update, error_update); + if (newton_iteration == 0) + error_update_0 = error_update; + error_update_norm = error_update; + error_update_norm.normalise(error_update_0); + solution_delta += newton_update; + std::cout << " | " << std::fixed << std::setprecision(3) << std::setw(7) + << std::scientific << lin_solver_output.first << " " + << lin_solver_output.second << " " + << error_residual_norm.norm << " " << error_residual_norm.u + << " " << error_residual_norm.p << " " + << error_residual_norm.J << " " << error_update_norm.norm + << " " << error_update_norm.u << " " << error_update_norm.p + << " " << error_update_norm.J << " " << std::endl; + } + AssertThrow(newton_iteration <= parameters.max_iterations_NR, + ExcMessage("No convergence in nonlinear solver!")); + } + template + void + Solid::print_conv_header() + { + static const unsigned int l_width = 144; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + std::cout << " SOLVER STEP " + << " | LIN_IT LIN_RES RES_NORM " + << " RES_U RES_P RES_J NU_NORM " + << " NU_U NU_P NU_J " << std::endl; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + } + template + void + Solid::print_conv_footer( + const BlockVector &solution_delta) + { + static const unsigned int l_width = 144; + for (unsigned int i = 0; i < l_width; ++i) + std::cout << "_"; + std::cout << std::endl; + const std::pair error_dil = + get_error_dilation(get_total_solution(solution_delta)); + std::cout << "Relative errors:" << std::endl + << "Displacement:\t" << error_update.u / error_update_0.u + << std::endl + << "Force: \t\t" << error_residual.u / error_residual_0.u + << std::endl + << "Dilatation:\t" << error_dil.first << std::endl + << "v / V_0:\t" << error_dil.second * vol_reference << " / " + << vol_reference << " = " << error_dil.second << std::endl; + } + template + std::pair + Solid::get_error_dilation( + const BlockVector &solution_total) const + { + double vol_current = 0.0; + double dil_L2_error = 0.0; + FEValues fe_values_ref( + fe, qf_cell, update_values | update_gradients | update_JxW_values); + std::vector> solution_grads_u_total(qf_cell.size()); + std::vector solution_values_J_total(qf_cell.size()); + for (typename DoFHandler::active_cell_iterator cell = + dof_handler_ref.begin_active(); + cell != dof_handler_ref.end(); + ++cell) + { + fe_values_ref.reinit(cell); + fe_values_ref[u_fe].get_function_gradients(solution_total, + solution_grads_u_total); + fe_values_ref[J_fe].get_function_values(solution_total, + solution_values_J_total); + const std::vector>> lqph = + quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const double det_F_qp = determinant( + StandardTensors::I + solution_grads_u_total[q_point]); + const double J_tilde_qp = solution_values_J_total[q_point]; + const double the_error_qp_squared = + std::pow((det_F_qp - J_tilde_qp), 2); + const double JxW = fe_values_ref.JxW(q_point); + dil_L2_error += the_error_qp_squared * JxW; + vol_current += det_F_qp * JxW; + } + } + Assert(vol_current > 0.0, ExcInternalError()); + + return std::make_pair(std::sqrt(dil_L2_error), vol_current / vol_reference); + } + template + void + Solid::get_error_residual(Errors &error_residual) + { + BlockVector error_res(dofs_per_block); + for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i) + if (!constraints.is_constrained(i)) + error_res(i) = system_rhs(i); + error_residual.norm = error_res.l2_norm(); + error_residual.u = error_res.block(u_dof).l2_norm(); + error_residual.p = error_res.block(p_dof).l2_norm(); + error_residual.J = error_res.block(J_dof).l2_norm(); + } + template + void + Solid::get_error_update( + const BlockVector &newton_update, + Errors & error_update) + { + BlockVector error_ud(dofs_per_block); + for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i) + if (!constraints.is_constrained(i)) + error_ud(i) = newton_update(i); + error_update.norm = error_ud.l2_norm(); + error_update.u = error_ud.block(u_dof).l2_norm(); + error_update.p = error_ud.block(p_dof).l2_norm(); + error_update.J = error_ud.block(J_dof).l2_norm(); + } + template + BlockVector + Solid::get_total_solution( + const BlockVector &solution_delta) const + { + BlockVector solution_total(solution_n); + solution_total += solution_delta; + return solution_total; + } + template + void + Solid::assemble_system( + const BlockVector &solution_delta) + { + timer.enter_subsection("Assemble system"); + std::cout << " ASM_SYS " << std::flush; + system_rhs = 0.0; + const BlockVector solution_total( + get_total_solution(solution_delta)); + const UpdateFlags uf_cell(update_values | update_gradients | + update_JxW_values); + const UpdateFlags uf_face(update_values | update_normal_vectors | + update_JxW_values); + PerTaskData_ASM per_task_data(dofs_per_cell); + ScratchData_ASM scratch_data( + fe, qf_cell, uf_cell, qf_face, uf_face, solution_total); + // ADOL-C is incompatible with TBB + // WorkStream::run(dof_handler_ref.begin_active(), + // dof_handler_ref.end(), + // std::bind(&Solid::assemble_system_one_cell, + // this, + // std::placeholders::_1, + // std::placeholders::_2, + // std::placeholders::_3), + // std::bind(&Solid::copy_local_to_global_system, + // this, + // std::placeholders::_1), + // scratch_data, + // per_task_data); + for (typename DoFHandler::active_cell_iterator cell = + dof_handler_ref.begin_active(); + cell != dof_handler_ref.end(); + ++cell) + { + assemble_system_one_cell(cell, scratch_data, per_task_data); + copy_local_to_global_system(per_task_data); + } + timer.leave_subsection(); + } + template + void + Solid::copy_local_to_global_system( + const PerTaskData_ASM &data) + { + if (data.cell_matrix.frobenius_norm() > 1e-12) + constraints.distribute_local_to_global(data.cell_matrix, + data.cell_rhs, + data.local_dof_indices, + tangent_matrix, + system_rhs); + } + template + void + Solid::assemble_system_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_ASM & scratch, + PerTaskData_ASM & data) const + { + data.reset(); + scratch.reset(); + scratch.fe_values_ref.reinit(cell); + cell->get_dof_indices(data.local_dof_indices); + + const std::vector>> lqph = + quadrature_point_history.get_data(cell); + Assert(lqph.size() == n_q_points, ExcInternalError()); + + const unsigned int n_independent_variables = data.local_dof_indices.size(); + const unsigned int n_dependent_variables = dofs_per_cell; + Assert(n_dependent_variables == n_independent_variables, + ExcMessage("Expect square system.")); + + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + ADHelper ad_helper(n_independent_variables, n_dependent_variables); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + // Values at all DoFs + std::vector dof_values(n_independent_variables); + for (unsigned int i = 0; i < n_independent_variables; ++i) + dof_values[i] = scratch.solution_total(data.local_dof_indices[i]); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no, // material_id + true, // overwrite_tape + true); // keep + + if (is_recording == true) + { + ad_helper.register_independent_variables(dof_values); + const std::vector dof_values_ad = + ad_helper.get_sensitive_variables(); + // Note: It is critical that this vector be initialised with zero'd + // values otherwise the results may be garbage! + std::vector residual_ad(n_dependent_variables, + ADNumberType(0.0)); + + // Compute all values, gradients etc. based on sensitive AD DoF values + std::vector> Grad_u(n_q_points); + std::vector p_tilde(n_q_points, ADNumberType(0.0)); + std::vector J_tilde(n_q_points, ADNumberType(0.0)); + + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + for (unsigned int k = 0; k < dofs_per_cell; ++k) + { + const unsigned int k_group = + fe.system_to_base_index(k).first.first; + if (k_group == u_dof) + Grad_u[q_point] += + dof_values_ad[k] * + scratch.fe_values_ref[u_fe].gradient(k, q_point); + else if (k_group == p_dof) + p_tilde[q_point] += + dof_values_ad[k] * + scratch.fe_values_ref[p_fe].value(k, q_point); + else if (k_group == J_dof) + J_tilde[q_point] += + dof_values_ad[k] * + scratch.fe_values_ref[J_fe].value(k, q_point); + else + Assert(k_group <= J_dof, ExcInternalError()); + } + } + + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + { + const Tensor<2, dim, ADNumberType> F = + unit_symmetric_tensor() + Grad_u[q_point]; + const Tensor<2, dim, ADNumberType> F_inv = invert(F); + const ADNumberType det_F = determinant(F); + Assert(numbers::value_is_greater_than(det_F, 0.0), + ExcMessage("Negative jacobian detected!")); + + const SymmetricTensor<2, dim, ADNumberType> tau = + lqph[q_point]->get_tau(F, p_tilde[q_point]); + const ADNumberType dPsi_vol_dJ = + lqph[q_point]->get_dPsi_vol_dJ(J_tilde[q_point]); + + const double JxW = scratch.fe_values_ref.JxW(q_point); + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + const unsigned int i_group = + fe.system_to_base_index(i).first.first; + if (i_group == u_dof) + { + const SymmetricTensor<2, dim, ADNumberType> symm_grad_Nx_i = + symmetrize( + scratch.fe_values_ref[u_fe].gradient(i, q_point) * + F_inv); + residual_ad[i] += (symm_grad_Nx_i * tau) * JxW; + } + else if (i_group == p_dof) + { + const double N_i = + scratch.fe_values_ref[p_fe].value(i, q_point); + residual_ad[i] += N_i * (det_F - J_tilde[q_point]) * JxW; + } + else if (i_group == J_dof) + { + const double N_i = + scratch.fe_values_ref[J_fe].value(i, q_point); + residual_ad[i] += + N_i * (dPsi_vol_dJ - p_tilde[q_point]) * JxW; + } + else + Assert(i_group <= J_dof, ExcInternalError()); + } + } + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; + ++face) + if (cell->face(face)->at_boundary() == true && + cell->face(face)->boundary_id() == 6) + { + scratch.fe_face_values_ref.reinit(cell, face); + for (unsigned int f_q_point = 0; f_q_point < n_q_points_f; + ++f_q_point) + { + const Tensor<1, dim> &N = + scratch.fe_face_values_ref.normal_vector(f_q_point); + static const double p0 = + -4.0 / (parameters.scale * parameters.scale); + const double time_ramp = (time.current() / time.end()); + const double pressure = p0 * parameters.p_p0 * time_ramp; + const Tensor<1, dim> traction = pressure * N; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + const unsigned int i_group = + fe.system_to_base_index(i).first.first; + if (i_group == u_dof) + { + const unsigned int component_i = + fe.system_to_component_index(i).first; + const double Ni = + scratch.fe_face_values_ref.shape_value(i, + f_q_point); + const double JxW = + scratch.fe_face_values_ref.JxW(f_q_point); + residual_ad[i] -= (Ni * traction[component_i]) * JxW; + } + } + } + } + + ad_helper.register_dependent_variables(residual_ad); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + ad_helper.activate_recorded_tape(tape_no); + // ad_helper.set_independent_variables(dof_values); // Unnecessary when keep + // == true + + // Compute the residual values and their jacobian for the new evaluation + // point + ad_helper.compute_values(data.cell_rhs); + data.cell_rhs *= -1.0; // RHS = - residual + ad_helper.compute_jacobian(data.cell_matrix); + } + template + void + Solid::make_constraints(const int &it_nr) + { + std::cout << " CST " << std::flush; + if (it_nr > 1) + return; + constraints.clear(); + const bool apply_dirichlet_bc = (it_nr == 0); + const FEValuesExtractors::Scalar x_displacement(0); + const FEValuesExtractors::Scalar y_displacement(1); + { + const int boundary_id = 0; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(x_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(x_displacement)); + } + { + const int boundary_id = 2; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(y_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(y_displacement)); + } + if (dim == 3) + { + const FEValuesExtractors::Scalar z_displacement(2); + { + const int boundary_id = 3; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + } + { + const int boundary_id = 4; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(z_displacement)); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + fe.component_mask(z_displacement)); + } + { + const int boundary_id = 6; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement) | + fe.component_mask(z_displacement))); + } + } + else + { + { + const int boundary_id = 3; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + } + { + const int boundary_id = 6; + if (apply_dirichlet_bc == true) + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + else + VectorTools::interpolate_boundary_values( + dof_handler_ref, + boundary_id, + ZeroFunction(n_components), + constraints, + (fe.component_mask(x_displacement))); + } + } + constraints.close(); + } + template + void + Solid::assemble_sc() + { + timer.enter_subsection("Perform static condensation"); + std::cout << " ASM_SC " << std::flush; + PerTaskData_SC per_task_data(dofs_per_cell, + element_indices_u.size(), + element_indices_p.size(), + element_indices_J.size()); + ScratchData_SC scratch_data; + WorkStream::run(dof_handler_ref.begin_active(), + dof_handler_ref.end(), + *this, + &Solid::assemble_sc_one_cell, + &Solid::copy_local_to_global_sc, + scratch_data, + per_task_data); + timer.leave_subsection(); + } + template + void + Solid::copy_local_to_global_sc( + const PerTaskData_SC &data) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + for (unsigned int j = 0; j < dofs_per_cell; ++j) + tangent_matrix.add(data.local_dof_indices[i], + data.local_dof_indices[j], + data.cell_matrix(i, j)); + } + template + void + Solid::assemble_sc_one_cell( + const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC & scratch, + PerTaskData_SC & data) + { + data.reset(); + scratch.reset(); + cell->get_dof_indices(data.local_dof_indices); + data.k_orig.extract_submatrix_from(tangent_matrix, + data.local_dof_indices, + data.local_dof_indices); + data.k_pu.extract_submatrix_from(data.k_orig, + element_indices_p, + element_indices_u); + data.k_pJ.extract_submatrix_from(data.k_orig, + element_indices_p, + element_indices_J); + data.k_JJ.extract_submatrix_from(data.k_orig, + element_indices_J, + element_indices_J); + data.k_pJ_inv.invert(data.k_pJ); + data.k_pJ_inv.mmult(data.A, data.k_pu); + data.k_JJ.mmult(data.B, data.A); + data.k_pJ_inv.Tmmult(data.C, data.B); + data.k_pu.Tmmult(data.k_bbar, data.C); + data.k_bbar.scatter_matrix_to(element_indices_u, + element_indices_u, + data.cell_matrix); + data.k_pJ_inv.add(-1.0, data.k_pJ); + data.k_pJ_inv.scatter_matrix_to(element_indices_p, + element_indices_J, + data.cell_matrix); + } + template + std::pair + Solid::solve_linear_system( + BlockVector &newton_update) + { + unsigned int lin_it = 0; + double lin_res = 0.0; + if (parameters.use_static_condensation == true) + { + BlockVector A(dofs_per_block); + BlockVector B(dofs_per_block); + { + assemble_sc(); + tangent_matrix.block(p_dof, J_dof) + .vmult(A.block(J_dof), system_rhs.block(p_dof)); + tangent_matrix.block(J_dof, J_dof) + .vmult(B.block(J_dof), A.block(J_dof)); + A.block(J_dof) = system_rhs.block(J_dof); + A.block(J_dof) -= B.block(J_dof); + tangent_matrix.block(p_dof, J_dof) + .Tvmult(A.block(p_dof), A.block(J_dof)); + tangent_matrix.block(u_dof, p_dof) + .vmult(A.block(u_dof), A.block(p_dof)); + system_rhs.block(u_dof) -= A.block(u_dof); + timer.enter_subsection("Linear solver"); + std::cout << " SLV " << std::flush; + if (parameters.type_lin == "CG") + { + const int solver_its = tangent_matrix.block(u_dof, u_dof).m() * + parameters.max_iterations_lin; + const double tol_sol = + parameters.tol_lin * system_rhs.block(u_dof).l2_norm(); + SolverControl solver_control(solver_its, tol_sol, false, false); + GrowingVectorMemory> GVM; + SolverCG> solver_CG(solver_control, GVM); + PreconditionSelector, Vector> + preconditioner(parameters.preconditioner_type, + parameters.preconditioner_relaxation); + preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof)); + solver_CG.solve(tangent_matrix.block(u_dof, u_dof), + newton_update.block(u_dof), + system_rhs.block(u_dof), + preconditioner); + lin_it = solver_control.last_step(); + lin_res = solver_control.last_value(); + } + else if (parameters.type_lin == "Direct") + { + SparseDirectUMFPACK A_direct; + A_direct.initialize(tangent_matrix.block(u_dof, u_dof)); + A_direct.vmult(newton_update.block(u_dof), + system_rhs.block(u_dof)); + lin_it = 1; + lin_res = 0.0; + } + else + Assert(false, ExcMessage("Linear solver type not implemented")); + timer.leave_subsection(); + } + constraints.distribute(newton_update); + timer.enter_subsection("Linear solver postprocessing"); + std::cout << " PP " << std::flush; + { + tangent_matrix.block(p_dof, u_dof) + .vmult(A.block(p_dof), newton_update.block(u_dof)); + A.block(p_dof) *= -1.0; + A.block(p_dof) += system_rhs.block(p_dof); + tangent_matrix.block(p_dof, J_dof) + .vmult(newton_update.block(J_dof), A.block(p_dof)); + } + constraints.distribute(newton_update); + { + tangent_matrix.block(J_dof, J_dof) + .vmult(A.block(J_dof), newton_update.block(J_dof)); + A.block(J_dof) *= -1.0; + A.block(J_dof) += system_rhs.block(J_dof); + tangent_matrix.block(p_dof, J_dof) + .Tvmult(newton_update.block(p_dof), A.block(J_dof)); + } + constraints.distribute(newton_update); + timer.leave_subsection(); + } + else + { + std::cout << " ------ " << std::flush; + timer.enter_subsection("Linear solver"); + std::cout << " SLV " << std::flush; + if (parameters.type_lin == "CG") + { + const Vector &f_u = system_rhs.block(u_dof); + const Vector &f_p = system_rhs.block(p_dof); + const Vector &f_J = system_rhs.block(J_dof); + Vector & d_u = newton_update.block(u_dof); + Vector & d_p = newton_update.block(p_dof); + Vector & d_J = newton_update.block(J_dof); + const auto K_uu = + linear_operator(tangent_matrix.block(u_dof, u_dof)); + const auto K_up = + linear_operator(tangent_matrix.block(u_dof, p_dof)); + const auto K_pu = + linear_operator(tangent_matrix.block(p_dof, u_dof)); + const auto K_Jp = + linear_operator(tangent_matrix.block(J_dof, p_dof)); + const auto K_JJ = + linear_operator(tangent_matrix.block(J_dof, J_dof)); + PreconditionSelector, Vector> + preconditioner_K_Jp_inv("jacobi"); + preconditioner_K_Jp_inv.use_matrix( + tangent_matrix.block(J_dof, p_dof)); + ReductionControl solver_control_K_Jp_inv( + tangent_matrix.block(J_dof, p_dof).m() * + parameters.max_iterations_lin, + 1.0e-30, + parameters.tol_lin); + SolverSelector> solver_K_Jp_inv; + solver_K_Jp_inv.select("cg"); + solver_K_Jp_inv.set_control(solver_control_K_Jp_inv); + const auto K_Jp_inv = + inverse_operator(K_Jp, solver_K_Jp_inv, preconditioner_K_Jp_inv); + const auto K_pJ_inv = transpose_operator(K_Jp_inv); + const auto K_pp_bar = K_Jp_inv * K_JJ * K_pJ_inv; + const auto K_uu_bar_bar = K_up * K_pp_bar * K_pu; + const auto K_uu_con = K_uu + K_uu_bar_bar; + PreconditionSelector, Vector> + preconditioner_K_con_inv(parameters.preconditioner_type, + parameters.preconditioner_relaxation); + preconditioner_K_con_inv.use_matrix( + tangent_matrix.block(u_dof, u_dof)); + ReductionControl solver_control_K_con_inv( + tangent_matrix.block(u_dof, u_dof).m() * + parameters.max_iterations_lin, + 1.0e-30, + parameters.tol_lin); + SolverSelector> solver_K_con_inv; + solver_K_con_inv.select("cg"); + solver_K_con_inv.set_control(solver_control_K_con_inv); + const auto K_uu_con_inv = + inverse_operator(K_uu_con, + solver_K_con_inv, + preconditioner_K_con_inv); + d_u = + K_uu_con_inv * (f_u - K_up * (K_Jp_inv * f_J - K_pp_bar * f_p)); + timer.leave_subsection(); + timer.enter_subsection("Linear solver postprocessing"); + std::cout << " PP " << std::flush; + d_J = K_pJ_inv * (f_p - K_pu * d_u); + d_p = K_Jp_inv * (f_J - K_JJ * d_J); + lin_it = solver_control_K_con_inv.last_step(); + lin_res = solver_control_K_con_inv.last_value(); + } + else if (parameters.type_lin == "Direct") + { + SparseDirectUMFPACK A_direct; + A_direct.initialize(tangent_matrix); + A_direct.vmult(newton_update, system_rhs); + lin_it = 1; + lin_res = 0.0; + std::cout << " -- " << std::flush; + } + else + Assert(false, ExcMessage("Linear solver type not implemented")); + timer.leave_subsection(); + constraints.distribute(newton_update); + } + return std::make_pair(lin_it, lin_res); + } + template + void + Solid::output_results() const + { + DataOut data_out; + std::vector + data_component_interpretation( + dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation.push_back( + DataComponentInterpretation::component_is_scalar); + data_component_interpretation.push_back( + DataComponentInterpretation::component_is_scalar); + std::vector solution_name(dim, "displacement"); + solution_name.push_back("pressure"); + solution_name.push_back("dilatation"); + data_out.attach_dof_handler(dof_handler_ref); + data_out.add_data_vector(solution_n, + solution_name, + DataOut::type_dof_data, + data_component_interpretation); + Vector soln(solution_n.size()); + for (unsigned int i = 0; i < soln.size(); ++i) + soln(i) = solution_n(i); + MappingQEulerian q_mapping(degree, dof_handler_ref, soln); + data_out.build_patches(q_mapping, degree); + std::ostringstream filename; + filename << "solution-" << dim << "d-" << time.get_timestep() << ".vtk"; + std::ofstream output(filename.str().c_str()); + data_out.write_vtk(output); + } +} // namespace Step44 diff --git a/tests/ad_common_tests/symmetric_tensor_functions_02.h b/tests/ad_common_tests/symmetric_tensor_functions_02.h new file mode 100644 index 0000000000..202eca1168 --- /dev/null +++ b/tests/ad_common_tests/symmetric_tensor_functions_02.h @@ -0,0 +1,226 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Test to check that tensor functions both compile and produce the right +// result when differentiated using the various auto-differentiable number +// types: Tensor inverse + +#include + +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestSymmetricTensor +{ + static SymmetricTensor<4, dim, NumberType> + dt_inv_dt(const SymmetricTensor<2, dim, NumberType> &t_inv) + { + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_inverse_of_a_tensor + SymmetricTensor<4, dim, NumberType> dt_inv_dt; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + dt_inv_dt[i][j][k][l] = + -0.5 * (t_inv[i][k] * t_inv[j][l] + t_inv[i][l] * t_inv[j][k]); + return dt_inv_dt; + } + + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &t) + { + // Previously, the invert function would hang for nested Sacado::Fad::DFad + const SymmetricTensor<2, dim, NumberType> t_inv = invert(t); + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 3.0 * scalar_product(t_inv, I); + } + + static SymmetricTensor<2, dim, NumberType> + dpsi_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = invert(t); + const SymmetricTensor<4, dim, NumberType> dt_inv_dt = + FunctionsTestSymmetricTensor::dt_inv_dt(t_inv); + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 3.0 * (I * dt_inv_dt); + } + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const SymmetricTensor<2, dim, NumberType> &t) + { + const SymmetricTensor<2, dim, NumberType> t_inv = invert(t); + const SymmetricTensor<4, dim, NumberType> dt_inv_dt = + FunctionsTestSymmetricTensor::dt_inv_dt(t_inv); + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + + SymmetricTensor<4, dim, NumberType> d2psi_dt_dt; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int j = i; j < dim; ++j) + for (unsigned int m = 0; m < dim; ++m) + for (unsigned int n = m; n < dim; ++n) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int l = k; l < dim; ++l) + d2psi_dt_dt[i][j][m][n] += + -3.0 * (0.5 * I[k][l] * + (dt_inv_dt[i][k][m][n] * t_inv[j][l] + + dt_inv_dt[i][l][m][n] * t_inv[j][k] + + t_inv[i][k] * dt_inv_dt[j][l][m][n] + + t_inv[i][l] * dt_inv_dt[j][k][m][n])); + + return d2psi_dt_dt; + } +}; + +template +void +test_symmetric_tensor() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Tensor<2,dim,ADNumberType> grad_u; + // const Tensor<2,dim,ADNumberType> F = + // Physics::Elasticity::Kinematics::F(grad_u); + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestSymmetricTensor func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> t_dof(0); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + + const SymmetricTensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + + const ADNumberType psi(func_ad::psi(t_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 1.15; + ad_helper.set_independent_variable(t, t_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + + // Verify the result + typedef FunctionsTestSymmetricTensor func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(t): " << func::psi(t) << std::endl; + Assert(std::abs(psi - func::psi(t)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t): " << func::dpsi_dt(t) << std::endl; + Assert(std::abs((dpsi_dt - func::dpsi_dt(t)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t): " << func::d2psi_dt_dt(t) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} diff --git a/tests/ad_common_tests/symmetric_tensor_functions_03.h b/tests/ad_common_tests/symmetric_tensor_functions_03.h new file mode 100644 index 0000000000..d85bef28ac --- /dev/null +++ b/tests/ad_common_tests/symmetric_tensor_functions_03.h @@ -0,0 +1,445 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Test to check that tensor functions both compile and produce the right +// result when differentiated using the various auto-differentiable number +// types: Eigenvalues + +#include + +#include + +#include "../tests.h" +#ifdef DEAL_II_ADOLC_WITH_ADVANCED_BRANCHING +# include +#endif + +#include + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +double +mu() +{ + return 100.0; +} + + +template +NumberType +J(const SymmetricTensor<2, dim, NumberType> &C) +{ + return std::sqrt(determinant(C)); +} + +template +struct IncompressibleNeoHookean +{ + // Incompressible Neo-Hookean material + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &C) + { + return 0.5 * mu() * (trace(C) - dim) - mu() * std::log(J(C)); + } + + static SymmetricTensor<2, dim, NumberType> + dpsi_dC(const SymmetricTensor<2, dim, NumberType> &C) + { + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 0.5 * mu() * (I - invert(C)); + } + + static SymmetricTensor<4, dim, NumberType> + d2psi_dC_dC(const SymmetricTensor<2, dim, NumberType> &C) + { + const SymmetricTensor<2, dim, NumberType> C_inv = invert(C); + + SymmetricTensor<4, dim, NumberType> dC_inv_dC; + for (unsigned int A = 0; A < dim; ++A) + for (unsigned int B = A; B < dim; ++B) + for (unsigned int C = 0; C < dim; ++C) + for (unsigned int D = C; D < dim; ++D) + dC_inv_dC[A][B][C][D] -= + 0.5 * (C_inv[A][C] * C_inv[B][D] + C_inv[A][D] * C_inv[B][C]); + + return -0.5 * mu() * dC_inv_dC; + } +}; + +template +struct IncompressibleNeoHookeanPrincipalStretches +{ + // Incompressible Neo-Hookean material + static NumberType + psi(const std::array>, dim> + eig_C) + { + NumberType psi = 0.0; + NumberType J = 1.0; + for (unsigned int d = 0; d < dim; ++d) + { + const NumberType &lambda_squared = eig_C[d].first; + psi += 0.5 * mu() * (lambda_squared - 1.0); + J *= std::sqrt(lambda_squared); + } + psi -= mu() * std::log(J); + return psi; + } + + // static SymmetricTensor<2,dim,NumberType> + // dpsi_dC (const std::array + // >,dim> eig_C) + // { + // SymmetricTensor<2,dim,NumberType> C_inv; + // for (unsigned int d=0; d &N = eig_C[d].second; + // C_inv += (1.0/lambda_squared)*symmetrize(outer_product(N,N)); + // } + // + // const SymmetricTensor<2,dim,NumberType> I = + // unit_symmetric_tensor(); return 0.5*mu()*(I - C_inv); + // } +}; + +template +void +test_NH(const bool nontrivial_initial_values) +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Standard definition of incompressible NeoHookean material, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << ", " + << "Nontrivial initial values: " << std::boolalpha + << nontrivial_initial_values << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef IncompressibleNeoHookean func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> C_dof(0); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> C = + unit_symmetric_tensor(); + if (nontrivial_initial_values) + for (unsigned int i = 0; i < C.n_independent_components; ++i) + C[C.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(C, C_dof); + + const SymmetricTensor<2, dim, ADNumberType> C_ad = + ad_helper.get_sensitive_variables(C_dof); + + const ADNumberType psi(func_ad::psi(C_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "C_ad: " << C_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + C *= 1.15; + ad_helper.set_independent_variable(C, C_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dC = + ad_helper.extract_gradient_component(Dpsi, C_dof); + + // Verify the result + typedef IncompressibleNeoHookean func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(C): " << func::psi(C) << std::endl; + Assert(std::abs(psi - func::psi(C)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dC: " << dpsi_dC << std::endl; + std::cout << "func::dpsi_dC(C): " << func::dpsi_dC(C) << std::endl; + Assert(std::abs((dpsi_dC - func::dpsi_dC(C)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dC_dC = + ad_helper.extract_hessian_component(D2psi, C_dof, C_dof); + std::cout << "d2psi_dC_dC: " << d2psi_dC_dC << std::endl; + std::cout << "func::d2psi_dC_dC(C): " << func::d2psi_dC_dC(C) + << std::endl; + Assert(std::abs((d2psi_dC_dC - func::d2psi_dC_dC(C)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} + + + +template +void +test_NH_eigen_energy(const enum SymmetricTensorEigenvectorMethod method, + const bool nontrivial_initial_values) +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout + << "*** Principal stretch definition of incompressible NeoHookean material (from free energy function), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << ", " + << "Eig method: " << static_cast(method) << ", " + << "Nontrivial initial values: " << std::boolalpha + << nontrivial_initial_values << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef IncompressibleNeoHookeanPrincipalStretches func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> C_dof(0); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> C = + unit_symmetric_tensor(); + if (nontrivial_initial_values) + for (unsigned int i = 0; i < C.n_independent_components; ++i) + C[C.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(C, C_dof); + + const SymmetricTensor<2, dim, ADNumberType> C_ad = + ad_helper.get_sensitive_variables(C_dof); + const auto eig_C_ad = eigenvectors(C_ad, method); + + const ADNumberType psi(func_ad::psi(eig_C_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "C_ad: " << C_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + C *= 1.15; + ad_helper.set_independent_variable(C, C_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dC = + ad_helper.extract_gradient_component(Dpsi, C_dof); + + // Verify the result + typedef IncompressibleNeoHookean func; + static const ScalarNumberType tol_val = + (nontrivial_initial_values ? + 1e-6 : + 1e5 * std::numeric_limits::epsilon()); + static const ScalarNumberType tol_grad = + (nontrivial_initial_values ? + 1e-4 : + 1e5 * std::numeric_limits::epsilon()); + static const ScalarNumberType tol_hess = + (nontrivial_initial_values ? + 2e-2 /*1e-2,5e-3*/ : + 1e5 * std::numeric_limits::epsilon()); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(C): " << func::psi(C) << std::endl; + std::cout << "DIFF NORM: " << std::abs(psi - func::psi(C)) << std::endl; + Assert(std::abs(psi - func::psi(C)) < tol_val, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dC: " << dpsi_dC << std::endl; + std::cout << "func::dpsi_dC(C): " << func::dpsi_dC(C) << std::endl; + std::cout << "DIFF NORM: " << std::abs((dpsi_dC - func::dpsi_dC(C)).norm()) + << std::endl; + Assert(std::abs((dpsi_dC - func::dpsi_dC(C)).norm()) < tol_grad, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dC_dC = + ad_helper.extract_hessian_component(D2psi, C_dof, C_dof); + std::cout << "d2psi_dC_dC: " << d2psi_dC_dC << std::endl; + std::cout << "func::d2psi_dC_dC(C): " << func::d2psi_dC_dC(C) + << std::endl; + // std::cout << "DIFF: " << (d2psi_dC_dC - func::d2psi_dC_dC(C)) << + // std::endl; + std::cout << "DIFF NORM: " + << std::abs((d2psi_dC_dC - func::d2psi_dC_dC(C)).norm()) + << std::endl; + Assert(std::abs((d2psi_dC_dC - func::d2psi_dC_dC(C)).norm()) < tol_hess, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} + + + +template +void +test_symmetric_tensor() +{ + // Then test the functions that compute the eigenvalues: + // Note: Derived from an energy function, only the eigenvalues are used + + // Non-trivial initial values (unequal eigenvalues): + { + const bool nontrivial_initial_values = true; + + // First verify that all manual calculations are correct + test_NH(nontrivial_initial_values); + + test_NH_eigen_energy( + SymmetricTensorEigenvectorMethod::hybrid, nontrivial_initial_values); + test_NH_eigen_energy( + SymmetricTensorEigenvectorMethod::ql_implicit_shifts, + nontrivial_initial_values); + test_NH_eigen_energy( + SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values); + } + // Trivial initial values (equal eigenvalues): + { + const bool nontrivial_initial_values = false; + + // First verify that all manual calculations are correct + test_NH(nontrivial_initial_values); + + // test_NH_eigen_energy(SymmetricTensorEigenvectorMethod::hybrid,nontrivial_initial_values); + // // This will never work. + test_NH_eigen_energy( + SymmetricTensorEigenvectorMethod::ql_implicit_shifts, + nontrivial_initial_values); + test_NH_eigen_energy( + SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values); + } +} diff --git a/tests/ad_common_tests/symmetric_tensor_functions_04.h b/tests/ad_common_tests/symmetric_tensor_functions_04.h new file mode 100644 index 0000000000..52545dafc2 --- /dev/null +++ b/tests/ad_common_tests/symmetric_tensor_functions_04.h @@ -0,0 +1,447 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Test to check that tensor functions both compile and produce the right +// result when differentiated using the various auto-differentiable number +// types: Eigenvalues and eignvectors + +#include + +#include + +#include "../tests.h" +#ifdef DEAL_II_ADOLC_WITH_ADVANCED_BRANCHING +# include +#endif + +#include + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +double +mu() +{ + return 100.0; +} + + +template +NumberType +J(const SymmetricTensor<2, dim, NumberType> &C) +{ + return std::sqrt(determinant(C)); +} + +template +struct IncompressibleNeoHookean +{ + // Incompressible Neo-Hookean material + static NumberType + psi(const SymmetricTensor<2, dim, NumberType> &C) + { + return 0.5 * mu() * (trace(C) - dim) - mu() * std::log(J(C)); + } + + static SymmetricTensor<2, dim, NumberType> + dpsi_dC(const SymmetricTensor<2, dim, NumberType> &C) + { + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 0.5 * mu() * (I - invert(C)); + } + + static SymmetricTensor<4, dim, NumberType> + d2psi_dC_dC(const SymmetricTensor<2, dim, NumberType> &C) + { + const SymmetricTensor<2, dim, NumberType> C_inv = invert(C); + + SymmetricTensor<4, dim, NumberType> dC_inv_dC; + for (unsigned int A = 0; A < dim; ++A) + for (unsigned int B = A; B < dim; ++B) + for (unsigned int C = 0; C < dim; ++C) + for (unsigned int D = C; D < dim; ++D) + dC_inv_dC[A][B][C][D] -= + 0.5 * (C_inv[A][C] * C_inv[B][D] + C_inv[A][D] * C_inv[B][C]); + + return -0.5 * mu() * dC_inv_dC; + } +}; + +template +struct IncompressibleNeoHookeanPrincipalStretches +{ + // // Incompressible Neo-Hookean material + // static NumberType + // psi (const std::array + // >,dim> eig_C) + // { + // NumberType psi = 0.0; + // NumberType J = 1.0; + // for (unsigned int d=0; d + dpsi_dC(const std::array>, + dim> eig_C) + { + SymmetricTensor<2, dim, NumberType> C_inv; + for (unsigned int d = 0; d < dim; ++d) + { + const NumberType & lambda_squared = eig_C[d].first; + const Tensor<1, dim, NumberType> &N = eig_C[d].second; + C_inv += (1.0 / lambda_squared) * symmetrize(outer_product(N, N)); + } + + const SymmetricTensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 0.5 * mu() * (I - C_inv); + } +}; + +template +void +test_NH(const bool nontrivial_initial_values) +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** Standard definition of incompressible NeoHookean material, " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << ", " + << "Nontrivial initial values: " << std::boolalpha + << nontrivial_initial_values << std::endl; + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef IncompressibleNeoHookean func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> C_dof(0); + const unsigned int n_AD_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> C = + unit_symmetric_tensor(); + if (nontrivial_initial_values) + for (unsigned int i = 0; i < C.n_independent_components; ++i) + C[C.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(C, C_dof); + + const SymmetricTensor<2, dim, ADNumberType> C_ad = + ad_helper.get_sensitive_variables(C_dof); + + const ADNumberType psi(func_ad::psi(C_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "C_ad: " << C_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + C *= 1.15; + ad_helper.set_independent_variable(C, C_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dC = + ad_helper.extract_gradient_component(Dpsi, C_dof); + + // Verify the result + typedef IncompressibleNeoHookean func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(C): " << func::psi(C) << std::endl; + Assert(std::abs(psi - func::psi(C)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dC: " << dpsi_dC << std::endl; + std::cout << "func::dpsi_dC(C): " << func::dpsi_dC(C) << std::endl; + Assert(std::abs((dpsi_dC - func::dpsi_dC(C)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dC_dC = + ad_helper.extract_hessian_component(D2psi, C_dof, C_dof); + std::cout << "d2psi_dC_dC: " << d2psi_dC_dC << std::endl; + std::cout << "func::d2psi_dC_dC(C): " << func::d2psi_dC_dC(C) + << std::endl; + Assert(std::abs((d2psi_dC_dC - func::d2psi_dC_dC(C)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} + + + +template +void +test_NH_eigen_stress(const enum SymmetricTensorEigenvectorMethod method, + const bool nontrivial_initial_values) +{ + typedef AD::VectorFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout + << "*** Principal stretch definition of incompressible NeoHookean material (from stress), " + << "dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << ", " + << "Eig method: " << static_cast(method) << ", " + << "Nontrivial initial values: " << std::boolalpha + << nontrivial_initial_values << std::endl; + + // Values computed from the AD energy function + // ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef IncompressibleNeoHookeanPrincipalStretches func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::SymmetricTensor<2> C_dof(0); + const unsigned int n_AD_independent_components = + SymmetricTensor<2, dim>::n_independent_components; + const unsigned int n_AD_dependent_components = + SymmetricTensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_independent_components, n_AD_dependent_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + SymmetricTensor<2, dim, ScalarNumberType> C = + unit_symmetric_tensor(); + if (nontrivial_initial_values) + for (unsigned int i = 0; i < C.n_independent_components; ++i) + C[C.unrolled_to_component_indices(i)] += 0.12 * (i + 0.02); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(C, C_dof); + + const SymmetricTensor<2, dim, ADNumberType> C_ad = + ad_helper.get_sensitive_variables(C_dof); + const auto eig_C_ad = eigenvectors(C_ad, method); + + for (unsigned int d = 0; d < dim; ++d) + std::cout << " Direction: " << d + << " Eigenvalue: " << eig_C_ad[d].first + << " Eigenvector: " << eig_C_ad[d].second << std::endl; + + const SymmetricTensor<2, dim, ADNumberType> dpsi_dC( + func_ad::dpsi_dC(eig_C_ad)); + + ad_helper.register_dependent_variable(dpsi_dC, C_dof); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "C_ad: " << C_ad << std::endl; + std::cout << "dpsi_dC: " << dpsi_dC << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + C *= 1.15; + ad_helper.set_independent_variable(C, C_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + // psi = ad_helper.compute_value(); + ad_helper.compute_values(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 1) + { + ad_helper.compute_jacobian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + // std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 1) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const SymmetricTensor<2, dim, ScalarNumberType> dpsi_dC = + ad_helper.extract_value_component(Dpsi, C_dof); + + // Verify the result + typedef IncompressibleNeoHookean func; + static const ScalarNumberType tol_val = + (nontrivial_initial_values ? + 1e-4 : + 1e6 * std::numeric_limits::epsilon()); + static const ScalarNumberType tol_jac = + (nontrivial_initial_values ? + 2.5e-3 : + 1e6 * std::numeric_limits::epsilon()); + // std::cout << "psi: " << psi << std::endl; + // std::cout << "func::psi(C): " << func::psi(C) << std::endl; + // Assert(std::abs(psi - func::psi(C)) < tol, ExcMessage("No match for + // function value.")); + std::cout << "dpsi_dC: " << dpsi_dC << std::endl; + std::cout << "func::dpsi_dC(C): " << func::dpsi_dC(C) << std::endl; + // std::cout << "DIFF NORM: " << std::abs((dpsi_dC - + // func::dpsi_dC(C)).norm()) << std::endl; + Assert(std::abs((dpsi_dC - func::dpsi_dC(C)).norm()) < tol_val, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 1) + { + const SymmetricTensor<4, dim, ScalarNumberType> d2psi_dC_dC = + ad_helper.extract_jacobian_component(D2psi, C_dof, C_dof); + std::cout << "d2psi_dC_dC: " << d2psi_dC_dC << std::endl; + std::cout << "func::d2psi_dC_dC(C): " << func::d2psi_dC_dC(C) + << std::endl; + // std::cout << "DIFF: " << (d2psi_dC_dC - func::d2psi_dC_dC(C)) << + // std::endl; std::cout << "DIFF NORM: " << std::abs((d2psi_dC_dC - + // func::d2psi_dC_dC(C)).norm()) << std::endl; + Assert(std::abs((d2psi_dC_dC - func::d2psi_dC_dC(C)).norm()) < tol_jac, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +} + + + +template +void +test_symmetric_tensor() +{ + // Then test the functions that compute the eigenvalues/vectors: + // Note: Derived from a stress, both the eigenvalues and eigenvectors are used + + // Non-trivial initial values (unequal eigenvalues): + { + const bool nontrivial_initial_values = true; + + // First verify that all manual calculations are correct + test_NH(nontrivial_initial_values); + + test_NH_eigen_stress( + SymmetricTensorEigenvectorMethod::hybrid, nontrivial_initial_values); + test_NH_eigen_stress( + SymmetricTensorEigenvectorMethod::ql_implicit_shifts, + nontrivial_initial_values); + test_NH_eigen_stress( + SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values); + } + // Trivial initial values (equal eigenvalues): + { + const bool nontrivial_initial_values = false; + + // First verify that all manual calculations are correct + test_NH(nontrivial_initial_values); + + // test_NH_eigen_stress(SymmetricTensorEigenvectorMethod::hybrid,nontrivial_initial_values); + // // This will never work. + test_NH_eigen_stress( + SymmetricTensorEigenvectorMethod::ql_implicit_shifts, + nontrivial_initial_values); + test_NH_eigen_stress( + SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values); + } +} diff --git a/tests/ad_common_tests/tensor_functions_02.h b/tests/ad_common_tests/tensor_functions_02.h new file mode 100644 index 0000000000..335060ffb0 --- /dev/null +++ b/tests/ad_common_tests/tensor_functions_02.h @@ -0,0 +1,220 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2016 - 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// Header file: +// Test to check that tensor functions both compile and produce the right +// result when differentiated using the various auto-differentiable number +// types: Tensor inverse + +#include + +#include + +#include + +#include "../tests.h" + +using namespace dealii; +namespace AD = dealii::Differentiation::AD; + +template +struct FunctionsTestTensor +{ + static Tensor<4, dim, NumberType> + dt_inv_dt(const Tensor<2, dim, NumberType> &t_inv) + { + // https://en.wikiversity.org/wiki/Introduction_to_Elasticity/Tensors#Derivative_of_the_inverse_of_a_tensor + Tensor<4, dim, NumberType> dt_inv_dt; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int J = 0; J < dim; ++J) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int L = 0; L < dim; ++L) + dt_inv_dt[J][i][k][L] = -t_inv[J][k] * t_inv[L][i]; + return dt_inv_dt; + } + + static NumberType + psi(const Tensor<2, dim, NumberType> &t) + { + // Previously, the invert function would hang for nested Sacado::Fad::DFad + const Tensor<2, dim, NumberType> t_inv = invert(t); + const Tensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 3.0 * scalar_product(t_inv, I); + } + + static Tensor<2, dim, NumberType> + dpsi_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + const Tensor<4, dim, NumberType> dt_inv_dt = + FunctionsTestTensor::dt_inv_dt(t_inv); + const Tensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + return 3.0 * double_contract<0, 1, 1, 0>(I, dt_inv_dt); + } + + static Tensor<4, dim, NumberType> + d2psi_dt_dt(const Tensor<2, dim, NumberType> &t) + { + const Tensor<2, dim, NumberType> t_inv = invert(t); + const Tensor<4, dim, NumberType> dt_inv_dt = + FunctionsTestTensor::dt_inv_dt(t_inv); + const Tensor<2, dim, NumberType> I = + unit_symmetric_tensor(); + + Tensor<4, dim, NumberType> d2psi_dt_dt; + for (unsigned int i = 0; i < dim; ++i) + for (unsigned int J = 0; J < dim; ++J) + for (unsigned int k = 0; k < dim; ++k) + for (unsigned int L = 0; L < dim; ++L) + for (unsigned int alpha = 0; alpha < dim; ++alpha) + d2psi_dt_dt[i][J][k][L] += + -3.0 * (dt_inv_dt[alpha][i][k][L] * t_inv[J][alpha] + + t_inv[alpha][i] * dt_inv_dt[J][alpha][k][L]); + + return d2psi_dt_dt; + } +}; + +template +void +test_tensor() +{ + typedef AD::ScalarFunction ADHelper; + typedef typename ADHelper::ad_type ADNumberType; + typedef typename ADHelper::scalar_type ScalarNumberType; + + std::cout << "*** dim = " << Utilities::to_string(dim) << ", " + << "Type code: " << static_cast(ad_type_code) << std::endl; + + // Tensor<2,dim,ADNumberType> grad_u; + // const Tensor<2,dim,ADNumberType> F = + // Physics::Elasticity::Kinematics::F(grad_u); + + // Values computed from the AD energy function + ScalarNumberType psi; + Vector Dpsi; + FullMatrix D2psi; + + // Function and its derivatives + typedef FunctionsTestTensor func_ad; + + // Setup the variable components and choose a value at which to + // evaluate the tape + const FEValuesExtractors::Tensor<2> t_dof(0); + const unsigned int n_AD_components = Tensor<2, dim>::n_independent_components; + ADHelper ad_helper(n_AD_components); + ad_helper.set_tape_buffer_sizes(); // Increase the buffer size from the + // default values + + Tensor<2, dim, ScalarNumberType> t = + unit_symmetric_tensor(); + for (unsigned int i = 0; i < t.n_independent_components; ++i) + t[t.unrolled_to_component_indices(i)] += 0.14 * (i + 0.07); + + const int tape_no = 1; + const bool is_recording = + ad_helper.start_recording_operations(tape_no /*material_id*/, + true /*overwrite_tape*/, + true /*keep*/); + if (is_recording == true) + { + ad_helper.register_independent_variable(t, t_dof); + + const Tensor<2, dim, ADNumberType> t_ad = + ad_helper.get_sensitive_variables(t_dof); + + const ADNumberType psi(func_ad::psi(t_ad)); + + ad_helper.register_dependent_variable(psi); + ad_helper.stop_recording_operations(false /*write_tapes_to_file*/); + + std::cout << "Recorded data..." << std::endl; + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + std::cout << "t_ad: " << t_ad << std::endl; + std::cout << "psi: " << psi << std::endl; + std::cout << std::endl; + } + else + { + Assert(is_recording == true, ExcInternalError()); + } + + // Do some work :-) + // Set a new evaluation point + if (AD::ADNumberTraits::is_taped == true) + { + std::cout + << "Using tape with different values for independent variables..." + << std::endl; + ad_helper.activate_recorded_tape(tape_no); + t *= 1.15; + ad_helper.set_independent_variable(t, t_dof); + + std::cout << "independent variable values: " << std::flush; + ad_helper.print_values(std::cout); + } + + // Compute the function value, gradient and hessian for the new evaluation + // point + psi = ad_helper.compute_value(); + ad_helper.compute_gradient(Dpsi); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + ad_helper.compute_hessian(D2psi); + } + + // Output the full stored function, gradient vector and hessian matrix + std::cout << "psi: " << psi << std::endl; + std::cout << "Dpsi: \n"; + Dpsi.print(std::cout); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + std::cout << "D2psi: \n"; + D2psi.print_formatted(std::cout, 3, true, 0, "0.0"); + } + + // Extract components of the solution + const Tensor<2, dim, ScalarNumberType> dpsi_dt = + ad_helper.extract_gradient_component(Dpsi, t_dof); + + // Verify the result + typedef FunctionsTestTensor func; + static const ScalarNumberType tol = + 1e5 * std::numeric_limits::epsilon(); + std::cout << "psi: " << psi << std::endl; + std::cout << "func::psi(t): " << func::psi(t) << std::endl; + Assert(std::abs(psi - func::psi(t)) < tol, + ExcMessage("No match for function value.")); + std::cout << "dpsi_dt: " << dpsi_dt << std::endl; + std::cout << "func::dpsi_dt(t): " << func::dpsi_dt(t) << std::endl; + Assert(std::abs((dpsi_dt - func::dpsi_dt(t)).norm()) < tol, + ExcMessage("No match for first derivative.")); + if (AD::ADNumberTraits::n_supported_derivative_levels >= 2) + { + const Tensor<4, dim, ScalarNumberType> d2psi_dt_dt = + ad_helper.extract_hessian_component(D2psi, t_dof, t_dof); + std::cout << "d2psi_dt_dt: " << d2psi_dt_dt << std::endl; + std::cout << "func::d2psi_dt_dt(t): " << func::d2psi_dt_dt(t) + << std::endl; + Assert(std::abs((d2psi_dt_dt - func::d2psi_dt_dt(t)).norm()) < tol, + ExcMessage("No match for second derivative.")); + } + + std::cout << std::endl << std::endl; +}