--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_scalar_scalar_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestScalarScalarCoupled<dim, ADNumberType> 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_vector_scalar_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestVectorScalarCoupled<dim, ADNumberType> 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor_scalar_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensorScalarCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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<dim, NumberType>());
+ // 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_vector_vector_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestVectorVectorCoupled<dim, ADNumberType> 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor_vector_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensorVectorCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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<dim, NumberType>());
+ 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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_symmetric_tensor_vector_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestSymmetricTensorVectorCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_symmetric_tensor_tensor_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestSymmetricTensorTensorCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_symmetric_tensor_symmetric_tensor_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestSymmetricTensorSymmetricTensorCoupled<dim, ADNumberType>
+ 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<dim, ScalarNumberType>();
+ 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim,
+ ScalarNumberType>
+ func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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 <int dim, typename NumberType>
+const double FunctionsTestTensorVectorScalarCoupled<dim, NumberType>::sf = 2.2;
+
+template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor_vector_scalar_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensorVectorScalarCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<dim, NumberType>());
+ 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 <int dim, typename NumberType>
+const double FunctionsTestTensorVectorScalarCoupled<dim, NumberType>::sf = 2.2;
+
+template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor_vector_scalar_coupled()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensorVectorScalarCoupled<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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."));
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_scalar()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestScalar<dim, ADNumberType> 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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<dim>());
+ const Tensor<2, dim, NumberType> I_ns(I);
+ return 4.0 * I_ns;
+ }
+};
+
+template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_vector()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestVector<dim, ADNumberType> 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensor<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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<Tensor<2, dim, NumberType>>(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<Tensor<2, dim, NumberType>>(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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_symmetric_tensor()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestSymmetricTensor<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_AD_vector_jacobian()
+{
+ typedef AD::VectorFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Function and its derivatives
+ typedef FunctionsTestScalar<dim, ADNumberType> 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<ScalarNumberType> 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<ADNumberType> s_ad =
+ ad_helper.get_sensitive_variables();
+
+ // ADNumberType f0 (s_ad*s_ad*s_ad);
+ std::vector<ADNumberType> 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<ADNumberType>::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<ScalarNumberType> funcs(n_vars_dep);
+ FullMatrix<ScalarNumberType> 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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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."));
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_AD_vector_jacobian()
+{
+ typedef AD::VectorFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Function and its derivatives
+ typedef FunctionsTestSquare<dim, ADNumberType> 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<ScalarNumberType> 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<ADNumberType> s_ad =
+ ad_helper.get_sensitive_variables();
+
+ std::vector<ADNumberType> 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<ADNumberType>::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<ScalarNumberType> funcs(n_vars_dep);
+ FullMatrix<ScalarNumberType> 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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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.."));
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_AD_vector_jacobian()
+{
+ typedef AD::VectorFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Function and its derivatives
+ typedef FunctionsTestSquare<dim, ADNumberType> 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<ScalarNumberType> 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<ADNumberType> s_ad =
+ ad_helper.get_sensitive_variables();
+
+ std::vector<ADNumberType> 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<ADNumberType>::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<ScalarNumberType> funcs(n_vars_dep);
+ FullMatrix<ScalarNumberType> 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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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.."));
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/logstream.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/fe/fe_values_extractors.h>
+
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+// Function and its derivatives
+template <int dim, typename NumberType>
+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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_AD_vector_jacobian()
+{
+ typedef AD::VectorFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << std::endl;
+
+ // Function and its derivatives
+ typedef FunctionsTestSquare<dim, ADNumberType> 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<ScalarNumberType> 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<ADNumberType> s_ad =
+ ad_helper.get_sensitive_variables();
+
+ std::vector<ADNumberType> 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<ADNumberType>::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<ScalarNumberType> funcs(n_vars_dep);
+ FullMatrix<ScalarNumberType> 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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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.."));
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/quadrature_point_data.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+#include <deal.II/lac/precondition_selector.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/solver_selector.h>
+#include <deal.II/lac/sparse_direct.h>
+
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <fstream>
+#include <functional>
+#include <iostream>
+
+#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 <int dim>
+ 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 <int dim>
+ const SymmetricTensor<2, dim>
+ StandardTensors<dim>::I = unit_symmetric_tensor<dim>();
+ template <int dim>
+ const SymmetricTensor<4, dim> StandardTensors<dim>::IxI = outer_product(I, I);
+ template <int dim>
+ const SymmetricTensor<4, dim>
+ StandardTensors<dim>::II = identity_tensor<dim>();
+ template <int dim>
+ const SymmetricTensor<4, dim>
+ StandardTensors<dim>::dev_P = deviator_tensor<dim>();
+ 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 <int dim>
+ 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<dim>::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 <typename NumberType>
+ NumberType
+ get_Psi_iso(const SymmetricTensor<2, dim, NumberType> &C_bar)
+ {
+ return c_1 * (trace(C_bar) - dim);
+ }
+ template <typename NumberType>
+ 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<dim>::I;
+ }
+ SymmetricTensor<2, dim>
+ get_tau_iso() const
+ {
+ return StandardTensors<dim>::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<dim>::IxI - (2.0 * StandardTensors<dim>::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<dim>::I);
+ const SymmetricTensor<4, dim> I_x_tau_iso =
+ outer_product(StandardTensors<dim>::I, tau_iso);
+ const SymmetricTensor<4, dim> c_bar = get_c_bar();
+ return (2.0 / dim) * trace(tau_bar) * StandardTensors<dim>::dev_P -
+ (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso) +
+ StandardTensors<dim>::dev_P * c_bar * StandardTensors<dim>::dev_P;
+ }
+ SymmetricTensor<4, dim>
+ get_c_bar() const
+ {
+ return SymmetricTensor<4, dim>();
+ }
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ class PointHistory
+ {
+ public:
+ PointHistory()
+ : F_inv(StandardTensors<dim>::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<dim>(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<dim>::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<dim, ad_type_code, number_t> 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<double> Dpsi(n_independent_variables);
+ ad_helper.compute_gradient(Dpsi);
+ FullMatrix<double> 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<dim, ad_type_code, double>::ad_type
+ ADNumberType;
+ AD::ScalarFunction<dim, ad_type_code, double> 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<double> Dpsi(n_independent_variables);
+ FullMatrix<double> 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_Compressible_Neo_Hook_Three_Field<dim>> material;
+ Tensor<2, dim> F_inv;
+ SymmetricTensor<2, dim> tau;
+ double d2Psi_vol_dJ2;
+ double dPsi_vol_dJ;
+ SymmetricTensor<4, dim> Jc;
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ 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<dim>::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<dim>::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<dim>::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<double> &solution_delta);
+ void
+ update_qph_incremental_one_cell(
+ const typename DoFHandler<dim>::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<double> &solution_delta);
+ std::pair<unsigned int, double>
+ solve_linear_system(BlockVector<double> &newton_update);
+ BlockVector<double>
+ get_total_solution(const BlockVector<double> &solution_delta) const;
+ void
+ output_results() const;
+ Parameters::AllParameters parameters;
+ double vol_reference;
+ Triangulation<dim> triangulation;
+ Time time;
+ mutable TimerOutput timer;
+ CellDataStorage<typename Triangulation<dim>::cell_iterator,
+ PointHistory<dim, number_t, ad_type_code>>
+ quadrature_point_history;
+ const unsigned int degree;
+ const FESystem<dim> fe;
+ DoFHandler<dim> 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<types::global_dof_index> dofs_per_block;
+ std::vector<types::global_dof_index> element_indices_u;
+ std::vector<types::global_dof_index> element_indices_p;
+ std::vector<types::global_dof_index> element_indices_J;
+ const QGauss<dim> qf_cell;
+ const QGauss<dim - 1> qf_face;
+ const unsigned int n_q_points;
+ const unsigned int n_q_points_f;
+ ConstraintMatrix constraints;
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> tangent_matrix;
+ BlockVector<double> system_rhs;
+ BlockVector<double> 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<double> &newton_update,
+ Errors & error_update);
+ std::pair<double, double>
+ get_error_dilation() const;
+ double
+ compute_vol_current() const;
+ static void
+ print_conv_header();
+ void
+ print_conv_footer();
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ Solid<dim, number_t, ad_type_code>::Solid(const std::string &input_file)
+ : parameters(input_file)
+ , triangulation(Triangulation<dim>::maximum_smoothing)
+ , time(parameters.end_time, parameters.delta_t)
+ , timer(std::cout, TimerOutput::never, TimerOutput::wall_times)
+ , degree(parameters.poly_degree)
+ , fe(FE_Q<dim>(parameters.poly_degree),
+ dim, // displacement
+ FE_DGPMonomial<dim>(parameters.poly_degree - 1),
+ 1, // pressure
+ FE_DGPMonomial<dim>(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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ Solid<dim, number_t, ad_type_code>::~Solid()
+ {
+ dof_handler_ref.clear();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::run()
+ {
+ make_grid();
+ system_setup();
+ {
+ ConstraintMatrix constraints;
+ constraints.close();
+ const ComponentSelectFunction<dim> J_mask(J_component, n_components);
+ VectorTools::project(dof_handler_ref,
+ constraints,
+ QGauss<dim>(degree + 2),
+ J_mask,
+ solution_n);
+ }
+ // output_results();
+ time.increment();
+ BlockVector<double> 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<dim> soln_pt(
+ dim == 3 ? Point<dim>(0.0, 1.0, 0.0) * parameters.scale :
+ Point<dim>(0.0, 1.0) * parameters.scale);
+ typename DoFHandler<dim>::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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_K
+ {
+ FullMatrix<double> cell_matrix;
+ std::vector<types::global_dof_index> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_K
+ {
+ FEValues<dim> fe_values_ref;
+ std::vector<std::vector<double>> Nx;
+ std::vector<std::vector<Tensor<2, dim>>> grad_Nx;
+ std::vector<std::vector<SymmetricTensor<2, dim>>> symm_grad_Nx;
+ ScratchData_K(const FiniteElement<dim> &fe_cell,
+ const QGauss<dim> & qf_cell,
+ const UpdateFlags uf_cell)
+ : fe_values_ref(fe_cell, qf_cell, uf_cell)
+ , Nx(qf_cell.size(), std::vector<double>(fe_cell.dofs_per_cell))
+ , grad_Nx(qf_cell.size(),
+ std::vector<Tensor<2, dim>>(fe_cell.dofs_per_cell))
+ , symm_grad_Nx(qf_cell.size(),
+ std::vector<SymmetricTensor<2, dim>>(
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_RHS
+ {
+ Vector<double> cell_rhs;
+ std::vector<types::global_dof_index> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_RHS
+ {
+ FEValues<dim> fe_values_ref;
+ FEFaceValues<dim> fe_face_values_ref;
+ std::vector<std::vector<double>> Nx;
+ std::vector<std::vector<SymmetricTensor<2, dim>>> symm_grad_Nx;
+ ScratchData_RHS(const FiniteElement<dim> &fe_cell,
+ const QGauss<dim> & qf_cell,
+ const UpdateFlags uf_cell,
+ const QGauss<dim - 1> & 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<double>(fe_cell.dofs_per_cell))
+ , symm_grad_Nx(qf_cell.size(),
+ std::vector<SymmetricTensor<2, dim>>(
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_SC
+ {
+ FullMatrix<double> cell_matrix;
+ std::vector<types::global_dof_index> local_dof_indices;
+ FullMatrix<double> k_orig;
+ FullMatrix<double> k_pu;
+ FullMatrix<double> k_pJ;
+ FullMatrix<double> k_JJ;
+ FullMatrix<double> k_pJ_inv;
+ FullMatrix<double> k_bbar;
+ FullMatrix<double> A;
+ FullMatrix<double> B;
+ FullMatrix<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_SC
+ {
+ void
+ reset()
+ {}
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_UQPH
+ {
+ void
+ reset()
+ {}
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_UQPH
+ {
+ const BlockVector<double> & solution_total;
+ std::vector<Tensor<2, dim>> solution_grads_u_total;
+ std::vector<double> solution_values_p_total;
+ std::vector<double> solution_values_J_total;
+ FEValues<dim> fe_values_ref;
+ ScratchData_UQPH(const FiniteElement<dim> & fe_cell,
+ const QGauss<dim> & qf_cell,
+ const UpdateFlags uf_cell,
+ const BlockVector<double> &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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::make_grid()
+ {
+ GridGenerator::hyper_rectangle(
+ triangulation,
+ (dim == 3 ? Point<dim>(0.0, 0.0, 0.0) : Point<dim>(0.0, 0.0)),
+ (dim == 3 ? Point<dim>(1.0, 1.0, 1.0) : Point<dim>(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<dim>::active_cell_iterator cell = triangulation
+ .begin_active(),
+ endc =
+ triangulation.end();
+ for (; cell != endc; ++cell)
+ for (unsigned int face = 0; face < GeometryInfo<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::system_setup()
+ {
+ timer.enter_subsection("Setup system");
+ std::vector<unsigned int> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim>::active_cell_iterator cell =
+ triangulation.begin_active();
+ cell != triangulation.end();
+ ++cell)
+ {
+ const std::vector<
+ std::shared_ptr<PointHistory<dim, number_t, ad_type_code>>>
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::update_qph_incremental(
+ const BlockVector<double> &solution_delta)
+ {
+ timer.enter_subsection("Update QPH data");
+ std::cout << " UQPH " << std::flush;
+ const BlockVector<double> 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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::update_qph_incremental_one_cell(
+ const typename DoFHandler<dim>::active_cell_iterator &cell,
+ ScratchData_UQPH & scratch,
+ PerTaskData_UQPH & /*data*/)
+ {
+ const std::vector<
+ std::shared_ptr<PointHistory<dim, number_t, ad_type_code>>>
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::solve_nonlinear_timestep(
+ BlockVector<double> &solution_delta)
+ {
+ std::cout << std::endl
+ << "Timestep " << time.get_timestep() << " @ " << time.current()
+ << "s" << std::endl;
+ BlockVector<double> 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<unsigned int, double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<double, double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ double
+ Solid<dim, number_t, ad_type_code>::compute_vol_current() const
+ {
+ double vol_current = 0.0;
+ FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+ for (typename Triangulation<dim>::active_cell_iterator cell =
+ triangulation.begin_active();
+ cell != triangulation.end();
+ ++cell)
+ {
+ fe_values_ref.reinit(cell);
+ const std::vector<
+ std::shared_ptr<const PointHistory<dim, number_t, ad_type_code>>>
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ std::pair<double, double>
+ Solid<dim, number_t, ad_type_code>::get_error_dilation() const
+ {
+ double dil_L2_error = 0.0;
+ FEValues<dim> fe_values_ref(fe, qf_cell, update_JxW_values);
+ for (typename Triangulation<dim>::active_cell_iterator cell =
+ triangulation.begin_active();
+ cell != triangulation.end();
+ ++cell)
+ {
+ fe_values_ref.reinit(cell);
+ const std::vector<
+ std::shared_ptr<const PointHistory<dim, number_t, ad_type_code>>>
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::get_error_residual(Errors &error_residual)
+ {
+ BlockVector<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::get_error_update(
+ const BlockVector<double> &newton_update,
+ Errors & error_update)
+ {
+ BlockVector<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ BlockVector<double>
+ Solid<dim, number_t, ad_type_code>::get_total_solution(
+ const BlockVector<double> &solution_delta) const
+ {
+ BlockVector<double> solution_total(solution_n);
+ solution_total += solution_delta;
+ return solution_total;
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim, number_t, ad_type_code>::assemble_system_tangent_one_cell,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ std::bind(&Solid<dim, number_t, ad_type_code>::copy_local_to_global_K,
+ this,
+ std::placeholders::_1),
+ scratch_data,
+ per_task_data);
+ timer.leave_subsection();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_system_tangent_one_cell(
+ const typename DoFHandler<dim>::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<const PointHistory<dim, number_t, ad_type_code>>>
+ 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<double> & N = scratch.Nx[q_point];
+ const std::vector<SymmetricTensor<2, dim>> &symm_grad_Nx =
+ scratch.symm_grad_Nx[q_point];
+ const std::vector<Tensor<2, dim>> &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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim, number_t, ad_type_code>::assemble_system_rhs_one_cell,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ std::bind(&Solid<dim, number_t, ad_type_code>::copy_local_to_global_rhs,
+ this,
+ std::placeholders::_1),
+ scratch_data,
+ per_task_data);
+ timer.leave_subsection();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_system_rhs_one_cell(
+ const typename DoFHandler<dim>::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<const PointHistory<dim, number_t, ad_type_code>>>
+ 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<double> & N = scratch.Nx[q_point];
+ const std::vector<SymmetricTensor<2, dim>> &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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim>(n_components),
+ constraints,
+ fe.component_mask(x_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ fe.component_mask(y_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement) |
+ fe.component_mask(z_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ fe.component_mask(z_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement) |
+ fe.component_mask(z_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ }
+ }
+ constraints.close();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_sc_one_cell(
+ const typename DoFHandler<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ std::pair<unsigned int, double>
+ Solid<dim, number_t, ad_type_code>::solve_linear_system(
+ BlockVector<double> &newton_update)
+ {
+ unsigned int lin_it = 0;
+ double lin_res = 0.0;
+ if (parameters.use_static_condensation == true)
+ {
+ BlockVector<double> A(dofs_per_block);
+ BlockVector<double> 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<Vector<double>> GVM;
+ SolverCG<Vector<double>> solver_CG(solver_control, GVM);
+ PreconditionSelector<SparseMatrix<double>, Vector<double>>
+ 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<double> &f_u = system_rhs.block(u_dof);
+ const Vector<double> &f_p = system_rhs.block(p_dof);
+ const Vector<double> &f_J = system_rhs.block(J_dof);
+ Vector<double> & d_u = newton_update.block(u_dof);
+ Vector<double> & d_p = newton_update.block(p_dof);
+ Vector<double> & 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<SparseMatrix<double>, Vector<double>>
+ 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<Vector<double>> 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<SparseMatrix<double>, Vector<double>>
+ 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<Vector<double>> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::output_results() const
+ {
+ DataOut<dim> data_out;
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ 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<std::string> 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<dim>::type_dof_data,
+ data_component_interpretation);
+ Vector<double> soln(solution_n.size());
+ for (unsigned int i = 0; i < soln.size(); ++i)
+ soln(i) = solution_n(i);
+ MappingQEulerian<dim> 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
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/quadrature_point_data.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q_eulerian.h>
+
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+#include <deal.II/lac/precondition_selector.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/solver_selector.h>
+#include <deal.II/lac/sparse_direct.h>
+
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <fstream>
+#include <functional>
+#include <iostream>
+
+#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 <int dim>
+ 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 <int dim>
+ const SymmetricTensor<2, dim>
+ StandardTensors<dim>::I = unit_symmetric_tensor<dim>();
+ template <int dim>
+ const SymmetricTensor<4, dim> StandardTensors<dim>::IxI = outer_product(I, I);
+ template <int dim>
+ const SymmetricTensor<4, dim>
+ StandardTensors<dim>::II = identity_tensor<dim>();
+ template <int dim>
+ const SymmetricTensor<4, dim>
+ StandardTensors<dim>::dev_P = deviator_tensor<dim>();
+ 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 <int dim>
+ 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 <typename NumberType>
+ 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 <typename NumberType>
+ 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 <typename NumberType>
+ 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<dim>::I;
+ SymmetricTensor<2, dim, NumberType> tau_vol(StandardTensors<dim>::I);
+ tau_vol *= p_tilde * det_F;
+ return tau_vol;
+ }
+ template <typename NumberType>
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_iso(const Tensor<2, dim, NumberType> &F) const
+ {
+ // return StandardTensors<dim>::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<dim>::dev_P[i][j][k][l] * tau_bar[k][l];
+
+ return tau_iso;
+ }
+ template <typename NumberType>
+ 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 <int dim>
+ class PointHistory
+ {
+ public:
+ PointHistory()
+ {}
+ virtual ~PointHistory()
+ {}
+ void
+ setup_lqp(const Parameters::AllParameters ¶meters)
+ {
+ material.reset(
+ new Material_Compressible_Neo_Hook_Three_Field<dim>(parameters.mu,
+ parameters.nu));
+ }
+
+ template <typename NumberType>
+ 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 <typename NumberType>
+ NumberType
+ get_dPsi_vol_dJ(const NumberType &J_tilde) const
+ {
+ return material->get_dPsi_vol_dJ(J_tilde);
+ }
+
+ private:
+ std::shared_ptr<Material_Compressible_Neo_Hook_Three_Field<dim>> material;
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ 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<double> &solution_delta);
+ void
+ assemble_system_one_cell(
+ const typename DoFHandler<dim>::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<dim>::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<double> &solution_delta);
+ std::pair<unsigned int, double>
+ solve_linear_system(BlockVector<double> &newton_update);
+ BlockVector<double>
+ get_total_solution(const BlockVector<double> &solution_delta) const;
+ void
+ output_results() const;
+ Parameters::AllParameters parameters;
+ double vol_reference;
+ Triangulation<dim> triangulation;
+ Time time;
+ mutable TimerOutput timer;
+ CellDataStorage<typename Triangulation<dim>::cell_iterator,
+ PointHistory<dim>>
+ quadrature_point_history;
+ const unsigned int degree;
+ const FESystem<dim> fe;
+ DoFHandler<dim> 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<types::global_dof_index> dofs_per_block;
+ std::vector<types::global_dof_index> element_indices_u;
+ std::vector<types::global_dof_index> element_indices_p;
+ std::vector<types::global_dof_index> element_indices_J;
+ const QGauss<dim> qf_cell;
+ const QGauss<dim - 1> qf_face;
+ const unsigned int n_q_points;
+ const unsigned int n_q_points_f;
+ ConstraintMatrix constraints;
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> tangent_matrix;
+ BlockVector<double> system_rhs;
+ BlockVector<double> 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<double> &newton_update,
+ Errors & error_update);
+ std::pair<double, double>
+ get_error_dilation(const BlockVector<double> &solution_total) const;
+ void
+ print_conv_header();
+ void
+ print_conv_footer(const BlockVector<double> &solution_delta);
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ Solid<dim, number_t, ad_type_code>::Solid(const std::string &input_file)
+ : parameters(input_file)
+ , triangulation(Triangulation<dim>::maximum_smoothing)
+ , time(parameters.end_time, parameters.delta_t)
+ , timer(std::cout, TimerOutput::never, TimerOutput::wall_times)
+ , degree(parameters.poly_degree)
+ , fe(FE_Q<dim>(parameters.poly_degree),
+ dim, // displacement
+ FE_DGPMonomial<dim>(parameters.poly_degree - 1),
+ 1, // pressure
+ FE_DGPMonomial<dim>(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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ Solid<dim, number_t, ad_type_code>::~Solid()
+ {
+ dof_handler_ref.clear();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::run()
+ {
+ make_grid();
+ system_setup();
+ {
+ ConstraintMatrix constraints;
+ constraints.close();
+ const ComponentSelectFunction<dim> J_mask(J_component, n_components);
+ VectorTools::project(dof_handler_ref,
+ constraints,
+ QGauss<dim>(degree + 2),
+ J_mask,
+ solution_n);
+ }
+ // output_results();
+ time.increment();
+ BlockVector<double> 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<dim> soln_pt(
+ dim == 3 ? Point<dim>(0.0, 1.0, 0.0) * parameters.scale :
+ Point<dim>(0.0, 1.0) * parameters.scale);
+ typename DoFHandler<dim>::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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_ASM
+ {
+ FullMatrix<double> cell_matrix;
+ Vector<double> cell_rhs;
+ std::vector<types::global_dof_index> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_ASM
+ {
+ const BlockVector<double> &solution_total;
+ FEValues<dim> fe_values_ref;
+ FEFaceValues<dim> fe_face_values_ref;
+ ScratchData_ASM(const FiniteElement<dim> & fe_cell,
+ const QGauss<dim> & qf_cell,
+ const UpdateFlags uf_cell,
+ const QGauss<dim - 1> & qf_face,
+ const UpdateFlags uf_face,
+ const BlockVector<double> &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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::PerTaskData_SC
+ {
+ FullMatrix<double> cell_matrix;
+ std::vector<types::global_dof_index> local_dof_indices;
+ FullMatrix<double> k_orig;
+ FullMatrix<double> k_pu;
+ FullMatrix<double> k_pJ;
+ FullMatrix<double> k_JJ;
+ FullMatrix<double> k_pJ_inv;
+ FullMatrix<double> k_bbar;
+ FullMatrix<double> A;
+ FullMatrix<double> B;
+ FullMatrix<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ struct Solid<dim, number_t, ad_type_code>::ScratchData_SC
+ {
+ void
+ reset()
+ {}
+ };
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::make_grid()
+ {
+ GridGenerator::hyper_rectangle(
+ triangulation,
+ (dim == 3 ? Point<dim>(0.0, 0.0, 0.0) : Point<dim>(0.0, 0.0)),
+ (dim == 3 ? Point<dim>(1.0, 1.0, 1.0) : Point<dim>(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<dim>::active_cell_iterator cell = triangulation
+ .begin_active(),
+ endc =
+ triangulation.end();
+ for (; cell != endc; ++cell)
+ for (unsigned int face = 0; face < GeometryInfo<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::system_setup()
+ {
+ timer.enter_subsection("Setup system");
+ std::vector<unsigned int> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim>::active_cell_iterator cell =
+ triangulation.begin_active();
+ cell != triangulation.end();
+ ++cell)
+ {
+ const std::vector<std::shared_ptr<PointHistory<dim>>> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::solve_nonlinear_timestep(
+ BlockVector<double> &solution_delta)
+ {
+ std::cout << std::endl
+ << "Timestep " << time.get_timestep() << " @ " << time.current()
+ << "s" << std::endl;
+ BlockVector<double> 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<unsigned int, double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::print_conv_footer(
+ const BlockVector<double> &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<double, double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ std::pair<double, double>
+ Solid<dim, number_t, ad_type_code>::get_error_dilation(
+ const BlockVector<double> &solution_total) const
+ {
+ double vol_current = 0.0;
+ double dil_L2_error = 0.0;
+ FEValues<dim> fe_values_ref(
+ fe, qf_cell, update_values | update_gradients | update_JxW_values);
+ std::vector<Tensor<2, dim>> solution_grads_u_total(qf_cell.size());
+ std::vector<double> solution_values_J_total(qf_cell.size());
+ for (typename DoFHandler<dim>::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<std::shared_ptr<const PointHistory<dim>>> 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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::get_error_residual(Errors &error_residual)
+ {
+ BlockVector<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::get_error_update(
+ const BlockVector<double> &newton_update,
+ Errors & error_update)
+ {
+ BlockVector<double> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ BlockVector<double>
+ Solid<dim, number_t, ad_type_code>::get_total_solution(
+ const BlockVector<double> &solution_delta) const
+ {
+ BlockVector<double> solution_total(solution_n);
+ solution_total += solution_delta;
+ return solution_total;
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_system(
+ const BlockVector<double> &solution_delta)
+ {
+ timer.enter_subsection("Assemble system");
+ std::cout << " ASM_SYS " << std::flush;
+ system_rhs = 0.0;
+ const BlockVector<double> 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<dim,number_t,ad_type_code>::assemble_system_one_cell,
+ // this,
+ // std::placeholders::_1,
+ // std::placeholders::_2,
+ // std::placeholders::_3),
+ // std::bind(&Solid<dim,number_t,ad_type_code>::copy_local_to_global_system,
+ // this,
+ // std::placeholders::_1),
+ // scratch_data,
+ // per_task_data);
+ for (typename DoFHandler<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_system_one_cell(
+ const typename DoFHandler<dim>::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<std::shared_ptr<const PointHistory<dim>>> 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<dim, ad_type_code, number_t> 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<double> 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<ADNumberType> 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<ADNumberType> residual_ad(n_dependent_variables,
+ ADNumberType(0.0));
+
+ // Compute all values, gradients etc. based on sensitive AD DoF values
+ std::vector<Tensor<2, dim, ADNumberType>> Grad_u(n_q_points);
+ std::vector<ADNumberType> p_tilde(n_q_points, ADNumberType(0.0));
+ std::vector<ADNumberType> 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<dim>() + 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<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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<dim>(n_components),
+ constraints,
+ fe.component_mask(x_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ fe.component_mask(y_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement) |
+ fe.component_mask(z_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ fe.component_mask(z_displacement));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement) |
+ fe.component_mask(z_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(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<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ else
+ VectorTools::interpolate_boundary_values(
+ dof_handler_ref,
+ boundary_id,
+ ZeroFunction<dim>(n_components),
+ constraints,
+ (fe.component_mask(x_displacement)));
+ }
+ }
+ constraints.close();
+ }
+ template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::assemble_sc_one_cell(
+ const typename DoFHandler<dim>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ std::pair<unsigned int, double>
+ Solid<dim, number_t, ad_type_code>::solve_linear_system(
+ BlockVector<double> &newton_update)
+ {
+ unsigned int lin_it = 0;
+ double lin_res = 0.0;
+ if (parameters.use_static_condensation == true)
+ {
+ BlockVector<double> A(dofs_per_block);
+ BlockVector<double> 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<Vector<double>> GVM;
+ SolverCG<Vector<double>> solver_CG(solver_control, GVM);
+ PreconditionSelector<SparseMatrix<double>, Vector<double>>
+ 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<double> &f_u = system_rhs.block(u_dof);
+ const Vector<double> &f_p = system_rhs.block(p_dof);
+ const Vector<double> &f_J = system_rhs.block(J_dof);
+ Vector<double> & d_u = newton_update.block(u_dof);
+ Vector<double> & d_p = newton_update.block(p_dof);
+ Vector<double> & 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<SparseMatrix<double>, Vector<double>>
+ 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<Vector<double>> 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<SparseMatrix<double>, Vector<double>>
+ 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<Vector<double>> 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+ void
+ Solid<dim, number_t, ad_type_code>::output_results() const
+ {
+ DataOut<dim> data_out;
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ 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<std::string> 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<dim>::type_dof_data,
+ data_component_interpretation);
+ Vector<double> soln(solution_n.size());
+ for (unsigned int i = 0; i < soln.size(); ++i)
+ soln(i) = solution_n(i);
+ MappingQEulerian<dim> 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
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/symmetric_tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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<dim, NumberType>();
+ 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<dim, NumberType>();
+ 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<dim, NumberType>();
+
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_symmetric_tensor()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> ADHelper;
+ typedef typename ADHelper::ad_type ADNumberType;
+ typedef typename ADHelper::scalar_type ScalarNumberType;
+
+ std::cout << "*** dim = " << Utilities::to_string(dim) << ", "
+ << "Type code: " << static_cast<int>(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<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestSymmetricTensor<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/symmetric_tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include "../tests.h"
+#ifdef DEAL_II_ADOLC_WITH_ADVANCED_BRANCHING
+# include <deal.II/base/symmetric_tensor.templates.h>
+#endif
+
+#include <iostream>
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+double
+mu()
+{
+ return 100.0;
+}
+
+
+template <int dim, typename NumberType>
+NumberType
+J(const SymmetricTensor<2, dim, NumberType> &C)
+{
+ return std::sqrt(determinant(C));
+}
+
+template <int dim, typename NumberType>
+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<dim, NumberType>();
+ 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 <int dim, typename NumberType>
+struct IncompressibleNeoHookeanPrincipalStretches
+{
+ // Incompressible Neo-Hookean material
+ static NumberType
+ psi(const std::array<std::pair<NumberType, Tensor<1, dim, NumberType>>, 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<std::pair<NumberType, Tensor<1,dim,NumberType>
+ // >,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<dim,NumberType>(); return 0.5*mu()*(I - C_inv);
+ // }
+};
+
+template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_NH(const bool nontrivial_initial_values)
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << ", "
+ << "Nontrivial initial values: " << std::boolalpha
+ << nontrivial_initial_values << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef IncompressibleNeoHookean<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_NH_eigen_energy(const enum SymmetricTensorEigenvectorMethod method,
+ const bool nontrivial_initial_values)
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << ", "
+ << "Eig method: " << static_cast<int>(method) << ", "
+ << "Nontrivial initial values: " << std::boolalpha
+ << nontrivial_initial_values << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef IncompressibleNeoHookeanPrincipalStretches<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol_val =
+ (nontrivial_initial_values ?
+ 1e-6 :
+ 1e5 * std::numeric_limits<ScalarNumberType>::epsilon());
+ static const ScalarNumberType tol_grad =
+ (nontrivial_initial_values ?
+ 1e-4 :
+ 1e5 * std::numeric_limits<ScalarNumberType>::epsilon());
+ static const ScalarNumberType tol_hess =
+ (nontrivial_initial_values ?
+ 2e-2 /*1e-2,5e-3*/ :
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+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<dim, number_t, ad_type_code>(nontrivial_initial_values);
+
+ test_NH_eigen_energy<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::hybrid, nontrivial_initial_values);
+ test_NH_eigen_energy<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::ql_implicit_shifts,
+ nontrivial_initial_values);
+ test_NH_eigen_energy<dim, number_t, ad_type_code>(
+ 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<dim, number_t, ad_type_code>(nontrivial_initial_values);
+
+ // test_NH_eigen_energy<dim,number_t,ad_type_code>(SymmetricTensorEigenvectorMethod::hybrid,nontrivial_initial_values);
+ // // This will never work.
+ test_NH_eigen_energy<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::ql_implicit_shifts,
+ nontrivial_initial_values);
+ test_NH_eigen_energy<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values);
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/symmetric_tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include "../tests.h"
+#ifdef DEAL_II_ADOLC_WITH_ADVANCED_BRANCHING
+# include <deal.II/base/symmetric_tensor.templates.h>
+#endif
+
+#include <iostream>
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+double
+mu()
+{
+ return 100.0;
+}
+
+
+template <int dim, typename NumberType>
+NumberType
+J(const SymmetricTensor<2, dim, NumberType> &C)
+{
+ return std::sqrt(determinant(C));
+}
+
+template <int dim, typename NumberType>
+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<dim, NumberType>();
+ 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 <int dim, typename NumberType>
+struct IncompressibleNeoHookeanPrincipalStretches
+{
+ // // Incompressible Neo-Hookean material
+ // static NumberType
+ // psi (const std::array<std::pair<NumberType, Tensor<1,dim,NumberType>
+ // >,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<std::pair<NumberType, Tensor<1, dim, NumberType>>,
+ 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<dim, NumberType>();
+ return 0.5 * mu() * (I - C_inv);
+ }
+};
+
+template <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_NH(const bool nontrivial_initial_values)
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << ", "
+ << "Nontrivial initial values: " << std::boolalpha
+ << nontrivial_initial_values << std::endl;
+
+ // Values computed from the AD energy function
+ ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef IncompressibleNeoHookean<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_NH_eigen_stress(const enum SymmetricTensorEigenvectorMethod method,
+ const bool nontrivial_initial_values)
+{
+ typedef AD::VectorFunction<dim, ad_type_code, number_t> 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<int>(ad_type_code) << ", "
+ << "Eig method: " << static_cast<int>(method) << ", "
+ << "Nontrivial initial values: " << std::boolalpha
+ << nontrivial_initial_values << std::endl;
+
+ // Values computed from the AD energy function
+ // ScalarNumberType psi;
+ Vector<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef IncompressibleNeoHookeanPrincipalStretches<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol_val =
+ (nontrivial_initial_values ?
+ 1e-4 :
+ 1e6 * std::numeric_limits<ScalarNumberType>::epsilon());
+ static const ScalarNumberType tol_jac =
+ (nontrivial_initial_values ?
+ 2.5e-3 :
+ 1e6 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+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<dim, number_t, ad_type_code>(nontrivial_initial_values);
+
+ test_NH_eigen_stress<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::hybrid, nontrivial_initial_values);
+ test_NH_eigen_stress<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::ql_implicit_shifts,
+ nontrivial_initial_values);
+ test_NH_eigen_stress<dim, number_t, ad_type_code>(
+ 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<dim, number_t, ad_type_code>(nontrivial_initial_values);
+
+ // test_NH_eigen_stress<dim,number_t,ad_type_code>(SymmetricTensorEigenvectorMethod::hybrid,nontrivial_initial_values);
+ // // This will never work.
+ test_NH_eigen_stress<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::ql_implicit_shifts,
+ nontrivial_initial_values);
+ test_NH_eigen_stress<dim, number_t, ad_type_code>(
+ SymmetricTensorEigenvectorMethod::jacobi, nontrivial_initial_values);
+ }
+}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// 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 <deal.II/base/tensor.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <iostream>
+
+#include "../tests.h"
+
+using namespace dealii;
+namespace AD = dealii::Differentiation::AD;
+
+template <int dim, typename NumberType>
+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<dim, NumberType>();
+ 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<dim, NumberType>();
+ 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<dim, NumberType>();
+
+ 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 <int dim, typename number_t, enum AD::NumberTypes ad_type_code>
+void
+test_tensor()
+{
+ typedef AD::ScalarFunction<dim, ad_type_code, number_t> ADHelper;
+ typedef typename ADHelper::ad_type ADNumberType;
+ typedef typename ADHelper::scalar_type ScalarNumberType;
+
+ std::cout << "*** dim = " << Utilities::to_string(dim) << ", "
+ << "Type code: " << static_cast<int>(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<ScalarNumberType> Dpsi;
+ FullMatrix<ScalarNumberType> D2psi;
+
+ // Function and its derivatives
+ typedef FunctionsTestTensor<dim, ADNumberType> 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<dim, ScalarNumberType>();
+ 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<ADNumberType>::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<ADNumberType>::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<ADNumberType>::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<dim, ScalarNumberType> func;
+ static const ScalarNumberType tol =
+ 1e5 * std::numeric_limits<ScalarNumberType>::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<ADNumberType>::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;
+}