[ AC_MSG_RESULT(no); all_cxx1x_classes_available=no ]
)
+ AC_MSG_CHECKING(for std::type_traits)
+ AC_TRY_COMPILE(
+ [#include <type_traits>],
+ [ const bool m0 = std::is_trivial<double>::value;
+ const bool m1 = std::is_standard_layout<double>::value;
+ const bool m2 = std::is_pod<double>::value; ],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no); all_cxx1x_classes_available=no ]
+ )
+
CXXFLAGS="${OLD_CXXFLAGS}"
dnl If the above classes and operations are all defined then we can
AC_MSG_RESULT(x86 derivate ($withcpu))
case "$GXX_VERSION" in
gcc*)
- dnl Tune for this processor, but only in optimized mode
- dnl (to prevent the effects of possible compiler bugs to affect
- dnl both debug as well as optimized versions)
+ dnl Tune for this processor
+ CXXFLAGSG="$CXXFLAGSG -march=$withcpu"
CXXFLAGSO="$CXXFLAGSO -march=$withcpu"
dnl Also set the mode for f77 compiler
+ F77FLAGSG="$F77FLAGSG -march=$withcpu"
F77FLAGSO="$F77FLAGSO -march=$withcpu"
;;
esac
AC_MSG_RESULT(native processor variant)
case "$GXX_VERSION" in
gcc*)
- dnl Tune for this processor, but only in optimized mode
- dnl (to prevent the effects of possible compiler bugs to affect
- dnl both debug as well as optimized versions)
+ dnl Tune for this processor
+ CXXFLAGSG="$CXXFLAGSG -march=native"
CXXFLAGSO="$CXXFLAGSO -march=native"
dnl Also set the mode for f77 compiler
+ F77FLAGSG="$F77FLAGSG -march=native"
F77FLAGSO="$F77FLAGSO -march=native"
;;
intel_icc*)
dnl Same, but for the icc compiler
CXXFLAGSO="$CXXFLAGSO -xhost"
+ CXXFLAGSG="$CXXFLAGSG -xhost"
;;
esac
;;
+dnl -------------------------------------------------------------
+dnl Check whether the compiler allows for vectorization and that
+dnl vectorization actually works. For this test, we use compiler
+dnl intrinsics similar to what is used in the deal.II library and
+dnl check whether the arithmetic operations are correctly performed
+dnl on examples where all numbers are exactly represented as
+dnl floating point numbers.
+dnl
+dnl Usage: DEAL_II_COMPILER_VECTORIZATION_LEVEL
+dnl 0 means no vectorization, 1 support for SSE2, 2 support for AVX
+dnl
+dnl -------------------------------------------------------------
+AC_DEFUN(DEAL_II_DETECT_VECTORIZATION_LEVEL, dnl
+[
+ AC_LANG(C++)
+ CXXFLAGS="$CXXFLAGSG"
+ dnl SSE2 check in debug mode
+ AC_MSG_CHECKING(whether CPU supports SSE2)
+ AC_TRY_RUN(
+ [
+#include <emmintrin.h>
+#include <mm_malloc.h>
+ int main()
+ {
+ __m128d a, b;
+ const unsigned int vector_bytes = sizeof(__m128d);
+ const int n_vectors = vector_bytes/sizeof(double);
+ __m128d * data =
+ reinterpret_cast<__m128d*>(_mm_malloc (2*vector_bytes, vector_bytes));
+ double * ptr = reinterpret_cast<double*>(&a);
+ ptr[0] = (volatile double)(1.0);
+ for (int i=1; i<n_vectors; ++i)
+ ptr[i] = 0.0;
+ b = _mm_set1_pd ((volatile double)(2.25));
+ data[0] = _mm_add_pd (a, b);
+ data[1] = _mm_mul_pd (b, data[0]);
+ ptr = reinterpret_cast<double*>(&data[1]);
+ unsigned int return_value = 0;
+ if (ptr[0] != 7.3125)
+ return_value = 1;
+ for (int i=1; i<n_vectors; ++i)
+ if (ptr[i] != 5.0625)
+ return_value = 1;
+ _mm_free (data);
+ return return_value;
+ }
+ ],
+ [
+ AC_MSG_RESULT(yes)
+ dnl AVX check in debug mode
+ AC_MSG_CHECKING(whether CPU supports AVX)
+ AC_TRY_RUN(
+ [
+#include <immintrin.h>
+#include <mm_malloc.h>
+ int main()
+ {
+ __m256d a, b;
+ const unsigned int vector_bytes = sizeof(__m256d);
+ const int n_vectors = vector_bytes/sizeof(double);
+ __m256d * data =
+ reinterpret_cast<__m256d*>(_mm_malloc (2*vector_bytes, vector_bytes));
+ double * ptr = reinterpret_cast<double*>(&a);
+ ptr[0] = (volatile double)(1.0);
+ for (int i=1; i<n_vectors; ++i)
+ ptr[i] = 0.0;
+ b = _mm256_set1_pd ((volatile double)(2.25));
+ data[0] = _mm256_add_pd (a, b);
+ data[1] = _mm256_mul_pd (b, data[0]);
+ ptr = reinterpret_cast<double*>(&data[1]);
+ unsigned int return_value = 0;
+ if (ptr[0] != 7.3125)
+ return_value = 1;
+ for (int i=1; i<n_vectors; ++i)
+ if (ptr[i] != 5.0625)
+ return_value = 1;
+ _mm_free (data);
+ }
+ ],
+ [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(DEAL_II_COMPILER_VECTORIZATION_LEVEL, 2,
+ [Equal to 0 in the generic case,
+ equal to 1 if CPU compiled for supports SSE2,
+ equal to 2 if CPU compiled for supports AVX])
+ ],
+ [
+ AC_MSG_RESULT(no)
+ AC_DEFINE(DEAL_II_COMPILER_VECTORIZATION_LEVEL, 1,
+ [Equal to 0 in the generic case,
+ equal to 1 if CPU compiled for supports SSE2,
+ equal to 2 if CPU compiled for supports AVX])
+ ])
+ ],
+ [
+ AC_DEFINE(DEAL_II_COMPILER_VECTORIZATION_LEVEL, 0,
+ [Equal to 0 in the generic case,
+ equal to 1 if CPU compiled for supports SSE2,
+ equal to 2 if CPU compiled for supports AVX])
+ AC_MSG_RESULT(no)
+ ])
+])
+
+
+
+dnl -------------------------------------------------------------
+dnl Check whether the compiler allows to use arithmetic operations
+dnl +-*/ on vectorized data types or whether we need to use
+dnl _mm_add_pd for addition and so on. +-*/ is preferred because
+dnl it allows the compiler to choose other optimizations like
+dnl fused multiply add, whereas _mm_add_pd explicitly enforces the
+dnl assembler command.
+dnl
+dnl Usage: DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+dnl
+dnl -------------------------------------------------------------
+AC_DEFUN(DEAL_II_CHECK_VECTOR_ARITHMETICS, dnl
+
+[
+ AC_MSG_CHECKING(whether compiler supports vector arithmetics)
+ AC_LANG(C++)
+ CXXFLAGS="$CXXFLAGSG"
+ AC_TRY_COMPILE(
+ [
+#include <emmintrin.h>
+ ],
+ [
+ __m128d a, b;
+ a = _mm_set_sd (1.0);
+ b = _mm_set1_pd (2.1);
+ __m128d c = a + b;
+ __m128d d = b - c;
+ __m128d e = c * a + d;
+ __m128d f = e/a;
+ (void)f;
+ ],
+ [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS, 1,
+ [Defined if the compiler can use arithmetic operations on
+ vectorized data types])
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ])
+])
+
+
+
dnl -------------------------------------------------------------
dnl In some cases, -threads (or whatever else command line option)
dnl switches on some preprocessor flags. If this is not the case,
-dnl -------------------------------------------------------------
-dnl Check whether the compiler allows to use arithmetic operations
-dnl +-*/ on vectorized data types or whether we need to use
-dnl _mm_add_pd for addition and so on. +-*/ is preferred because
-dnl it allows the compiler to choose other optimizations like
-dnl fused multiply add, whereas _mm_add_pd explicitly enforces the
-dnl assembler command.
-dnl
-dnl Usage: DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
-dnl
-dnl -------------------------------------------------------------
-AC_DEFUN(DEAL_II_CHECK_VECTOR_ARITHMETICS, dnl
-
-[
- AC_MSG_CHECKING(whether compiler supports vector arithmetics)
- AC_LANG(C++)
- CXXFLAGS="$CXXFLAGSG"
- AC_TRY_COMPILE(
- [
-#include <emmintrin.h>
- ],
- [
- __m128d a, b;
- a = _mm_set_sd (1.0);
- b = _mm_set1_pd (2.1);
- __m128d c = a + b;
- __m128d d = b - c;
- __m128d e = c * a + d;
- __m128d f = e/a;
- (void)f;
- ],
- [
- AC_MSG_RESULT(yes)
- AC_DEFINE(DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS, 1,
- [Defined if the compiler can use arithmetic operations on
- vectorized data types])
- ],
- [
- AC_MSG_RESULT(no)
- ])
-])
-
-
-
-dnl -------------------------------------------------------------
-dnl Check for existence of a strong inline function. This can be used
-dnl to force a compiler to inline some functions also at low optimization
-dnl levels. We use it in vectorized data types, where we want inlining
-dnl also for debug code. If we cannot find a good inlining routine, we
-dnl just use 'inline'.
-dnl
-dnl Usage: DEAL_II_ALWAYS_INLINE
-dnl
-dnl -------------------------------------------------------------
-AC_DEFUN(DEAL_II_CHECK_ALWAYS_INLINE, dnl
-
-[
- if test "$GXX" = "yes" ; then
- dnl force inline for gcc compiler
- TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))'
- else
- case "$GXX_VERSION" in
- clang*)
- dnl force inline for clang compiler
- TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))'
- ;;
-
- *)
- dnl for all other compilers, try with __forceinline
- TEMP_ALWAYS_INLINE=__forceinline
- ;;
- esac
- fi
- AC_MSG_CHECKING(for forced inlining)
- AC_LANG(C++)
- CXXFLAGS="$CXXFLAGSG"
- AC_TRY_COMPILE(
- [
- $TEMP_ALWAYS_INLINE
- void f() {};
- ],
- [
- f();
- ],
- [
- AC_MSG_RESULT(yes)
- AC_DEFINE_UNQUOTED(DEAL_II_ALWAYS_INLINE, $TEMP_ALWAYS_INLINE,
- [Forces the compiler to always inline functions, also in
- debug mode])
- ],
- [
- AC_MSG_RESULT(no)
- AC_DEFINE(DEAL_II_ALWAYS_INLINE, inline,
- [Forces the compiler to always inline functions, also in
- debug mode])
- ])
-])
-
-
-
dnl -------------------------------------------------------------
dnl
dnl Usage: DEAL_II_CHECK_MIN_VECTOR_CAPACITY
AC_DEFINE([HAVE_LIBLAPACK], [1],
[Defined if deal.II was configured with LAPACK support])
AC_SUBST(DEAL_II_USE_LAPACK, "yes")
+ USE_CONTRIB_LAPACK='yes'
],
[AC_MSG_ERROR([LAPACK library $lapack not found])]
)
],,$F77LIBS)
AC_SUBST(DEAL_II_USE_BLAS, "yes")
AC_SUBST(NEEDS_F77LIBS, "yes")
+ USE_CONTRIB_BLAS='yes'
else
DEAL_II_CHECK_BLAS_FRAMEWORK
if test "x$framework_works" != "xyes"; then
AC_SUBST(DEAL_II_USE_BLAS, "yes")
AC_SUBST(NEEDS_F77LIBS, "yes")
+ USE_CONTRIB_BLAS='yes'
fi
fi
fi
DEAL_II_TRILINOS_VERSION_SUBMINOR = @DEAL_II_TRILINOS_VERSION_SUBMINOR@
DEAL_II_TRILINOS_LIBPREFIX = @DEAL_II_TRILINOS_LIBPREFIX@
+USE_CONTRIB_BLAS = @USE_CONTRIB_BLAS@
+USE_CONTRIB_LAPACK = @USE_CONTRIB_LAPACK@
+
USE_CONTRIB_MUMPS = @USE_CONTRIB_MUMPS@
DEAL_II_MUMPS_DIR = @DEAL_II_MUMPS_DIR@
DEAL_II_SCALAPACK_DIR = @DEAL_II_SCALAPACK_DIR@
#! /bin/sh
-# From configure.in Revision: 25395 .
+# From configure.in Revision: 25397 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.68 for deal.II 7.2.pre.
#
TECPLOT_INCLUDE_DIR
USE_CONTRIB_HSL
HSL_INCLUDE_DIR
+USE_CONTRIB_LAPACK
+USE_CONTRIB_BLAS
NEEDS_F77LIBS
DEAL_II_USE_BLAS
DEAL_II_P4EST_DIR
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }; all_cxx1x_classes_available=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::type_traits" >&5
+$as_echo_n "checking for std::type_traits... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <type_traits>
+int
+main ()
+{
+ const bool m0 = std::is_trivial<double>::value;
+ const bool m1 = std::is_standard_layout<double>::value;
+ const bool m2 = std::is_pod<double>::value;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }; all_cxx1x_classes_available=no
+
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }; all_cxx1x_classes_available=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::type_traits" >&5
+$as_echo_n "checking for std::type_traits... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <type_traits>
+int
+main ()
+{
+ const bool m0 = std::is_trivial<double>::value;
+ const bool m1 = std::is_standard_layout<double>::value;
+ const bool m2 = std::is_pod<double>::value;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }; all_cxx1x_classes_available=no
+
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
conftest$ac_exeext conftest.$ac_ext
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports vector arithmetics" >&5
-$as_echo_n "checking whether compiler supports vector arithmetics... " >&6; }
- ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-
- CXXFLAGS="$CXXFLAGSG"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-#include <emmintrin.h>
-
-int
-main ()
-{
-
- __m128d a, b;
- a = _mm_set_sd (1.0);
- b = _mm_set1_pd (2.1);
- __m128d c = a + b;
- __m128d d = b - c;
- __m128d e = c * a + d;
- __m128d f = e/a;
- (void)f;
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
-$as_echo "#define DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS 1" >>confdefs.h
-
-
-else
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-
-
- if test "$GXX" = "yes" ; then
- TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))'
- else
- case "$GXX_VERSION" in
- clang*)
- TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))'
- ;;
-
- *)
- TEMP_ALWAYS_INLINE=__forceinline
- ;;
- esac
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for forced inlining" >&5
-$as_echo_n "checking for forced inlining... " >&6; }
- ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-
- CXXFLAGS="$CXXFLAGSG"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
- $TEMP_ALWAYS_INLINE
- void f() {};
-
-int
-main ()
-{
-
- f();
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
-cat >>confdefs.h <<_ACEOF
-#define DEAL_II_ALWAYS_INLINE $TEMP_ALWAYS_INLINE
-_ACEOF
-
-
-else
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-$as_echo "#define DEAL_II_ALWAYS_INLINE inline" >>confdefs.h
-
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
NEEDS_F77LIBS="yes"
+ USE_CONTRIB_BLAS='yes'
else
if (echo $target | grep apple-darwin > /dev/null) ; then
NEEDS_F77LIBS="yes"
+ USE_CONTRIB_BLAS='yes'
fi
fi
fi
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for HSL subroutines" >&5
$as_echo_n "checking for HSL subroutines... " >&6; }
hsl_subroutines=""
DEAL_II_USE_LAPACK="yes"
+ USE_CONTRIB_LAPACK='yes'
else
as_fn_error $? "LAPACK library $lapack not found" "$LINENO" 5
$as_echo "x86 derivate ($withcpu)" >&6; }
case "$GXX_VERSION" in
gcc*)
- CXXFLAGSO="$CXXFLAGSO -march=$withcpu"
+ CXXFLAGSG="$CXXFLAGSG -march=$withcpu"
+ CXXFLAGSO="$CXXFLAGSO -march=$withcpu"
- F77FLAGSO="$F77FLAGSO -march=$withcpu"
+ F77FLAGSG="$F77FLAGSG -march=$withcpu"
+ F77FLAGSO="$F77FLAGSO -march=$withcpu"
;;
esac
;;
$as_echo "native processor variant" >&6; }
case "$GXX_VERSION" in
gcc*)
- CXXFLAGSO="$CXXFLAGSO -march=native"
+ CXXFLAGSG="$CXXFLAGSG -march=native"
+ CXXFLAGSO="$CXXFLAGSO -march=native"
- F77FLAGSO="$F77FLAGSO -march=native"
+ F77FLAGSG="$F77FLAGSG -march=native"
+ F77FLAGSO="$F77FLAGSO -march=native"
;;
intel_icc*)
CXXFLAGSO="$CXXFLAGSO -xhost"
+ CXXFLAGSG="$CXXFLAGSG -xhost"
;;
esac
;;
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+ CXXFLAGS="$CXXFLAGSG"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CPU supports SSE2" >&5
+$as_echo_n "checking whether CPU supports SSE2... " >&6; }
+ if test "$cross_compiling" = yes; then :
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <emmintrin.h>
+#include <mm_malloc.h>
+ int main()
+ {
+ __m128d a, b;
+ const unsigned int vector_bytes = sizeof(__m128d);
+ const int n_vectors = vector_bytes/sizeof(double);
+ __m128d * data =
+ reinterpret_cast<__m128d*>(_mm_malloc (2*vector_bytes, vector_bytes));
+ double * ptr = reinterpret_cast<double*>(&a);
+ ptr[0] = (volatile double)(1.0);
+ for (int i=1; i<n_vectors; ++i)
+ ptr[i] = 0.0;
+ b = _mm_set1_pd ((volatile double)(2.25));
+ data[0] = _mm_add_pd (a, b);
+ data[1] = _mm_mul_pd (b, data[0]);
+ ptr = reinterpret_cast<double*>(&data[1]);
+ unsigned int return_value = 0;
+ if (ptr[0] != 7.3125)
+ return_value = 1;
+ for (int i=1; i<n_vectors; ++i)
+ if (ptr[i] != 5.0625)
+ return_value = 1;
+ _mm_free (data);
+ return return_value;
+ }
+
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CPU supports AVX" >&5
+$as_echo_n "checking whether CPU supports AVX... " >&6; }
+ if test "$cross_compiling" = yes; then :
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <immintrin.h>
+#include <mm_malloc.h>
+ int main()
+ {
+ __m256d a, b;
+ const unsigned int vector_bytes = sizeof(__m256d);
+ const int n_vectors = vector_bytes/sizeof(double);
+ __m256d * data =
+ reinterpret_cast<__m256d*>(_mm_malloc (2*vector_bytes, vector_bytes));
+ double * ptr = reinterpret_cast<double*>(&a);
+ ptr[0] = (volatile double)(1.0);
+ for (int i=1; i<n_vectors; ++i)
+ ptr[i] = 0.0;
+ b = _mm256_set1_pd ((volatile double)(2.25));
+ data[0] = _mm256_add_pd (a, b);
+ data[1] = _mm256_mul_pd (b, data[0]);
+ ptr = reinterpret_cast<double*>(&data[1]);
+ unsigned int return_value = 0;
+ if (ptr[0] != 7.3125)
+ return_value = 1;
+ for (int i=1; i<n_vectors; ++i)
+ if (ptr[i] != 5.0625)
+ return_value = 1;
+ _mm_free (data);
+ }
+
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 2" >>confdefs.h
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 1" >>confdefs.h
+
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+else
+
+
+$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 0" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports vector arithmetics" >&5
+$as_echo_n "checking whether compiler supports vector arithmetics... " >&6; }
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+ CXXFLAGS="$CXXFLAGSG"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <emmintrin.h>
+
+int
+main ()
+{
+
+ __m128d a, b;
+ a = _mm_set_sd (1.0);
+ b = _mm_set1_pd (2.1);
+ __m128d c = a + b;
+ __m128d d = b - c;
+ __m128d e = c * a + d;
+ __m128d f = e/a;
+ (void)f;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS 1" >>confdefs.h
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
$as_echo "" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ---------------- configuring other programs -----------------" >&5
DEAL_II_HAVE_VERBOSE_TERMINATE
DEAL_II_HAVE_GLIBC_STACKTRACE
DEAL_II_HAVE_DEMANGLER
-DEAL_II_CHECK_VECTOR_ARITHMETICS
-DEAL_II_CHECK_ALWAYS_INLINE
DEAL_II_CHECK_MIN_VECTOR_CAPACITY
DEAL_II_CHECK_ABORT
DEAL_II_CHECK_GETRUSAGE
is given, use -lblas. Default is to use -lblas only
if required by other libraries.],
DEAL_II_WITH_BLAS($withval))
+AC_SUBST(USE_CONTRIB_BLAS)
+AC_SUBST(USE_CONTRIB_LAPACK)
DEAL_II_CONFIGURE_HSL
AC_SUBST(USE_CONTRIB_HSL)
dnl Possibly add some flags if optimizations are requested
DEAL_II_CHECK_CPU_OPTIMIZATIONS
-dnl Last check: test whether CXXFLAGS and F77FLAGS are ok
+dnl Test whether CXXFLAGS and F77FLAGS are ok
DEAL_II_CHECK_CXXFLAGS_CONSISTENCY
DEAL_II_CHECK_F77FLAGS_CONSISTENCY
+dnl Last check: check whether the compiler and host system allows for
+dnl vectorization
+DEAL_II_DETECT_VECTORIZATION_LEVEL
+DEAL_II_CHECK_VECTOR_ARITHMETICS
+
dnl -------------------------------------------------------------
dnl Third party programs
dnl -------------------------------------------------------------
<a href="../../doxygen/deal.II/step_35.html" target="body">35</a>
<a href="../../doxygen/deal.II/step_36.html" target="body">36</a>
- <a href="../../doxygen/deal.II/step_38.html" target="body">38</a><br />
+ <a href="../../doxygen/deal.II/step_37.html" target="body">37</a><br />
+ <a href="../../doxygen/deal.II/step_38.html" target="body">38</a>
<a href="../../doxygen/deal.II/step_39.html" target="body">39</a>
- <a href="../../doxygen/deal.II/step_40.html" target="body">40</a>
- <a href="../../doxygen/deal.II/step_41.html" target="body">41</a><br />
+ <a href="../../doxygen/deal.II/step_40.html" target="body">40</a><br />
+ <a href="../../doxygen/deal.II/step_41.html" target="body">41</a>
<a href="../../doxygen/deal.II/step_43.html" target="body">43</a>
- <a href="../../doxygen/deal.II/step_44.html" target="body">44</a>
- <a href="../../doxygen/deal.II/step_45.html" target="body">45</a><br />
+ <a href="../../doxygen/deal.II/step_44.html" target="body">44</a><br />
+ <a href="../../doxygen/deal.II/step_45.html" target="body">45</a>
<a href="../../doxygen/deal.II/step_46.html" target="body">46</a>
+ <a href="../../doxygen/deal.II/step_48.html" target="body">48</a><br />
</p>
<hr>
<map id="StepsMap" name="StepsMap">
-<area shape="poly" id="node1" href="../deal.II/step_1.html" title="Creating a grid. Refining it. Writing it to a file" alt="" coords="657,60,657,28,635,5,603,5,580,28,580,60,603,83,635,83"/>
-<area shape="poly" id="node2" href="../deal.II/step_2.html" title="Assigning degrees of freedom to a grid." alt="" coords="657,185,657,153,635,131,603,131,580,153,580,185,603,208,635,208"/>
-<area shape="poly" id="node3" href="../deal.II/step_3.html" title="Solving Poisson's equation." alt="" coords="657,311,657,279,635,256,603,256,580,279,580,311,603,333,635,333"/>
-<area shape="poly" id="node4" href="../deal.II/step_4.html" title="Dimension independent programming. Boundary conditions." alt="" coords="657,436,657,404,635,381,603,381,580,404,580,436,603,459,635,459"/>
-<area shape="poly" id="node5" href="../deal.II/step_5.html" title="Reading a grid from disk. Computations on successively refined grids." alt="" coords="427,561,427,529,404,507,372,507,349,529,349,561,372,584,404,584"/>
-<area shape="rect" id="node10" href="../deal.II/step_10.html" title="Higher order mappings." alt="" coords="451,529,487,562"/>
-<area shape="rect" id="node15" href="../deal.II/step_15.html" title="1d problems. A nonlinear problem." alt="" coords="511,529,547,562"/>
-<area shape="rect" id="node20" href="../deal.II/step_20.html" title="Mixed finite elements for the mixed Laplacian. Block solvers." alt="" coords="185,533,220,558"/>
-<area shape="rect" id="node23" href="../deal.II/step_23.html" title="Time dependent problems. The wave equation." alt="" coords="571,533,607,558"/>
-<area shape="rect" id="node28" href="../deal.II/step_29.html" title="A complex-valued Helmholtz equation. Sparse direct solvers." alt="" coords="631,529,667,562"/>
-<area shape="rect" id="node33" href="../deal.II/step_34.html" title="Boundary element methods for potential flow." alt="" coords="743,533,779,558"/>
-<area shape="rect" id="node35" href="../deal.II/step_36.html" title="Finding eigenvalues of the Schrödinger equation." alt="" coords="803,529,839,562"/>
-<area shape="rect" id="node36" href="../deal.II/step_38.html" title="Solve the Laplace Beltrami operator on a Half Sphere." alt="" coords="743,654,779,687"/>
-<area shape="rect" id="node39" href="../deal.II/step_41.html" title="Solving the obstacle problem (a variational inequality)" alt="" coords="863,533,899,558"/>
-<area shape="poly" id="node6" href="../deal.II/step_6.html" title="Adaptive local refinement. Higher order elements" alt="" coords="427,687,427,655,404,632,372,632,349,655,349,687,372,709,404,709"/>
-<area shape="rect" id="node7" href="../deal.II/step_7.html" title="Helmholtz equation. Computing errors. Boundary integrals." alt="" coords="430,758,463,791"/>
-<area shape="rect" id="node8" href="../deal.II/step_8.html" title="Systems of PDE. Elasticity." alt="" coords="313,758,345,791"/>
-<area shape="rect" id="node9" href="../deal.II/step_9.html" title="Advection equation. Multithreading. Refinement criteria." alt="" coords="487,758,520,791"/>
-<area shape="rect" id="node13" href="../deal.II/step_13.html" title="Modularity. Software design." alt="" coords="370,758,405,791"/>
-<area shape="rect" id="node16" href="../deal.II/step_16.html" title="Multigrid on adaptive meshes." alt="" coords="597,758,632,791"/>
-<area shape="rect" id="node22" href="../deal.II/step_22.html" title="The Stokes equation on adaptive meshes." alt="" coords="81,762,116,787"/>
-<area shape="rect" id="node26" href="../deal.II/step_27.html" title="hp-adaptive finite element methods." alt="" coords="253,758,288,791"/>
-<area shape="rect" id="node27" href="../deal.II/step_28.html" title="Handling multiple meshes at the same time. Neutron transport." alt="" coords="657,758,692,791"/>
-<area shape="rect" id="node37" href="../deal.II/step_39.html" title="Interior Penalty for the Laplace equation. Adaptive refinement. Multigrid." alt="" coords="545,923,580,957"/>
-<area shape="rect" id="node38" href="../deal.II/step_40.html" title="Solving the Laplace equation on adaptive meshes on thousands of processors." alt="" coords="195,923,231,957"/>
-<area shape="rect" id="node42" href="../deal.II/step_45.html" title="Periodic boundary conditions" alt="" coords="193,758,228,791"/>
-<area shape="rect" id="node12" href="../deal.II/step_12.html" title="Discontinuous Galerkin for linear advection." alt="" coords="485,841,520,874"/>
-<area shape="rect" id="node17" href="../deal.II/step_17.html" title="Parallel computing using MPI. Using PETSc." alt="" coords="253,845,288,870"/>
-<area shape="rect" id="node41" href="../deal.II/step_44.html" title="Quasi-static finite-strain elasticity" alt="" coords="341,1006,376,1031"/>
-<area shape="rect" id="node43" href="../deal.II/step_46.html" title="Coupling different physical models (flow, elasticity) in different parts of the domain" alt="" coords="193,841,228,874"/>
-<area shape="rect" id="node11" href="../deal.II/step_11.html" title="Higher order mappings. Dealing with constraints." alt="" coords="451,654,487,687"/>
-<area shape="rect" id="node29" href="../deal.II/step_30.html" title="Anisotropic refinement for DG methods." alt="" coords="485,923,520,957"/>
-<area shape="rect" id="node32" href="../deal.II/step_33.html" title="Hyperbolic conservation laws: the Euler equations of gas dynamics." alt="" coords="425,927,460,953"/>
-<area shape="rect" id="node14" href="../deal.II/step_14.html" title="Duality based error estimates. Adaptivity." alt="" coords="425,841,460,874"/>
-<area shape="rect" id="node18" href="../deal.II/step_18.html" title="Quasistatic elasticity. More parallel computing." alt="" coords="313,927,348,953"/>
-<area shape="rect" id="node31" href="../deal.II/step_32.html" title="A parallel Boussinesq flow solver for thermal convection in the earth mantle." alt="" coords="195,1006,231,1031"/>
-<area shape="rect" id="node19" href="../deal.II/step_19.html" title="Handling input parameter files. Converting output formats." alt="" coords="313,841,348,874"/>
-<area shape="rect" id="node21" href="../deal.II/step_21.html" title="Two-phase flow in porous media." alt="" coords="81,658,116,683"/>
-<area shape="rect" id="node40" href="../deal.II/step_43.html" title="Efficient ways to solve two-phase flow problems on adaptive meshes in 2d and 3d." alt="" coords="23,927,59,953"/>
-<area shape="rect" id="node30" href="../deal.II/step_31.html" title="Boussinesq flow for thermal convection." alt="" coords="81,845,116,870"/>
-<area shape="rect" id="node34" href="../deal.II/step_35.html" title="A projection solver for the Navier-Stokes equations." alt="" coords="21,845,56,870"/>
-<area shape="rect" id="node24" href="../deal.II/step_24.html" title="The wave equation with absorbing boundary conditions. Extracting point values." alt="" coords="627,658,663,683"/>
-<area shape="rect" id="node25" href="../deal.II/step_25.html" title="The nonlinear sine-Gordon soliton equation" alt="" coords="717,762,752,787"/>
+<area shape="poly" id="node1" href="../deal.II/step_1.html" title="Creating a grid. Refining it. Writing it to a file" alt="" coords="548,60,548,28,525,5,493,5,471,28,471,60,493,83,525,83"/>
+<area shape="poly" id="node2" href="../deal.II/step_2.html" title="Assigning degrees of freedom to a grid." alt="" coords="548,185,548,153,525,131,493,131,471,153,471,185,493,208,525,208"/>
+<area shape="poly" id="node3" href="../deal.II/step_3.html" title="Solving Poisson's equation." alt="" coords="548,311,548,279,525,256,493,256,471,279,471,311,493,333,525,333"/>
+<area shape="poly" id="node4" href="../deal.II/step_4.html" title="Dimension independent programming. Boundary conditions." alt="" coords="548,436,548,404,525,381,493,381,471,404,471,436,493,459,525,459"/>
+<area shape="poly" id="node5" href="../deal.II/step_5.html" title="Reading a grid from disk. Computations on successively refined grids." alt="" coords="437,561,437,529,415,507,383,507,360,529,360,561,383,584,415,584"/>
+<area shape="rect" id="node10" href="../deal.II/step_10.html" title="Higher order mappings." alt="" coords="239,529,275,562"/>
+<area shape="rect" id="node15" href="../deal.II/step_15.html" title="1d problems. A nonlinear problem." alt="" coords="299,529,335,562"/>
+<area shape="rect" id="node20" href="../deal.II/step_20.html" title="Mixed finite elements for the mixed Laplacian. Block solvers." alt="" coords="462,533,497,558"/>
+<area shape="rect" id="node23" href="../deal.II/step_23.html" title="Time dependent problems. The wave equation." alt="" coords="146,533,181,558"/>
+<area shape="rect" id="node28" href="../deal.II/step_29.html" title="A complex-valued Helmholtz equation. Sparse direct solvers." alt="" coords="522,529,557,562"/>
+<area shape="rect" id="node33" href="../deal.II/step_34.html" title="Boundary element methods for potential flow." alt="" coords="582,533,617,558"/>
+<area shape="rect" id="node35" href="../deal.II/step_36.html" title="Finding eigenvalues of the Schrödinger equation." alt="" coords="642,529,677,562"/>
+<area shape="rect" id="node37" href="../deal.II/step_38.html" title="Solve the Laplace Beltrami operator on a Half Sphere." alt="" coords="625,654,660,687"/>
+<area shape="rect" id="node40" href="../deal.II/step_41.html" title="Solving the obstacle problem (a variational inequality)" alt="" coords="754,533,789,558"/>
+<area shape="poly" id="node6" href="../deal.II/step_6.html" title="Adaptive local refinement. Higher order elements" alt="" coords="408,687,408,655,385,632,353,632,331,655,331,687,353,709,385,709"/>
+<area shape="rect" id="node7" href="../deal.II/step_7.html" title="Helmholtz equation. Computing errors. Boundary integrals." alt="" coords="153,758,185,791"/>
+<area shape="rect" id="node8" href="../deal.II/step_8.html" title="Systems of PDE. Elasticity." alt="" coords="442,758,475,791"/>
+<area shape="rect" id="node9" href="../deal.II/step_9.html" title="Advection equation. Multithreading. Refinement criteria." alt="" coords="499,758,532,791"/>
+<area shape="rect" id="node13" href="../deal.II/step_13.html" title="Modularity. Software design." alt="" coords="41,758,76,791"/>
+<area shape="rect" id="node16" href="../deal.II/step_16.html" title="Multigrid on adaptive meshes." alt="" coords="101,841,136,874"/>
+<area shape="rect" id="node22" href="../deal.II/step_22.html" title="The Stokes equation on adaptive meshes." alt="" coords="609,762,644,787"/>
+<area shape="rect" id="node26" href="../deal.II/step_27.html" title="hp-adaptive finite element methods." alt="" coords="322,758,357,791"/>
+<area shape="rect" id="node27" href="../deal.II/step_28.html" title="Handling multiple meshes at the same time. Neutron transport." alt="" coords="210,758,245,791"/>
+<area shape="rect" id="node38" href="../deal.II/step_39.html" title="Interior Penalty for the Laplace equation. Adaptive refinement. Multigrid." alt="" coords="265,923,300,957"/>
+<area shape="rect" id="node39" href="../deal.II/step_40.html" title="Solving the Laplace equation on adaptive meshes on thousands of processors." alt="" coords="553,923,588,957"/>
+<area shape="rect" id="node43" href="../deal.II/step_45.html" title="Periodic boundary conditions" alt="" coords="382,758,417,791"/>
+<area shape="rect" id="node12" href="../deal.II/step_12.html" title="Discontinuous Galerkin for linear advection." alt="" coords="205,841,240,874"/>
+<area shape="rect" id="node17" href="../deal.II/step_17.html" title="Parallel computing using MPI. Using PETSc." alt="" coords="497,845,532,870"/>
+<area shape="rect" id="node42" href="../deal.II/step_44.html" title="Quasi-static finite-strain elasticity" alt="" coords="378,1010,413,1035"/>
+<area shape="rect" id="node44" href="../deal.II/step_46.html" title="Coupling different physical models (flow, elasticity) in different parts of the domain" alt="" coords="377,841,412,874"/>
+<area shape="rect" id="node11" href="../deal.II/step_11.html" title="Higher order mappings. Dealing with constraints." alt="" coords="239,654,275,687"/>
+<area shape="rect" id="node29" href="../deal.II/step_30.html" title="Anisotropic refinement for DG methods." alt="" coords="205,923,240,957"/>
+<area shape="rect" id="node32" href="../deal.II/step_33.html" title="Hyperbolic conservation laws: the Euler equations of gas dynamics." alt="" coords="145,927,180,953"/>
+<area shape="rect" id="node14" href="../deal.II/step_14.html" title="Duality based error estimates. Adaptivity." alt="" coords="41,841,76,874"/>
+<area shape="rect" id="node36" href="../deal.II/step_37.html" title="Matrix-free methods. Multigrid. Cell-based finite element operator." alt="" coords="85,923,120,957"/>
+<area shape="rect" id="node18" href="../deal.II/step_18.html" title="Quasistatic elasticity. More parallel computing." alt="" coords="437,927,472,953"/>
+<area shape="rect" id="node31" href="../deal.II/step_32.html" title="A parallel Boussinesq flow solver for thermal convection in the earth mantle." alt="" coords="553,1010,588,1035"/>
+<area shape="rect" id="node19" href="../deal.II/step_19.html" title="Handling input parameter files. Converting output formats." alt="" coords="437,841,472,874"/>
+<area shape="rect" id="node21" href="../deal.II/step_21.html" title="Two-phase flow in porous media." alt="" coords="565,658,600,683"/>
+<area shape="rect" id="node41" href="../deal.II/step_43.html" title="Efficient ways to solve two-phase flow problems on adaptive meshes in 2d and 3d." alt="" coords="697,927,732,953"/>
+<area shape="rect" id="node30" href="../deal.II/step_31.html" title="Boussinesq flow for thermal convection." alt="" coords="669,845,704,870"/>
+<area shape="rect" id="node34" href="../deal.II/step_35.html" title="A projection solver for the Navier-Stokes equations." alt="" coords="609,845,644,870"/>
+<area shape="rect" id="node24" href="../deal.II/step_24.html" title="The wave equation with absorbing boundary conditions. Extracting point values." alt="" coords="53,658,88,683"/>
+<area shape="rect" id="node25" href="../deal.II/step_25.html" title="The nonlinear sine-Gordon soliton equation" alt="" coords="5,927,40,953"/>
+<area shape="rect" id="node45" href="../deal.II/step_48.html" title="Parallelization via MPI. The wave equation, in linear and nonlinear variants. Mass lumping. Cell-based finite element operator." alt="" coords="85,1006,120,1039"/>
</map>
my @steps = (1,2,3,4,5,6,7,8,9,
10,11,12,13,14,15,16,17,18,19,
20,21,22,23,24,25, 27,28,29,
- 30,31,32,33,34,35,36, 38,39,
- 40,41, 43,44,45,46);
+ 30,31,32,33,34,35,36,37,38,39,
+ 40,41, 43,44,45,46, 48);
# List of additional node attributes to highlight purpose and state of the example
problem. The Schrödinger wave equation.
</td></tr>
+ <tr valign="top">
+ <td><a href="../../doxygen/deal.II/step_37.html">Step-37</a></td>
+ <td> Solving a Poisson problem with a multilevel preconditioner without
+ explicitly storing the matrix (a matrix-free method).
+ </td></tr>
+
<tr valign="top">
<td><a href="../../doxygen/deal.II/step_38.html">Step-38</a></td>
<td>Solving the Laplace-Beltrami equation on curved manifolds embedded
<td><a href="../../doxygen/deal.II/step_46.html">Step-46</a></td>
<td> Coupling different kinds of equations in different parts of the domain.
</td></tr>
+
+ <tr valign="top">
+ <td><a href="../../doxygen/deal.II/step_48.html">Step-48</a></td>
+ <td> Explicit time stepping for the Sine–Gordon equation based on
+ a diagonal mass matrix. Efficient implementation of (nonlinear) finite
+ element operators.
+ </td></tr>
</table>
<a href="../../doxygen/deal.II/step_9.html">Step-9</a>,
<a href="../../doxygen/deal.II/step_28.html">Step-28</a>,
<a href="../../doxygen/deal.II/step_32.html">Step-32</a>,
- <a href="../../doxygen/deal.II/step_44.html">Step-44</a>
+ <a href="../../doxygen/deal.II/step_44.html">Step-44</a>,
+ <a href="../../doxygen/deal.II/step_48.html">Step-48</a>
</td>
<td> Multithreading
</td>
</td>
</tr>
+ <tr valign="top">
+ <td></td>
+ <td>
+ <a href="../../doxygen/deal.II/step_37.html">Step-37</a>,
+ <a href="../../doxygen/deal.II/step_48.html">Step-48</a>
+ </td>
+ <td> Matrix-free methods
+ </td>
+ </tr>
+
<tr><td><br></td></tr>
<tr valign="top">
<a href="../../doxygen/deal.II/step_16.html">Step-16</a>,
<a href="../../doxygen/deal.II/step_31.html">Step-31</a>,
<a href="../../doxygen/deal.II/step_32.html">Step-32</a>,
+ <a href="../../doxygen/deal.II/step_37.html">Step-37</a>,
<a href="../../doxygen/deal.II/step_39.html">Step-39</a>,
<a href="../../doxygen/deal.II/step_41.html">Step-41</a>,
<a href="../../doxygen/deal.II/step_43.html">Step-43</a>
<td>
<a href="../../doxygen/deal.II/step_23.html">Step-23</a>,
<a href="../../doxygen/deal.II/step_24.html">Step-24</a>,
- <a href="../../doxygen/deal.II/step_25.html">Step-25</a>
+ <a href="../../doxygen/deal.II/step_25.html">Step-25</a>,
+ <a href="../../doxygen/deal.II/step_48.html">Step-48</a>
</td>
<td> The wave equation, in linear and nonlinear variants
</td>
<td></td>
<td><a href="../../doxygen/deal.II/step_23.html">Step-23</a>,
<a href="../../doxygen/deal.II/step_24.html">Step-24</a>,
- <a href="../../doxygen/deal.II/step_25.html">Step-25</a></td>
+ <a href="../../doxygen/deal.II/step_25.html">Step-25</a>,
+ <a href="../../doxygen/deal.II/step_48.html">Step-48</a>
+ </td>
<td> The wave equation, in linear and nonlinear variants
</td>
</tr>
contributions to the energy are positive, and so that decaying solutions have
finite energy on unbounded domains.)
-Beyond this, clearly, adaptivity (i.e. time-adaptive grids) would be of
-interest to problems like these. Their complexity leads us to leave this out
-of this program again, though the general comments in the introduction of @ref
-step_23 "step-23" remain true.
-
-
-
+Beyond this, there are two obvious areas:
+
+- Clearly, adaptivity (i.e. time-adaptive grids) would be of interest
+ to problems like these. Their complexity leads us to leave this out
+ of this program again, though the general comments in the
+ introduction of @ref step_23 "step-23" remain true.
+
+- Faster schemes to solve this problem. While computers today are
+ plenty fast enough to solve 2d and, frequently, even 3d stationary
+ problems within not too much time, time dependent problems present
+ an entirely different class of problems. We address this topic in
+ step-48 where we show how to solve this problem in parallel and
+ without assembling or inverting any matrix at all.
# settings
include $D/common/Make.global_options
+################################################################
+# This example program will only work if Trilinos is installed. If this
+# # is not the case, then simply redefine the main targets to do nothing
+ifneq ($(USE_CONTRIB_LAPACK),yes)
+default run clean:
+ @echo
+ @echo "==========================================================="
+ @echo "= This program can only be run with LAPACK enabled. ="
+ @echo "==========================================================="
+ @echo
+else
+#
+#################################################################
+
+
# Since the whole project consists of only one file, we need not
# consider difficult dependencies. We only have to declare the
# them:
include Makefile.dep
+endif # USE_CONTRIB_LAPACK
This program was contributed by Katharina Kormann and Martin
Kronbichler.
-This program is currently under construction.
-
-The algorithm for the matrix-vector product is built upon the report "MPI
-parallelization of a cell-based matrix-vector product for finite elements. An
-application from quantum dynamics" by Katharina Kormann, Uppsala
-University, June 2009.
+The algorithm for the matrix-vector product is built upon the preprint "A generic interface for parallel cell-based finite element operator application" by Martin Kronbichler and Katharina Kormann, Uppsala
+University, October 2011, and the paper "Parallel finite element operator application: Graph partitioning and coloring" by Katharina Kormann and Martin Kronbichler in: Proceedings of the 7th IEEE International Conference on e-Science, 2011.
</i>
<a name="Intro"></a>
<h1>Introduction</h1>
-This example shows how to implement a matrix-free method, that is, a method
-that does not explicitly store the matrix elements, for a
-second-order Poisson equation with variable coefficients on a fairly
-unstructured mesh representing a circle.
+This example shows how to implement a matrix-free method, that is, a
+method that does not explicitly store the matrix elements, for a
+second-order Poisson equation with variable coefficients on a
+hypercube. The eliptic equation will be solved with a multigrid
+method.
+
+The major motivation for matrix-free methods is the fact that today
+access to main memory (i.e., for objects that don't fit in the cache)
+has become the bottleneck in scientific computing: To perform a
+matrix-vector product, modern CPUs spend far more time waiting for
+data to arrive from memory than on actually doing the floating point
+multiplications and additions. Thus, if we could substitute looking up
+matrix elements in memory by re-computing them — or rather, the
+operator represented by these entries —, we may win in terms of
+overall run-time (even if this requires a significant number of
+additional floating point operations). That said, to realize this with
+a trivial implementation is not enough and one needs to really look at
+what it takes to make this happen. This tutorial program (and the
+papers referenced above) show how one can implement such a scheme and
+demonstrates the speedup that can be obtained.
+
+
+<h3>The test case</h3>
+
+In this example, we consider the Poisson problem @f{eqnarray*} -
+\nabla \cdot a(\mathbf x) \nabla u &=& 1, \\ u &=& 0 \quad \text{on}\
+\partial \Omega @f} where $a(\mathbf x)$ is a variable coefficient.
+Below, we explain how to implement a matrix-vector product for this
+problem without explicitly forming the matrix. The construction can,
+of course, be done in a similar way for other equations as well.
+
+We choose as domain $\Omega=[0,1]^3$ and $a(\mathbf x)=\frac{1}{0.05 +
+2\|\mathbf x\|^2}$. Since the coefficient is symmetric around the
+origin but the domain is not, we will end up with a non-symmetric
+solution.
+
<h3>Matrix-vector product implementation</h3>
In order to find out how we can write a code that performs a matrix-vector
product, but does not need to store the matrix elements, let us start at
-looking how some finite-element related matrix <i>A</i> is assembled:
+looking how a finite element matrix <i>A</i> is assembled:
@f{eqnarray*}
-A = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T A_\mathrm{cell}
-P_\mathrm{cell,{loc-glob}}.
+A = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}}
+P_{\mathrm{cell,{loc-glob}}}^T A_{\mathrm{cell}} P_{\mathrm{cell,{loc-glob}}}.
@f}
In this formula, the matrix <i>P</i><sub>cell,loc-glob</sub> is a rectangular
matrix that defines the index mapping from local degrees of freedom in the
current cell to the global degrees of freedom. The information from which this
operator can be built is usually encoded in the <code>local_dof_indices</code>
-variable we have always used in the assembly of matrices.
+variable we have always used in the assembly of matrices. Moreover,
+<i>A</i><sub>cell</sub> denotes the cell-operation associated with <i>A</i>.
If we are to perform a matrix-vector product, we can hence use that
@f{eqnarray*}
-y &=& A\cdot x = \left(\sum_{\text{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
-A_\mathrm{cell} P_\mathrm{cell,{loc-glob}}\right) \cdot x
+y &=& A\cdot u = \left(\sum_{\text{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
+A_\mathrm{cell} P_\mathrm{cell,{loc-glob}}\right) \cdot u
\\
&=& \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
-A_\mathrm{cell} x_\mathrm{cell}
+A_\mathrm{cell} u_\mathrm{cell}
\\
&=& \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
-y_\mathrm{cell},
+v_\mathrm{cell},
@f}
-where <i>x</i><sub>cell</sub> are the values of <i>x</i> at the degrees of freedom
-of the respective cell, and <i>x</i><sub>cell</sub> correspondingly for the result.
+where <i>u</i><sub>cell</sub> are the values of <i>u</i> at the degrees of freedom
+of the respective cell, and
+<i>v</i><sub>cell</sub>=<i>A</i><sub>cell</sub><i>u</i><sub>cell</sub>
+correspondingly for the result.
A naive attempt to implement the local action of the Laplacian would hence be
to use the following code:
@code
-MatrixFree::vmult (Vector<double> &dst,
- const Vector<double> &src) const
+MatrixFree<dim>::vmult (Vector<double> &dst,
+ const Vector<double> &src) const
{
dst = 0;
QGauss<dim> quadrature_formula(fe.degree+1);
FEValues<dim> fe_values (fe, quadrature_formula,
- update_gradients | update_JxW_values);
+ update_gradients | update_JxW_values|
+ update_quadrature_points);
const unsigned int dofs_per_cell = fe.dofs_per_cell;
const unsigned int n_q_points = quadrature_formula.size();
FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
Vector<double> cell_src (dofs_per_cell),
cell_dst (dofs_per_cell);
+ const Coefficient<dim> coefficient;
+ std::vector<double> coefficient_values(n_q_points);
std::vector<unsigned int> local_dof_indices (dofs_per_cell);
{
cell_matrix = 0;
fe_values.reinit (cell);
+ coefficient.value_list(fe_values.get_quadrature_points(),
+ coefficient_values);
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int q=0; q<n_q_points; ++q)
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
- cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
- fe_values.shape_grad(j,q_point) *
- fe_values.JxW(q_point));
+ cell_matrix(i,j) += (fe_values.shape_grad(i,q) *
+ fe_values.shape_grad(j,q) *
+ fe_values.JxW(q)*
+ coefficient_values[q]);
cell->get_dof_indices (local_dof_indices);
Here we neglected boundary conditions as well as any hanging nodes we may
have, though neither would be very difficult to include using the
ConstraintMatrix class. Note how we first generate the local matrix in the
-usual way. To form the actual product as expressed in the above formula, we
-read in the values of <code>src</code> of the cell-related degrees of freedom
+usual way as a sum over all quadrature points for each local matrix entry.
+To form the actual product as expressed in the above formula, we
+extract the values of <code>src</code> of the cell-related degrees of freedom
(the action of <i>P</i><sub>cell,loc-glob</sub>), multiply by the local matrix
(the action of <i>A</i><sub>cell</sub>), and finally add the result to the
destination vector <code>dst</code> (the action of
is not more difficult than that, in principle.
While this code is completely correct, it is very slow. For every cell, we
-generate a local matrix, which takes three nested loops with as many
-elements as there are degrees of freedom on the actual cell to compute. The
+generate a local matrix, which takes three nested loops with loop length equal
+to the number of local degrees of freedom to compute. The
multiplication itself is then done by two nested loops, which means that it
is much cheaper.
<code>fe_values.shape_grad(i,q)[d]</code>. The matrix consists of
<code>dim*n_q_points</code> rows and @p dofs_per_cell columns). The matrix
<i>D</i><sub>cell</sub> is diagonal and contains the values
-<code>fe_values.JxW(q)</code> (or, rather, @p dim copies of it).
-
-Every numerical analyst learns in one of her first classes that for
-forming a product of the form
-@f{eqnarray*}
-A_\mathrm{cell}\cdot x_\mathrm{cell} = B_\mathrm{cell} D_\mathrm{cell}
- B_\mathrm{cell}^T \cdot x_\mathrm{cell},
-@f}
-one should never form the matrix-matrix products, but rather multiply with the
-vector from right to left so that only three successive matrix-vector products
-are formed. To put this into code, we can write:
-@code
-...
- for (; cell!=endc; ++cell)
- {
- fe_values.reinit (cell);
-
- cell->get_dof_indices (local_dof_indices);
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- cell_src(i) = src(local_dof_indices(i));
-
- temp_vector = 0;
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
- for (unsigned int d=0; d<dim; ++d)
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- temp_vector(q_point*dim+d) += fe_values.shape_grad(i,q_point)[d] *
- cell_src(i);
-
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
- for (unsigned int d=0; d<dim; ++d)
- temp_vector(q_point*dim+d) *= fe_values.JxW(q_point);
-
- cell_dst = 0;
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
- for (unsigned int d=0; d<dim; ++d)
- cell_dst(i) += fe_values.shape_grad(i,q_point)[d] *
- temp_vector(q_point*dim+d);
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- dst(local_dof_indices(i)) += cell_dst(i);
- }
-}
-@endcode
-
-This removed the three nested loops in the calculation of the local matrix
-(here the loop over <i>d</i> is a not really a loop, rather two or three
-operations). What happens is as follows: We first transform the vector of
-values on the local dofs to a vector of gradients on the quadrature
-points. In the second loop, we multiply these gradients by the integration
-weight. The third loop applies the second gradient (in transposed form), so
-that we get back to a vector of (Laplacian) values on the cell dofs.
-
-This improves the situation a lot and reduced the complexity of the product
-from something like $\mathcal {O}(\mathrm{dofs\_per\_cell}^3)$ to $\mathcal
-{O}(\mathrm{dofs\_per\_cell}^2)$. In fact, all the remainder is just to make
-a slightly more clever use of data in order to gain some extra speed. It does
-not change the code structure, though.
-
-The bottleneck in the above code is the operations done by the call
-<code>fe_values.reinit(cell)</code>, which take about as much time as the
+<code>fe_values.JxW(q) * coefficient_values[q]</code> (or, rather, @p
+dim copies of each of these values). This kind of representation of
+finite element matrices can often be found in the engineering literature.
+
+When the cell-based matrix is applied to a vector @f{eqnarray*}
+A_\mathrm{cell}\cdot u_\mathrm{cell} = B_\mathrm{cell}^T
+D_\mathrm{cell} B_\mathrm{cell} \cdot u_\mathrm{cell}, @f} one would
+then never form the matrix-matrix products, but rather multiply with
+the vector from right to left so that only three successive
+matrix-vector products are formed. This removed the three nested
+loops in the calculation of the local matrix. What happens is as
+follows: We first transform the vector of values on the local dofs to
+a vector of gradients on the quadrature points. In the second loop, we
+multiply these gradients by the integration weight. The third loop
+applies the second gradient (in transposed form), so that we get back
+to a vector of (Laplacian) values on the cell dofs. This reduces the
+complexity of the work on one cell from something like $\mathcal
+{O}(\mathrm{dofs\_per\_cell}^3)$ to $\mathcal
+{O}(\mathrm{dofs\_per\_cell}^2)$.
+
+The bottleneck in the above code is the operations done by the call to
+FEValues::reinit for every <code>cell</code>, which take about as much time as the
other steps together (at least if the mesh is unstructured; deal.II can
recognize that the gradients are often unchanged on structured meshes). That
is certainly not ideal and we would like to do better than this. What the
reinit function does is to calculate the gradient in real space by
transforming the gradient on the reference cell using the Jacobian of the
transformation from real to reference cell. This is done for each basis
-function on the cell, for each quadrature point. The Jacobian does not depend on
-the basis function, but it is different on different quadrature points in
-general. The trick is now to factor out the Jacobian transformation and first
-apply the operation that leads us to <code>temp_vector</code> only with the
-gradient on the reference cell. That transforms the vector of values on the
-local dofs to a vector of gradients on the quadrature points. There, we first
-apply the Jacobian that we factored out from the gradient, then we apply the
-weights of the quadrature, and we apply with the transposed Jacobian for
-preparing the third loop which again uses the gradients on the unit cell.
+function on the cell, for each quadrature point. The Jacobian does not depend
+on the basis function, but it is different on different quadrature points in
+general. If you only build the matrix once as we've done in all
+previous tutorial programs, there is nothing one can do about the need
+to call FEValues::reinit on every cell since this transformation has
+to be done when we want to compute the local matrix elements.
+
+However, in a matrix-free implementation, we are not interested in
+applying the matrix only once. Rather, in iterative solvers, we need
+to expect that we have to apply the matrix many times, and so we can
+think about whether we may be able to cache something between
+different applications. On the other hand, we realize that we must not
+cache too much data since otherwise we get back to the situation where
+memory access becomes the dominating factor.
+
+The trick is now to factor out the Jacobian transformation and first
+apply the gradient on the reference cell only. That transforms the vector of
+values on the local dofs to a vector of gradients on the quadrature
+points. There, we first apply the Jacobian that we factored out from the
+gradient, then we apply the weights of the quadrature, and we apply the
+transposed Jacobian for preparing the third loop which again uses the
+gradients on the unit cell.
Let us again write this in terms of matrices. Let the matrix
<i>B</i><sub>cell</sub> denote the cell-related gradient matrix, with each row
-containing the values of the quadrature points. It is constructed by a
+containing the values on the quadrature points. It is constructed by a
matrix-matrix product as
@f{eqnarray*}
B_\mathrm{cell} = J_\mathrm{cell} B_\mathrm{ref\_cell},
@f}
where <i>B</i><sub>ref_cell</sub> denotes the gradient on the reference cell
-and <i>J</i><sub>cell</sub> denotes the Jacobian
-transformation. <i>J</i><sub>cell</sub> is block-diagonal, and the blocks size
-is equal to the dimension of the problem. Each diagonal block is the Jacobian
-transformation that goes from the reference cell to the real cell.
+and <i>J</i><sub>cell</sub> denotes the Jacobian transformation from unit to
+real cell (in the language of transformations, the operation represented by
+<i>J</i><sub>cell</sub> represents a covariant
+transformation). <i>J</i><sub>cell</sub> is block-diagonal, and the blocks
+size is equal to the dimension of the problem. Each diagonal block is the
+Jacobian transformation that goes from the reference cell to the real cell.
Putting things together, we find that
@f{eqnarray*}
-A_\mathrm{cell} = B_\mathrm{cell}^T D B_\mathrm{cell}
- = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T
- D_\mathrm{cell}
+A_\mathrm{cell} = B_\mathrm{cell}^T D B_\mathrm{cell}
+ = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T
+ D_\mathrm{cell}
J_\mathrm{cell} B_\mathrm{ref\_cell},
@f}
so we calculate the product (starting the local product from the right)
@f{eqnarray*}
-y_\mathrm{cell} = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T D J_\mathrm{cell}
-B_\mathrm{ref\_cell} x_\mathrm{cell}, \quad
-y = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
-y_\mathrm{cell}.
+v_\mathrm{cell} = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T D J_\mathrm{cell}
+B_\mathrm{ref\_cell} u_\mathrm{cell}, \quad
+v = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T
+v_\mathrm{cell}.
@f}
@code
...
fe_values_reference.reinit (reference_cell.begin());
FEValues<dim> fe_values (fe, quadrature_formula,
- update_inverse_jacobians | update_JxW_values);
+ update_inverse_jacobians | update_JxW_values |
+ update_quadrature_points);
for (; cell!=endc; ++cell)
{
fe_values.reinit (cell);
+ coefficient.value_list(fe_values.get_quadrature_points(),
+ coefficient_values);
cell->get_dof_indices (local_dof_indices);
cell_src(i) = src(local_dof_indices(i));
temp_vector = 0;
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int q=0; q<n_q_points; ++q)
for (unsigned int d=0; d<dim; ++d)
for (unsigned int i=0; i<dofs_per_cell; ++i)
- temp_vector(q_point*dim+d) +=
- fe_values_reference.shape_grad(i,q_point)[d] * cell_src(i);
+ temp_vector(q*dim+d) +=
+ fe_values_reference.shape_grad(i,q)[d] * cell_src(i);
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int q=0; q<n_q_points; ++q)
{
- // apply the Jacobian of the mapping from unit to real cell
+ // apply the tranpose inverse Jacobian of the mapping
Tensor<1,dim> temp;
for (unsigned int d=0; d<dim; ++d)
- temp[d] = temp_vector(q_point*dim+d);
+ temp[d] = temp_vector(q*dim+d);
for (unsigned int d=0; d<dim; ++d)
{
double sum = 0;
for (unsigned int e=0; e<dim; ++e)
- sum += fe_values.inverse_jacobian(q_point)[d][e] *
+ sum += fe_values.inverse_jacobian(q)[e][d] *
temp[e];
- temp_vector(q_point*dim+d) = sum;
+ temp_vector(q*dim+d) = sum;
}
- // multiply with integration weight
+ // multiply by coefficient and integration weight
for (unsigned int d=0; d<dim; ++d)
- temp_vector(q_point*dim+d) *= fe_values.JxW(q_point);
+ temp_vector(q*dim+d) *= fe_values.JxW(q) * coefficient_values[q];
- // apply the transpose of the Jacobian of the mapping from unit
- // to real cell
+ // apply the inverse Jacobian of the mapping
for (unsigned int d=0; d<dim; ++d)
- temp[d] = temp_vector(q_point*dim+d);
+ temp[d] = temp_vector(q*dim+d);
for (unsigned int d=0; d<dim; ++d)
{
double sum = 0;
for (unsigned int e=0; e<dim; ++e)
- sum += fe_values.inverse_jacobian(q_point)[e][d] *
+ sum += fe_values.inverse_jacobian(q)[d][e] *
temp[e];
- temp_vector(q_point*dim+d) = sum;
+ temp_vector(q*dim+d) = sum;
}
}
cell_dst = 0;
for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int q=0; q<n_q_points; ++q)
for (unsigned int d=0; d<dim; ++d)
- cell_dst(i) += fe_values_reference.shape_grad(i,q_point)[d] *
- temp_vector(q_point*dim+d);
+ cell_dst(i) += fe_values_reference.shape_grad(i,q)[d] *
+ temp_vector(q*dim+d);
for (unsigned int i=0; i<dofs_per_cell; ++i)
dst(local_dof_indices(i)) += cell_dst(i);
Note how we create an additional FEValues object for the reference cell
gradients and how we initialize it to the reference cell. The actual
-derivative data is then applied by the Jacobians (deal.II calls the Jacobian
-matrix from unit to real cell inverse_jacobian, because the transformation
-direction in deal.II is from real to unit cell).
-
-To sum up, we want to look at the additional costs introduced by this
-written-out matrix-matrix-vector product compared to a sparse matrix. To first
-approximation, we have increased the number of operations for the local
-matrix-vector product by a factor of 4 in 2D and 6 in 3D, i.e., two
-matrix-vector products with matrices that have <code>dim</code> as many
-columns than before (here we assume that the number of quadrature points is
-the same as the number of degrees of freedom per cell, which is usual for
-scalar problems). Then, we also need to keep in mind that we touch some
-degrees of freedom several times because they belong to several cells. This
-also increases computational costs. A realistic value compared to a sparse
-matrix is that we now have to perform about 10 times as many operations (a bit
-less in 2D, a bit more in 3D).
-
-The above is, in essence, what happens in the code below and if you have
-difficulties in understanding the implementation, you should try to first
-understand what happens in the code above. In the actual implementation
-there are a few more points done to be even more efficient, namely:
-<ul>
- <li>We pre-compute the inverse of the Jacobian of the transformation and
- store it in an extra array. This allows us to fuse the three
- operations <i>J</i><sub>cell</sub><sup>T</sup><i> D</i><sub>cell</sub>
- <i>J</i><sub>cell</sub> (apply Jacobian, multiply by
- weights, apply transposed
- Jacobian) into one second-rank tensor that is also symmetric (so we
- only need to store half the tensor).
- <li>We work on several cells at once when we apply the gradients of the
- unit cell (it is always the same matrix with the reference cell
- data). This allows us to replace the matrix-vector product by a
- matrix-matrix product (several vectors of cell-data form a matrix),
- which enables a faster implementation. Obviously, we need some adapted
- data structures for that, but it isn't too hard to provide that. What
- is nice is that dense matrix-matrix products are close to today's
- processors' peak performance if the matrices are neither too small nor
- too large — and these operations are the most expensive part in
- the implementation shown here.
-</ul>
+derivative data is then applied by the inverse, transposed Jacobians (deal.II
+calls the Jacobian matrix from unit to real cell inverse_jacobian, because the
+transformation direction in deal.II is from real to unit cell).
+
+Finally, we are using tensor product basis functions and now that we have
+separated out the gradient on the reference cell <i>B</i><sub>ref_cell</sub>,
+we can exploit the tensor-product structure to further reduce the
+complexity. We illustrate this in two space dimensions, but the same technique
+can be used in higher dimensions. On the reference cell, the basis functions
+are of the tensor product form $\phi(x,y,z) = \varphi_i(x) \varphi_j(y)$. The
+part of the matrix <i>B</i><sub>ref_cell</sub> that computes the first
+component has the form $B_\mathrm{sub\_cell}^x = B_\mathrm{grad,x} \otimes
+B_\mathrm{val,y}$, where <i>B</i><sub>grad,x</sub> and
+<i>B</i><sub>val,y</sub> contain the evaluation of all the 1D basis functions
+on all the 1D quadrature points. Forming a matrix <i>U</i> with <i>U(j,i)</i>
+containing the coefficient belonging to basis function $\varphi_i(x)
+\varphi_j(y)$, we get $(B_\mathrm{grad,x} \otimes
+B_\mathrm{val,y})u_\mathrm{cell} = B_\mathrm{val,y} U B_\mathrm{grad,x}$. This
+reduces the complexity for computing this product from $p^4$ to $2 p^3$, where
+<i>p</i>-1 is the degree of the finite element (i.e., equivalently,
+<i>p</i> is the number of shape functions in each coordinate
+direction), or $p^{2d}$ to $d p^{d+1}$ in general.
+
The implementation of the matrix-free matrix-vector product shown in this
tutorial is slower than a matrix-vector product using a sparse matrix for
-linear and quadratic elements, but on par with third order elements and faster
-for even higher order elements. An additional gain with this implementation is
-that we do not have to build the sparse matrix itself, which can also be quite
-expensive depending on the underlying differential equation.
+linear elements, but faster for all higher order elements thanks to the
+reduced complexity due to the tensor product structure and due to less memory
+transfer during computations. The impact of reduced memory transfer is
+particularly beneficial when working on a multi-core processor where several
+processing units share access to memory. In that case, an algorithm which is
+computation bound will show almost perfect parallel speedup, whereas an
+algorithm that is bound by memory transfer might not achieve similar speedup
+(even when the work is perfectly parallel and one could expect perfect scaling
+like in sparse matrix-vector products). An additional gain with this
+implementation is that we do not have to build the sparse matrix itself, which
+can also be quite expensive depending on the underlying differential
+equation. Moreover, the above framework is simple to generalize to nonlinear
+operations, as we demonstrate in step-48.
<h3>Combination with multigrid</h3>
Above, we have gone to significant lengths to implement a matrix-vector
product that does not actually store the matrix elements. In many user codes,
-however, one wants more than just performing some uncertain number of
+however, one wants more than just performing some number of
matrix-vector products — one wants to do as little of these operations
as possible when solving linear equation systems. In theory, we could use the
CG method without preconditioning; however, that would not be very
efficient. Rather, one uses preconditioners for improving speed. On the other
hand, most of the more frequently used preconditioners such as SSOR, ILU or
-algebraic multigrid (AMG) can now no longer be used here because their
+algebraic multigrid (AMG) cannot be used here because their
implementation requires knowledge of the elements of the system matrix.
-One solution is to use multigrid methods as shown in
-step-16. They are known to be very fast, and they are suitable for our
-purpose since they can be designed based purely on matrix-vector products. All
-one needs to do is to find a smoother that works with matrix-vector products
-only (our choice requires knowledge of the diagonal entries of the matrix,
-though). One such candidate would be a damped Jacobi iteration, but that is
-often not sufficiently good in damping high-frequency errors.
-A Chebyshev preconditioner, eventually, is what we use here. It can be
-seen as an extension of the Jacobi method by using Chebyshev polynomials. With
-degree zero, the Jacobi method with optimal damping parameter is retrieved,
-whereas higher order corrections improve the smoothing properties if some
-parameters are suitably chosen. The effectiveness of Chebyshev smoothing in
-multigrid has been demonstrated, e.g., in the article <i>M. Adams, M. Brezina,
-J. Hu, R. Tuminaro. Parallel multigrid smoothers: polynomial versus
-Gauss–Seidel, J. Comput. Phys. 188:593–610, 2003</i>. This
-publication also identifies one more advantage of Chebyshev smoothers that we
-exploit here, namely that they are easy to parallelize, whereas
-SOR/Gauss–Seidel smoothing relies on substitutions, which can often only
-be parallelized by working on diagonal sub-blocks of the matrix, which
-decreases efficiency.
-
-The implementation into the multigrid framework is then straightforward. This
+One solution is to use multigrid methods as shown in step-16. They are known
+to be very fast, and they are suitable for our purpose since they can be
+designed based purely on matrix-vector products. All one needs to do is to
+find a smoother that works with matrix-vector products only (our choice
+requires knowledge of the diagonal entries of the matrix, though). One such
+candidate would be a damped Jacobi iteration, but that is often not
+sufficiently good in damping high-frequency errors. A Chebyshev
+preconditioner, eventually, is what we use here. It can be seen as an
+extension of the Jacobi method by using Chebyshev polynomials. With degree
+zero, the Jacobi method with optimal damping parameter is retrieved, whereas
+higher order corrections improve the smoothing properties if some parameters
+are suitably chosen. The effectiveness of Chebyshev smoothing in multigrid has
+been demonstrated, e.g., in the article <a
+href="http://www.sciencedirect.com/science/article/pii/S0021999103001943">
+<i>M. Adams, M. Brezina, J. Hu, R. Tuminaro. Parallel multigrid smoothers:
+polynomial versus Gauss–Seidel, J. Comput. Phys. 188:593–610,
+2003</i> </a>. This publication also identifies one more advantage of
+Chebyshev smoothers that we exploit here, namely that they are easy to
+parallelize, whereas SOR/Gauss–Seidel smoothing relies on substitutions,
+for which a naive parallelization works on diagonal sub-blocks of the matrix,
+thereby decreases efficiency (for more detail see e.g. Y. Saad,
+Iterative Methods for Sparse Linear Systems, SIAM, 2nd edition, 2003, chapters
+11 & 12).
+
+The implementation into the multigrid framework is then straightforward. The multigrid implementation in this
program is based on an earlier version of step-16 that demonstrated multigrid
-on uniformly refined grids. However, the present matrix-free techniques would
-obviously also apply to the adaptive meshes the current step-16 uses.
+on uniformly refined grids.
-<h3>The test case</h3>
-
-In order to demonstrate the capabilities of the method, we work on a rather
-general Poisson problem, based on a more or less unstructured mesh (where
-the Jacobians are different from cell to cell), higher order mappings to a
-curved boundary, and a non-constant coefficient in the equation. If we
-worked on a constant-coefficient case with structured mesh, we could
-decrease the operation count by a factor of 4 in 2D and 6 in 3D by building
-a local matrix (which is then the same for all cells), and doing the
-products as in the first developing step of the above code pieces.
<h3>Program output</h3>
Since this example solves the same problem as step-5 (except for
-a different coefficient), we refer to the graphical output there. Here, we
-evaluate some aspects of the multigrid solver.
+a different coefficient), there is little to say about the
+solution. We show a picture anyway, illustrating the size of the
+solution through both isocontours and volume rendering:
+@image html "step-37.solution.png"
+
+Of more interest is to evaluate some aspects of the multigrid solver.
When we run this program in 2D for quadratic ($Q_2$) elements, we get the
following output:
@code
Cycle 0
-Number of degrees of freedom: 337
-System matrix memory consumption: 0.02573 MiB.
-Multigrid objects memory consumption: 0.05083 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 81
+System matrix memory consumption: 0.008982 MB.
+Multigrid objects memory consumption: 0.02617 MB.
+Total setup time (wall) 0.001811s
+Time solve (5 iterations) (CPU/wall) 0s/0.0002651s
Cycle 1
-Number of degrees of freedom: 1313
-System matrix memory consumption: 0.09257 MiB.
-Multigrid objects memory consumption: 0.1794 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 289
+System matrix memory consumption: 0.01817 MB.
+Multigrid objects memory consumption: 0.05779 MB.
+Total setup time (wall) 0.001223s
+Time solve (5 iterations) (CPU/wall) 0s/0.000926s
Cycle 2
-Number of degrees of freedom: 5185
-System matrix memory consumption: 0.3553 MiB.
-Multigrid objects memory consumption: 0.6779 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 1089
+System matrix memory consumption: 0.05286 MB.
+Multigrid objects memory consumption: 0.1581 MB.
+Total setup time (wall) 0.003045s
+Time solve (6 iterations) (CPU/wall) 0.012s/0.003393s
Cycle 3
-Number of degrees of freedom: 20609
-System matrix memory consumption: 1.397 MiB.
-Multigrid objects memory consumption: 2.645 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 4225
+System matrix memory consumption: 0.1957 MB.
+Multigrid objects memory consumption: 0.5228 MB.
+Total setup time (wall) 0.008561s
+Time solve (6 iterations) (CPU/wall) 0.02s/0.01133s
Cycle 4
-Number of degrees of freedom: 82177
-System matrix memory consumption: 5.546 MiB.
-Multigrid objects memory consumption: 10.46 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 16641
+System matrix memory consumption: 0.7343 MB.
+Multigrid objects memory consumption: 1.925 MB.
+Total setup time (wall) 0.02938s
+Time solve (6 iterations) (CPU/wall) 0.068s/0.03312s
Cycle 5
-Number of degrees of freedom: 328193
-System matrix memory consumption: 22.11 MiB.
-Multigrid objects memory consumption: 41.65 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 66049
+System matrix memory consumption: 2.856 MB.
+Multigrid objects memory consumption: 7.435 MB.
+Total setup time (wall) 0.1128s
+Time solve (6 iterations) (CPU/wall) 0.228s/0.09577s
+
+Cycle 6
+Number of degrees of freedom: 263169
+System matrix memory consumption: 11.28 MB.
+Multigrid objects memory consumption: 29.3 MB.
+Total setup time (wall) 0.4553s
+Time solve (6 iterations) (CPU/wall) 1.272s/0.3955s
@endcode
As in step-16, we see that the number of CG iterations remains constant with
increasing number of degrees of freedom. We can also see that the various
objects we have to store for the multigrid method on the individual levels of
-our mesh together make up about twice as much as the matrix on the finest
-level.
+our mesh together make up more than twice as much as the matrix on the finest
+level. For the present example, about half the memory consumption of the
+multigrid objects are the level transfer matrices, and the other half is
+consumed by the matrix-free objects (and there, mainly the indices and the
+variable coefficient).
-Not much changes if we run the
-program in three spatial dimensions, with the exception that the multilevel
-objects now take up comparatively less space (because in 3d, each level has
-only one eighth the number of cells of the next finer one, whereas in 2d this
-factor if one quarter):
+Not much changes if we run the program in three spatial dimensions, with the
+exception that the multilevel objects now take up some more memory (because
+the level transfer matrices are denser) and the computing times are somewhat
+larger:
@code
Cycle 0
-Number of degrees of freedom: 517
-System matrix memory consumption: 0.1001 MiB.
-Multigrid objects memory consumption: 0.1463 MiB.
-Convergence in 9 CG iterations.
+Number of degrees of freedom: 125
+System matrix memory consumption: 0.01093 MB.
+Multigrid objects memory consumption: 0.03094 MB.
+Total setup time (wall) 0.002481s
+Time solve (5 iterations) (CPU/wall) 0s/0.000334s
Cycle 1
-Number of degrees of freedom: 3817
-System matrix memory consumption: 0.6613 MiB.
-Multigrid objects memory consumption: 0.8896 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 729
+System matrix memory consumption: 0.04105 MB.
+Multigrid objects memory consumption: 0.1274 MB.
+Total setup time (wall) 0.004471s
+Time solve (5 iterations) (CPU/wall) 0.004s/0.001979s
Cycle 2
-Number of degrees of freedom: 29521
-System matrix memory consumption: 5.1 MiB.
-Multigrid objects memory consumption: 6.653 MiB.
-Convergence in 10 CG iterations.
+Number of degrees of freedom: 4913
+System matrix memory consumption: 0.2821 MB.
+Multigrid objects memory consumption: 0.8048 MB.
+Total setup time (wall) 0.01651s
+Time solve (4 iterations) (CPU/wall) 0.036s/0.01295s
Cycle 3
-Number of degrees of freedom: 232609
-System matrix memory consumption: 40.4 MiB.
-Multigrid objects memory consumption: 52.24 MiB.
-Convergence in 11 CG iterations.
+Number of degrees of freedom: 35937
+System matrix memory consumption: 1.948 MB.
+Multigrid objects memory consumption: 5.734 MB.
+Total setup time (wall) 0.1072s
+Time solve (5 iterations) (CPU/wall) 0.16s/0.0709s
Cycle 4
-Number of degrees of freedom: 1847617
-System matrix memory consumption: 322 MiB.
-Multigrid objects memory consumption: 415.1 MiB.
-Convergence in 11 CG iterations.
+Number of degrees of freedom: 274625
+System matrix memory consumption: 14.49 MB.
+Multigrid objects memory consumption: 44.41 MB.
+Total setup time (wall) 0.8173s
+Time solve (5 iterations) (CPU/wall) 1.52s/0.5093s
+
+Cycle 5
+Number of degrees of freedom: 2146689
+System matrix memory consumption: 115.9 MB.
+Multigrid objects memory consumption: 342.6 MB.
+Total setup time (wall) 6.387s
+Time solve (5 iterations) (CPU/wall) 12.45s/3.767s
@endcode
<h3>Comparison with a sparse matrix</h3>
-In order to understand the capabilities of this class, we compare the memory
-consumption and execution (wallclock) time for assembly and 50 matrix-vector
-products (MV) on a <b>3D problem with one million unknowns</b> the classical
-sparse matrix implementation (SpM) and the MatrixFree implementation shown
-here (M-F). Both matrices are based on @p double %numbers. The program is run
-on a 2.8 GHz Opteron processor with the <a href="http://www.amd.com/acml">ACML
-BLAS</a>. We present results running on one core core and four cores,
-respectively. Moreover, we measure the time it takes to construct the
-individual matrices and filling them with data (@p setup and @p assemble
-functions). The sparse matrix is initialized using a
-CompressedSimpleSparsityPattern for calling the
-DoFTools::make_sparsity_pattern function, and then copied to a SparsityPattern
-object. The boundary nodes are eliminated using the ConstraintMatrix class, so
-that only elements that are actually nonzero are stored in the matrix.
+In order to understand the capabilities of the matrix-free implementation, we
+compare the performance on the 3d example above with a SparseMatrix
+implementation and we measure the computation times for both initialization of
+the problem (distribute DoFs, setup and assemble matrices, setup multigrid
+structures) and the actual solution for the matrix-free variant and the
+variant based on sparse matrices. We base the preconditioner on float
+numbers and the actual matrix and vectors on double numbers, as shown
+above. Tests are run on an Intel Core i7-2620M notebook processor (two cores
+and <a href="http://en.wikipedia.org/wiki/Advanced_Vector_Extensions">AVX</a>
+support, i.e., four operations on doubles can be done with one CPU
+instruction, which is heavily used in FEEvaluation) and optimized mode. The
+example makes use of multithreading, so both cores are actually used.
<table align="center" border="1">
<tr>
<th> </th>
- <th colspan="2">Memory consumption</th>
- <th colspan="2">Time assembly</th>
- <th colspan="2">Time 50 MV, 1 CPU</th>
- <th colspan="2">Time 50 MV, 4 CPUs</th>
+ <th colspan="2">Sparse matrix</th>
+ <th colspan="2">Matrix-free implementation</th>
</tr>
<tr>
- <th align="center">element order</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
+ <th>n_dofs</th>
+ <th>Setup + assemble</th>
+ <th> Solve </th>
+ <th>Setup + assemble</th>
+ <th> Solve </th>
</tr>
<tr>
- <td align="center">1</td>
- <td align="center">299 MiB</td>
- <td align="center">394 MiB</td>
- <td align="center">8.09 s</td>
- <td align="center">3.43 s</td>
- <td align="center">5.50 s</td>
- <td align="center">22.4 s</td>
- <td align="center">4.30 s</td>
- <td align="center">11.0 s</td>
+ <td align="right">125</td>
+ <td align="center">0.0048s</td>
+ <td align="center">0.00075s</td>
+ <td align="center">0.0025s</td>
+ <td align="center">0.00033s</td>
</tr>
<tr>
- <td align="center">2</td>
- <td align="center">698 MiB</td>
- <td align="center">177 MiB</td>
- <td align="center">12.43 s</td>
- <td align="center">1.32 s</td>
- <td align="center">12.0 s</td>
- <td align="center">18.6 s</td>
- <td align="center">9.10 s</td>
- <td align="center">6.31 s</td>
+ <td align="right">729</td>
+ <td align="center">0.014s</td>
+ <td align="center">0.0022s</td>
+ <td align="center">0.0026s</td>
+ <td align="center">0.0018s</td>
</tr>
<tr>
- <td align="center">3</td>
- <td align="center">1295 MiB</td>
- <td align="center">124 MiB</td>
- <td align="center">41.1 s</td>
- <td align="center">1.31 s</td>
- <td align="center">21.2 s</td>
- <td align="center">23.7 s</td>
- <td align="center">16.0 s</td>
- <td align="center">7.43 s</td>
+ <td align="right">4,913</td>
+ <td align="center">0.10s</td>
+ <td align="center">0.012s</td>
+ <td align="center">0.017s</td>
+ <td align="center">0.013s</td>
</tr>
<tr>
- <td align="center">4</td>
- <td align="center">2282 MiB</td>
- <td align="center">107 MiB</td>
- <td align="center">117 s</td>
- <td align="center">1.97 s</td>
- <td align="center">40.8 s</td>
- <td align="center">36.3 s</td>
- <td align="center">19.7 s</td>
- <td align="center">10.9 s</td>
+ <td align="right">35,937</td>
+ <td align="center">0.80s</td>
+ <td align="center">0.14s</td>
+ <td align="center">0.11s</td>
+ <td align="center">0.071s</td>
</tr>
<tr>
- <td align="center">5</td>
- <td align="center">3597 MiB</td>
- <td align="center">96.4 MiB</td>
- <td align="center">510 s</td>
- <td align="center">5.52 s</td>
- <td align="center">75.7 s</td>
- <td align="center">53.9 s</td>
- <td align="center">29.3 s</td>
- <td align="center">15.7 s</td>
+ <td align="right">274,625</td>
+ <td align="center">5.93s</td>
+ <td align="center">1.05s</td>
+ <td align="center">0.82s</td>
+ <td align="center">0.51s</td>
</tr>
<tr>
- <td align="center">6</td>
- <td align="center">5679 MiB</td>
- <td align="center">96.3 MiB</td>
- <td align="center">2389 s</td>
- <td align="center">26.1 s</td>
- <td align="center">135 s</td>
- <td align="center">79.1 s</td>
- <td align="center">45.8 s</td>
- <td align="center">24.3 s</td>
+ <td align="right">2,146,689</td>
+ <td align="center">46.7s</td>
+ <td align="center">8.44s</td>
+ <td align="center">6.39s</td>
+ <td align="center">3.77s</td>
</tr>
</table>
-There are a few interesting things with the %numbers in this table.
-
-Firstly, we see the disappointing fact that for <b>linear elements</b> the
-MatrixFree class does actually consume more memory than a SparseMatrix with
-its SparsityPattern, despite the efforts made in this program. As mentioned
-earlier, this is mostly because the Transformation data is stored for every
-quadrature point. For each quadrature point, the transformation consists of
-six doubles, and there are about eight times as many quadrature points as
-there are degrees of freedom. In first approximation, this means that the
-matrix consumes 384 (= @p sizeof(double) * 6 * 8) bytes for each degree of
-freedom. On the other hand, the sparse matrix has a bandwidth of 27 or less,
-so each dof gives rise to at most 324 (= 27 * 12) bytes. A more clever
-implementation would try to compress the Jacobian transformation data, by
-exploiting similarities between the mappings within the cells, as well as from
-one cell to the next. This could dramatically reduce the memory requirements,
-and hence, increase the speed for lower-order implementations.
-
-Secondly, we observe that the memory requirements for a SparseMatrix grow
-quickly as the order of the elements increases. This is because there are
-increasingly many entries in each row, which exist due to more degrees of
-freedom that couple to each other. The matrix-free implementation does not
-suffer from this drawback. Here, the memory consumption decreases instead,
-since there are less DoFs that are shared among elements, which decreases the
-relative amount of quadrature points. Regarding the execution speed, we see
-that the matrix-free variant gets more competitive with higher order, and it
-does scale better when run on multiple processors (3.5 speedup with four
-processors compared to the serial case, compared to 2-2.5 speedup for the
-SparseMatrix). The advantage in %parallel scaling was expected, because the
-matrix-free variant is less memory-bound for higher order implementations, so
-that the additional computing power from many cores can better be exploited.
-
-A third thing, which is unrelated to this tutorial program, is the fact that
-standard matrix assembly gets really slow for high order elements. The %numbers
-shown here are based on the usual routines that many other tutorial programs
-make use of. A closer analysis of this shows that the cell data does not fit
-into cache anymore. One could circumvent this problem by writing the assembly
-as a matrix-matrix product, and using (cache-aware) BLAS implementations.
-
-For completeness, here comes a similar table for a <b>2D problem with 5.7
-million unknowns</b>. Since the excess in work for the matrix-free
-implementation is less compared to 3D, the implementation is more competitive
-for lower-order elements.
-
-<table align="center" border="1">
- <tr>
- <th> </th>
- <th colspan="2">Memory consumption</th>
- <th colspan="2">Time assembly</th>
- <th colspan="2">Time 50 MV, 4 CPUs</th>
- </tr>
- <tr>
- <th align="center">element order</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- <th align="center">SpM</th>
- <th align="center">M-F</th>
- </tr>
- <tr>
- <td align="center">1</td>
- <td align="center">659 MiB</td>
- <td align="center">661 MiB</td>
- <td align="center">18.8 s</td>
- <td align="center">6.45 s</td>
- <td align="center">11.0 s</td>
- <td align="center">28.8 s</td>
- </tr>
- <tr>
- <td align="center">2</td>
- <td align="center">1119 MiB</td>
- <td align="center">391 MiB</td>
- <td align="center">15.6 s</td>
- <td align="center">2.46 s</td>
- <td align="center">17.1 s</td>
- <td align="center">16.2 s</td>
- </tr>
- <tr>
- <td align="center">3</td>
- <td align="center">1711 MiB</td>
- <td align="center">318 MiB</td>
- <td align="center">17.4 s</td>
- <td align="center">1.82 s</td>
- <td align="center">23.1 s</td>
- <td align="center">13.7 s</td>
- </tr>
- <tr>
- <td align="center">4</td>
- <td align="center">2434 MiB</td>
- <td align="center">285 MiB</td>
- <td align="center">24.2 s</td>
- <td align="center">1.34 s</td>
- <td align="center">31.1 s</td>
- <td align="center">14.6 s</td>
- </tr>
- <tr>
- <td align="center">5</td>
- <td align="center">3289 MiB</td>
- <td align="center">266 MiB</td>
- <td align="center">35.9 s</td>
- <td align="center">1.26 s</td>
- <td align="center">29.6 s</td>
- <td align="center">16.7 s</td>
- </tr>
- <tr>
- <td align="center">6</td>
- <td align="center">4274 MiB</td>
- <td align="center">254 MiB</td>
- <td align="center">58.0 s</td>
- <td align="center">1.12 s</td>
- <td align="center">35.9 s</td>
- <td align="center">19.4 s</td>
- </tr>
-</table>
+The table clearly shows that the matrix-free implementation is twice as fast
+for the solver, and more than six times as fast when it comes to
+initialization costs. As the problem size is made a factor 8 larger, we note
+that the times usually go up by a factor eight, too (as the solver iterations
+are constant at 5). There are two deviations. The first is in the sparse
+matrix between 5k and 36k degrees of freedom, where the time increases by a
+factor 12. This is the threshold when the cache in the processor can no longer
+hold all data necessary for the matrix-vector products and all matrix elements
+must be fetched from main memory. The second deviation is the times for the
+matrix-free solve which increase by less than a factor 8. This is because of
+more parallelism from more cells, exploited by the (involved) dynamic tasks
+scheduling approach CellFEOperator. Note that about 30% of the time in the
+matrix-free solver is spent on restriction and prolongation, which use sparse
+matrices. So the speedup could be even better if all parts where done
+efficiently.
+
+Of course, this picture does not necessarily translate to all cases, as there
+are problems where knowledge of matrix entries enables much better solvers (as
+happens when the coefficient is varying more strongly than in the above
+example). Moreover, it also depends on the computer system. The present system
+has good memory performance, so sparse matrices perform comparably
+well. Nonetheless, the matrix-free implementation gives a nice speedup already
+for the <i>Q</i><sub>2</sub> elements used in this example. This becomes
+particularly apparent for time-dependent or nonlinear problems where sparse
+matrices would need to be reassembled over and over again, which becomes much
+easier with this class. And of course, thanks to the better complexity of the
+products, the method gains increasingly larger advantages when the order of the
+elements increases (the matrix-free implementation has costs
+4<i>d</i><sup>2</sup><i>p</i> per degree of freedom, compared to
+2<i>p<sup>d</sup></i> for the sparse matrix, so it will win anyway for order 4
+and higher in 3d).
+
+<h3>Possibilities for extensions</h3>
+
+Above, we have shown figures for second-order finite elements. Our
+implementation gains more compared to sparse matrices if higher order elements
+are used. However, FE_Q elements with equidistant nodes are badly conditioned
+if the order increases. In this case, the smoother and the multigrid solver
+break down. Node clustering close to the element boundaries resolves this
+problem (and the multigrid solver converges in 5 or 6 iterations also for very
+high order). Elements with this properties are the Gauss-Lobatto FE_Q
+elements, which are presented in step-48.
-Matrix-free methods. Multigrid.
+Matrix-free methods. Multigrid. Cell-based finite element operator.
-/* Authors: Katharina Kormann, Martin Kronbichler, Uppsala University, 2009 */
+/* Author: Katharina Kormann, Martin Kronbichler, Uppsala University, 2009-2011 */
/* $Id$ */
/* */
-/* Copyright (C) 2009, 2010, 2012 by the deal.II authors */
+/* Copyright (C) 2009, 2010, 2011, 2012 by the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
/* to the file deal.II/doc/license.html for the text and */
/* further information on this license. */
-
- // To start with the include files are more
- // or less the same as in step-16:
+ // First include the necessary files
+ // from the deal.II library.
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
-#include <deal.II/base/work_stream.h>
+#include <deal.II/base/timer.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vectors.h>
+ // This includes the data structures for the
+ // efficient implementation of matrix-free
+ // methods or more generic finite element
+ // operators with the class MatrixFree.
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
#include <fstream>
#include <sstream>
-using namespace dealii;
-
-
-
- // @sect3{Equation data}
-
- // We define a variable coefficient function
- // for the Poisson problem. It is similar to
- // the function in step-5 but we use the form
- // $a(\mathbf x)=\frac{1}{0.1 + \|\bf x\|^2}$
- // instead of a discontinuous one. It is
- // merely to demonstrate the possibilities of
- // this implementation, rather than making
- // much sense physically.
-template <int dim>
-class Coefficient : public Function<dim>
-{
- public:
- Coefficient () : Function<dim>() {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
-
- virtual void value_list (const std::vector<Point<dim> > &points,
- std::vector<double> &values,
- const unsigned int component = 0) const;
-};
-
-
-
-template <int dim>
-double Coefficient<dim>::value (const Point<dim> &p,
- const unsigned int /*component*/) const
-{
- return 1./(0.1+p.square());
-}
-
-
-template <int dim>
-void Coefficient<dim>::value_list (const std::vector<Point<dim> > &points,
- std::vector<double> &values,
- const unsigned int component) const
+namespace Step37
{
- Assert (values.size() == points.size(),
- ExcDimensionMismatch (values.size(), points.size()));
- Assert (component == 0,
- ExcIndexRange (component, 0, 1));
-
- const unsigned int n_points = points.size();
-
- for (unsigned int i=0; i<n_points; ++i)
- values[i] = 1./(0.1+points[i].square());
-}
+ using namespace dealii;
+
+
+ // To be efficient, the operations
+ // performed in the matrix-free
+ // implementation require knowledge of loop
+ // lengths at compile time, which are given
+ // by the degree of the finite
+ // element. Hence, we collect the values of
+ // the two template parameters that can be
+ // changed at one place in the code. Of
+ // course, one could make the degree of the
+ // finite element a run-time parameter by
+ // compiling the computational kernels for
+ // all degrees that are likely (say,
+ // between 1 and 6) and selecting the
+ // appropriate kernel at run time. Here, we
+ // simply choose second order $Q_2$
+ // elements and choose dimension 3 as
+ // standard.
+ const unsigned int degree_finite_element = 2;
+ const unsigned int dimension = 3;
+
+
+ // @sect3{Equation data}
+
+ // We define a variable coefficient function
+ // for the Poisson problem. It is similar to
+ // the function in step-5 but we use the form
+ // $a(\mathbf x)=\frac{1}{0.05 + 2\|\bf
+ // x\|^2}$ instead of a discontinuous one. It
+ // is merely to demonstrate the possibilities
+ // of this implementation, rather than making
+ // much sense physically. We define the
+ // coefficient in the same way as functions
+ // in earlier tutorial programs. There is one
+ // new function, namely a @p value method
+ // with template argument @p number.
+ template <int dim>
+ class Coefficient : public Function<dim>
+ {
+ public:
+ Coefficient () : Function<dim>() {}
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ template <typename number>
+ number value (const Point<dim,number> &p,
+ const unsigned int component = 0) const;
- // @sect3{Matrix-free implementation}
-
- // In this program, we want to make
- // use of the ability of deal.II to
- // runs things in %parallel if compute
- // resources are available. We will
- // follow the general framework laid
- // out in the @ref threads module and
- // use the WorkStream class to do
- // operations on the range of all
- // cells.
- //
- // To this end, we first have to have
- // a few declarations that we use for
- // defining the %parallel layout of
- // the vector multiplication function
- // with the WorkStream concept in the
- // Matrix-free class. These comprise
- // so-called scratch data that we use
- // for calculating cell-related
- // information, and copy data that is
- // eventually used in a separate
- // function for writing local data
- // into the global vector. The reason
- // for this split-up definition is
- // that many threads at a time can
- // execute the local multiplications
- // (and filling up the copy data),
- // but than that copy data needs to
- // be worked on by one process at a
- // time.
-namespace WorkStreamData
-{
- template <typename number>
- struct ScratchData
- {
- ScratchData ();
- ScratchData (const ScratchData &scratch);
- FullMatrix<number> solutions;
+ virtual void value_list (const std::vector<Point<dim> > &points,
+ std::vector<double> &values,
+ const unsigned int component = 0) const;
};
- template<typename number>
- ScratchData<number>::ScratchData ()
- :
- solutions ()
- {}
- template<typename number>
- ScratchData<number>::ScratchData (const ScratchData &)
- :
- solutions ()
- {}
+ // This is the new function mentioned
+ // above: Evaluate the coefficient for
+ // abstract type @p number: It might be
+ // just a usual double, but it can also be
+ // a somewhat more complicated type that we
+ // call VectorizedArray. This data type is
+ // essentially a short array of doubles
+ // whose length depends on the particular
+ // computer system in use. For example,
+ // systems based on x86-64 support the
+ // streaming SIMD extensions (SSE), where
+ // the processor's vector units can process
+ // two doubles (or four single-precision
+ // floats) by one CPU instruction. Newer
+ // processors with support for the
+ // so-called advanced vector extensions
+ // (AVX) with 256 bit operands can use four
+ // doubles and eight floats,
+ // respectively. Vectorization is a
+ // single-instruct/multiple-data (SIMD)
+ // concept, that is, one CPU instruction is
+ // used to process multiple data values at
+ // once. Often, finite element programs do
+ // not use vectorization explicitly as the
+ // benefits of this concept are only in
+ // arithmetic intensive operations. The
+ // bulk of typical finite element workloads
+ // are memory bandwidth limited (operations
+ // on sparse matrices and vectors) where
+ // the additional computational power is
+ // useless.
+ //
+ // Behind the scenes, optimized BLAS
+ // packages might heavily rely on
+ // vectorization, though. Also, optimizing
+ // compilers might automatically transform
+ // loops involving standard code into more
+ // efficient vectorized form. However, the
+ // data flow must be very regular in order
+ // for compilers to produce efficient
+ // code. For example, already the automatic
+ // vectorization of the prototype operation
+ // that benefits from vectorization,
+ // matrix-matrix products, fails on most
+ // compilers (as of writing this tutorial
+ // in early 2012, neither gcc-4.6 nor the
+ // Intel compiler v. 12 manage to produce
+ // useful vectorized code for the
+ // FullMatrix::mmult function, and not even
+ // on the more simpler case where the
+ // matrix bounds are compile-time constants
+ // instead of run-time constants as in
+ // FullMatrix::mmult). The main reason for
+ // this is that the information to be
+ // processed at the innermost loop (that is
+ // where vectorization is applied) is not
+ // necessarily a multiple of the vector
+ // length, leaving parts of the resources
+ // unused. Moreover, the data that can
+ // potentially be processed together might
+ // not be laid out in a contiguous way in
+ // memory or not with the necessary
+ // alignment to address boundaries that are
+ // needed by the processor. Or the compiler
+ // might not be able to prove that.
+ //
+ // In the matrix-free implementation in
+ // deal.II, we have therefore chosen to
+ // apply vectorization at the level which
+ // is most appropriate for finite element
+ // computations: The cell-wise computations
+ // are typically exactly the same for all
+ // cells (except for reading from and
+ // writing to vectors), and hence SIMD can
+ // be used to process several cells at
+ // once. In all what follows, you can think
+ // of an AlignedVector to hold data from
+ // several cells. For example, we evaluate
+ // the coefficient shown here not on a
+ // simple point as usually done, but we
+ // hand it a
+ // Point<dim,VectorizedArray<double> >
+ // point, which is actually a collection of
+ // two points in the case of SSE2. Do not
+ // confuse the entries in
+ // VectorizedArray<double> with the
+ // different coordinates of the
+ // point. Indeed, the data is laid out such
+ // that <code>p[0]</code> returns a
+ // VectorizedArray<double>, which in turn
+ // contains the x-coordinate for the first
+ // point and the second point. You may
+ // access the coordinates individually
+ // using e.g. <code>p[0][j]</code>, j=1,2,
+ // but it is recommended to define
+ // operations on a VectorizedArray as much
+ // as possible in order to make use of
+ // vectorized operations.
+ //
+ // In the function implementation, we
+ // assume that the number type overloads
+ // basic arithmetic operations, so we just
+ // write the code as usual. The standard
+ // functions @p value and value_list that
+ // are virtual functions contained in the
+ // base class are then computed from the
+ // templated function with double type, in
+ // order to avoid duplicating code.
+ template <int dim>
template <typename number>
- struct CopyData : public ScratchData<number>
+ number Coefficient<dim>::value (const Point<dim,number> &p,
+ const unsigned int /*component*/) const
{
- CopyData ();
- CopyData (const CopyData &scratch);
- unsigned int first_cell;
- unsigned int n_dofs;
- };
+ return 1. / (0.05 + 2.*p.square());
+ }
- template <typename number>
- CopyData<number>::CopyData ()
- :
- ScratchData<number> ()
- {}
-
- template <typename number>
- CopyData<number>::CopyData (const CopyData &)
- :
- ScratchData<number> ()
- {}
-
-}
-
-
-
- // Next comes the implementation of the
- // matrix-free class. It provides some
- // standard information we expect for
- // matrices (like returning the dimensions
- // of the matrix), it implements
- // matrix-vector multiplications in several
- // forms, and it provides functions for
- // filling the matrix with data.
- //
- // We choose to make this class generic,
- // i.e., we do not implement the actual
- // differential operator (here: Laplace
- // operator) directly in this class. We
- // instead let the actual transformation
- // (which happens on the level of quadrature
- // points, see the discussion in the
- // introduction) be a template parameter that
- // is implemented by another class. We then
- // only have to store a list of these objects
- // for each quadrature point on each cell in
- // a big list – we choose a
- // <code>Table<2,Transformation></code> data
- // format) – and call a transform
- // command of the @p Transformation
- // class. This template magic makes it easy
- // to reuse this MatrixFree class for other
- // problems that are based on a symmetric
- // operation without the need for substantial
- // changes.
-template <typename number, class Transformation>
-class MatrixFree : public Subscriptor
-{
- public:
- MatrixFree ();
-
- void reinit (const unsigned int n_dofs,
- const unsigned int n_cells,
- const FullMatrix<double> &cell_matrix,
- const unsigned int n_points_per_cell);
- void clear();
-
- unsigned int m () const;
- unsigned int n () const;
- ConstraintMatrix & get_constraints ();
-
- void set_local_dof_indices (const unsigned int cell_no,
- const std::vector<unsigned int> &local_dof_indices);
- void set_derivative_data (const unsigned int cell_no,
- const unsigned int quad_point,
- const Transformation &trans_in);
-
- template <typename number2>
- void vmult (Vector<number2> &dst,
- const Vector<number2> &src) const;
- template <typename number2>
- void Tvmult (Vector<number2> &dst,
- const Vector<number2> &src) const;
- template <typename number2>
- void vmult_add (Vector<number2> &dst,
- const Vector<number2> &src) const;
- template <typename number2>
- void Tvmult_add (Vector<number2> &dst,
- const Vector<number2> &src) const;
-
- number el (const unsigned int row,
- const unsigned int col) const;
- void calculate_diagonal () const;
-
- std::size_t memory_consumption () const;
-
- // The private member variables of the
- // @p MatrixFree class are a
- // small matrix that does the
- // transformation from solution values to
- // quadrature points, a list with the
- // mapping between local degrees of freedom
- // and global degrees of freedom for each
- // cell (stored as a two-dimensional array,
- // where each row corresponds to one
- // cell, and the columns within individual
- // cells are the local degrees of freedom),
- // the transformation variable for
- // implementing derivatives, a constraint
- // matrix for handling boundary conditions
- // as well as a few other variables that
- // store matrix properties.
- private:
- typedef std::vector<std::pair<unsigned int,unsigned int> >::const_iterator
- CellChunkIterator;
- template <typename number2>
- void local_vmult (CellChunkIterator cell_range,
- WorkStreamData::ScratchData<number> &scratch,
- WorkStreamData::CopyData<number> ©,
- const Vector<number2> &src) const;
-
- template <typename number2>
- void
- copy_local_to_global (const WorkStreamData::CopyData<number> ©,
- Vector<number2> &dst) const;
-
- FullMatrix<number> B_ref_cell;
- Table<2,unsigned int> indices_local_to_global;
- Table<2,Transformation> derivatives;
-
- ConstraintMatrix constraints;
-
- mutable Vector<number> diagonal_values;
- mutable bool diagonal_is_calculated;
-
- struct MatrixSizes
- {
- unsigned int n_dofs, n_cells;
- unsigned int m, n;
- unsigned int n_points, n_comp;
- std::vector<std::pair<unsigned int,unsigned int> > chunks;
- } matrix_sizes;
-};
-
-
-
- // This is the constructor of the @p
- // MatrixFree class. All it does is to
- // subscribe to the general deal.II @p
- // Subscriptor scheme that makes sure that we
- // do not delete an object of this class as
- // long as it used somewhere else, e.g. in a
- // preconditioner.
-template <typename number, class Transformation>
-MatrixFree<number,Transformation>::MatrixFree ()
- :
- Subscriptor()
-{}
-
-
-
- // The next functions return the
- // number of rows and columns of the
- // global matrix (i.e. the dimensions
- // of the operator this class
- // represents, the point of this
- // tutorial program was, after all,
- // that we don't actually store the
- // elements of the rows and columns
- // of this operator). Since the
- // matrix is square, the returned
- // numbers are the same.
-template <typename number, class Transformation>
-unsigned int
-MatrixFree<number,Transformation>::m () const
-{
- return matrix_sizes.n_dofs;
-}
-
-
-
-template <typename number, class Transformation>
-unsigned int
-MatrixFree<number,Transformation>::n () const
-{
- return matrix_sizes.n_dofs;
-}
-
-
-
- // One more function that just returns an
- // %internal variable. Note that the user
- // will need to change this variable, so it
- // returns a non-constant reference to the
- // ConstraintMatrix.
-template <typename number, class Transformation>
-ConstraintMatrix &
-MatrixFree<number,Transformation>::get_constraints ()
-{
- return constraints;
-}
-
-
-
- // The following function takes a vector of
- // local dof indices on cell level and writes
- // the data into the
- // @p indices_local_to_global field
- // in order to have fast access to it. It
- // performs a few sanity checks like whether
- // the sizes in the matrix are set
- // correctly. One tiny thing: Whenever we
- // enter this function, we probably make some
- // modification to the matrix. This means
- // that the diagonal of the matrix, which we
- // might have computed to have fast access to
- // those elements, is invalidated. We set the
- // respective flag to @p false.
-template <typename number, class Transformation>
-void MatrixFree<number,Transformation>::
-set_local_dof_indices (const unsigned int cell_no,
- const std::vector<unsigned int> &local_dof_indices)
-{
- Assert (local_dof_indices.size() == matrix_sizes.m,
- ExcDimensionMismatch(local_dof_indices.size(),
- matrix_sizes.m));
- for (unsigned int i=0; i<matrix_sizes.m; ++i)
- {
- Assert (local_dof_indices[i] < matrix_sizes.n_dofs, ExcInternalError());
- indices_local_to_global(cell_no,i) = local_dof_indices[i];
- }
- diagonal_is_calculated = false;
-}
-
-
-
- // Next a function that writes the derivative
- // data on a certain cell and a certain
- // quadrature point to the array that keeps
- // the data around. Even though the array @p
- // derivatives stands for the majority of the
- // matrix memory consumption, it still pays
- // off to have that data around since it
- // would be quite expensive to manually
- // compute it every time we make a
- // matrix-vector product.
-template <typename number, class Transformation>
-void MatrixFree<number,Transformation>::
-set_derivative_data (const unsigned int cell_no,
- const unsigned int quad_point,
- const Transformation &trans_in)
-{
- Assert (quad_point < matrix_sizes.n_points, ExcInternalError());
- derivatives(cell_no,quad_point) = trans_in;
- diagonal_is_calculated = false;
-}
-
-
-
- // Now finally to the central function of the
- // matrix-free class, implementing the
- // multiplication of the matrix with a
- // vector. This function does not actually
- // work on all cells of a mesh, but only the
- // subset of cells specified by the first
- // argument @p cell_range. Since this
- // function operates similarly irrespective
- // on which cell chunk we are sitting, we can
- // call it simultaneously on many processors,
- // but with different cell range data.
- //
- // The goal of this function is to provide
- // the multiplication of a vector with the
- // local contributions of a set of cells. As
- // mentioned in the introduction, if we were
- // to deal with a single cell, this would
- // amount to performing the product
- // @f{eqnarray*}
- // P^T_\mathrm{cell,local-global} A_\mathrm{cell}
- // P_\mathrm{cell,local-global} x
- // @f}
- // where
- // @f{eqnarray*}
- // A_\mathrm{cell} =
- // B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T
- // D_\mathrm{cell}
- // J_\mathrm{cell} B_\mathrm{ref\_cell}
- // @f}
- // and <i>P</i><sub>cell,local-global</sub>
- // is the transformation from local to global
- // indices.
- //
- // To do this, we would have to do the
- // following steps:
- // <ol>
- // <li> Form $x_\mathrm{cell} =
- // P_\mathrm{cell,local-global} x$. This is
- // done by using the command
- // ConstraintMatrix::get_dof_values.
- // <li> Form $x_1 = B_\mathrm{ref\_cell}
- // x_\mathrm{cell}$. The vector
- // <i>x</i><sub>1</sub> contains the
- // reference cell gradient to the local
- // cell vector.
- // <li> Form $x_2 = J_\mathrm{cell}^T
- // D_\mathrm{cell} J_\mathrm{cell}
- // x_1$. This is a block-diagonal
- // operation, with the block size equal to
- // @p dim. The blocks just
- // correspond to the individual quadrature
- // points. The operation on each quadrature
- // point is implemented by the
- // Transformation class object that this
- // class is equipped with. Compared to the
- // introduction, the matrix
- // <i>D</i><sub>cell</sub> now contains the
- // @p JxW values and the
- // inhomogeneous coefficient.
- // <li> Form $y_\mathrm{cell} =
- // B_\mathrm{ref\_cell}^T x_2$. This gives
- // the local result of the matrix-vector
- // product.
- // <li> Form $y \leftarrow y +
- // P_\mathrm{cell,local-global}^T
- // y_\mathrm{cell}$. This adds the local
- // result to the global vector, which is
- // realized using the method
- // ConstraintMatrix::distribute_local_to_global.
- // Note that we do this in an extra
- // function called
- // @p copy_local_to_global
- // because that operation must not be done
- // in %parallel, in order to avoid two or
- // more processes trying to add to the same
- // positions in the result vector <i>y</i>.
- // </ol>
- // The steps 1 to 4 can be done in %parallel
- // by multiple processes.
-
- // Now, it turns out that the most expensive
- // part of the above is the multiplication
- // <i>B</i><sub>ref_cell</sub>
- // <i>x</i><sub>cell</sub> in the second step
- // and the transpose operation in step
- // 4. Note that the matrix
- // <i>J</i><sup>T</sup><i> D J</i> is
- // block-diagonal, and hence, its application
- // is cheaper. Since the matrix
- // <i>B</i><sub>ref_cell</sub> is the same
- // for all cells, all that changes is the
- // vector <i>x</i><sub>cell</sub>. Hence,
- // nothing prevents us from collecting
- // several cell vectors to a (rectangular)
- // matrix, and then perform a matrix-matrix
- // product. These matrices are both full, but
- // not very large, having of the order @p
- // dofs_per_cell rows and columns. This is an
- // operation that can be much better
- // optimized than matrix-vector products. The
- // functions @p FullMatrix<number>::mmult and
- // @p FullMatrix<number>::mTmult use the BLAS
- // dgemm function (as long as BLAS has been
- // detected in deal.II configuration), which
- // provides optimized kernels for doing this
- // product. In our case, a matrix-matrix
- // product is between three and five times
- // faster than doing the matrix-vector
- // product on one cell after the other. The
- // variables that hold the solution on the
- // respective cell's support points and the
- // quadrature points are thus full matrices,
- // which we set to the correct size as a
- // first action in this function. The number
- // of rows in the two matrices @p
- // scratch.solutions and @p copy.solutions is
- // given by the number of cells they work on,
- // and the number of columns is the number of
- // degrees of freedom per cell for the first
- // and the number of quadrature points times
- // the number of components per point for the
- // latter.
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::
-local_vmult (CellChunkIterator cell_range,
- WorkStreamData::ScratchData<number> &scratch,
- WorkStreamData::CopyData<number> ©,
- const Vector<number2> &src) const
-{
- const unsigned int chunk_size = cell_range->second - cell_range->first;
-
- scratch.solutions.reinit (chunk_size, matrix_sizes.n, true);
- copy.solutions.reinit (chunk_size, matrix_sizes.m, true);
- copy.first_cell = cell_range->first;
- copy.n_dofs = chunk_size*matrix_sizes.m;
-
- constraints.get_dof_values(src, &indices_local_to_global(copy.first_cell,0),
- ©.solutions(0,0),
- ©.solutions(0,0)+copy.n_dofs);
-
- copy.solutions.mmult (scratch.solutions, B_ref_cell);
-
- for (unsigned int i=0, k = copy.first_cell; i<chunk_size; ++i, ++k)
- for (unsigned int j=0; j<matrix_sizes.n_points; ++j)
- derivatives(k,j).transform(&scratch.solutions(i, j*matrix_sizes.n_comp));
-
- scratch.solutions.mTmult (copy.solutions, B_ref_cell);
-}
+ template <int dim>
+ double Coefficient<dim>::value (const Point<dim> &p,
+ const unsigned int component) const
+ {
+ return value<double>(p,component);
+ }
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::
-copy_local_to_global (const WorkStreamData::CopyData<number> ©,
- Vector<number2> &dst) const
-{
- constraints.distribute_local_to_global (©.solutions(0,0),
- ©.solutions(0,0)+copy.n_dofs,
- &indices_local_to_global(copy.first_cell,0),
- dst);
-}
+ template <int dim>
+ void Coefficient<dim>::value_list (const std::vector<Point<dim> > &points,
+ std::vector<double> &values,
+ const unsigned int component) const
+ {
+ Assert (values.size() == points.size(),
+ ExcDimensionMismatch (values.size(), points.size()));
+ Assert (component == 0,
+ ExcIndexRange (component, 0, 1));
+
+ const unsigned int n_points = points.size();
+ for (unsigned int i=0; i<n_points; ++i)
+ values[i] = value<double>(points[i],component);
+ }
+
+
+
+ // @sect3{Matrix-free implementation}
+
+ // The following class, called
+ // <code>LaplaceOperator</code>,
+ // implements the differential
+ // operator. For all practical
+ // purposes, it is a matrix, i.e.,
+ // you can ask it for its size
+ // (member functions <code>m(),
+ // n()</code>) and you can apply it
+ // to a vector (the various
+ // variants of the
+ // <code>vmult()</code>
+ // function). The difference to a
+ // real matrix of course lies in
+ // the fact that this class doesn't
+ // actually store the
+ // <i>elements</i> of the matrix,
+ // but only knows how to compute
+ // the action of the operator when
+ // applied to a vector.
+
+ // In this program, we want to make use of
+ // the data cache for finite element operator
+ // application that is integrated in
+ // deal.II. The main class that collects all
+ // data is called MatrixFree. It contains
+ // mapping information (Jacobians) and index
+ // relations between local and global degrees
+ // of freedom. It also contains constraints
+ // like the ones from Dirichlet boundary
+ // conditions (or hanging nodes, if we had
+ // any). Moreover, it can issue a loop over
+ // all cells in %parallel, where it makes
+ // sure that only cells are worked on that do
+ // not share any degree of freedom (this
+ // makes the loop thread-safe when writing
+ // into destination vectors). This is a more
+ // advanced strategy compared to the
+ // WorkStream class described in the @ref
+ // threads module that serializes operations
+ // that might not be thread-safe. Of course,
+ // to not destroy thread-safety, we have to
+ // be careful when writing into class-global
+ // structures.
+ //
+ // First comes the implementation of the
+ // matrix-free class. It provides some
+ // standard information we expect for
+ // matrices (like returning the dimensions of
+ // the matrix), it implements matrix-vector
+ // multiplications in several forms
+ // (transposed and untransposed), and it
+ // provides functions for initializing the
+ // structure with data. The class has three
+ // template arguments, one for the dimension
+ // (as many deal.II classes carry), one for the
+ // degree of the finite element (which we
+ // need to enable efficient computations
+ // through the FEEvaluation class), and one
+ // for the underlying scalar type. We want to use
+ // <code>double</code> numbers
+ // (i.e., double precision, 64-bit
+ // floating point) for the final
+ // matrix, but floats (single
+ // precision, 32-bit floating point
+ // numbers) for the multigrid level
+ // matrices (as that is only a
+ // preconditioner, and floats can
+ // be worked with twice as fast).
+ //
+ // In this class, we store the actual MatrixFree
+ // object, the variable
+ // coefficient that is evaluated at all
+ // quadrature points (so that we don't have
+ // to recompute it during matrix-vector
+ // products), and a vector that contains the
+ // diagonal of the matrix that we need for
+ // the multigrid smoother. We choose to let
+ // the user provide the diagonal in this
+ // program, but we could also integrate a
+ // function in this class to evaluate the
+ // diagonal. Unfortunately, this forces us to
+ // define matrix entries at two places,
+ // once when we evaluate the product and once
+ // for the diagonal, but the work is still
+ // much less than when we compute sparse
+ // matrices.
+ //
+ // As a sidenote, if we implemented
+ // several different operations on
+ // the same grid and degrees of
+ // freedom (like a mass matrix and
+ // a Laplace matrix), we would have
+ // to have two classes like the
+ // current one for each of the
+ // operators (maybe with a common
+ // base class). However, in that
+ // case, we would not store a
+ // MatrixFree object in this
+ // class to avoid doing the
+ // expensive work of pre-computing
+ // everything MatrixFree stores
+ // twice. Rather, we would keep
+ // this object in the main class
+ // and simply store a reference.
+ //
+ // @note Observe how we store the values
+ // for the coefficient: We use a vector
+ // type
+ // <code>AlignedVector<VectorizedArray<number>
+ // ></code> structure. One would think that
+ // one can use
+ // <code>std::vector<VectorizedArray<number>
+ // ></code> as well, but there are some
+ // technicalities with vectorization: A
+ // certain alignment of the data with the
+ // memory address boundaries is required
+ // (essentially, a VectorizedArray of 16
+ // bytes length as in SSE needs to start at
+ // a memory address that is divisible by
+ // 16). The chosen class makes sure that
+ // this alignment is respected, whereas
+ // std::vector can in general not, which
+ // may lead to segmentation faults at
+ // strange places for some systems or
+ // suboptimal performance for other
+ // systems.
+ template <int dim, int fe_degree, typename number>
+ class LaplaceOperator : public Subscriptor
+ {
+ public:
+ LaplaceOperator ();
- // Now to the @p vmult function that is
- // called externally: In addition to what we
- // do in a @p vmult_add function, we set the
- // destination to zero first.
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::vmult (Vector<number2> &dst,
- const Vector<number2> &src) const
-{
- dst = 0;
- vmult_add (dst, src);
-}
+ void clear();
+ void reinit (const MGDoFHandler<dim> &dof_handler,
+ const ConstraintMatrix &constraints,
+ const unsigned int level = numbers::invalid_unsigned_int);
+ unsigned int m () const;
+ unsigned int n () const;
- // Transposed matrix-vector products (needed
- // for the multigrid operations to be
- // well-defined): do the same. Since we
- // implement a symmetric operation, we can
- // refer to the @p vmult_add operation.
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::Tvmult (Vector<number2> &dst,
- const Vector<number2> &src) const
-{
- dst = 0;
- Tvmult_add (dst,src);
-}
+ void vmult (Vector<double> &dst,
+ const Vector<double> &src) const;
+ void Tvmult (Vector<double> &dst,
+ const Vector<double> &src) const;
+ void vmult_add (Vector<double> &dst,
+ const Vector<double> &src) const;
+ void Tvmult_add (Vector<double> &dst,
+ const Vector<double> &src) const;
+ number el (const unsigned int row,
+ const unsigned int col) const;
+ void set_diagonal (const Vector<number> &diagonal);
+ std::size_t memory_consumption () const;
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::Tvmult_add (Vector<number2> &dst,
- const Vector<number2> &src) const
-{
- vmult_add (dst,src);
-}
+ private:
+ void local_apply (const MatrixFree<dim,number> &data,
+ Vector<double> &dst,
+ const Vector<double> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+ void evaluate_coefficient(const Coefficient<dim> &function);
+ MatrixFree<dim,number> data;
+ AlignedVector<VectorizedArray<number> > coefficient;
- // This is the @p vmult_add function that
- // multiplies the matrix with vector @p src
- // and adds the result to vector @p dst. We
- // include a few sanity checks to make sure
- // that the size of the vectors is the same
- // as the dimension of the matrix. We call a
- // %parallel function that applies the
- // multiplication on a chunk of cells at once
- // using the WorkStream module (cf. also the
- // @ref threads module). The subdivision into
- // chunks will be performed in the reinit
- // function and is stored in the field @p
- // matrix_sizes.chunks. What the rather
- // cryptic command to @p std_cxx1x::bind does
- // is to transform a function that has
- // several arguments (source vector, chunk
- // information) into a function which has
- // three arguments (in the first case) or one
- // argument (in the second), which is what
- // the WorkStream::run function expects. The
- // placeholders <code>_1, std_cxx1x::_2, _3</code> in
- // the local vmult specify variable input
- // values, given by the chunk information,
- // scratch data and copy data that the
- // WorkStream::run function will provide,
- // whereas the other arguments to the @p
- // local_vmult function are bound: to @p this
- // and a constant reference to the @p src in
- // the first case, and @p this and a
- // reference to the output vector in the
- // second. Similarly, the placeholder
- // @p _1 argument in the
- // @p copy_local_to_global function
- // sets the first explicit argument of that
- // function, which is of class
- // @p CopyData. We need to
- // abstractly specify these arguments because
- // the tasks defined by different cell chunks
- // will be scheduled by the WorkStream class,
- // and we will reuse available scratch and
- // copy data.
-template <typename number, class Transformation>
-template <typename number2>
-void
-MatrixFree<number,Transformation>::vmult_add (Vector<number2> &dst,
- const Vector<number2> &src) const
-{
- Assert (src.size() == n(), ExcDimensionMismatch(src.size(), n()));
- Assert (dst.size() == m(), ExcDimensionMismatch(dst.size(), m()));
-
- WorkStream::run (matrix_sizes.chunks.begin(), matrix_sizes.chunks.end(),
- std_cxx1x::bind(&MatrixFree<number,Transformation>::
- template local_vmult<number2>,
- this, std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, boost::cref(src)),
- std_cxx1x::bind(&MatrixFree<number,Transformation>::
- template copy_local_to_global<number2>,
- this, std_cxx1x::_1, boost::ref(dst)),
- WorkStreamData::ScratchData<number>(),
- WorkStreamData::CopyData<number>(),
- 2*multithread_info.n_default_threads,1);
-
- // One thing to be cautious about:
- // The deal.II classes expect that
- // the matrix still contains a
- // diagonal entry for constrained
- // dofs (otherwise, the matrix
- // would be singular, which is not
- // what we want). Since the
- // <code>distribute_local_to_global</code>
- // command of the constraint matrix
- // which we used for adding the
- // local elements into the global
- // vector does not do anything with
- // constrained elements, we have to
- // circumvent that problem by
- // artificially setting the
- // diagonal to some non-zero value
- // and adding the source values. We
- // simply set it to one, which
- // corresponds to copying the
- // respective elements of the
- // source vector into the matching
- // entry of the destination vector.
- for (unsigned int i=0; i<matrix_sizes.n_dofs; ++i)
- if (constraints.is_constrained(i) == true)
- dst(i) += 1.0 * src(i);
-}
-
-
-
- // The next function initializes the
- // structures of the matrix. It writes the
- // number of total degrees of freedom in the
- // problem as well as the number of cells to
- // the MatrixSizes struct and copies the
- // small matrix that transforms the solution
- // from support points to quadrature
- // points. It uses the small matrix for
- // determining the number of degrees of
- // freedom per cell (number of rows in @p
- // B_ref_cell). The number of quadrature
- // points needs to be passed through the last
- // variable @p n_points_per_cell, since the
- // number of columns in the small matrix is
- // @p dim*n_points_per_cell for the Laplace
- // problem (the Laplacian is a tensor and has
- // @p dim components). In this function, we
- // also give the fields containing the
- // derivative information and the local dof
- // indices the correct sizes. They will be
- // filled by calling the respective set
- // function defined above.
-template <typename number, class Transformation>
-void MatrixFree<number,Transformation>::
-reinit (const unsigned int n_dofs_in,
- const unsigned int n_cells_in,
- const FullMatrix<double> &B_ref_cell_in,
- const unsigned int n_points_per_cell)
-{
- B_ref_cell = B_ref_cell_in;
-
- derivatives.reinit (n_cells_in, n_points_per_cell);
- indices_local_to_global.reinit (n_cells_in, B_ref_cell.m());
-
- diagonal_is_calculated = false;
-
- matrix_sizes.n_dofs = n_dofs_in;
- matrix_sizes.n_cells = n_cells_in;
- matrix_sizes.m = B_ref_cell.m();
- matrix_sizes.n = B_ref_cell.n();
- matrix_sizes.n_points = n_points_per_cell;
- matrix_sizes.n_comp = B_ref_cell.n()/matrix_sizes.n_points;
- Assert(matrix_sizes.n_comp * n_points_per_cell == B_ref_cell.n(),
- ExcInternalError());
-
- // One thing to make the matrix-vector
- // product with this class efficient is to
- // decide how many cells should be combined
- // to one chunk, which will determine the
- // size of the full matrix that we work
- // on. If we choose too few cells, then the
- // gains from using the matrix-matrix
- // product will not be fully utilized
- // (dgemm tends to provide more efficiency
- // the larger the matrix dimensions get),
- // so we choose at least 60 cells for one
- // chunk (except when there are very few
- // cells, like on the coarse levels of the
- // multigrid scheme). If we choose too
- // many, we will degrade parallelization
- // (we need to have sufficiently
- // independent tasks). We need to also
- // think about the fact that most high
- // performance BLAS implementations
- // internally work with square
- // sub-matrices. Choosing as many cells in
- // a chunk as there are degrees of freedom
- // on each cell (coded in @p
- // matrix_sizes.m) respects the BLAS GEMM
- // design, whenever we exceed 60. Clearly,
- // the chunk size is an
- // architecture-dependent value and the
- // interested user can squeeze out some
- // extra performance by hand-tuning this
- // parameter. Once we have chosen the
- // number of cells we collect in one chunk,
- // we determine how many chunks we have on
- // the given cell range and recalculate the
- // actual chunk size in order to evenly
- // distribute the chunks.
- const unsigned int divisor = std::max(60U, matrix_sizes.m);
- const unsigned int n_chunks = std::max (matrix_sizes.n_cells/divisor + 1,
- 2*multithread_info.n_default_threads);
-
- const unsigned int chunk_size = (matrix_sizes.n_cells/n_chunks +
- (matrix_sizes.n_cells%n_chunks>0));
-
- std::pair<unsigned int, unsigned int> chunk;
- for (unsigned int i=0; i<n_chunks; ++i)
- {
- chunk.first = i*chunk_size;
- if ((i+1)*chunk_size > matrix_sizes.n_cells)
- chunk.second = matrix_sizes.n_cells;
- else
- chunk.second = (i+1)*chunk_size;
-
- if (chunk.second > chunk.first)
- matrix_sizes.chunks.push_back(chunk);
- else
- break;
- }
-}
+ Vector<number> diagonal_values;
+ bool diagonal_is_available;
+ };
- // Then we need a function if we want to
- // delete the content of the matrix,
- // e.g. when we are finished with one grid
- // level and continue to the next one. Just
- // set all the field sizes to 0.
-template <typename number, class Transformation>
-void
-MatrixFree<number,Transformation>::clear ()
-{
- B_ref_cell.reinit(0,0);
- derivatives.reinit (0,0);
- indices_local_to_global.reinit(0,0);
+ // This is the constructor of the @p
+ // LaplaceOperator class. All it does is to
+ // subscribe to the general deal.II @p
+ // Subscriptor scheme that makes sure that we
+ // do not delete an object of this class as
+ // long as it used somewhere else, e.g. in a
+ // preconditioner.
+ template <int dim, int fe_degree, typename number>
+ LaplaceOperator<dim,fe_degree,number>::LaplaceOperator ()
+ :
+ Subscriptor()
+ {}
- constraints.clear();
- diagonal_values.reinit (0);
- diagonal_is_calculated = false;
-
- matrix_sizes.n_dofs = 0;
- matrix_sizes.n_cells = 0;
- matrix_sizes.chunks.clear();
-}
+ // The next functions return the
+ // number of rows and columns of
+ // the global matrix (i.e. the
+ // dimensions of the operator this
+ // class represents, the point of
+ // this tutorial program was, after
+ // all, that we don't actually
+ // store the elements of the rows
+ // and columns of this
+ // operator). Since the matrix is
+ // square, the returned numbers are
+ // the same. We get the number from
+ // the vector partitioner stored in
+ // the data field (a partitioner
+ // distributes elements of a vector
+ // onto a number of different
+ // machines if programs are run in
+ // %parallel; since this program is
+ // written to run on only a single
+ // machine, the partitioner will
+ // simply say that all elements of
+ // the vector -- or, in the current
+ // case, all rows and columns of a
+ // matrix -- are stored on the
+ // current machine).
+ template <int dim, int fe_degree, typename number>
+ unsigned int
+ LaplaceOperator<dim,fe_degree,number>::m () const
+ {
+ return data.get_vector_partitioner()->size();
+ }
- // The next function returns the entries of the
- // matrix. Since this class is intended not
- // to store the matrix entries, it would make
- // no sense to provide all those
- // elements. However, diagonal entries are
- // explicitly needed for the implementation
- // of the Chebyshev smoother that we intend
- // to use in the multigrid
- // preconditioner. This matrix is equipped
- // with a vector that stores the diagonal,
- // and we compute it when this function is
- // called for the first time.
-template <typename number, class Transformation>
-number
-MatrixFree<number,Transformation>::el (const unsigned int row,
- const unsigned int col) const
-{
- Assert (row == col, ExcNotImplemented());
- if (diagonal_is_calculated == false)
- calculate_diagonal();
- return diagonal_values(row);
-}
+ template <int dim, int fe_degree, typename number>
+ unsigned int
+ LaplaceOperator<dim,fe_degree,number>::n () const
+ {
+ return data.get_vector_partitioner()->size();
+ }
- // Regarding the calculation of the diagonal,
- // remember that this is as simple (or
- // complicated) as assembling a right hand
- // side in deal.II. Well, it is a bit easier
- // to do this within this class since we have
- // all the derivative information
- // available. What we do is to go through all
- // the cells (now in serial, since this
- // function should not be called very often
- // anyway), then all the degrees of
- // freedom. At this place, we first copy the
- // first basis functions in all the
- // quadrature points to a temporary array,
- // apply the derivatives from the Jacobian
- // matrix, and finally multiply with the
- // second basis function. This is exactly the
- // value that would be written into the
- // diagonal of a sparse matrix. Note that we
- // need to condense hanging node constraints
- // and set the constrained diagonals to one.
-template <typename number, class Transformation>
-void
-MatrixFree<number,Transformation>::calculate_diagonal() const
-{
- diagonal_values.reinit (matrix_sizes.n_dofs);
- std::vector<number> calculation (matrix_sizes.n);
- for (unsigned int cell=0; cell<matrix_sizes.n_cells; ++cell)
- for (unsigned int dof=0; dof<matrix_sizes.m; ++dof)
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::clear ()
+ {
+ data.clear();
+ diagonal_is_available = false;
+ diagonal_values.reinit(0);
+ }
+
+
+ // @sect4{Initialization}
+
+ // Once we have created the multi-grid
+ // dof_handler and the constraints, we can
+ // call the reinit function for each level
+ // of the multi-grid routine (and the
+ // active cells). The main purpose of the
+ // reinit function is to setup the <code>
+ // MatrixFree </code> instance for the
+ // problem. Also, the coefficient is
+ // evaluated. For this, we need to activate
+ // the update flag in the AdditionalData
+ // field of MatrixFree that enables the
+ // storage of quadrature point coordinates
+ // in real space (by default, it only
+ // caches data for gradients (inverse
+ // transposed Jacobians) and JxW
+ // values). Note that if we call the reinit
+ // function without specifying the level
+ // (i.e., giving <code>level =
+ // numbers::invalid_unsigned_int</code>),
+ // we have told the class to loop over the
+ // active cells.
+ //
+ // We also set one option regarding
+ // task parallelism. We choose to
+ // use the @p partition_color
+ // strategy, which is based on
+ // subdivision of cells into
+ // partitions where cells in
+ // partition $k$ (or, more
+ // precisely, the degrees of
+ // freedom on these cells) only
+ // interact with cells in
+ // partitions $k-1$, $k$, and
+ // $k+1$. Within each partition,
+ // cells are colored in such a way
+ // that cells with the same color
+ // do not share degrees of freedom
+ // and can, therefore, be worked on
+ // at the same time without
+ // interference. This determines a
+ // task dependency graph that is
+ // scheduled by the Intel Threading
+ // Building Blocks library. Another
+ // option would be the strategy @p
+ // partition_partition, which
+ // performs better when the grid is
+ // more unstructured. We could also
+ // manually set the size of chunks
+ // that form one task in the
+ // scheduling process by setting @p
+ // tasks_block_size, but the
+ // default strategy to let the
+ // function decide works well
+ // already.
+ //
+ // To initialize the coefficient,
+ // we directly give it the
+ // Coefficient class defined above
+ // and then select the method
+ // <code>coefficient_function.value</code>
+ // with vectorized number (which
+ // the compiler can deduce from the
+ // point data type). The use of the
+ // FEEvaluation class (and its
+ // template arguments) will be
+ // explained below.
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::reinit (const MGDoFHandler<dim> &dof_handler,
+ const ConstraintMatrix &constraints,
+ const unsigned int level)
+ {
+ typename MatrixFree<dim,number>::AdditionalData additional_data;
+ additional_data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_color;
+ additional_data.level_mg_handler = level;
+ additional_data.mapping_update_flags = (update_gradients | update_JxW_values |
+ update_quadrature_points);
+ data.reinit (dof_handler, constraints, QGauss<1>(fe_degree+1),
+ additional_data);
+ evaluate_coefficient(Coefficient<dim>());
+ }
+
+
+
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::
+ evaluate_coefficient (const Coefficient<dim> &coefficient_function)
+ {
+ const unsigned int n_cells = data.get_size_info().n_macro_cells;
+ FEEvaluation<dim,fe_degree,fe_degree+1,1,number> phi (data);
+ coefficient.resize (n_cells * phi.n_q_points);
+ for (unsigned int cell=0; cell<n_cells; ++cell)
{
- memcpy (&calculation[0],&B_ref_cell(dof,0),
- matrix_sizes.n*sizeof(number));
- for (unsigned int q=0; q<matrix_sizes.n_points; ++q)
- derivatives(cell,q).transform(&calculation[q*matrix_sizes.n_comp]);
- double diag_value = 0;
- for (unsigned int q=0; q<matrix_sizes.n; ++q)
- diag_value += calculation[q] * B_ref_cell(dof,q);
- diagonal_values(indices_local_to_global(cell,dof)) += diag_value;
+ phi.reinit (cell);
+ for (unsigned int q=0; q<phi.n_q_points; ++q)
+ coefficient[cell*phi.n_q_points+q] =
+ coefficient_function.value(phi.quadrature_point(q));
}
- constraints.condense (diagonal_values);
- for (unsigned int i=0; i<matrix_sizes.n_dofs; ++i)
- if (constraints.is_constrained(i) == true)
- diagonal_values(i) = 1.0;
-
- diagonal_is_calculated = true;
-}
-
-
-
- // Eventually, we provide a function that
- // calculates how much memory this class
- // uses. We just need to sum up the memory
- // consumption of the arrays, the
- // constraints, the small matrix and of the
- // local variables. Just as a remark: In 2D
- // and with data type @p double,
- // about 80 per cent of the memory
- // consumption is due to the
- // @p derivatives array, while in 3D
- // this number is even 85 per cent.
-template <typename number, class Transformation>
-std::size_t MatrixFree<number,Transformation>::memory_consumption () const
-{
- std::size_t glob_size = derivatives.memory_consumption() +
- indices_local_to_global.memory_consumption() +
- constraints.memory_consumption() +
- B_ref_cell.memory_consumption() +
- diagonal_values.memory_consumption() +
- matrix_sizes.chunks.size()*2*sizeof(unsigned int) +
- sizeof(*this);
- return glob_size;
-}
-
-
-
- // @sect3{Laplace operator implementation}
-
- // This class implements the local action of
- // a Laplace operator on a quadrature
- // point. This is a very basic class
- // implementation, providing functions for
- // initialization with a Tensor of rank 2 and
- // implementing the @p transform operation
- // needed by the @p MatrixFree class. There
- // is one point worth noting: The
- // quadrature-point related action of the
- // Laplace operator is a tensor of rank
- // two. It is symmetric since it is the
- // product of the inverse Jacobian
- // transformation between unit and real cell
- // with its transpose (times quadrature
- // weights and a coefficient, which are
- // scalar), so we can just save the diagonal
- // and upper diagonal part. We could use the
- // SymmetricTensor<2,dim> class for doing
- // this, however, that class is only based on
- // @p double %numbers. Since we also want to
- // use @p float %numbers for the multigrid
- // preconditioner (in order to save memory
- // and computing time), we manually implement
- // this operator. Note that @p dim is a
- // template argument and hence known at
- // compile-time, so the compiler knows that
- // this symmetric rank-2 tensor has 3 entries
- // if used in 2D and 6 entries if used in 3D.
-template <int dim,typename number>
-class LaplaceOperator
-{
- public:
- LaplaceOperator ();
-
- LaplaceOperator (const Tensor<2,dim> &tensor);
-
- void transform (number * result) const;
-
- LaplaceOperator<dim,number>&
- operator = (const Tensor<2,dim> &tensor);
-
- unsigned int memory_consumption () const;
-
- private:
- number transformation[dim*(dim+1)/2];
-};
-
-template<int dim,typename number>
-LaplaceOperator<dim,number>::LaplaceOperator()
-{}
-
-
-template<int dim,typename number>
-LaplaceOperator<dim,number>::LaplaceOperator(const Tensor<2,dim> &tensor)
-{
- *this = tensor;
-}
-
- // Now implement the transformation, which is
- // just a so-called contraction
- // operation between a tensor of rank two and a
- // tensor of rank one. Unfortunately, we
- // need to implement this by hand, since we
- // chose not to use the
- // SymmetricTensor<2,dim> class (note that
- // the resulting values are entries in a full
- // matrix that consists of doubles or
- // floats). It feels a bit unsafe to operate
- // on a pointer to the data, but that is the
- // only possibility if we do not want to copy
- // data back and forth, which is expensive
- // since this is the innermost position of
- // the loop in the @p vmult
- // operation of the MatrixFree class. We need
- // to pay attention to the fact that we only
- // saved half of the (symmetric) rank-two
- // tensor.
- //
- // At first sight, it seems inefficient that
- // we have an @p if clause at this position
- // in the code at the innermost loop, but
- // note once again that @p dim is known when
- // this piece of code is compiled, so the
- // compiler can optimize away the @p if
- // statement (and actually even inline these
- // few lines of code into the @p MatrixFree
- // class).
-template <int dim, typename number>
-void LaplaceOperator<dim,number>::transform (number* result) const
-{
- if (dim == 2)
- {
- const number temp = result[0];
- result[0] = transformation[0] * temp + transformation[1] * result[1];
- result[1] = transformation[1] * temp + transformation[2] * result[1];
- }
- else if (dim == 3)
- {
- const number temp1 = result[0];
- const number temp2 = result[1];
- result[0] = transformation[0] * temp1 + transformation[1] * temp2 +
- transformation[2] * result[2];
- result[1] = transformation[1] * temp1 + transformation[3] * temp2 +
- transformation[4] * result[2];
- result[2] = transformation[2] * temp1 + transformation[4] * temp2 +
- transformation[5] * result[2];
- }
- else
- AssertThrow(false, ExcNotImplemented());
-}
+ }
+
+
+
+ // @sect4{Local evaluation of Laplace operator}
+
+ // Here comes the main function of this
+ // class, the evaluation of the
+ // matrix-vector product (or, in general, a
+ // finite element operator
+ // evaluation). This is done in a function
+ // that takes exactly four arguments, the
+ // MatrixFree object, the destination and
+ // source vectors, and a range of cells
+ // that are to be worked on. The method
+ // <code>cell_loop</code> in the MatrixFree
+ // class will internally call this function
+ // with some range of cells that is
+ // obtained by checking which cells are
+ // possible to work on simultaneously so
+ // that write operations do not cause any
+ // race condition. Note that the total
+ // range of cells as visible in this class
+ // is usually not equal to the number of
+ // (active) cells in the triangulation. In
+ // fact, "cell" may be the wrong term to
+ // begin with, since it is rather a
+ // collection of quadrature points from
+ // several cells, and the MatrixFree class
+ // groups the quadrature points of several
+ // cells into one block to enable a higher
+ // degree of vectorization. The number of
+ // such "cells" is stored in MatrixFree and
+ // can be queried through
+ // MatrixFree::get_size_info().n_macro_cells. Compared
+ // to the deal.II cell iterators, in this
+ // class all cells are laid out in a plain
+ // array with no direct knowledge of level
+ // or neighborship relations, which makes
+ // it possible to index the cells by
+ // unsigned integers.
+ //
+ // The implementation of the Laplace
+ // operator is quite simple: First, we need
+ // to create an object FEEvaluation that
+ // contains the computational kernels and
+ // has data fields to store temporary
+ // results (e.g. gradients evaluated on all
+ // quadrature points on a collection of a
+ // few cells). Note that temporary results
+ // do not use a lot of memory, and since we
+ // specify template arguments with the
+ // element order, the data is stored on the
+ // stack (without expensive memory
+ // allocation). Usually, one only needs to
+ // set two template arguments, the
+ // dimension as first argument and the
+ // degree of the finite element as
+ // the second argument (this is equal to
+ // the number of degrees of freedom per
+ // dimension minus one for FE_Q
+ // elements). However, here we also want to
+ // be able to use float numbers for the
+ // multigrid preconditioner, which is the
+ // last (fifth) template
+ // argument. Therefore, we cannot rely on
+ // the default template arguments and must
+ // also fill the third and fourth field,
+ // consequently. The third argument
+ // specifies the number of quadrature
+ // points per direction and has a default
+ // value equal to the degree of the element
+ // plus one. The fourth argument sets
+ // the number of components (one can also
+ // evaluate vector-valued functions in
+ // systems of PDEs, but the default is a
+ // scalar element), and finally the last
+ // argument sets the number type.
+ //
+ // Next, we loop over the given cell range and
+ // then we continue with the actual
+ // implementation:
+ // <ol>
+ // <li>Tell the FEEvaluation object the
+ // (macro) cell we want to work on.
+ // <li>Read in the values of the
+ // source vectors (@p read_dof_values),
+ // including the resolution of
+ // constraints. This stores
+ // $u_\mathrm{cell}$ as described in the
+ // introduction.
+ // <li>Compute the unit-cell gradient
+ // (the evaluation of finite element
+ // functions). Since FEEvaluation can
+ // combine value computations with
+ // gradient computations, it uses a
+ // unified interface to all kinds of
+ // derivatives of order between zero and
+ // two. We only want gradients, no values
+ // and no second derivatives, so we set
+ // the function arguments to true in the
+ // gradient slot (second slot), and to
+ // false in the values slot (first slot)
+ // and Hessian slot (third slot). Note
+ // that the FEEvaluation class internally
+ // evaluates shape functions in an
+ // efficient way where one dimension is
+ // worked on at a time (using the tensor
+ // product form of shape functions and
+ // quadrature points as mentioned in the
+ // introduction). This gives complexity
+ // equal to $\mathcal O(d^2 (p+1)^{d+1})$
+ // for polynomial degree $p$ in $d$
+ // dimensions, compared to the naive
+ // approach with loops over all local
+ // degrees of freedom and quadrature
+ // points that is used in FEValues that
+ // costs $\mathcal O(d (p+1)^{2d})$.
+ // <li>Next comes the application of the
+ // Jacobian transformation, the
+ // multiplication by the variable
+ // coefficient and the quadrature
+ // weight. FEEvaluation has an access
+ // function @p get_gradient that applies
+ // the Jacobian and returns the gradient
+ // in real space. Then, we just need to
+ // multiply by the (scalar) coefficient,
+ // and let the function @p
+ // submit_gradient apply the second
+ // Jacobian (for the test function) and
+ // the quadrature weight and Jacobian
+ // determinant (JxW). Note that the
+ // submitted gradient is stored in the
+ // same data field as where it is read
+ // from in @p get_gradient. Therefore,
+ // you need to make sure to not read from
+ // the same quadrature point again after
+ // having called @p submit_gradient on
+ // that particular quadrature point. In
+ // general, it is a good idea to copy the
+ // result of @p get_gradient when it is
+ // used more often than once.
+ // <li>Next follows the summation over
+ // quadrature points for all test
+ // functions that corresponds to the
+ // actual integration step. For the
+ // Laplace operator, we just multiply by
+ // the gradient, so we call the integrate
+ // function with the respective argument
+ // set. If you have an equation where you
+ // test by both the values of the test
+ // functions and the gradients, both
+ // template arguments need to be set to
+ // true. Calling first the integrate
+ // function for values and then gradients
+ // in a separate call leads to wrong
+ // results, since the second call will
+ // internally overwrite the results from
+ // the first call. Note that there is no
+ // function argument for the second
+ // derivative for integrate step.
+ // <li>Eventually, the local
+ // contributions in the vector
+ // $v_\mathrm{cell}$ as mentioned in the
+ // introduction need to be added into the
+ // result vector (and constraints are
+ // applied). This is done with a call to
+ // @p distribute_local_to_global, the
+ // same name as the corresponding
+ // function in the ConstraintMatrix (only
+ // that we now store the local vector in
+ // the FEEvaluation object, as are the
+ // indices between local and global
+ // degrees of freedom). </ol>
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::
+ local_apply (const MatrixFree<dim,number> &data,
+ Vector<double> &dst,
+ const Vector<double> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ FEEvaluation<dim,fe_degree,fe_degree+1,1,number> phi (data);
+ AssertDimension (coefficient.size(),
+ data.get_size_info().n_macro_cells * phi.n_q_points);
- // The final function in this group
- // takes the content of a rank-2
- // tensor and writes it to the field
- // @p transformation of
- // this class. We save the upper part
- // of the symmetric tensor row-wise:
- // we first take the (0,0)-entry,
- // then the (0,1)-entry, and so
- // on. We only implement this for
- // dimensions two and three, which
- // for the moment should do just
- // fine:
-template <int dim, typename number>
-LaplaceOperator<dim,number>&
-LaplaceOperator<dim,number>::operator=(const Tensor<2,dim> &tensor)
-{
- if (dim == 2)
- {
- transformation[0] = tensor[0][0];
- transformation[1] = tensor[0][1];
- transformation[2] = tensor[1][1];
- Assert (std::fabs(tensor[1][0]-tensor[0][1])<1e-15,
- ExcInternalError());
- }
- else if (dim == 3)
- {
- transformation[0] = tensor[0][0];
- transformation[1] = tensor[0][1];
- transformation[2] = tensor[0][2];
- transformation[3] = tensor[1][1];
- transformation[4] = tensor[1][2];
- transformation[5] = tensor[2][2];
- Assert (std::fabs(tensor[1][0]-tensor[0][1])<1e-15,
- ExcInternalError());
- Assert (std::fabs(tensor[2][0]-tensor[0][2])<1e-15,
- ExcInternalError());
- Assert (std::fabs(tensor[2][1]-tensor[1][2])<1e-15,
- ExcInternalError());
- }
- else
- AssertThrow(false, ExcNotImplemented());
- return *this;
-}
+ for (unsigned int cell=cell_range.first; cell<cell_range.second; ++cell)
+ {
+ phi.reinit (cell);
+ phi.read_dof_values(src);
+ phi.evaluate (false,true,false);
+ for (unsigned int q=0; q<phi.n_q_points; ++q)
+ phi.submit_gradient (coefficient[cell*phi.n_q_points+q] *
+ phi.get_gradient(q), q);
+ phi.integrate (false,true);
+ phi.distribute_local_to_global (dst);
+ }
+ }
+
+
+
+ // @sect4{vmult functions}
+
+ // Now to the @p vmult function that is
+ // called externally: In addition to what
+ // we do in a @p vmult_add function further
+ // down, we set the destination to zero
+ // first. The transposed matrix-vector is
+ // needed for well-defined multigrid
+ // preconditioner operations. Since we
+ // solve a Laplace problem, this is the
+ // same operation, and we just refer to the
+ // vmult operation.
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::vmult (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ dst = 0;
+ vmult_add (dst, src);
+ }
-template<int dim,typename number>
-unsigned int
-LaplaceOperator<dim,number>::memory_consumption () const
-{
- return sizeof(*this);
-}
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::Tvmult (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ dst = 0;
+ vmult_add (dst,src);
+ }
- // @sect3{LaplaceProblem class}
- // This class is based on the same
- // class in step-16. However, we
- // replaced the SparseMatrix<double>
- // class by our matrix-free
- // implementation, which means that
- // we can also skip the sparsity
- // patterns.
-template <int dim>
-class LaplaceProblem
-{
- public:
- LaplaceProblem (const unsigned int degree);
- void run ();
-
- private:
- void setup_system ();
- void assemble_system ();
- void assemble_multigrid ();
- void solve ();
- void output_results (const unsigned int cycle) const;
-
- Triangulation<dim> triangulation;
- FE_Q<dim> fe;
- MGDoFHandler<dim> mg_dof_handler;
-
- MatrixFree<double,LaplaceOperator<dim,double> > system_matrix;
- typedef MatrixFree<float,LaplaceOperator<dim,float> > MatrixFreeType;
- MGLevelObject<MatrixFreeType> mg_matrices;
- FullMatrix<float> coarse_matrix;
-
- Vector<double> solution;
- Vector<double> system_rhs;
-};
-
-
-
-template <int dim>
-LaplaceProblem<dim>::LaplaceProblem (const unsigned int degree)
- :
- fe (degree),
- mg_dof_handler (triangulation)
-{}
-
-
-
- // @sect4{LaplaceProblem::setup_system}
-
- // This is the function of step-16 with
- // relevant changes due to the MatrixFree
- // class. What we need to do is to somehow
- // create a local gradient matrix that does
- // not contain any cell-related data
- // (gradient on the reference cell). The
- // way to get to this matrix is to create
- // an FEValues object with gradient
- // information on a cell that corresponds
- // to the reference cell, which is a cube
- // with side length 1. So we create a
- // pseudo triangulation, initialize the
- // FEValues to the only cell of that
- // triangulation, and read off the
- // gradients (which we put in a
- // FullMatrix). That full matrix is then
- // passed to the reinit function of the
- // MatrixFree class used as a system matrix
- // and, further down, as multigrid matrices
- // on the individual levels. We need to
- // implement Dirichlet boundary conditions
- // here, which is done with the
- // ConstraintMatrix function as shown,
- // e.g., in step-22.
-template <int dim>
-void LaplaceProblem<dim>::setup_system ()
-{
- system_matrix.clear();
- mg_matrices.clear();
-
- mg_dof_handler.distribute_dofs (fe);
-
- std::cout << "Number of degrees of freedom: "
- << mg_dof_handler.n_dofs()
- << std::endl;
-
- const unsigned int nlevels = triangulation.n_levels();
- mg_matrices.resize(0, nlevels-1);
-
- QGauss<dim> quadrature_formula(fe.degree+1);
- FEValues<dim> fe_values_reference (fe, quadrature_formula,
- update_gradients);
- Triangulation<dim> reference_cell;
- GridGenerator::hyper_cube (reference_cell, 0, 1);
- fe_values_reference.reinit (reference_cell.begin());
- FullMatrix<double> ref_cell_gradients (fe.dofs_per_cell,
- quadrature_formula.size()*dim);
- for (unsigned int i=0; i<fe.dofs_per_cell; ++i)
- {
- for (unsigned int j=0; j<quadrature_formula.size(); ++j)
- {
- for (unsigned int d=0; d<dim; ++d)
- ref_cell_gradients(i,j*dim+d) = fe_values_reference.shape_grad(i,j)[d];
- }
- }
- system_matrix.reinit (mg_dof_handler.n_dofs(), triangulation.n_active_cells(),
- ref_cell_gradients, quadrature_formula.size());
- VectorTools::interpolate_boundary_values (mg_dof_handler,
- 0,
- ZeroFunction<dim>(),
- system_matrix.get_constraints());
- system_matrix.get_constraints().close();
- std::cout.precision(4);
- std::cout << "System matrix memory consumption: "
- << system_matrix.memory_consumption()/double(1<<20)
- << " MiB."
- << std::endl;
-
- solution.reinit (mg_dof_handler.n_dofs());
- system_rhs.reinit (mg_dof_handler.n_dofs());
-
- // Next, initialize the matrices for the
- // multigrid method on all the
- // levels. Unfortunately, the function
- // MGTools::make_boundary_list cannot write
- // Dirichlet boundary conditions into a
- // ConstraintMatrix object directly, so we
- // first have to make the boundary list and
- // then manually fill the boundary
- // conditions using the command
- // ConstraintMatrix::add_line. Once this is
- // done, we close the ConstraintMatrix so
- // it can be used for matrix-vector
- // products.
- typename FunctionMap<dim>::type dirichlet_boundary;
- ZeroFunction<dim> homogeneous_dirichlet_bc (1);
- dirichlet_boundary[0] = &homogeneous_dirichlet_bc;
- std::vector<std::set<unsigned int> > boundary_indices(triangulation.n_levels());
- MGTools::make_boundary_list (mg_dof_handler,
- dirichlet_boundary,
- boundary_indices);
- for (unsigned int level=0;level<nlevels;++level)
- {
- mg_matrices[level].reinit(mg_dof_handler.n_dofs(level),
- triangulation.n_cells(level),
- ref_cell_gradients,
- quadrature_formula.size());
- std::set<unsigned int>::iterator bc_it = boundary_indices[level].begin();
- for ( ; bc_it != boundary_indices[level].end(); ++bc_it)
- mg_matrices[level].get_constraints().add_line(*bc_it);
- mg_matrices[level].get_constraints().close();
- }
- coarse_matrix.reinit (mg_dof_handler.n_dofs(0),
- mg_dof_handler.n_dofs(0));
-}
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::Tvmult_add (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ vmult_add (dst,src);
+ }
+
+
+
+ // This function implements the loop over all
+ // cells. This is done with the @p cell_loop
+ // of the MatrixFree class, which takes
+ // the operator() of this class with arguments
+ // MatrixFree, OutVector, InVector,
+ // cell_range. Note that we could also use a
+ // simple function as local operation in case
+ // we had constant coefficients (all we need
+ // then is the MatrixFree, the vectors and
+ // the cell range), but since the coefficient
+ // is stored in a variable of this class, we
+ // cannot use that variant here. The cell loop
+ // is automatically performed on several threads
+ // if multithreading is enabled (this class
+ // uses a quite elaborate algorithm to work on
+ // cells that do not share any degrees of
+ // freedom that could possibly give rise to
+ // race conditions, using the dynamic task
+ // scheduler of the Intel Threading Building
+ // Blocks).
+ //
+ // After the cell loop, we need to touch
+ // the constrained degrees of freedom:
+ // Since the assembly loop automatically
+ // resolves constraints (just as the
+ // ConstraintMatrix::distribute_local_to_global
+ // call does), it does not compute any
+ // contribution for constrained degrees of
+ // freedom. In other words, the entries for
+ // constrained DoFs remain zero after the
+ // first part of this function, as if the
+ // matrix had empty rows and columns for
+ // constrained degrees of freedom. On the
+ // other hand, iterative solvers like CG
+ // only work for non-singular matrices, so
+ // we have to modify the operation on
+ // constrained DoFs. The easiest way to do
+ // that is to pretend that the sub-block of
+ // the matrix that corresponds to
+ // constrained DoFs is the identity matrix,
+ // in which case application of the matrix
+ // would simply copy the elements of the
+ // right hand side vector into the left
+ // hand side. In general, however, one
+ // needs to make sure that the diagonal
+ // entries of this sub-block are of the
+ // same order of magnitude as the diagonal
+ // elements of the rest of the matrix.
+ // Here, the domain extent is of unit size,
+ // so we can simply choose unit size. If we
+ // had domains that are far away from unit
+ // size, we would need to choose a number
+ // that is close to the size of other
+ // diagonal matrix entries, so that these
+ // artificial eigenvalues do not change the
+ // eigenvalue spectrum (and make
+ // convergence with CG more difficult).
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::vmult_add (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ data.cell_loop (&LaplaceOperator::local_apply, this, dst, src);
+
+ const std::vector<unsigned int> &
+ constrained_dofs = data.get_constrained_dofs();
+ for (unsigned int i=0; i<constrained_dofs.size(); ++i)
+ dst(constrained_dofs[i]) += src(constrained_dofs[i]);
+ }
+
+
+
+ // The next function is used to return entries of
+ // the matrix. Since this class is intended
+ // not to store the matrix entries, it would
+ // make no sense to provide access to all those
+ // elements. However, diagonal entries are
+ // explicitly needed for the implementation
+ // of the Chebyshev smoother that we intend
+ // to use in the multigrid
+ // preconditioner. This matrix is equipped
+ // with a vector that stores the diagonal.
+ template <int dim, int fe_degree, typename number>
+ number
+ LaplaceOperator<dim,fe_degree,number>::el (const unsigned int row,
+ const unsigned int col) const
+ {
+ Assert (row == col, ExcNotImplemented());
+ Assert (diagonal_is_available == true, ExcNotInitialized());
+ return diagonal_values(row);
+ }
+
+
+
+ // Regarding the calculation of the
+ // diagonal, we expect the user to
+ // provide a vector with the
+ // diagonal entries (and we will
+ // compute them in the code
+ // below). We only need it for the
+ // level matrices of multigrid, not
+ // the system matrix (since we only
+ // need these diagonals for the
+ // multigrid smoother). Since we
+ // fill only elements into
+ // unconstrained entries, we have
+ // to set constrained entries to
+ // one in order to avoid the same
+ // problems as discussed above.
+ template <int dim, int fe_degree, typename number>
+ void
+ LaplaceOperator<dim,fe_degree,number>::set_diagonal(const Vector<number>&diagonal)
+ {
+ AssertDimension (m(), diagonal.size());
+
+ diagonal_values = diagonal;
+
+ const std::vector<unsigned int> &
+ constrained_dofs = data.get_constrained_dofs();
+ for (unsigned int i=0; i<constrained_dofs.size(); ++i)
+ diagonal_values(constrained_dofs[i]) = 1.0;
+
+ diagonal_is_available = true;
+ }
+
+
+
+ // Eventually, we provide a function that
+ // calculates how much memory this class
+ // uses. We just need to sum up the memory
+ // consumption in the MatrixFree object and
+ // the memory for storing the other member
+ // variables. As a remark: In 3D and for
+ // Cartesian meshes, most memory is
+ // consumed for storing the vector indices
+ // on the local cells (corresponding to
+ // local_dof_indices). For general
+ // (non-Cartesian) meshes, the cached
+ // Jacobian transformation consumes most
+ // memory.
+ template <int dim, int fe_degree, typename number>
+ std::size_t
+ LaplaceOperator<dim,fe_degree,number>::memory_consumption () const
+ {
+ return (data.memory_consumption () +
+ coefficient.memory_consumption() +
+ diagonal_values.memory_consumption() +
+ MemoryConsumption::memory_consumption(diagonal_is_available));
+ }
+
+
+
+ // @sect3{LaplaceProblem class}
+
+ // This class is based on the one in
+ // step-16. However, we replaced the
+ // SparseMatrix<double> class by our
+ // matrix-free implementation, which means
+ // that we can also skip the sparsity
+ // patterns. Notice that we define the
+ // LaplaceOperator class with the degree of finite
+ // element as template argument (the value is
+ // defined at the top of the file), and that
+ // we use float numbers for the multigrid
+ // level matrices.
+ //
+ // The class also has a member variable to
+ // keep track of all the time we spend on
+ // setting up the entire chain of data
+ // before we actually go about solving the
+ // problem. In addition, there is an output
+ // stream (that is disabled by default)
+ // that can be used to output details for
+ // the individual setup operations instead
+ // of the summary only that is printed out
+ // by default.
+ template <int dim>
+ class LaplaceProblem
+ {
+ public:
+ LaplaceProblem ();
+ void run ();
+
+ private:
+ void setup_system ();
+ void assemble_system ();
+ void assemble_multigrid ();
+ void solve ();
+ void output_results (const unsigned int cycle) const;
+
+ typedef LaplaceOperator<dim,degree_finite_element,double> SystemMatrixType;
+ typedef LaplaceOperator<dim,degree_finite_element,float> LevelMatrixType;
+
+ Triangulation<dim> triangulation;
+ FE_Q<dim> fe;
+ MGDoFHandler<dim> mg_dof_handler;
+ ConstraintMatrix constraints;
+
+ SystemMatrixType system_matrix;
+ MGLevelObject<LevelMatrixType> mg_matrices;
+ FullMatrix<float> coarse_matrix;
+ MGLevelObject<ConstraintMatrix> mg_constraints;
+
+ Vector<double> solution;
+ Vector<double> system_rhs;
+
+ double setup_time;
+ ConditionalOStream time_details;
+ };
- // @sect4{LaplaceProblem::assemble_system}
-
- // The assemble function is significantly
- // reduced compared to step-16. All we need
- // to do is to assemble the right hand side
- // and to calculate the cell-dependent part
- // of the Laplace operator. The first task is
- // standard. The second is also not too hard
- // given the discussion in the introduction:
- // We need to take the inverse of the
- // Jacobian of the transformation from unit
- // to real cell, multiply it with its
- // transpose and multiply the resulting
- // rank-2 tensor with the quadrature weights
- // and the coefficient values at the
- // quadrature points. To make this work, we
- // add the update flag @p
- // update_inverse_jacobians to the FEValues
- // constructor, and query the inverse of the
- // Jacobian in a loop over the quadrature
- // points (note that the Jacobian is not
- // related to any kind of degrees of freedom
- // directly). In the end, we condense the
- // constraints from Dirichlet boundary
- // conditions away from the right hand side.
-template <int dim>
-void LaplaceProblem<dim>::assemble_system ()
-{
- QGauss<dim> quadrature_formula(fe.degree+1);
- MappingQ<dim> mapping (fe.degree);
- FEValues<dim> fe_values (mapping, fe, quadrature_formula,
- update_values | update_inverse_jacobians |
- update_quadrature_points | update_JxW_values);
+ // When we initialize the finite element, we
+ // of course have to use the degree specified
+ // at the top of the file as well (otherwise,
+ // an exception will be thrown at some point,
+ // since the computational kernel defined in
+ // the templated LaplaceOperator class and the
+ // information from the finite element read
+ // out by MatrixFree will not match).
+ template <int dim>
+ LaplaceProblem<dim>::LaplaceProblem ()
+ :
+ fe (degree_finite_element),
+ mg_dof_handler (triangulation),
+ time_details (std::cout, false)
+ {}
- const unsigned int dofs_per_cell = fe.dofs_per_cell;
- const unsigned int n_q_points = quadrature_formula.size();
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- const Coefficient<dim> coefficient;
- std::vector<double> coefficient_values (n_q_points);
- unsigned int cell_no = 0;
+ // @sect4{LaplaceProblem::setup_system}
+
+ // This is the function of step-16 with
+ // relevant changes due to the LaplaceOperator
+ // class. We do not use adaptive grids, so we
+ // do not have to compute edge matrices. Thus,
+ // all we do is to implement Dirichlet
+ // boundary conditions through the
+ // ConstraintMatrix, set up the
+ // (one-dimensional) quadrature that should
+ // be used by the matrix-free class, and call
+ // the initialization functions.
+ //
+ // In the process, we output data on both
+ // the run time of the program as well as
+ // on memory consumption, where we output
+ // memory data in megabytes (1 million
+ // bytes).
+ template <int dim>
+ void LaplaceProblem<dim>::setup_system ()
+ {
+ Timer time;
+ time.start ();
+ setup_time = 0;
+
+ system_matrix.clear();
+ mg_matrices.clear();
+ mg_constraints.clear();
+
+ mg_dof_handler.distribute_dofs (fe);
+
+ std::cout << "Number of degrees of freedom: "
+ << mg_dof_handler.n_dofs()
+ << std::endl;
+
+ constraints.clear();
+ VectorTools::interpolate_boundary_values (mg_dof_handler,
+ 0,
+ ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+ setup_time += time.wall_time();
+ time_details << "Distribute DoFs & B.C. (CPU/wall) "
+ << time() << "s/" << time.wall_time() << "s" << std::endl;
+ time.restart();
+
+ system_matrix.reinit (mg_dof_handler, constraints);
+ std::cout.precision(4);
+ std::cout << "System matrix memory consumption: "
+ << system_matrix.memory_consumption()*1e-6
+ << " MB."
+ << std::endl;
+
+ solution.reinit (mg_dof_handler.n_dofs());
+ system_rhs.reinit (mg_dof_handler.n_dofs());
+
+ setup_time += time.wall_time();
+ time_details << "Setup matrix-free system (CPU/wall) "
+ << time() << "s/" << time.wall_time() << "s" << std::endl;
+ time.restart();
+
+ // Next, initialize the matrices
+ // for the multigrid method on
+ // all the levels. The function
+ // MGTools::make_boundary_list
+ // returns for each multigrid
+ // level which degrees of freedom
+ // are located on a Dirichlet
+ // boundary; we force these DoFs
+ // to have value zero by adding
+ // to the ConstraintMatrix object
+ // a zero condition by using the
+ // command
+ // ConstraintMatrix::add_line. Once
+ // this is done, we close the
+ // ConstraintMatrix on each level
+ // so it can be used to read out
+ // indices internally in the
+ // MatrixFree.
+ const unsigned int nlevels = triangulation.n_levels();
+ mg_matrices.resize(0, nlevels-1);
+ mg_constraints.resize (0, nlevels-1);
+
+ typename FunctionMap<dim>::type dirichlet_boundary;
+ ZeroFunction<dim> homogeneous_dirichlet_bc (1);
+ dirichlet_boundary[0] = &homogeneous_dirichlet_bc;
+ std::vector<std::set<unsigned int> > boundary_indices(triangulation.n_levels());
+ MGTools::make_boundary_list (mg_dof_handler,
+ dirichlet_boundary,
+ boundary_indices);
+ for (unsigned int level=0;level<nlevels;++level)
+ {
+ std::set<unsigned int>::iterator bc_it = boundary_indices[level].begin();
+ for ( ; bc_it != boundary_indices[level].end(); ++bc_it)
+ mg_constraints[level].add_line(*bc_it);
+
+ mg_constraints[level].close();
+ mg_matrices[level].reinit(mg_dof_handler,
+ mg_constraints[level],
+ level);
+ }
+ coarse_matrix.reinit (mg_dof_handler.n_dofs(0),
+ mg_dof_handler.n_dofs(0));
+ setup_time += time.wall_time();
+ time_details << "Setup matrix-free levels (CPU/wall) "
+ << time() << "s/" << time.wall_time() << "s" << std::endl;
+ }
+
+
+
+ // @sect4{LaplaceProblem::assemble_system}
+
+ // The assemble function is significantly
+ // reduced compared to step-16. All we need
+ // to do is to assemble the right hand
+ // side. That is the same as in many other
+ // tutorial programs. In the end, we condense
+ // the constraints from Dirichlet boundary
+ // conditions away from the right hand side.
+ template <int dim>
+ void LaplaceProblem<dim>::assemble_system ()
+ {
+ Timer time;
+ QGauss<dim> quadrature_formula(fe.degree+1);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_values | update_JxW_values);
- typename DoFHandler<dim>::active_cell_iterator cell = mg_dof_handler.begin_active(),
- endc = mg_dof_handler.end();
- for (; cell!=endc; ++cell, ++cell_no)
- {
- cell->get_dof_indices (local_dof_indices);
- fe_values.reinit (cell);
- coefficient.value_list (fe_values.get_quadrature_points(),
- coefficient_values);
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- {
- double rhs_val = 0;
- for (unsigned int q=0; q<n_q_points; ++q)
- rhs_val += (fe_values.shape_value(i,q) * 1.0 *
- fe_values.JxW(q));
- system_rhs(local_dof_indices[i]) += rhs_val;
- }
-
- system_matrix.set_local_dof_indices (cell_no, local_dof_indices);
- for (unsigned int q=0; q<n_q_points; ++q)
- system_matrix.set_derivative_data (cell_no, q,
- (transpose
- (fe_values.inverse_jacobian(q)) *
- fe_values.inverse_jacobian(q)) *
- fe_values.JxW(q) *
- coefficient_values[q]);
- }
- system_matrix.get_constraints().condense(system_rhs);
-}
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ const Coefficient<dim> coefficient;
+ std::vector<double> coefficient_values (n_q_points);
- // @sect4{LaplaceProblem::assemble_multigrid}
-
- // Here is another assemble
- // function. The integration core is
- // the same as above. Only the loop
- // goes over all existing cells now
- // and the results must be entered
- // into the correct matrix.
-
- // Since we only do multilevel
- // preconditioning, no right-hand side is
- // assembled here. Compared to step-16, there
- // is one new thing here: we manually
- // calculate the matrix on the coarsest
- // level. In step-16, we could simply copy
- // the entries from the respective sparse
- // matrix, but this is obviously not possible
- // here. We could have integrated this into the
- // MatrixFree class as well, but it is simple
- // enough, so calculate it here instead.
-template <int dim>
-void LaplaceProblem<dim>::assemble_multigrid ()
-{
- coarse_matrix = 0;
- QGauss<dim> quadrature_formula(fe.degree+1);
- MappingQ<dim> mapping (fe.degree);
- FEValues<dim> fe_values (mapping, fe, quadrature_formula,
- update_gradients | update_inverse_jacobians |
- update_quadrature_points | update_JxW_values);
-
- const unsigned int dofs_per_cell = fe.dofs_per_cell;
- const unsigned int n_q_points = quadrature_formula.size();
-
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- const Coefficient<dim> coefficient;
- std::vector<double> coefficient_values (n_q_points);
-
- std::vector<unsigned int> cell_no(triangulation.n_levels());
- typename MGDoFHandler<dim>::cell_iterator cell = mg_dof_handler.begin(),
- endc = mg_dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- const unsigned int level = cell->level();
- cell->get_mg_dof_indices (local_dof_indices);
- fe_values.reinit (cell);
- coefficient.value_list (fe_values.get_quadrature_points(),
- coefficient_values);
-
- mg_matrices[level].set_local_dof_indices (cell_no[level],
- local_dof_indices);
- for (unsigned int q=0; q<n_q_points; ++q)
- mg_matrices[level].set_derivative_data (cell_no[level], q,
- (transpose
- (fe_values.inverse_jacobian(q)) *
- fe_values.inverse_jacobian(q)) *
- fe_values.JxW(q) *
- coefficient_values[q]);
-
- ++cell_no[level];
- if (level == 0)
- {
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- {
- double add_value = 0;
- for (unsigned int q=0; q<n_q_points; ++q)
- add_value += (fe_values.shape_grad(i,q) *
- fe_values.shape_grad(j,q) *
- coefficient_values[q] *
- fe_values.JxW(q));
- coarse_matrix(local_dof_indices[i],
- local_dof_indices[j]) += add_value;
- }
- }
- }
+ typename DoFHandler<dim>::active_cell_iterator cell = mg_dof_handler.begin_active(),
+ endc = mg_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell->get_dof_indices (local_dof_indices);
+ fe_values.reinit (cell);
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ double rhs_val = 0;
+ for (unsigned int q=0; q<n_q_points; ++q)
+ rhs_val += (fe_values.shape_value(i,q) * 1.0 *
+ fe_values.JxW(q));
+ system_rhs(local_dof_indices[i]) += rhs_val;
+ }
+ }
+ constraints.condense(system_rhs);
+ setup_time += time.wall_time();
+ time_details << "Assemble right hand side (CPU/wall) "
+ << time() << "s/" << time.wall_time() << "s" << std::endl;
+ }
+
+
+ // @sect4{LaplaceProblem::assemble_multigrid}
+
+ // Here is another assemble
+ // function. Again, it is simpler than
+ // assembling matrices. We need to compute
+ // the diagonal of the Laplace matrices on
+ // the individual levels, send the final
+ // matrices to the LaplaceOperator class,
+ // and we need to compute the full matrix
+ // on the coarsest level (since that is
+ // inverted exactly in the deal.II
+ // multigrid implementation).
+ template <int dim>
+ void LaplaceProblem<dim>::assemble_multigrid ()
+ {
+ Timer time;
+ coarse_matrix = 0;
+ QGauss<dim> quadrature_formula(fe.degree+1);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_gradients | update_inverse_jacobians |
+ update_quadrature_points | update_JxW_values);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ const Coefficient<dim> coefficient;
+ std::vector<double> coefficient_values (n_q_points);
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ Vector<double> local_diagonal (dofs_per_cell);
+
+ const unsigned int n_levels = triangulation.n_levels();
+ std::vector<Vector<float> > diagonals (n_levels);
+ for (unsigned int level=0; level<n_levels; ++level)
+ diagonals[level].reinit (mg_dof_handler.n_dofs(level));
+
+ std::vector<unsigned int> cell_no(triangulation.n_levels());
+ typename MGDoFHandler<dim>::cell_iterator cell = mg_dof_handler.begin(),
+ endc = mg_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ const unsigned int level = cell->level();
+ cell->get_mg_dof_indices (local_dof_indices);
+ fe_values.reinit (cell);
+ coefficient.value_list (fe_values.get_quadrature_points(),
+ coefficient_values);
- // In a final step, we need to
- // condense the boundary conditions
- // on the coarse matrix. There is
- // no built-in function for doing
- // this on a full matrix, so
- // manually delete the rows and
- // columns of the matrix that are
- // constrained.
- for (unsigned int i=0; i<coarse_matrix.m(); ++i)
- if (mg_matrices[0].get_constraints().is_constrained(i))
- for (unsigned int j=0; j<coarse_matrix.n(); ++j)
- if (i!=j)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
{
- coarse_matrix(i,j) = 0;
- coarse_matrix(j,i) = 0;
+ double local_diag = 0;
+ for (unsigned int q=0; q<n_q_points; ++q)
+ local_diag += ((fe_values.shape_grad(i,q) *
+ fe_values.shape_grad(i,q)) *
+ coefficient_values[q] * fe_values.JxW(q));
+ local_diagonal(i) = local_diag;
}
-}
+ mg_constraints[level].distribute_local_to_global(local_diagonal,
+ local_dof_indices,
+ diagonals[level]);
+ if (level == 0)
+ {
+ local_matrix = 0;
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ {
+ double add_value = 0;
+ for (unsigned int q=0; q<n_q_points; ++q)
+ add_value += (fe_values.shape_grad(i,q) *
+ fe_values.shape_grad(j,q) *
+ coefficient_values[q] *
+ fe_values.JxW(q));
+ local_matrix(i,j) = add_value;
+ }
+ mg_constraints[0].distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ coarse_matrix);
+ }
+ }
+ for (unsigned int level=0; level<n_levels; ++level)
+ mg_matrices[level].set_diagonal (diagonals[level]);
- // @sect4{LaplaceProblem::solve}
+ setup_time += time.wall_time();
+ time_details << "Assemble MG diagonal (CPU/wall) "
+ << time() << "s/" << time.wall_time() << "s" << std::endl;
+ }
- // The solution process again looks like
- // step-16. We now use a Chebyshev smoother
- // instead of SOR (SOR would be very
- // difficult to implement because we do not
- // have the matrix elements available
- // explicitly, and it is difficult to make it
- // work efficiently in %parallel). The
- // multigrid classes provide a simple
- // interface for using the Chebyshev smoother
- // which is defined in a preconditioner
- // class: MGSmootherPrecondition.
-template <int dim>
-void LaplaceProblem<dim>::solve ()
-{
- GrowingVectorMemory<> vector_memory;
-
- MGTransferPrebuilt<Vector<double> > mg_transfer;
- mg_transfer.build_matrices(mg_dof_handler);
-
- MGCoarseGridHouseholder<float, Vector<double> > mg_coarse;
- mg_coarse.initialize(coarse_matrix);
-
- typedef PreconditionChebyshev<MatrixFreeType,Vector<double> > SMOOTHER;
- MGSmootherPrecondition<MatrixFreeType, SMOOTHER, Vector<double> >
- mg_smoother(vector_memory);
-
- // Then, we initialize the smoother
- // with our level matrices and the
- // required, additional data for
- // the Chebyshev smoother. In
- // particular, we use a higher
- // polynomial degree for higher
- // order elements, since smoothing
- // gets more difficult for
- // these. Smooth out a range of
- // $[\lambda_{\max}/10,\lambda_{\max}]$. In
- // order to compute the maximum
- // eigenvalue of the corresponding
- // matrix, the Chebyshev
- // initializations performs a few
- // steps of a CG algorithm. Since
- // all we need is a rough estimate,
- // we choose some eight iterations
- // (more if the finite element
- // polynomial degree is larger,
- // less if it is smaller than
- // quadratic).
- typename SMOOTHER::AdditionalData smoother_data;
- smoother_data.smoothing_range = 10.;
- smoother_data.degree = fe.degree;
- smoother_data.eig_cg_n_iterations = 4+2*fe.degree;
- mg_smoother.initialize(mg_matrices, smoother_data);
-
- MGMatrix<MatrixFreeType, Vector<double> >
- mg_matrix(&mg_matrices);
-
- Multigrid<Vector<double> > mg(mg_dof_handler,
- mg_matrix,
- mg_coarse,
- mg_transfer,
- mg_smoother,
- mg_smoother);
- PreconditionMG<dim, Vector<double>,
- MGTransferPrebuilt<Vector<double> > >
- preconditioner(mg_dof_handler, mg, mg_transfer);
-
- // Finally, write out the memory
- // consumption of the Multigrid object
- // (or rather, of its most significant
- // components, since there is no built-in
- // function for the total multigrid
- // object), then create the solver object
- // and solve the system. This is very
- // easy, and we didn't even see any
- // difference in the solve process
- // compared to step-16. The magic is all
- // hidden behind the implementation of
- // the MatrixFree::vmult operation.
- const unsigned int multigrid_memory
- = (mg_matrices.memory_consumption() +
- mg_transfer.memory_consumption() +
- coarse_matrix.memory_consumption());
- std::cout << "Multigrid objects memory consumption: "
- << multigrid_memory/double(1<<20)
- << " MiB."
- << std::endl;
-
- SolverControl solver_control (1000, 1e-12);
- SolverCG<> cg (solver_control);
-
- cg.solve (system_matrix, solution, system_rhs,
- preconditioner);
-
- std::cout << "Convergence in " << solver_control.last_step()
- << " CG iterations." << std::endl;
-}
+ // @sect4{LaplaceProblem::solve}
- // @sect4{LaplaceProblem::output_results}
+ // The solution process again looks like
+ // step-16. We now use a Chebyshev smoother
+ // instead of SOR (SOR would be very
+ // difficult to implement because we do not
+ // have the matrix elements available
+ // explicitly, and it is difficult to make it
+ // work efficiently in %parallel). The
+ // multigrid classes provide a simple
+ // interface for using the Chebyshev smoother
+ // which is defined in a preconditioner
+ // class: MGSmootherPrecondition.
+ template <int dim>
+ void LaplaceProblem<dim>::solve ()
+ {
+ Timer time;
+ GrowingVectorMemory<> vector_memory;
+
+ MGTransferPrebuilt<Vector<double> > mg_transfer;
+ mg_transfer.build_matrices(mg_dof_handler);
+ setup_time += time.wall_time();
+ time_details << "MG build transfer time (CPU/wall) " << time()
+ << "s/" << time.wall_time() << "s\n";
+ time.restart();
+
+ MGCoarseGridHouseholder<float, Vector<double> > mg_coarse;
+ mg_coarse.initialize(coarse_matrix);
+ setup_time += time.wall_time();
+ time_details << "MG coarse time (CPU/wall) " << time()
+ << "s/" << time.wall_time() << "s\n";
+ time.restart();
+
+ typedef PreconditionChebyshev<LevelMatrixType,Vector<double> > SMOOTHER;
+ MGSmootherPrecondition<LevelMatrixType, SMOOTHER, Vector<double> >
+ mg_smoother(vector_memory);
+
+ // Then, we initialize the smoother with
+ // our level matrices and the mandatory
+ // additional data for the Chebyshev
+ // smoother. We use quite a high degree
+ // here (6), since matrix-vector products
+ // are comparably cheap and more parallel
+ // than the level-transfer operations. We
+ // choose to smooth out a range of $[1.2
+ // \hat{\lambda}_{\max}/10,1.2
+ // \hat{\lambda}_{\max}]$ in the smoother
+ // where $\hat{\lambda}_{\max}$ is an
+ // estimate of the largest eigenvalue. In
+ // order to compute that eigenvalue, the
+ // Chebyshev initializations performs a
+ // few steps of a CG algorithm without
+ // preconditioner. Since the highest
+ // eigenvalue is usually the easiest one
+ // to find and a rough estimate is enough,
+ // we choose 10 iterations.
+ typename SMOOTHER::AdditionalData smoother_data;
+ smoother_data.smoothing_range = 10.;
+ smoother_data.degree = 6;
+ smoother_data.eig_cg_n_iterations = 10;
+ mg_smoother.initialize(mg_matrices, smoother_data);
+
+ MGMatrix<LevelMatrixType, Vector<double> >
+ mg_matrix(&mg_matrices);
+
+ Multigrid<Vector<double> > mg(mg_dof_handler,
+ mg_matrix,
+ mg_coarse,
+ mg_transfer,
+ mg_smoother,
+ mg_smoother);
+ PreconditionMG<dim, Vector<double>,
+ MGTransferPrebuilt<Vector<double> > >
+ preconditioner(mg_dof_handler, mg, mg_transfer);
+
+ // Finally, write out the memory
+ // consumption of the Multigrid object
+ // (or rather, of its most significant
+ // components, since there is no built-in
+ // function for the total multigrid
+ // object), then create the solver object
+ // and solve the system. This is very
+ // easy, and we didn't even see any
+ // difference in the solve process
+ // compared to step-16. The magic is all
+ // hidden behind the implementation of
+ // the LaplaceOperator::vmult
+ // operation. Note that we print out the
+ // solve time and the accumulated setup
+ // time through standard out, i.e., in
+ // any case, whereas detailed times for
+ // the setup operations are only printed
+ // in case the flag for detail_times in
+ // the constructor is changed.
+ const std::size_t multigrid_memory
+ = (mg_matrices.memory_consumption() +
+ mg_transfer.memory_consumption() +
+ coarse_matrix.memory_consumption());
+ std::cout << "Multigrid objects memory consumption: "
+ << multigrid_memory * 1e-6
+ << " MB."
+ << std::endl;
+
+ SolverControl solver_control (1000, 1e-12*system_rhs.l2_norm());
+ SolverCG<> cg (solver_control);
+ setup_time += time.wall_time();
+ time_details << "MG build smoother time (CPU/wall) " << time()
+ << "s/" << time.wall_time() << "s\n";
+ std::cout << "Total setup time (wall) " << setup_time
+ << "s\n";
+
+ time.reset();
+ time.start();
+ cg.solve (system_matrix, solution, system_rhs,
+ preconditioner);
+
+
+ std::cout << "Time solve ("
+ << solver_control.last_step()
+ << " iterations) (CPU/wall) " << time() << "s/"
+ << time.wall_time() << "s\n";
+ }
+
+
+
+ // @sect4{LaplaceProblem::output_results}
+
+ // Here is the data output, which is a
+ // simplified version of step-5. We use the
+ // standard VTU (= compressed VTK) output for
+ // each grid produced in the refinement
+ // process.
+ template <int dim>
+ void LaplaceProblem<dim>::output_results (const unsigned int cycle) const
+ {
+ DataOut<dim> data_out;
- // Here is the data output, which is a
- // simplified version of step-5. We use the
- // standard VTK output for each grid
- // produced in the refinement process.
-template <int dim>
-void LaplaceProblem<dim>::output_results (const unsigned int cycle) const
-{
- DataOut<dim> data_out;
+ data_out.attach_dof_handler (mg_dof_handler);
+ data_out.add_data_vector (solution, "solution");
+ data_out.build_patches ();
- data_out.attach_dof_handler (mg_dof_handler);
- data_out.add_data_vector (solution, "solution");
- data_out.build_patches ();
+ std::ostringstream filename;
+ filename << "solution-"
+ << cycle
+ << ".vtu";
- std::ostringstream filename;
- filename << "solution-"
- << cycle
- << ".vtk";
+ std::ofstream output (filename.str().c_str());
+ data_out.write_vtu (output);
+ }
- std::ofstream output (filename.str().c_str());
- data_out.write_vtk (output);
-}
+ // @sect4{LaplaceProblem::run}
- // @sect4{LaplaceProblem::run}
+ // The function that runs the program is
+ // very similar to the one in step-16. We
+ // make less refinement steps in 3D
+ // compared to 2D, but that's it.
+ template <int dim>
+ void LaplaceProblem<dim>::run ()
+ {
+ for (unsigned int cycle=0; cycle<9-dim; ++cycle)
+ {
+ std::cout << "Cycle " << cycle << std::endl;
- // The function that runs the program is
- // very similar to the one in step-16. We
- // make less refinement steps in 3D
- // compared to 2D, but that's it.
-template <int dim>
-void LaplaceProblem<dim>::run ()
-{
- for (unsigned int cycle=0; cycle<8-dim; ++cycle)
- {
- std::cout << "Cycle " << cycle << std::endl;
-
- if (cycle == 0)
- {
- GridGenerator::hyper_ball(triangulation);
- static const HyperBallBoundary<dim> boundary;
- triangulation.set_boundary (0, boundary);
- triangulation.refine_global (3-dim);
- }
- triangulation.refine_global (1);
- setup_system ();
- assemble_system ();
- assemble_multigrid ();
- solve ();
- output_results (cycle);
- std::cout << std::endl;
- };
+ if (cycle == 0)
+ {
+ GridGenerator::hyper_cube (triangulation, 0., 1.);
+ triangulation.refine_global (3-dim);
+ }
+ triangulation.refine_global (1);
+ setup_system ();
+ assemble_system ();
+ assemble_multigrid ();
+ solve ();
+ output_results (cycle);
+ std::cout << std::endl;
+ };
+ }
}
// @sect3{The <code>main</code> function}
- // This is as in all other programs:
-int main ()
+ // This is as in most other programs.
+int main (int argc, char** argv)
{
try
{
- deallog.depth_console (0);
- LaplaceProblem<2> laplace_problem (2);
+ using namespace Step37;
+
+ deallog.depth_console(0);
+ LaplaceProblem<dimension> laplace_problem;
laplace_problem.run ();
}
catch (std::exception &exc)
--- /dev/null
+# $Id$
+
+
+# For the small projects Makefile, you basically need to fill in only
+# four fields.
+#
+# The first is the name of the application. It is assumed that the
+# application name is the same as the base file name of the single C++
+# file from which the application is generated.
+target = $(basename $(shell echo step-*.cc))
+
+# The second field determines whether you want to run your program in
+# debug or optimized mode. The latter is significantly faster, but no
+# run-time checking of parameters and internal states is performed, so
+# you should set this value to `on' while you develop your program,
+# and to `off' when running production computations.
+debug-mode = on
+
+
+# As third field, we need to give the path to the top-level deal.II
+# directory. You need to adjust this to your needs. Since this path is
+# probably the most often needed one in the Makefile internals, it is
+# designated by a single-character variable, since that can be
+# reference using $D only, i.e. without the parentheses that are
+# required for most other parameters, as e.g. in $(target).
+D = ../../
+
+
+# The last field specifies the names of data and other files that
+# shall be deleted when calling `make clean'. Object and backup files,
+# executables and the like are removed anyway. Here, we give a list of
+# files in the various output formats that deal.II supports.
+clean-up-files = *gmv *gnuplot *gpl *eps *pov *vtk *vtu *ucd *.d2
+
+
+
+
+#
+#
+# Usually, you will not need to change anything beyond this point.
+#
+#
+# The next statement tells the `make' program where to find the
+# deal.II top level directory and to include the file with the global
+# settings
+include $D/common/Make.global_options
+
+
+# Since the whole project consists of only one file, we need not
+# consider difficult dependencies. We only have to declare the
+# libraries which we want to link to the object file. deal.II has two
+# libraries: one for the debug mode version of the
+# application and one for optimized mode.
+libs.g := $(lib-deal2.g)
+libs.o := $(lib-deal2.o)
+
+
+# We now use the variable defined above to switch between debug and
+# optimized mode to select the set of libraries to link with. Included
+# in the list of libraries is the name of the object file which we
+# will produce from the single C++ file. Note that by default we use
+# the extension .g.o for object files compiled in debug mode and .o for
+# object files in optimized mode (or whatever local default on your
+# system is instead of .o)
+ifeq ($(debug-mode),on)
+ libraries = $(target).g.$(OBJEXT) $(libs.g)
+else
+ libraries = $(target).$(OBJEXT) $(libs.o)
+endif
+
+
+# Now comes the first production rule: how to link the single object
+# file produced from the single C++ file into the executable. Since
+# this is the first rule in the Makefile, it is the one `make' selects
+# if you call it without arguments.
+$(target)$(EXEEXT) : $(libraries)
+ @echo ============================ Linking $@
+ @$(CXX) -o $@ $^ $(LIBS) $(LDFLAGS)
+
+
+# To make running the application somewhat independent of the actual
+# program name, we usually declare a rule `run' which simply runs the
+# program. You can then run it by typing `make run'. This is also
+# useful if you want to call the executable with arguments which do
+# not change frequently. You may then want to add them to the
+# following rule:
+run: $(target)$(EXEEXT)
+ @echo ============================ Running $<
+ @./$(target)$(EXEEXT)
+
+
+# As a last rule to the `make' program, we define what to do when
+# cleaning up a directory. This usually involves deleting object files
+# and other automatically created files such as the executable itself,
+# backup files, and data files. Since the latter are not usually quite
+# diverse, you needed to declare them at the top of this file.
+clean:
+ -rm -f *.$(OBJEXT) *~ Makefile.dep $(target)$(EXEEXT) $(clean-up-files)
+
+
+# Since we have not yet stated how to make an object file from a C++
+# file, we should do so now. Since the many flags passed to the
+# compiler are usually not of much interest, we suppress the actual
+# command line using the `at' sign in the first column of the rules
+# and write the string indicating what we do instead.
+./%.g.$(OBJEXT) :
+ @echo "==============debug========= $(<F) -> $@"
+ @$(CXX) -w $(CXXFLAGS.g) -c $< -o $@
+./%.$(OBJEXT) :
+ @echo "==============optimized===== $(<F) -> $@"
+ @$(CXX) $(CXXFLAGS.o) -c $< -o $@
+
+
+# The following statement tells make that the rules `run' and `clean'
+# are not expected to produce files of the same name as Makefile rules
+# usually do.
+.PHONY: run clean
+
+
+# Finally there is a rule which you normally need not care much about:
+# since the executable depends on some include files from the library,
+# besides the C++ application file of course, it is necessary to
+# re-generate the executable when one of the files it depends on has
+# changed. The following rule creates a dependency file
+# `Makefile.dep', which `make' uses to determine when to regenerate
+# the executable. This file is automagically remade whenever needed,
+# i.e. whenever one of the cc-/h-files changed. Make detects whether
+# to remake this file upon inclusion at the bottom of this file.
+#
+# If the creation of Makefile.dep fails, blow it away and fail
+Makefile.dep: $(target).cc Makefile \
+ $(shell echo $D/include/deal.II/*/*.h)
+ @echo ============================ Remaking $@
+ @$D/common/scripts/make_dependencies $(INCLUDE) -B. $(target).cc \
+ > $@ \
+ || (rm -f $@ ; false)
+ @if test -s $@ ; then : else rm $@ ; fi
+
+# To make the dependencies known to `make', we finally have to include
+# them:
+include Makefile.dep
+
+
--- /dev/null
+step-25 step-37 step-40
--- /dev/null
+
+<i>
+This program was contributed by Katharina Kormann and Martin
+Kronbichler.
+
+The algorithm for the matrix-vector product is built upon the preprint
+"A generic interface for parallel cell-based finite element
+operator application" by Martin Kronbichler and Katharina
+Kormann, Uppsala University, October 2011, and the paper
+"Parallel finite element operator application: Graph partitioning
+and coloring" by Katharina Kormann and Martin Kronbichler in:
+Proceedings of the 7th IEEE International Conference on e-Science,
+2011. </i>
+
+<a name="Intro"></a>
+<h1>Introduction</h1>
+
+This program demonstrates how to use the cell-based implementation of
+finite element operators with the MatrixFree class,
+first introduced in step-37, to solve nonlinear partial
+differential equations. Moreover, we demonstrate how the
+MatrixFree class handles constraints and how it can be
+parallelized over distributed nodes. Finally, we will use an explicit
+time-stepping method to solve the problem and introduce Gauss-Lobatto
+finite elements that are very convenient in this case since they have
+a diagonally, and thus trivially invertable, mass matrix. Moreover,
+this type of elements clusters the nodes towards the element
+boundaries which is why it has good properties for high-order
+discretization methods.
+
+<h3> Problem statement and discretization </h3>
+
+As an example, we choose to solve the sine-Gordon soliton equation
+\f{eqnarray*}
+u_{tt} &=& \Delta u -\sin(u) \quad\mbox{for}\quad (x,t) \in
+\Omega \times (t_0,t_f],\\
+{\mathbf n} \cdot \nabla u &=& 0
+\quad\mbox{for}\quad (x,t) \in \partial\Omega \times (t_0,t_f],\\
+u(x,t_0) &=& u_0(x).
+\f}
+
+that was already introduced in step-25. As a simple explicit time
+integration method, we choose leap frog scheme using the second-order
+formulation of the equation. Then, the scheme reads in weak form
+
+\f{eqnarray*}
+(v,u^{n+1}) = (v,2 u^n-u^{n-1} -
+(\Delta t)^2 \sin(u^n)) - (\nabla v, (\Delta t)^2 \nabla u^n),
+\f}
+where <i> v</i> denotes a test function and the index <i>n</i> stands for
+the time step number.
+
+For the spatial discretization, we choose FE_Q elements
+with basis functions defined to interpolate the support points of the
+Gauss-Lobatto quadrature rule. Moreover, when we compute the integrals
+over the basis functions to form the mass matrix and the operator on
+the right hand side of the equation above, we use the
+Gauss-Lobatto quadrature rule with the same support points as the
+node points of the finite element to evaluate the integrals. Since the
+finite element is Lagrangian, this will yield a diagonal mass matrix
+on the left hand side of the equation, making the solution of the
+linear system in each time step trivial.
+
+Using this quadrature rule, for a <i>p</i>th order finite element, we
+use a <i>(2p-1)</i>th order accurate formula to evaluate the
+integrals. Since the product of two <i>p</i>th order basis functions
+when computing a mass matrix gives a function with polynomial degree
+<i>2p</i> in each direction, the integrals are not exactly
+evaluated. However, considering the fact that the interpolation order
+of finite elements of degree <i>p</i> is <i>p+1</i>, the overall
+convergence properties are not disturbed by the quadrature error, in
+particular not when we use high orders.
+
+Apart from the fact, that we avoid solving linear systems with this
+type of elements when using explicit time-stepping, they come with two
+other advantages. When we are using the sum-factorization approach to
+evaluate the finite element operator (cf. step-37), we have to
+evaluate the function at the quadrature points. In the case of
+Gauss-Lobatto elements, where quadrature points and node points of the
+finite element coincide, this operation is trivial since the value
+of the function at the quadrature points is given by its one-dimensional
+coefficients. In this way, the complexity of a finite element operator
+evaluation is further reduced compared to equidistant elements.
+
+The third advantage is the fact that these elements are better conditioned as
+equidistant Lagrange polynomials for increasing order so that we can use
+higher order elements for an accurate solution of the equation. Lagrange
+elements FE_Q with equidistant points should not be used for polynomial
+degrees four and higher.
+
+To sum up the discussion, by using the right finite element and
+quadrature rule combination, we end up with a scheme where we in each
+time step need to compute the right hand side vector corresponding
+to the formulation above and then multiply it by the inverse of the
+diagonal mass matrix. In practice, of course, we extract the diagonal
+elements and invert them only once at the beginning of the program.
+
+<h3>Implementation of constraints</h3>
+
+The usual way to handle constraints in <code>deal.II</code> is to use
+the ConstraintMatrix class that builds a sparse matrix storing
+information about which degrees of freedom (DoF) are constrained and
+how they are constrained. This format uses an unnecessarily large
+amount of memory since there are not so many different types of
+constraints: for example, in the case of hanging nodes when using
+linear finite element on every cell, constraints most have the form
+$x_k = \frac 12 x_i + \frac 12 x_j$ where the coefficients $\frac 12$
+are always the same and only $i,j,k$ are different. While storing this
+redundant information is not a problem in general because it is only
+needed once during matrix and right hand side assembly, it becomes a
+problem when we want to use the matrix-free approach since there this
+information has to be accessed every time we apply the operator. Thus,
+instead of a ConstraintMatrix, we use a variable that we call
+<code>constraint_pool</code> that collects the weights of the
+different constraints. Then, we only have to store an identifier of
+each constraint in the mesh instead of all the weights. Moreover, we
+do not want to apply the constraints in a pre- and postprocessing step
+but want to take care of constraints as we evaluate the finite element
+operator. Therefore, we embed the constraint information into the
+variable <code>indices_local_to_global</code> that is used to extract
+the cell information from the global vector. If a DoF is constrained,
+the <code>indices_local_to_global</code> variable contains the global
+indices of the DoFs that it is constrained to. Then, we have another
+variable <code>constraint_indicator</code> at hand that holds, for
+each cell, the local indices of DoFs that are constrained as well as
+the identifer of the type of constraint. Actually, you will not see
+these data structures in the example program since the class
+<code>FEEvaluationGL</code> takes care of the constraints without user
+interaction.
+
+
+<h3> Parallelization </h3>
+
+The MatrixFree class comes with the option to be
+parallelized on three levels: MPI parallelization on clusters of
+distributed nodes, thread parallelization scheduled by the Threading
+Building Blocks library, and finally with a vectorization by clustering of two
+(or more) cells into a SIMD data type for the operator application. We
+have already seen how shared-memory parallelism can be exploited in
+step-37. Here, we demonstrate MPI parallelization.
+
+To facilitate parallelism with distributed memory (MPI), we use a special
+vector type parallel::distributed::Vector that holds the
+processor-local part of the solution as well as information on and data
+fields for the ghost DoFs, i.e. DoFs that are owned by a remote
+processor but needed on cells that are treated by the present
+processor. Moreover, it holds the MPI-send information for DoFs that
+are owned locally but needed by other processors. This is similar to
+the PETScWrappers::MPI::Vector and TrilinosWrappers::MPI::Vector data
+types we have used in step-40 and step-32 before, but since we do not
+need any other parallel functionality of these libraries, we use the
+parallel::distributed::Vector class of deal.II instead of linking in
+another large library.
+
+Note that this program is designed to be run with a distributed triangulation
+(parallel::distributed::Triangulation), which requires deal.II to be
+configured with <a href="http://www.p4est.org/">p4est</a> as described
+in the <a href="../../readme.html">deal.II ReadMe</a> file. However, a
+non-distributed triangulation is also supported, in which case the
+computation will be run in serial.
+
+<h3> The test case </h3>
+
+In our example, we choose the initial value to be \f{eqnarray*} u(x,t) =
+\prod_{i=1}^{d} -4 \arctan \left(
+\frac{m}{\sqrt{1-m^2}}\frac{\sin\left(\sqrt{1-m^2} t +c_2\right)}{\cosh(mx_i+c_1)}\right)
+\f} and solve the equation over the time interval [-10,10]. The
+constants are chosen to be $c_1=c_1=0$ and <i> m=0.5</i>. As mentioned
+in step-25, in one dimension <i>u</i> as a function of <i>t</i> is the exact
+solution of the sine-Gordon equation. For higher dimension, this is however
+not the case.
--- /dev/null
+time dependent
--- /dev/null
+<h1>Results</h1>
+
+<h3>Comparison with a sparse matrix</h3>
+
+In order to demonstrate the gain in using the CellFEOperator class instead of
+the standard <code>deal.II</code> assembly routines for evaluating the
+information from old time steps, we study a simple serial run of the code on a
+nonadaptive mesh. Since much time is spent on evaluating the sine function, we
+do not only show the numbers of the full sine-Gordon equation but also for the
+wave equation (the sine-term skipped from the sine-Gordon equation). We use
+both second and fourth order elements. The results are summarized in the
+following table.
+
+<table align="center" border="1">
+ <tr>
+ <th> </th>
+ <th colspan="3">wave equation</th>
+ <th colspan="2">sine-Gordon</th>
+ </tr>
+ <tr>
+ <th> </th>
+ <th>MF</th>
+ <th>SpMV</th>
+ <th>dealii</th>
+ <th>MF</th>
+ <th>dealii</th>
+ </tr>
+ <tr>
+ <td>2D, $\mathcal{Q}_2$</td>
+ <td align="right"> 0.0106</td>
+ <td align="right"> 0.00971</td>
+ <td align="right"> 0.109</td>
+ <td align="right"> 0.0243</td>
+ <td align="right"> 0.124</td>
+ </tr>
+ <tr>
+ <td>2D, $\mathcal{Q}_4$</td>
+ <td align="right"> 0.0328</td>
+ <td align="right"> 0.0706</td>
+ <td align="right"> 0.528</td>
+ <td align="right"> 0.0714</td>
+ <td align="right"> 0.502</td>
+ </tr>
+ <tr>
+ <td>3D, $\mathcal{Q}_2$</td>
+ <td align="right"> 0.0151</td>
+ <td align="right"> 0.0320</td>
+ <td align="right"> 0.331</td>
+ <td align="right"> 0.0376</td>
+ <td align="right"> 0.364</td>
+ </tr>
+ <tr>
+ <td>3D, $\mathcal{Q}_4$</td>
+ <td align="right"> 0.0918</td>
+ <td align="right"> 0.844</td>
+ <td align="right"> 6.83</td>
+ <td align="right"> 0.194</td>
+ <td align="right"> 6.95</td>
+ </tr>
+</table>
+
+It is apparent that the CellFEOperator outperforms the standard assembly
+routines in deal.II by far. In 3D and for fourth order elements, one operator
+application is also almost ten times as fast as a sparse matrix-vector
+product.
+
+<h3>Parallel run in 3D</h3>
+
+To demonstrate how the example scales for a parallel run and to demsonstrate
+that hanging node constraints can be handled in an efficient way, we run the
+example in 3D with $\mathcal{Q}_4$ elements. First, we run it on a notebook
+with 2 cores (Sandy Bridge CPU) at 2.7 GHz.
+@code
+\$ make debug-mode=off run
+ Number of global active cells: 17592
+ Number of degrees of freedom: 1193881
+ Time step size: 0.0117233, finest cell: 0.46875
+
+ Time: -10, solution norm: 29.558
+ Time: -7.66, solution norm: 129.13
+ Time: -5.31, solution norm: 67.753
+ Time: -2.97, solution norm: 79.245
+ Time: -0.621, solution norm: 123.52
+ Time: 1.72, solution norm: 43.525
+ Time: 4.07, solution norm: 93.285
+ Time: 6.41, solution norm: 97.722
+ Time: 8.76, solution norm: 36.734
+ Time: 10, solution norm: 94.115
+
+ Performed 1706 time steps.
+ Average wallclock time per time step: 0.038261s
+ Spent 11.977s on output and 65.273s on computations.
+@endcode
+
+It takes 0.04 seconds for one time step on a notebook with more than a million
+degrees of freedom (note that we would need many processors to reach such
+numbers when solving linear systems). If we run the same 3D code on a
+cluster with 2 nodes and each node runs 8 threads, we get the following times:
+
+@code
+\$ mpirun --bynode -n 2 ./\step-48
+...
+ Performed 1706 time steps.
+ Average wallclock time per time step: 0.0123188s
+ Spent 6.74378s on output and 21.0158s on computations.
+@endcode
+
+We observe a considerable speedup over the notebook (16 cores versus 2 cores;
+nonethess, one notebook core is considerably faster than one core of the
+cluster because of a newer processor architecture). If we run the same program
+on 4 nodes with 8 threads on each node, we get:
+@code
+\$ mpirun --bynode -n 4 ./\step-48
+...
+ Performed 1706 time steps.
+ Average wallclock time per time step: 0.00689865s
+ Spent 3.54145s on output and 11.7691s on computations.
+@endcode
+
+By comparing the times for two nodes and four nodes, we observe the nice
+scaling behavior of the implementation. Of course, the code can also be run in
+MPI-mode only by disabling the multithreading flag in the code. If we use the
+same 32 cores as for the hybrid parallelization above, we observe the
+following run-time:
+
+@code
+\$ mpirun -n 32 ./\step-48
+...
+ Performed 1706 time steps.
+ Average wallclock time per time step: 0.0189041s
+ Spent 0.968967s on output and 32.2504s on computations.
+@endcode
+
+We observe slower speed for computations, but faster output (which makes
+sense, as output is only parallelized by MPI and not threads), whereas the
+computations are faster if we use hybrid parallelism in the given case.
+
+<h3>Possibilities for extensions</h3>
+
+There are several things in this program that could be improved to make it
+even more efficient (besides improved boundary conditions and physical
+stuff as discussed in step-25):
+
+<ul> <li> <b>Faster evaluation of sine terms:</b> As becomes obvious
+ from the comparison of the plain wave equation and the sine-Gordon
+ equation above, the evaluation of the sine terms dominates the total
+ time for the finite element operator application. There are a few
+ reasons for this: Firstly, the deal.II sine computation of a
+ VectorizedArray field is not vectorized (as opposed to the rest of
+ the operator application). This could be cured by handing the sine
+ computation to a library with vectorized sine computations like
+ Intel's math kernel library (MKL). By using the function
+ <code>vdSin</code> in MKL, the program uses half the computing time
+ in 2D and 40 percent less time in 3D. On the other hand, the sine
+ computation is structurally much more complicated than the simple
+ arithmetic operations like additions and multiplications in the rest
+ of the local operation.
+
+ <li> <b>Higher order time stepping:</b> While the implementation allows for
+ arbitrary order in the spatial part (by adjusting the degree of the finite
+ element), the time stepping scheme is a standard second-order leap-frog
+ scheme. Since solutions in wave propagation problems are usually very
+ smooth, the error is likely dominated by the time stepping part. Of course,
+ this could be cured by using smaller time steps (at a fixed spatial
+ resolution), but it would be more efficient to use higher order time
+ stepping as well. While it would be straight-forward to do so for a
+ first-order system (use some Runge–Kutta scheme of higher order,
+ probably combined with adaptive time step selection like the <a
+ href="http://en.wikipedia.org/wiki/Dormand%E2%80%93Prince_method">Dormand–Prince
+ method</a>), it is more challenging for the second-order formulation. At
+ least in the finite difference community, people usually use the PDE to find
+ spatial correction terms that improve the temporal error.
+
+</ul>
--- /dev/null
+Parallelization via MPI. The wave equation, in linear and nonlinear variants. Mass lumping. Cell-based finite element operator.
--- /dev/null
+/* $Id$ */
+/* Author: Katharina Kormann, Martin Kronbichler, Uppsala University, 2011 */
+
+/* $Id$ */
+/* */
+/* Copyright (C) 2011, 2012 by the deal.II authors */
+/* */
+/* This file is subject to QPL and may not be distributed */
+/* without copyright and license information. Please refer */
+/* to the file deal.II/doc/license.html for the text and */
+/* further information on this license. */
+
+
+ // The necessary files from the deal.II
+ // library.
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/conditional_ostream.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
+#include <deal.II/lac/trilinos_vector.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/distributed/tria.h>
+
+ // This includes the data structures for the
+ // efficient implementation of matrix-free
+ // methods.
+#include <deal.II/lac/parallel_vector.h>
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+
+
+namespace Step48
+{
+ using namespace dealii;
+
+ // We start by defining two global
+ // variables to collect all parameters
+ // subject to changes at one place:
+ // One for the dimension and one for
+ // the finite element degree. The
+ // dimension is used in the main
+ // function as a template argument for
+ // the actual classes (like in all
+ // other deal.II programs), whereas
+ // the degree of the finite element is
+ // more crucial, as it is passed as a
+ // template argument to the
+ // implementation of the Sine-Gordon
+ // operator. Therefore, it needs to be
+ // a compile-time constant.
+ const unsigned int dimension = 2;
+ const unsigned int fe_degree = 4;
+
+
+ // @sect3{SineGordonOperation}
+
+ // The <code>SineGordonOperation</code> class
+ // implements the cell-based operation that is
+ // needed in each time step. This nonlinear
+ // operation can be implemented
+ // straight-forwardly based on the
+ // <code>MatrixFree</code> class, in the
+ // same way as a linear operation would be
+ // treated by this implementation of the
+ // finite element operator application. We
+ // apply two template arguments to the class,
+ // one for the dimension and one for the
+ // degree of the finite element. This is a
+ // difference to other functions in deal.II
+ // where only the dimension is a template
+ // argument. This is necessary to provide the
+ // inner loops in @p FEEvaluation with
+ // information about loop lengths etc., which
+ // is essential for efficiency. On the other
+ // hand, it makes it more challenging to
+ // implement the degree as a run-time
+ // parameter.
+ template <int dim, int fe_degree>
+ class SineGordonOperation
+ {
+ public:
+ typedef VectorizedArray<double> vector_t;
+
+ SineGordonOperation(const MatrixFree<dim,double> &data_in,
+ const double time_step);
+
+ void apply (parallel::distributed::Vector<double> &dst,
+ const std::vector<parallel::distributed::Vector<double>*> &src) const;
+
+ private:
+ const MatrixFree<dim,double> &data;
+ const vector_t delta_t_sqr;
+ parallel::distributed::Vector<double> inv_mass_matrix;
+
+ void local_apply (const MatrixFree<dim,double> &data,
+ parallel::distributed::Vector<double> &dst,
+ const std::vector<parallel::distributed::Vector<double>*>&src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+ };
+
+
+
+ // @sect4{SineGordonOperation::SineGordonOperation}
+
+ // This is the constructor of the
+ // SineGordonOperation class. It receives a
+ // reference to the MatrixFree holding the
+ // problem information and the time step size
+ // as input parameters. The initialization
+ // routine sets up the mass matrix. Since we
+ // use Gauss-Lobatto elements, the mass matrix
+ // is a diagonal matrix and can be stored as a
+ // vector. The computation of the mass matrix
+ // diagonal is simple to achieve with the data
+ // structures provided by FEEvaluation: Just
+ // loop over all (macro-) cells and integrate
+ // over the function that is constant one on
+ // all quadrature points by using the
+ // <code>integrate</code> function with @p
+ // true argument at the slot for
+ // values. Finally, we invert the diagonal
+ // entries since we have to multiply by the
+ // inverse mass matrix in each time step.
+ template <int dim, int fe_degree>
+ SineGordonOperation<dim,fe_degree>::
+ SineGordonOperation(const MatrixFree<dim,double> &data_in,
+ const double time_step)
+ :
+ data(data_in),
+ delta_t_sqr(make_vectorized_array(time_step*time_step))
+ {
+ vector_t one = make_vectorized_array (1.);
+
+ data.initialize_dof_vector (inv_mass_matrix);
+
+ FEEvaluationGL<dim,fe_degree> fe_eval(data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+
+ for (unsigned int cell=0; cell<data.get_size_info().n_macro_cells; ++cell)
+ {
+ fe_eval.reinit(cell);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ fe_eval.submit_value(one,q);
+ fe_eval.integrate (true,false);
+ fe_eval.distribute_local_to_global (inv_mass_matrix);
+ }
+
+ inv_mass_matrix.compress();
+ for (unsigned int k=0; k<inv_mass_matrix.local_size(); ++k)
+ if (inv_mass_matrix.local_element(k)>1e-15)
+ inv_mass_matrix.local_element(k) = 1./inv_mass_matrix.local_element(k);
+ else
+ inv_mass_matrix.local_element(k) = 0;
+ }
+
+
+
+ // @sect4{SineGordonOperation::local_apply}
+
+ // This operator implements the core operation
+ // of the program, the integration over a
+ // range of cells for the nonlinear operator
+ // of the Sine-Gordon problem. The
+ // implementation is based on the
+ // FEEvaluationGL class since we are using
+ // the cell-based implementation for
+ // Gauss-Lobatto elements.
+
+ // The nonlinear function that we have to
+ // evaluate for the time stepping routine
+ // includes the value of the function at
+ // the present time @p current as well as
+ // the value at the previous time step @p
+ // old. Both values are passed to the
+ // operator in the collection of source
+ // vectors @p src, which is simply an STL
+ // vector of pointers to the actual
+ // solution vectors. This construct of
+ // collecting several source vectors into
+ // one is necessary as the cell loop in @p
+ // MatrixFree takes exactly one source
+ // and one destination vector, even if we
+ // happen to use many vectors like the two
+ // in this case. Note that the cell loop
+ // accepts any valid class for input and
+ // output, which does not only include
+ // vectors but general data types. However,
+ // only in case it encounters a
+ // parallel::distributed::Vector<Number> or
+ // an STL vector collecting these vectors,
+ // it calls functions that exchange data at
+ // the beginning and the end of the
+ // loop. In the loop over the cells, we
+ // first have to read in the values in the
+ // vectors related to the local
+ // values. Then, we evaluate the value and
+ // the gradient of the current solution
+ // vector and the values of the old vector
+ // at the quadrature points. Then, we
+ // combine the terms in the scheme in the
+ // loop over the quadrature
+ // points. Finally, we integrate the result
+ // against the test function and accumulate
+ // the result to the global solution vector
+ // @p dst.
+ template <int dim, int fe_degree>
+ void SineGordonOperation<dim, fe_degree>::
+ local_apply (const MatrixFree<dim> &data,
+ parallel::distributed::Vector<double> &dst,
+ const std::vector<parallel::distributed::Vector<double>*>&src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ AssertDimension (src.size(), 2);
+ FEEvaluationGL<dim,fe_degree> current (data), old (data);
+ for (unsigned int cell=cell_range.first; cell<cell_range.second; ++cell)
+ {
+ current.reinit (cell);
+ old.reinit (cell);
+
+ current.read_dof_values (*src[0]);
+ old.read_dof_values (*src[1]);
+
+ current.evaluate (true, true, false);
+ old.evaluate (true, false, false);
+
+ for (unsigned int q=0; q<current.n_q_points; ++q)
+ {
+ const vector_t current_value = current.get_value(q);
+ const vector_t old_value = old.get_value(q);
+
+ current.submit_value (2.*current_value - old_value -
+ delta_t_sqr * std::sin(current_value),q);
+ current.submit_gradient (- delta_t_sqr *
+ current.get_gradient(q), q);
+ }
+
+ current.integrate (true,true);
+ current.distribute_local_to_global (dst);
+ }
+ }
+
+
+
+ //@sect4{SineGordonOperation::apply}
+
+ // This function performs the time stepping
+ // routine based on the cell-local
+ // strategy. First the destination vector is
+ // set to zero, then the cell-loop is called,
+ // and finally the solution is multiplied by
+ // the inverse mass matrix. The structure of
+ // the cell loop is implemented in the cell
+ // finite element operator class. On each cell
+ // it applies the routine defined as the
+ // <code>operator ()</code> method of the
+ // class <code>SineGordonOperation</code>,
+ // i.e., <code>*this</code>. One could also
+ // provide a function with the same signature
+ // that is not part of a class.
+ template <int dim, int fe_degree>
+ void SineGordonOperation<dim, fe_degree>::
+ apply (parallel::distributed::Vector<double> &dst,
+ const std::vector<parallel::distributed::Vector<double>*> &src) const
+ {
+ dst = 0;
+ data.cell_loop (&SineGordonOperation<dim,fe_degree>::local_apply,
+ this, dst, src);
+ dst.scale(inv_mass_matrix);
+ }
+
+
+ //@sect3{Equation data}
+
+ // We define a time-dependent function that is
+ // used as initial value. Different solutions
+ // can be obtained by varying the starting
+ // time. This function has already been
+ // explained in step-25.
+ template <int dim>
+ class ExactSolution : public Function<dim>
+ {
+ public:
+ ExactSolution (const unsigned int n_components = 1,
+ const double time = 0.) : Function<dim>(n_components, time) {}
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ };
+
+ template <int dim>
+ double ExactSolution<dim>::value (const Point<dim> &p,
+ const unsigned int /* component */) const
+ {
+ double t = this->get_time ();
+
+ const double m = 0.5;
+ const double c1 = 0.;
+ const double c2 = 0.;
+ const double factor = (m / std::sqrt(1.-m*m) *
+ std::sin(std::sqrt(1.-m*m)*t+c2));
+ double result = 1.;
+ for (unsigned int d=0; d<dim; ++d)
+ result *= -4. * std::atan (factor / std::cosh(m*p[d]+c1));
+ return result;
+ }
+
+
+
+ // @sect3{SineGordonProblem class}
+
+ // This is the main class that builds on the
+ // class in step-25. However, we replaced
+ // the SparseMatrix<double> class by the
+ // MatrixFree class to store
+ // the geometry data. Also, we use a
+ // distributed triangulation in this example.
+ template <int dim>
+ class SineGordonProblem
+ {
+ public:
+ SineGordonProblem ();
+ void run ();
+
+ private:
+ ConditionalOStream pcout;
+
+ void make_grid_and_dofs ();
+ void oldstyle_operation ();
+ void assemble_system ();
+ void output_results (const unsigned int timestep_number) const;
+
+#ifdef DEAL_II_USE_P4EST
+ parallel::distributed::Triangulation<dim> triangulation;
+#else
+ Triangulation<dim> triangulation;
+#endif
+ FE_Q<dim> fe;
+ DoFHandler<dim> dof_handler;
+ ConstraintMatrix constraints;
+ IndexSet locally_relevant_dofs;
+
+ MatrixFree<dim,double> matrix_free_data;
+
+ parallel::distributed::Vector<double> solution, old_solution, old_old_solution;
+
+ const unsigned int n_global_refinements;
+ double time, time_step;
+ const double final_time;
+ const double cfl_number;
+ const unsigned int output_timestep_skip;
+ };
+
+
+ //@sect4{SineGordonProblem::SineGordonProblem}
+
+ // This is the constructor of the
+ // SineGordonProblem class. The time interval
+ // and time step size are defined
+ // here. Moreover, we use the degree of the
+ // finite element that we defined at the top
+ // of the program to initialize a FE_Q finite
+ // element based on Gauss-Lobatto support
+ // points. These points are convenient because
+ // in conjunction with a QGaussLobatto
+ // quadrature rule of the same order they give
+ // a diagonal mass matrix without compromising
+ // accuracy too much (note that the
+ // integration is inexact, though), see also
+ // the discussion in the introduction.
+ template <int dim>
+ SineGordonProblem<dim>::SineGordonProblem ()
+ :
+ pcout (std::cout,
+ Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)==0),
+#ifdef DEAL_II_USE_P4EST
+ triangulation (MPI_COMM_WORLD),
+#endif
+ fe (QGaussLobatto<1>(fe_degree+1)),
+ dof_handler (triangulation),
+ n_global_refinements (11-2*dim),
+ time (-10),
+ final_time (10),
+ cfl_number (.1/fe_degree),
+ output_timestep_skip (200)
+ {}
+
+ //@sect4{SineGordonProblem::make_grid_and_dofs}
+
+ // As in step-25 this functions sets up a cube
+ // grid in <code>dim</code> dimensions of
+ // extent $[-15,15]$. We refine the mesh more
+ // in the center of the domain since the
+ // solution is concentrated there. We first
+ // refine all cells whose center is within a
+ // radius of 11, and then refine once more for
+ // a radius 6. This is simple ad-hoc
+ // refinement could be done better by adapting
+ // the mesh to the solution using error
+ // estimators during the time stepping as done
+ // in other example programs, and using
+ // parallel::distributed::SolutionTransfer to
+ // transfer the solution to the new mesh.
+ template <int dim>
+ void SineGordonProblem<dim>::make_grid_and_dofs ()
+ {
+ GridGenerator::hyper_cube (triangulation, -15, 15);
+ triangulation.refine_global (n_global_refinements);
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = triangulation.begin_active(),
+ end_cell = triangulation.end();
+ for ( ; cell != end_cell; ++cell)
+ if (cell->is_locally_owned())
+ if (cell->center().norm() < 11)
+ cell->set_refine_flag();
+ triangulation.execute_coarsening_and_refinement();
+
+ cell = triangulation.begin_active();
+ end_cell = triangulation.end();
+ for ( ; cell != end_cell; ++cell)
+ if (cell->is_locally_owned())
+ if (cell->center().norm() < 6)
+ cell->set_refine_flag();
+ triangulation.execute_coarsening_and_refinement();
+ }
+
+ pcout << " Number of global active cells: "
+#ifdef DEAL_II_USE_P4EST
+ << triangulation.n_global_active_cells()
+#else
+ << triangulation.n_active_cells()
+#endif
+ << std::endl;
+
+ dof_handler.distribute_dofs (fe);
+
+ pcout << " Number of degrees of freedom: "
+ << dof_handler.n_dofs()
+ << std::endl;
+
+
+ // We generate hanging node constraints for
+ // ensuring continuity of the solution. As in
+ // step-40, we need to equip the constraint
+ // matrix with the IndexSet of locally
+ // relevant degrees of freedom to avoid it to
+ // consume too much memory for big
+ // problems. Next, the <code> MatrixFree
+ // </code> for the problem is set up. Note
+ // that we specify the MPI communicator which
+ // we are going to use, and that we also want
+ // to use shared-memory parallelization (hence
+ // one would use multithreading for intra-node
+ // parallelism and not MPI; note that we here
+ // choose the standard option &mdash if we
+ // wanted to disable shared memory
+ // parallelization, we would choose @p
+ // none). Finally, three solution vectors are
+ // initialized. MatrixFree stores the
+ // layout that is to be used by distributed
+ // vectors, so we just ask it to initialize
+ // the vectors.
+ DoFTools::extract_locally_relevant_dofs (dof_handler,
+ locally_relevant_dofs);
+ constraints.clear();
+ constraints.reinit (locally_relevant_dofs);
+ DoFTools::make_hanging_node_constraints (dof_handler, constraints);
+ constraints.close();
+
+ QGaussLobatto<1> quadrature (fe_degree+1);
+ typename MatrixFree<dim>::AdditionalData additional_data;
+ additional_data.mpi_communicator = MPI_COMM_WORLD;
+ additional_data.tasks_parallel_scheme =
+ MatrixFree<dim>::AdditionalData::partition_partition;
+
+ matrix_free_data.reinit (dof_handler, constraints,
+ quadrature, additional_data);
+
+ matrix_free_data.initialize_dof_vector (solution);
+ old_solution.reinit (solution);
+ old_old_solution.reinit (solution);
+ }
+
+
+
+ //@sect4{SineGordonProblem::output_results}
+
+ // This function prints the norm of the
+ // solution and writes the solution vector to
+ // a file. The norm is standard (except for
+ // the fact that we need to be sure to only
+ // count norms on locally owned cells), and
+ // the second is similar to what we did in
+ // step-40. However, we first need to generate
+ // an appropriate vector for output: The ones
+ // we used during time stepping contained
+ // information about ghosts dofs that one
+ // needs write access to during the loops over
+ // cell. However, that is not the same as
+ // needed when outputting. So we first
+ // initialize a vector with locally relevant
+ // degrees of freedom by copying the solution
+ // (note how we use the function @p copy_from
+ // to transfer data between vectors with the
+ // same local range, but different layouts of
+ // ghosts). Then, we import the values on the
+ // ghost DoFs and then distribute the
+ // constraints (as constraints are zero in the
+ // vectors during loop over all cells).
+ template <int dim>
+ void
+ SineGordonProblem<dim>::output_results (const unsigned int timestep_number) const
+ {
+ parallel::distributed::Vector<double> locally_relevant_solution;
+ locally_relevant_solution.reinit (dof_handler.locally_owned_dofs(),
+ locally_relevant_dofs,
+ MPI_COMM_WORLD);
+ locally_relevant_solution.copy_from (solution);
+ locally_relevant_solution.update_ghost_values ();
+ constraints.distribute (locally_relevant_solution);
+
+ Vector<float> norm_per_cell (triangulation.n_active_cells());
+ VectorTools::integrate_difference (dof_handler,
+ locally_relevant_solution,
+ ZeroFunction<dim>(),
+ norm_per_cell,
+ QGauss<dim>(fe_degree+1),
+ VectorTools::L2_norm);
+ const double solution_norm =
+ std::sqrt(Utilities::MPI::sum (norm_per_cell.norm_sqr(), MPI_COMM_WORLD));
+
+ pcout << " Time:"
+ << std::setw(8) << std::setprecision(3) << time
+ << ", solution norm: "
+ << std::setprecision(5) << std::setw(7) << solution_norm
+ << std::endl;
+
+ DataOut<dim> data_out;
+
+ data_out.attach_dof_handler (dof_handler);
+ data_out.add_data_vector (locally_relevant_solution, "solution");
+ data_out.build_patches ();
+
+ const std::string filename =
+ "solution-" + Utilities::int_to_string (timestep_number, 3) +
+ "." + Utilities::int_to_string (Utilities::MPI::
+ this_mpi_process(MPI_COMM_WORLD),4);
+
+ std::ofstream output ((filename + ".vtu").c_str());
+ data_out.write_vtu (output);
+
+ if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
+ {
+ std::vector<std::string> filenames;
+ for (unsigned int i=0;
+ i<Utilities::MPI::n_mpi_processes (MPI_COMM_WORLD); ++i)
+ filenames.push_back ("solution-" +
+ Utilities::int_to_string (timestep_number, 3) +
+ "." +
+ Utilities::int_to_string (i, 4) +
+ ".vtu");
+
+ std::ofstream master_output ((filename + ".pvtu").c_str());
+ data_out.write_pvtu_record (master_output, filenames);
+ }
+ }
+
+
+ // @sect4{SineGordonProblem::run}
+
+ // This function is called by the main
+ // function and calls the subroutines
+ // of the class.
+ //
+ // The first step is to set up the grid and
+ // the cell operator. Then, the time step is
+ // computed from the CFL number given in the
+ // constructor and the finest mesh size. The
+ // finest mesh size is computed as the
+ // diameter of the last cell in the
+ // triangulation, which is the last cell on
+ // the finest level of the mesh. This is only
+ // possible for Cartesian meshes, otherwise,
+ // one needs to loop over all cells). Note
+ // that we need to query all the processors
+ // for their finest cell since the not all
+ // processors might hold a region where the
+ // mesh is at the finest level. Then, we
+ // readjust the time step a little to hit the
+ // final time exactly if necessary.
+ template <int dim>
+ void
+ SineGordonProblem<dim>::run ()
+ {
+ make_grid_and_dofs();
+
+ const double local_min_cell_diameter =
+ triangulation.last()->diameter()/std::sqrt(dim);
+ const double global_min_cell_diameter
+ = -Utilities::MPI::max(-local_min_cell_diameter, MPI_COMM_WORLD);
+ time_step = cfl_number * global_min_cell_diameter;
+ time_step = (final_time-time)/(int((final_time-time)/time_step));
+ pcout << " Time step size: " << time_step << ", finest cell: "
+ << global_min_cell_diameter << std::endl << std::endl;
+
+ // Next the initial value is set. Since we
+ // have a two-step time stepping method, we
+ // also need a value of the solution at
+ // time-time_step. For accurate results, one
+ // would need to compute this from the time
+ // derivative of the solution at initial time,
+ // but here we ignore this difficulty and just
+ // set it to the initial value function at
+ // that artificial time.
+
+ // We create an output of the initial
+ // value. Then we also need to collect
+ // the two starting solutions in an STL
+ // vector of pointers field and to set up
+ // an instance of the <code>
+ // SineGordonOperation class </code>
+ // based on the finite element degree
+ // specified at the top of this file.
+ VectorTools::interpolate (dof_handler,
+ ExactSolution<dim> (1, time),
+ solution);
+ VectorTools::interpolate (dof_handler,
+ ExactSolution<dim> (1, time-time_step),
+ old_solution);
+ output_results (0);
+
+ std::vector<parallel::distributed::Vector<double>*> previous_solutions;
+ previous_solutions.push_back(&old_solution);
+ previous_solutions.push_back(&old_old_solution);
+
+ SineGordonOperation<dim,fe_degree> sine_gordon_op (matrix_free_data,
+ time_step);
+
+ // Now loop over the time steps. In each
+ // iteration, we shift the solution
+ // vectors by one and call the <code>
+ // apply </code> function of the <code>
+ // SineGordonOperator </code>. Then, we
+ // write the solution to a file. We clock
+ // the wall times for the computational
+ // time needed as wall as the time needed
+ // to create the output and report the
+ // numbers when the time stepping is
+ // finished.
+ //
+ // Note how this shift is implemented: We
+ // simply call the swap method on the two
+ // vectors which swaps only some pointers
+ // without the need to copy data
+ // around. Obviously, this is a more
+ // efficient way to move data around. Let
+ // us see what happens in more detail:
+ // First, we exchange
+ // <code>old_solution</code> with
+ // <code>old_old_solution</code>, which
+ // means that
+ // <code>old_old_solution</code> gets
+ // <code>old_solution</code>, which is
+ // what we expect. Similarly,
+ // <code>old_solution</code> gets the
+ // content from <code>solution</code> in
+ // the next step. Afterward,
+ // <code>solution</code> holds
+ // <code>old_old_solution</code>, but
+ // that will be overwritten during this
+ // step.
+ unsigned int timestep_number = 1;
+
+ Timer timer;
+ double wtime = 0;
+ double output_time = 0;
+ for (time+=time_step; time<=final_time; time+=time_step, ++timestep_number)
+ {
+ timer.restart();
+ old_old_solution.swap (old_solution);
+ old_solution.swap (solution);
+ sine_gordon_op.apply (solution, previous_solutions);
+ wtime += timer.wall_time();
+
+ timer.restart();
+ if (timestep_number % output_timestep_skip == 0)
+ output_results(timestep_number / output_timestep_skip);
+
+ output_time += timer.wall_time();
+ }
+ timer.restart();
+ output_results(timestep_number / output_timestep_skip + 1);
+ output_time += timer.wall_time();
+
+ pcout << std::endl
+ << " Performed " << timestep_number << " time steps."
+ << std::endl;
+
+ pcout << " Average wallclock time per time step: "
+ << wtime / timestep_number << "s" << std::endl;
+
+ pcout << " Spent " << output_time << "s on output and "
+ << wtime << "s on computations." << std::endl;
+ }
+}
+
+
+
+ // @sect3{The <code>main</code> function}
+
+ // This is as in all other programs:
+int main (int argc, char ** argv)
+{
+ using namespace Step48;
+ using namespace dealii;
+
+ Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv);
+
+ try
+ {
+ deallog.depth_console (0);
+
+ SineGordonProblem<dimension> sg_problem;
+ sg_problem.run ();
+ }
+ catch (std::exception &exc)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Exception on processing: " << std::endl
+ << exc.what() << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+
+ return 1;
+ }
+ catch (...)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Unknown exception!" << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
*/
#undef DEAL_II_ABORT_NOTHROW_EXCEPTION
-/* Forces the compiler to always inline functions, also in debug mode */
-#undef DEAL_II_ALWAYS_INLINE
-
/* Flag indicating whether there is a bug in the compiler that leads to bogus
warnings for inline class members in anonymous namespaces */
#undef DEAL_II_ANON_NAMESPACE_BOGUS_WARNING
types */
#undef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+/* Equal to 0 in the generic case, equal to 1 if CPU compiled for supports
+ SSE2, equal to 2 if CPU compiled for supports AVX */
+#undef DEAL_II_COMPILER_VECTORIZATION_LEVEL
+
/* Defined if the compiler has a bug in deducing the type of pointers to const
member functions. */
#undef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
namespace internal
{
-#if DEAL_II_USE_MT
+#if DEAL_II_USE_MT == 1
/**
* Take a range argument and call the
* given function with its begin and end.
+ /**
+ * This is a class specialized to for loops with a fixed range given by
+ * unsigned integers. This is an abstract base class that an actual worker
+ * function is derived from. There is a public function apply that issues a
+ * for loop in parallel, subdividing the work onto available processor cores
+ * whenever there is enough work to be done (i.e., the number of elements is
+ * larger than grain_size). Inside the function, a virtual function
+ * apply_to_subrange specifying a range of two integers <tt>[lower,
+ * upper)</tt> is called which needs to be defined in a derived class.
+ *
+ * The parallelization cases covered by this class are a subset of what is
+ * possible with the function apply_to_subranges (which also covers the case
+ * of more general iterators that might not be described by an integer
+ * range). However, for simple integer ranges one might prefer this class,
+ * like when there are many structurally similar loops, e.g., some simple
+ * copy or arithmetic operations on an array of pointers. In that case,
+ * apply_to_subranges will generate a lot of code (or rather, a lot of
+ * symbols) because it passes the long names generated by std::bind to the
+ * templated parallel for functions in TBB. This can considerably increase
+ * compile times and the size of the object code. Similarly, the incorrect
+ * use of std::bind often results in very cryptic error messages, which can
+ * be avoided by this class (only a virtual function needs to be defined in
+ * a derived class). Finally, the additional cost of a virtual function is
+ * negligible in the context of parallel functions: It is much more
+ * expensive to actually issue the work onto a thread, which in turn should
+ * be much less than the actual work done in the for loop.
+ */
+ struct ParallelForInteger
+ {
+ /**
+ * This function runs the for loop over the
+ * given range <tt>[lower,upper)</tt>,
+ * possibly in parallel when end-begin is
+ * larger than the minimum parallel grain
+ * size. This function is marked const because
+ * it any operation that changes the data of a
+ * derived class will inherently not be
+ * thread-safe when several threads work with
+ * the same data simultaneously.
+ */
+ void apply_parallel (const std::size_t begin,
+ const std::size_t end,
+ const std::size_t minimum_parallel_grain_size) const;
+
+ /**
+ * Virtual function for working on subrange to
+ * be defined in a derived class. This
+ * function is marked const because it any
+ * operation that changes the data of a
+ * derived class will inherently not be
+ * thread-safe when several threads work with
+ * the same data simultaneously.
+ */
+ virtual void apply_to_subrange (const std::size_t,
+ const std::size_t) const = 0;
+ };
+
+
+
namespace internal
{
#if DEAL_II_USE_MT == 1
* operation.
*
* The second argument denotes a
- * function object that will be use
+ * function object that will be used
* to reduce the result of two
* computations into one number. An
* example if we want to simply
* subranges accumulated internally.
*
* @warning If ResultType is a floating point
- * type, then accumulation is not a
- * commutative operation. In other words,
+ * type, then accumulation is not an
+ * associative operation. In other words,
* if the given function object is called
* three times on three subranges,
* returning values $a,b,c$, then the
* returned result of this function is
- * $a+b+c$. However, depending on how the
+ * $(a+b)+c$. However, depending on how the
* three sub-tasks are distributed on
* available CPU resources, the result may
- * also be $a+c+b$ or any other
+ * also be $(a+c)+b$ or any other
* permutation; because floating point
- * addition does not commute (as opposed, of
+ * addition is not associative (as opposed, of
* course, to addition of real %numbers),
* the result of invoking this function
* several times may differ on the order of
*/
extern unsigned int minimum_parallel_grain_size;
}
-}
+
+} // end of namespace internal
+
+
+/* --------------------------- inline functions ------------------------- */
+
+namespace parallel
+{
+
+#if DEAL_II_USE_MT == 1
+
+ namespace internal
+ {
+ /**
+ * This is the function actually called by TBB for the ParallelForInteger
+ * class.
+ */
+ struct ParallelForWrapper
+ {
+ ParallelForWrapper (const parallel::ParallelForInteger &worker)
+ :
+ worker_ (worker)
+ {}
+
+ void operator() (const tbb::blocked_range<std::size_t> &range) const
+ {
+ worker_.apply_to_subrange (range.begin(), range.end());
+ }
+
+ const parallel::ParallelForInteger &worker_;
+ };
+ }
+
+#endif
+
+
+ inline
+ void
+ ParallelForInteger::apply_parallel (const std::size_t begin,
+ const std::size_t end,
+ const std::size_t minimum_parallel_grain_size) const
+ {
+#if DEAL_II_USE_MT == 0
+ // make sure we don't get compiler
+ // warnings about unused arguments
+ (void) grainsize;
+
+ apply_to_subrange (begin, end);
+#else
+ internal::ParallelForWrapper worker(*this);
+ tbb::parallel_for (tbb::blocked_range<std::size_t>
+ (begin, end, minimum_parallel_grain_size),
+ worker,
+ tbb::auto_partitioner());
+#endif
+ }
+
+} // end of namespace parallel
DEAL_II_NAMESPACE_CLOSE
Number
Point<dim,Number>::square () const
{
- Number q=0;
+ Number q = Number();
for (unsigned int i=0; i<dim; ++i)
q += this->values[i] * this->values[i];
return q;
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+#ifndef __deal2__std_cxx1x_type_traits_h
+#define __deal2__std_cxx1x_type_traits_h
+
+
+#include <deal.II/base/config.h>
+
+#ifdef DEAL_II_CAN_USE_CXX1X
+
+# include <type_traits>
+DEAL_II_NAMESPACE_OPEN
+namespace std_cxx1x
+{
+ // TODO: could fill up with more types from
+ // C++11 type traits
+ using std::is_pod;
+ using std::is_standard_layout;
+ using std::is_trivial;
+}
+DEAL_II_NAMESPACE_CLOSE
+
+#else
+
+#include <boost/type_traits.hpp>
+DEAL_II_NAMESPACE_OPEN
+namespace std_cxx1x
+{
+ using boost::is_pod;
+
+ // boost does not have is_standard_layout and
+ // is_trivial, but those are both a subset of
+ // is_pod
+ template <typename T>
+ struct is_standard_layout
+ {
+ static const bool value = boost::is_pod<T>::value;
+ };
+
+ template <typename T>
+ struct is_trivial
+ {
+ static const bool value = boost::has_trivial_copy<T>::value &&
+ boost::has_trivial_assign<T>::value &&
+ boost::has_trivial_constructor<T>::value &&
+ boost::has_trivial_destructor<T>::value;
+ };
+}
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
+
+#endif
Number &
operator [] (const TableIndices<rank> &indices);
+ /**
+ * Access to an element according
+ * to unrolled index. The
+ * function
+ * <tt>s.access_raw_entry(i)</tt>
+ * does the same as
+ * <tt>s[s.unrolled_to_component_indices(i)]</tt>,
+ * but more efficiently.
+ */
+ Number
+ access_raw_entry (const unsigned int unrolled_index) const;
+
+ /**
+ * Access to an element according
+ * to unrolled index. The
+ * function
+ * <tt>s.access_raw_entry(i)</tt>
+ * does the same as
+ * <tt>s[s.unrolled_to_component_indices(i)]</tt>,
+ * but more efficiently.
+ */
+ Number &
+ access_raw_entry (const unsigned int unrolled_index);
+
/**
* Return the Frobenius-norm of a tensor,
* i.e. the square root of the sum of
}
}
- static Number dummy_but_referenceable = 0;
+ static Number dummy_but_referenceable = Number();
return dummy_but_referenceable;
}
+template <int rank, int dim, typename Number>
+inline
+Number
+SymmetricTensor<rank,dim,Number>::access_raw_entry (const unsigned int index) const
+{
+ AssertIndexRange (index, data.dimension);
+ return data[index];
+}
+
+
+
+template <int rank, int dim, typename Number>
+inline
+Number &
+SymmetricTensor<rank,dim,Number>::access_raw_entry (const unsigned int index)
+{
+ AssertIndexRange (index, data.dimension);
+ return data[index];
+}
+
+
+
namespace internal
{
template <int dim, typename Number>
inline
Number determinant (const Tensor<2,2,Number> &t)
{
- return ((t[0][0] * t[1][1]) -
- (t[1][0] * t[0][1]));
+ return ((t[0][0] * t[1][1]) - (t[1][0] * t[0][1]));
}
inline
Number determinant (const Tensor<2,3,Number> &t)
{
- // get this using Maple:
- // with(linalg);
- // a := matrix(3,3);
- // x := det(a);
- // readlib(C);
- // C(x, optimized);
- return ( t[0][0]*t[1][1]*t[2][2]
- -t[0][0]*t[1][2]*t[2][1]
- -t[1][0]*t[0][1]*t[2][2]
- +t[1][0]*t[0][2]*t[2][1]
- +t[2][0]*t[0][1]*t[1][2]
- -t[2][0]*t[0][2]*t[1][1] );
+ // use exactly the same expression with the
+ // same order of operations as for the inverse
+ // to let the compiler use common
+ // subexpression elimination when using
+ // determinant and inverse in nearby code
+ const Number t4 = t[0][0]*t[1][1],
+ t6 = t[0][0]*t[1][2],
+ t8 = t[0][1]*t[1][0],
+ t00 = t[0][2]*t[1][0],
+ t01 = t[0][1]*t[2][0],
+ t04 = t[0][2]*t[2][0],
+ det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+
+ t00*t[2][1]+t01*t[1][2]-t04*t[1][1]);
+ return det;
}
for (unsigned int k=0; k<dim; ++k)
{
- Tensor<2,dim-1> minor;
+ Tensor<2,dim-1,Number> minor;
for (unsigned int i=0; i<dim-1; ++i)
for (unsigned int j=0; j<dim-1; ++j)
minor[i][j] = t[i][j<k ? j : j+1];
// this is Maple output,
// thus a bit unstructured
{
- const Number t4 = 1.0/(t[0][0]*t[1][1]-t[0][1]*t[1][0]);
+ const Number det = t[0][0]*t[1][1]-t[1][0]*t[0][1];
+ const Number t4 = 1.0/det;
return_tensor[0][0] = t[1][1]*t4;
return_tensor[0][1] = -t[0][1]*t4;
return_tensor[1][0] = -t[1][0]*t4;
t00 = t[0][2]*t[1][0],
t01 = t[0][1]*t[2][0],
t04 = t[0][2]*t[2][0],
- t07 = 1.0/(t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+
- t00*t[2][1]+t01*t[1][2]-t04*t[1][1]);
+ det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+
+ t00*t[2][1]+t01*t[1][2]-t04*t[1][1]),
+ t07 = 1.0/det;
return_tensor[0][0] = (t[1][1]*t[2][2]-t[1][2]*t[2][1])*t07;
return_tensor[0][1] = (t[0][2]*t[2][1]-t[0][1]*t[2][2])*t07;
return_tensor[0][2] = (t[0][1]*t[1][2]-t[0][2]*t[1][1])*t07;
// if desired, take over the
// inversion of a 4x4 tensor
// from the FullMatrix
-
default:
AssertThrow (false, ExcNotImplemented());
}
Tensor<1,dim,Number>::Tensor (const bool initialize)
{
if (initialize)
- // need to create an object Number(0) to
+ // need to create an object Number() to
// initialize to zero to avoid confusion with
// Tensor::operator=(scalar) when using
// something like
// Tensor<1,dim,Tensor<1,dim,Number> >.
for (unsigned int i=0; i!=dim; ++i)
- values[i] = Number(0);
+ values[i] = Number();
}
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011, 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__vectorization_h
+#define __deal2__vectorization_h
+
+#include <deal.II/base/config.h>
+#include <deal.II/base/std_cxx1x/type_traits.h>
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/base/parallel.h>
+
+#include <cmath>
+#include <cstring>
+
+// set flag DEAL_II_COMPILER_VECTORIZATION_LEVEL depending on whether we are
+// in debug or optimized mode
+// Note:
+// The flag DEAL_II_COMPILER_VECTORIZATION_LEVEL is essentially constructed
+// according to the following scheme
+// #ifdef __AVX__
+// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 2
+// #elif defined (__SSE2__)
+// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 1
+// #else
+// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 0
+// #endif
+// In addition to checking the flags __AVX__ and __SSE2__, a configure test
+// ensures that these feature are not only present but also working properly.
+
+#if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 // AVX
+ #include <immintrin.h>
+ #include <mm_malloc.h>
+#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL == 1 // SSE2
+ #include <emmintrin.h>
+ #include <mm_malloc.h>
+#endif
+
+
+
+// forward declarations
+namespace dealii
+{
+template <typename Number> class VectorizedArray;
+}
+namespace std
+{
+ template <typename Number> ::dealii::VectorizedArray<Number>
+ sqrt(const ::dealii::VectorizedArray<Number>&);
+ template <typename Number> ::dealii::VectorizedArray<Number>
+ abs(const ::dealii::VectorizedArray<Number>&);
+ template <typename Number> ::dealii::VectorizedArray<Number>
+ max(const ::dealii::VectorizedArray<Number>&, const ::dealii::VectorizedArray<Number>&);
+ template <typename Number> ::dealii::VectorizedArray<Number>
+ min (const ::dealii::VectorizedArray<Number>&, const ::dealii::VectorizedArray<Number>&);
+}
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+// for safety, also check that __AVX__ is defined in case the user manually
+// set some conflicting compile flags which prevent compilation
+
+#if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 && defined(__AVX__)
+
+ /**
+ * Specialization of VectorizedArray class for double and AVX.
+ */
+template <>
+class VectorizedArray<double>
+{
+public:
+ /**
+ * This gives the number of vectors collected
+ * in this class.
+ */
+ static const unsigned int n_array_elements = 4;
+
+ /**
+ * This function can be used to set all data
+ * fields to a given scalar.
+ */
+ VectorizedArray&
+ operator = (const double x)
+ {
+ data = _mm256_set1_pd(x);
+ return *this;
+ }
+
+ /**
+ * Access operator.
+ */
+ double &
+ operator [] (const unsigned int comp)
+ {
+ AssertIndexRange (comp, 4);
+ return *(reinterpret_cast<double*>(&data)+comp);
+ }
+
+ /**
+ * Constant access operator.
+ */
+ const double &
+ operator [] (const unsigned int comp) const
+ {
+ AssertIndexRange (comp, 4);
+ return *(reinterpret_cast<const double*>(&data)+comp);
+ }
+
+ /**
+ * Addition.
+ */
+ VectorizedArray &
+ operator += (const VectorizedArray &vec)
+ {
+ // if the compiler supports vector
+ // arithmetics, we can simply use += operator
+ // on the given data type. Otherwise, we need
+ // to use the built-in intrinsic command for
+ // __m256d
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data += vec.data;
+#else
+ data = _mm256_add_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Subtraction.
+ */
+ VectorizedArray &
+ operator -= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data -= vec.data;
+#else
+ data = _mm256_sub_pd(data,vec.data);
+#endif
+ return *this;
+ }
+ /**
+ * Multiplication.
+ */
+ VectorizedArray &
+ operator *= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data *= vec.data;
+#else
+ data = _mm256_mul_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Division.
+ */
+ VectorizedArray &
+ operator /= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data /= vec.data;
+#else
+ data = _mm256_div_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Actual data field. Since this class
+ * represents a POD data type, it remains
+ * public.
+ */
+ __m256d data;
+
+private:
+ /**
+ * Returns the square root of this field. Not
+ * for use in user code. Use sqrt(x) instead.
+ */
+ VectorizedArray
+ get_sqrt () const
+ {
+ VectorizedArray res;
+ res.data = _mm256_sqrt_pd(data);
+ return res;
+ }
+
+ /**
+ * Returns the absolute value of this
+ * field. Not for use in user code. Use
+ * abs(x) instead.
+ */
+ VectorizedArray
+ get_abs () const
+ {
+ // to compute the absolute value, perform
+ // bitwise andnot with -0. This will leave all
+ // value and exponent bits unchanged but force
+ // the sign value to +.
+ __m256d mask = _mm256_set1_pd (-0.);
+ VectorizedArray res;
+ res.data = _mm256_andnot_pd(mask, data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise maximum of this
+ * field and another one. Not for use in user
+ * code. Use max(x,y) instead.
+ */
+ VectorizedArray
+ get_max (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm256_max_pd (data, other.data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise minimum of this
+ * field and another one. Not for use in user
+ * code. Use min(x,y) instead.
+ */
+ VectorizedArray
+ get_min (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm256_min_pd (data, other.data);
+ return res;
+ }
+
+ /**
+ * Make a few functions friends.
+ */
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::sqrt (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::abs (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::max (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::min (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+};
+
+
+
+ /**
+ * Specialization for float and AVX.
+ */
+template<>
+class VectorizedArray<float>
+{
+public:
+ /**
+ * This gives the number of vectors collected
+ * in this class.
+ */
+ static const unsigned int n_array_elements = 8;
+
+ /**
+ * This function can be used to set all data
+ * fields to a given scalar.
+ */
+ VectorizedArray&
+ operator = (const float x)
+ {
+ data = _mm256_set1_ps(x);
+ return *this;
+ }
+
+ /**
+ * Access operator.
+ */
+ float &
+ operator [] (const unsigned int comp)
+ {
+ AssertIndexRange (comp, 8);
+ return *(reinterpret_cast<float*>(&data)+comp);
+ }
+
+ /**
+ * Constant access operator.
+ */
+ const float &
+ operator [] (const unsigned int comp) const
+ {
+ AssertIndexRange (comp, 8);
+ return *(reinterpret_cast<const float*>(&data)+comp);
+ }
+
+ /**
+ * Addition.
+ */
+ VectorizedArray &
+ operator += (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data += vec.data;
+#else
+ data = _mm256_add_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Subtraction.
+ */
+ VectorizedArray &
+ operator -= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data -= vec.data;
+#else
+ data = _mm256_sub_ps(data,vec.data);
+#endif
+ return *this;
+ }
+ /**
+ * Multiplication.
+ */
+ VectorizedArray &
+ operator *= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data *= vec.data;
+#else
+ data = _mm256_mul_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Division.
+ */
+ VectorizedArray &
+ operator /= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data /= vec.data;
+#else
+ data = _mm256_div_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Actual data field. Since this class
+ * represents a POD data type, it remains
+ * public.
+ */
+ __m256 data;
+
+private:
+
+ /**
+ * Returns the square root of this field. Not
+ * for use in user code. Use sqrt(x) instead.
+ */
+ VectorizedArray
+ get_sqrt () const
+ {
+ VectorizedArray res;
+ res.data = _mm256_sqrt_ps(data);
+ return res;
+ }
+ /**
+ * Returns the absolute value of this
+ * field. Not for use in user code. Use
+ * abs(x) instead.
+ */
+ VectorizedArray
+ get_abs () const
+ {
+ // to compute the absolute value, perform
+ // bitwise andnot with -0. This will leave all
+ // value and exponent bits unchanged but force
+ // the sign value to +.
+ __m256 mask = _mm256_set1_ps (-0.f);
+ VectorizedArray res;
+ res.data = _mm256_andnot_ps(mask, data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise maximum of this
+ * field and another one. Not for use in user
+ * code. Use max(x,y) instead.
+ */
+ VectorizedArray
+ get_max (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm256_max_ps (data, other.data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise minimum of this
+ * field and another one. Not for use in user
+ * code. Use min(x,y) instead.
+ */
+ VectorizedArray
+ get_min (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm256_min_ps (data, other.data);
+ return res;
+ }
+
+ /**
+ * Make a few functions friends.
+ */
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::sqrt (const VectorizedArray<Number2> &);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::abs (const VectorizedArray<Number2> &);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::max (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::min (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+};
+
+
+
+// for safety, also check that __SSE2__ is defined in case the user manually
+// set some conflicting compile flags which prevent compilation
+
+#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
+
+
+
+ /**
+ * Specialization for double and SSE2.
+ */
+template <>
+class VectorizedArray<double>
+{
+public:
+ /**
+ * This gives the number of vectors collected
+ * in this class.
+ */
+ static const unsigned int n_array_elements = 2;
+
+ /**
+ * This function can be used to set all data
+ * fields to a given scalar.
+ */
+
+ VectorizedArray&
+ operator = (const double x)
+ {
+ data = _mm_set1_pd(x);
+ return *this;
+ }
+
+ /**
+ * Access operator.
+ */
+ double &
+ operator [] (const unsigned int comp)
+ {
+ AssertIndexRange (comp, 2);
+ return *(reinterpret_cast<double*>(&data)+comp);
+ }
+
+ /**
+ * Constant access operator.
+ */
+ const double &
+ operator [] (const unsigned int comp) const
+ {
+ AssertIndexRange (comp, 2);
+ return *(reinterpret_cast<const double*>(&data)+comp);
+ }
+
+ /**
+ * Addition.
+ */
+ VectorizedArray &
+ operator += (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data += vec.data;
+#else
+ data = _mm_add_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Subtraction.
+ */
+ VectorizedArray &
+ operator -= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data -= vec.data;
+#else
+ data = _mm_sub_pd(data,vec.data);
+#endif
+ return *this;
+ }
+ /**
+ * Multiplication.
+ */
+ VectorizedArray &
+ operator *= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data *= vec.data;
+#else
+ data = _mm_mul_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Division.
+ */
+ VectorizedArray &
+ operator /= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data /= vec.data;
+#else
+ data = _mm_div_pd(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Actual data field. Since this class
+ * represents a POD data type, it remains
+ * public.
+ */
+ __m128d data;
+
+private:
+ /**
+ * Returns the square root of this field. Not
+ * for use in user code. Use sqrt(x) instead.
+ */
+ VectorizedArray
+ get_sqrt () const
+ {
+ VectorizedArray res;
+ res.data = _mm_sqrt_pd(data);
+ return res;
+ }
+
+ /**
+ * Returns the absolute value of this
+ * field. Not for use in user code. Use abs(x)
+ * instead.
+ */
+ VectorizedArray
+ get_abs () const
+ {
+ // to compute the absolute value, perform
+ // bitwise andnot with -0. This will leave all
+ // value and exponent bits unchanged but force
+ // the sign value to +.
+ __m128d mask = _mm_set1_pd (-0.);
+ VectorizedArray res;
+ res.data = _mm_andnot_pd(mask, data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise maximum of this
+ * field and another one. Not for use in user
+ * code. Use max(x,y) instead.
+ */
+ VectorizedArray
+ get_max (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm_max_pd (data, other.data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise minimum of this
+ * field and another one. Not for use in user
+ * code. Use min(x,y) instead.
+ */
+ VectorizedArray
+ get_min (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm_min_pd (data, other.data);
+ return res;
+ }
+
+ /**
+ * Make a few functions friends.
+ */
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::sqrt (const VectorizedArray<Number2> &);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::abs (const VectorizedArray<Number2> &);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::max (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::min (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+};
+
+
+
+ /**
+ * Specialization for float and SSE2.
+ */
+template <>
+class VectorizedArray<float>
+{
+public:
+ /**
+ * This gives the number of vectors collected
+ * in this class.
+ */
+ static const unsigned int n_array_elements = 4;
+
+ /**
+ * This function can be used to set all data
+ * fields to a given scalar.
+ */
+
+ VectorizedArray&
+ operator = (const float x)
+ {
+ data = _mm_set1_ps(x);
+ return *this;
+ }
+
+ /**
+ * Access operator.
+ */
+ float &
+ operator [] (const unsigned int comp)
+ {
+ AssertIndexRange (comp, 4);
+ return *(reinterpret_cast<float*>(&data)+comp);
+ }
+
+ /**
+ * Constant access operator.
+ */
+ const float &
+ operator [] (const unsigned int comp) const
+ {
+ AssertIndexRange (comp, 4);
+ return *(reinterpret_cast<const float*>(&data)+comp);;
+ }
+
+ /**
+ * Addition.
+ */
+ VectorizedArray &
+ operator += (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data += vec.data;
+#else
+ data = _mm_add_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Subtraction.
+ */
+ VectorizedArray &
+ operator -= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data -= vec.data;
+#else
+ data = _mm_sub_ps(data,vec.data);
+#endif
+ return *this;
+ }
+ /**
+ * Multiplication.
+ */
+ VectorizedArray &
+ operator *= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data *= vec.data;
+#else
+ data = _mm_mul_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Division.
+ */
+ VectorizedArray &
+ operator /= (const VectorizedArray &vec)
+ {
+#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
+ data /= vec.data;
+#else
+ data = _mm_div_ps(data,vec.data);
+#endif
+ return *this;
+ }
+
+ /**
+ * Actual data field. Since this class
+ * represents a POD data type, it remains
+ * public.
+ */
+ __m128 data;
+
+private:
+ /**
+ * Returns the square root of this field. Not
+ * for use in user code. Use sqrt(x) instead.
+ */
+ VectorizedArray
+ get_sqrt () const
+ {
+ VectorizedArray res;
+ res.data = _mm_sqrt_ps(data);
+ return res;
+ }
+
+ /**
+ * Returns the absolute value of this
+ * field. Not for use in user code. Use
+ * abs(x) instead.
+ */
+ VectorizedArray
+ get_abs () const
+ {
+ // to compute the absolute value, perform
+ // bitwise andnot with -0. This will leave all
+ // value and exponent bits unchanged but force
+ // the sign value to +.
+ __m128 mask = _mm_set1_ps (-0.f);
+ VectorizedArray res;
+ res.data = _mm_andnot_ps(mask, data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise maximum of this
+ * field and another one. Not for use in user
+ * code. Use max(x,y) instead.
+ */
+ VectorizedArray
+ get_max (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm_max_ps (data, other.data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise minimum of this
+ * field and another one. Not for use in user
+ * code. Use min(x,y) instead.
+ */
+ VectorizedArray
+ get_min (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = _mm_min_ps (data, other.data);
+ return res;
+ }
+
+ /**
+ * Make a few functions friends.
+ */
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::sqrt (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::abs (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::max (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::min (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+};
+
+
+#endif // if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0
+
+
+ /**
+ * This generic class defines a unified interface to a vectorized data
+ * type. For general template arguments, this class simply corresponds to
+ * the template argument. For example, VectorizedArray<long double> is
+ * nothing else but a wrapper around <tt>long double</tt> with exactly one
+ * data field of type <tt>long double</tt> and overloaded arithmetic
+ * operations. This means that <tt>VectorizedArray<ComplicatedType></tt> has
+ * a similar layout as ComplicatedType, provided that ComplicatedType
+ * defines basic arithmetic operations. For floats and doubles, an array of
+ * numbers are packed together, though. The number of elements packed
+ * together depend on the computer system and compiler flags that are used
+ * for compilation of deal.II. The fundamental idea of these packed data
+ * types is to use one single CPU instruction to perform arithmetic
+ * operations on the whole array using the processor's vector units. Most
+ * computer systems by 2010 standards will use an array of two doubles and
+ * four floats, respectively (this corresponds to the SSE/SSE2 data sets)
+ * when compiling deal.II on 64-bit operating systems. On Intel Sandy Bridge
+ * processors and newer or AMD Bulldozer processors and newer, four doubles
+ * and eight floats are used when deal.II is configured e.g. using gcc with
+ * --with-cpu=native or --with-cpu=corei7-avx.
+ *
+ * This behavior of this class is made similar to the basic data types
+ * double and float. The definition of a vectorized array does not
+ * initialize the data field but rather leaves it undefined, as is the case
+ * for double and float. However, when calling something like
+ * VectorType<double> a = VectorType<double>(), it sets all numbers in this
+ * field to zero. In other words, this class is a plain old data (POD) type
+ * which has an equivalent C representation and can e.g. be safely copied
+ * with std::memcpy. This POD layout is also necessary for ensuring correct
+ * alignment of data with address boundaries when collected in a vector
+ * (i.e., when the first element in a vector is properly aligned, all
+ * subsequent elements will be correctly aligned, too).
+ *
+ * Note that for proper functioning of this class, certain data alignment
+ * rules must be respected. This is because the computer expects the
+ * starting address of a VectorizedArray<double> field at specific addresses
+ * in memory (usually, the address of the vectorized array should be a
+ * multiple of the length of the array in bytes). Otherwise, a segmentation
+ * fault or a severe loss of performance might occur. When creating a single
+ * data field on the stack like <tt>VectorizedArray<double> a =
+ * VectorizedArray<double>()</tt>, the compiler will take care of data
+ * alignment automatically. However, when allocating a long vector of
+ * VectorizedArray<double> data, one needs to respect these rules. Use the
+ * class AlignedVector for this purpose. It is a class very similar to
+ * std::vector otherwise but always makes sure that data is correctly
+ * aligned.
+ *
+ * @author Katharina Kormann, Martin Kronbichler, 2010, 2011
+ */
+template <typename Number>
+class VectorizedArray
+{
+public:
+ /**
+ * This gives the number of vectors collected
+ * in this class.
+ */
+ static const unsigned int n_array_elements = 1;
+
+ // POD means that there should be no
+ // user-defined constructors, destructors and
+ // copy functions (the standard is somewhat
+ // relaxed in C++2011, though).
+
+ /**
+ * This function assigns a scalar to this
+ * class.
+ */
+
+ VectorizedArray&
+ operator = (const Number scalar)
+ {
+ data = scalar;
+ return *this;
+ }
+
+ /**
+ * Access operator (only valid with component
+ * 0)
+ */
+ Number &
+ operator [] (const unsigned int comp)
+ {
+ AssertIndexRange (comp, 1);
+ return data;
+ }
+
+ /**
+ * Constant access operator (only valid with
+ * component 0)
+ */
+ const Number &
+ operator [] (const unsigned int comp) const
+ {
+ AssertIndexRange (comp, 1);
+ return data;
+ }
+
+ /**
+ * Addition
+ */
+ VectorizedArray &
+ operator += (const VectorizedArray<Number> &vec)
+ {
+ data+=vec.data;
+ return *this;
+ }
+
+ /**
+ * Subtraction
+ */
+ VectorizedArray &
+ operator -= (const VectorizedArray<Number> &vec)
+ {
+ data-=vec.data;
+ return *this;
+ }
+
+ /**
+ * Multiplication
+ */
+ VectorizedArray &
+ operator *= (const VectorizedArray<Number> &vec)
+ {
+ data*=vec.data;
+ return *this;
+ }
+
+ /**
+ * Division
+ */
+ VectorizedArray &
+ operator /= (const VectorizedArray<Number> &vec)
+ {
+ data/=vec.data;
+ return *this;
+ }
+
+ /**
+ * Actual data field. Since this class
+ * represents a POD data type, it is declared
+ * public.
+ */
+ Number data;
+
+private:
+ /**
+ * Returns the square root of this field. Not
+ * for use in user code. Use sqrt(x) instead.
+ */
+ VectorizedArray
+ get_sqrt () const
+ {
+ VectorizedArray res;
+ res.data = std::sqrt(data);
+ return res;
+ }
+
+ /**
+ * Returns the absolute value of this
+ * field. Not for use in user code. Use
+ * abs(x) instead.
+ */
+ VectorizedArray
+ get_abs () const
+ {
+ VectorizedArray res;
+ res.data = std::fabs(data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise maximum of this
+ * field and another one. Not for use in user
+ * code. Use max(x,y) instead.
+ */
+ VectorizedArray
+ get_max (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = std::max (data, other.data);
+ return res;
+ }
+
+ /**
+ * Returns the component-wise minimum of this
+ * field and another one. Not for use in user
+ * code. Use min(x,y) instead.
+ */
+ VectorizedArray
+ get_min (const VectorizedArray &other) const
+ {
+ VectorizedArray res;
+ res.data = std::min (data, other.data);
+ return res;
+ }
+
+ /**
+ * Make a few functions friends.
+ */
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::sqrt (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::abs (const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::max (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+ template <typename Number2> friend VectorizedArray<Number2>
+ std::min (const VectorizedArray<Number2>&, const VectorizedArray<Number2>&);
+};
+
+/**
+ * Create a vectorized array that sets all entries in the array to the given
+ * scalar.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+make_vectorized_array (const Number &u)
+{
+ VectorizedArray<Number> result;
+ result = u;
+ return result;
+}
+
+/**
+ * Addition of two vectorized arrays with operator +.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator + (const VectorizedArray<Number> &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp = u;
+ return tmp+=v;
+}
+
+/**
+ * Subtraction of two vectorized arrays with operator -.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator - (const VectorizedArray<Number> &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp = u;
+ return tmp-=v;
+}
+
+/**
+ * Multiplication of two vectorized arrays with operator *.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator * (const VectorizedArray<Number> &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp = u;
+ return tmp*=v;
+}
+
+/**
+ * Division of two vectorized arrays with operator /.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator / (const VectorizedArray<Number> &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp = u;
+ return tmp/=v;
+}
+
+/**
+ * Addition of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator + (const Number &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return tmp+=v;
+}
+
+/**
+ * Addition of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array in case the scalar
+ * is a double (needed in order to be able to write simple code with constants
+ * that are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator + (const double &u,
+ const VectorizedArray<float> &v)
+{
+ VectorizedArray<float> tmp;
+ tmp = u;
+ return tmp+=v;
+}
+
+/**
+ * Addition of a vectorized array and a scalar (expanded to a vectorized array
+ * with @p n_array_elements equal entries).
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator + (const VectorizedArray<Number> &v,
+ const Number &u)
+{
+ return u + v;
+}
+
+/**
+ * Addition of a vectorized array and a scalar (expanded to a vectorized array
+ * with @p n_array_elements equal entries) in case the scalar is a double
+ * (needed in order to be able to write simple code with constants that are
+ * usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator + (const VectorizedArray<float> &v,
+ const double &u)
+{
+ return u + v;
+}
+
+/**
+ * Subtraction of a vectorized array from a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries).
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator - (const Number &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return tmp-=v;
+}
+
+/**
+ * Subtraction of a vectorized array from a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries) in case the scalar is a
+ * double (needed in order to be able to write simple code with constants that
+ * are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator - (const double &u,
+ const VectorizedArray<float> &v)
+{
+ VectorizedArray<float> tmp;
+ tmp = float(u);
+ return tmp-=v;
+}
+
+/**
+ * Subtraction of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) from a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator - (const VectorizedArray<Number> &v,
+ const Number &u)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return v-tmp;
+}
+
+/**
+ * Subtraction of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) from a vectorized array in case the scalar
+ * is a double (needed in order to be able to write simple code with constants
+ * that are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator - (const VectorizedArray<float> &v,
+ const double &u)
+{
+ VectorizedArray<float> tmp;
+ tmp = float(u);
+ return v-tmp;
+}
+
+/**
+ * Multiplication of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator * (const Number &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return tmp*=v;
+}
+
+/**
+ * Multiplication of a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array in case the scalar
+ * is a double (needed in order to be able to write simple code with constants
+ * that are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator * (const double &u,
+ const VectorizedArray<float> &v)
+{
+ VectorizedArray<float> tmp;
+ tmp = float(u);
+ return tmp*=v;
+}
+
+/**
+ * Multiplication of a vectorized array and a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries).
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator * (const VectorizedArray<Number> &v,
+ const Number &u)
+{
+ return u * v;
+}
+
+/**
+ * Multiplication of a vectorized array and a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries) in case the scalar is a
+ * double (needed in order to be able to write simple code with constants that
+ * are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator * (const VectorizedArray<float> &v,
+ const double &u)
+{
+ return u * v;
+}
+
+/**
+ * Quotient between a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator / (const Number &u,
+ const VectorizedArray<Number> &v)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return tmp/=v;
+}
+
+/**
+ * Quotient between a scalar (expanded to a vectorized array with @p
+ * n_array_elements equal entries) and a vectorized array in case the scalar
+ * is a double (needed in order to be able to write simple code with constants
+ * that are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator / (const double &u,
+ const VectorizedArray<float> &v)
+{
+ VectorizedArray<float> tmp;
+ tmp = float(u);
+ return tmp/=v;
+}
+
+/**
+ * Quotient between a vectorized array and a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries).
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator / (const VectorizedArray<Number> &v,
+ const Number &u)
+{
+ VectorizedArray<Number> tmp;
+ tmp = u;
+ return v/tmp;
+}
+
+/**
+ * Quotient between a vectorized array and a scalar (expanded to a vectorized
+ * array with @p n_array_elements equal entries) in case the scalar is a
+ * double (needed in order to be able to write simple code with constants that
+ * are usually double numbers).
+ *
+ * @relates VectorizedArray
+ */
+inline
+VectorizedArray<float>
+operator / (const VectorizedArray<float> &v,
+ const double &u)
+{
+ VectorizedArray<float> tmp;
+ tmp = float(u);
+ return v/tmp;
+}
+
+/**
+ * Unary operator + on a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator + (const VectorizedArray<Number> &u)
+{
+ return u;
+}
+
+/**
+ * Unary operator - on a vectorized array.
+ *
+ * @relates VectorizedArray
+ */
+template <typename Number>
+inline
+VectorizedArray<Number>
+operator - (const VectorizedArray<Number> &u)
+{
+ // to get a negative sign, subtract the input from zero (could also
+ // multiply by -1, but this one is slightly simpler)
+ return VectorizedArray<Number>()-u;
+}
+
+
+
+/**
+ * This namespace defines the copy and set functions used in
+ * AlignedVector. These functions operate in parallel when there are enough
+ * elements in the vector.
+ */
+namespace internal
+{
+ /**
+ * Move and class that actually issues the copy commands in
+ * AlignedVector. This class is based on the specialized for loop base class
+ * ParallelForLoop in parallel.h whose purpose is the following: When
+ * calling a parallel for loop on AlignedVector with apply_to_subranges, it
+ * generates different code for every different argument we might choose (as
+ * it is templated). This gives a lot of code (e.g. it triples the memory
+ * required for compiling the file matrix_free.cc and the final object size
+ * is several times larger) which is completely useless. Therefore, this
+ * class channels all copy commands through one call to apply_to_subrange
+ * for all possible types, which makes the copy operation much cleaner
+ * (thanks to a virtual function, whose cost is negligible in this context).
+ *
+ * @relates AlignedVector
+ */
+ template <typename T>
+ class AlignedVectorMove : private parallel::ParallelForInteger
+ {
+ static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
+ public:
+ /**
+ * Constructor. Issues a parallel call if
+ * there are sufficiently many elements,
+ * otherwise work in serial. Copies the data
+ * from source to destination and then calls
+ * destructor on the source. If the optional
+ * argument is set to true, the source is left
+ * untouched instead.
+ */
+ AlignedVectorMove (T* source_begin,
+ T* source_end,
+ T* destination,
+ bool copy_only = false)
+ :
+ source_ (source_begin),
+ destination_ (destination),
+ copy_only_ (copy_only)
+ {
+ Assert (source_end >= source_begin, ExcInternalError());
+ const std::size_t size = source_end - source_begin;
+ if (size < minimum_parallel_grain_size)
+ apply_to_subrange (0, size);
+ else
+ apply_parallel (0, size, minimum_parallel_grain_size);
+ }
+
+ /**
+ * This method moves elements from the source
+ * to the destination given in the constructor
+ * on a subrange given by two integers.
+ */
+ virtual void apply_to_subrange (const std::size_t begin,
+ const std::size_t end) const
+ {
+ // for classes trivial assignment can use
+ // memcpy
+ if (std_cxx1x::is_trivial<T>::value == true)
+ std::memcpy (destination_+begin, source_+begin, (end-begin)*sizeof(T));
+ else
+ if (copy_only_ == false)
+ for (std::size_t i=begin; i<end; ++i)
+ {
+ // initialize memory, copy, and destruct
+ new (&destination_[i]) T;
+ destination_[i] = source_[i];
+ source_[i].~T();
+ }
+ else
+ for (std::size_t i=begin; i<end; ++i)
+ {
+ new (&destination_[i]) T;
+ destination_[i] = source_[i];
+ }
+ }
+
+ private:
+ T* source_;
+ T* destination_;
+ const bool copy_only_;
+ };
+
+ /**
+ * Class that issues the set commands for AlignedVector.
+ *
+ * @relates AlignedVector
+ */
+ template <typename T>
+ class AlignedVectorSet : private parallel::ParallelForInteger
+ {
+ static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
+ public:
+ /**
+ * Constructor. Issues a parallel call if
+ * there are sufficiently many elements,
+ * otherwise work in serial.
+ */
+ AlignedVectorSet (const std::size_t size,
+ const T& element,
+ T* destination)
+ :
+ element_ (element),
+ destination_ (destination),
+ trivial_element (false)
+ {
+ if (size == 0)
+ return;
+
+ if (std_cxx1x::is_trivial<T>::value == true)
+ {
+ const unsigned char zero [sizeof(T)] = {};
+ if (std::memcmp(zero, &element, sizeof(T)) == 0)
+ trivial_element = true;
+ }
+ if (size < minimum_parallel_grain_size)
+ apply_to_subrange (0, size);
+ else
+ apply_parallel (0, size, minimum_parallel_grain_size);
+ }
+
+ private:
+
+ /**
+ * This sets elements on a subrange given by
+ * two integers.
+ */
+ virtual void apply_to_subrange (const std::size_t begin,
+ const std::size_t end) const
+ {
+ // for classes with trivial assignment of zero
+ // can use memset
+ if (std_cxx1x::is_trivial<T>::value == true && trivial_element)
+ std::memset (destination_+begin, 0, (end-begin)*sizeof(T));
+ else
+ for (std::size_t i=begin; i<end; ++i)
+ {
+ // initialize memory and set
+ new (&destination_[i]) T;
+ destination_[i] = element_;
+ }
+ }
+
+ const T& element_;
+ mutable T* destination_;
+ bool trivial_element;
+ };
+} // end of namespace internal
+
+
+/**
+ * This is a replacement class for std::vector to be used in combination with
+ * VectorizedArray and derived data types. It allocates memory aligned to
+ * addresses of a vectorized data type (for SSE, this is necessary in order to
+ * avoid segfaults, and for AVX it considerably increases performance). This
+ * could also be achieved by proving std::vector with a user-defined
+ * allocator. On the other hand, writing an own small vector class lets us
+ * insert assertions more easily, and cut some unnecessary functionality. Note
+ * that this vector is a bit more memory-consuming than std::vector because of
+ * alignment, so it is recommended to only use this vector on long vectors.
+ *
+ * @p author Katharina Kormann, Martin Kronbichler, 2011
+ */
+template < class T >
+class AlignedVector
+{
+ public:
+ /**
+ * Declare standard types used in all
+ * containers. These types parallel those
+ * in the <tt>C++</tt> standard libraries
+ * <tt>vector<...></tt> class.
+ */
+ typedef T value_type;
+ typedef value_type *pointer;
+ typedef const value_type *const_pointer;
+ typedef value_type *iterator;
+ typedef const value_type *const_iterator;
+ typedef value_type &reference;
+ typedef const value_type &const_reference;
+ typedef std::size_t size_type;
+
+ /**
+ * Empty constructor. Sets the vector size to
+ * zero.
+ */
+ AlignedVector ()
+ :
+ _data (0),
+ _end_data (0),
+ _end_allocated (0)
+ {};
+
+ /**
+ * Sets the vector size to the given size and
+ * initializes all elements with T().
+ */
+ AlignedVector (const size_type size)
+ :
+ _data (0),
+ _end_data (0),
+ _end_allocated (0)
+ {
+ if (size > 0)
+ resize (size);
+ }
+
+ /**
+ * Destructor.
+ */
+ ~AlignedVector ()
+ {
+ clear();
+ }
+
+ /**
+ * Copy constructor.
+ */
+ AlignedVector (const AlignedVector<T> &vec)
+ :
+ _data (0),
+ _end_data (0),
+ _end_allocated (0)
+ {
+ // do not invalidate old data
+ resize_fast (vec._end_data - vec._data);
+ internal::AlignedVectorMove<T> (vec._data, vec._end_data, _data, true);
+ }
+
+ /**
+ * Assignment to the input vector @p vec.
+ */
+ AlignedVector &
+ operator = (const AlignedVector<T> &vec)
+ {
+ clear();
+ resize_fast (vec._end_data - vec._data);
+ internal::AlignedVectorMove<T> (vec._data, vec._end_data, _data, true);
+ return *this;
+ }
+
+ /**
+ * Change the size of the vector. It keeps old
+ * elements previously available but does not
+ * initialize the newly allocated memory,
+ * leaving it in an undefined state.
+ */
+ void resize_fast (const size_type size)
+ {
+ reserve (size);
+ _end_data = _data + size;
+ }
+
+ /**
+ * Change the size of the vector. It keeps old
+ * elements previously available, and
+ * initializes each element with the specified
+ * data. If the new vector size is shorter
+ * than the old one, the memory is not
+ * released unless the new size is zero.
+ */
+ void resize (const size_type size_in,
+ const T &init = T())
+ {
+ const size_type old_size = size();
+ if (std_cxx1x::is_trivial<T>::value == false && size_in < old_size)
+ {
+ // call destructor on fields that are released
+ while (_end_data != _data+size_in)
+ (--_end_data)->~T();
+ }
+
+ resize_fast (size_in);
+ // now _size is set correctly, need to set the
+ // values
+ if (size_in > old_size)
+ internal::AlignedVectorSet<T> (size_in-old_size, init,
+ _data+old_size);
+ }
+
+ /**
+ * Reserve memory space for @p size
+ * elements. If the argument @p size is set to
+ * zero, all previously allocated memory is
+ * released.
+ *
+ * In order to avoid too frequent reallocation
+ * (which involves copy of the data), this
+ * function doubles the amount of memory
+ * occupied when the given size is larger than
+ * the previously allocated size.
+ */
+ void reserve (const size_type size_alloc)
+ {
+ const size_type old_size = _end_data - _data;
+ const size_type allocated_size = _end_allocated - _data;
+ if (size_alloc > allocated_size)
+ {
+ // if we continuously increase the size of the
+ // vector, we might be reallocating a lot of
+ // times. therefore, try to increase the size
+ // more aggressively
+ size_type new_size = size_alloc;
+ if (size_alloc < (2 * allocated_size))
+ new_size = 2 * allocated_size;
+
+ const size_type size_actual_allocate = new_size * sizeof(T);
+
+#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0
+
+ // allocate and align along boundaries of the
+ // size of VectorizedArray<double>, which is
+ // 16 bytes for SSE and 32 bytes for AVX
+ T* new_data = static_cast<T*>(_mm_malloc (size_actual_allocate,
+ sizeof(VectorizedArray<double>)));
+#else
+ T * new_data = static_cast<T*>(malloc (size_actual_allocate));
+#endif
+ if (new_data == 0)
+ throw std::bad_alloc();
+
+ // copy data in case there was some content
+ // before and release the old memory with the
+ // function corresponding to the one used for
+ // allocating
+ std::swap (_data, new_data);
+ _end_data = _data + old_size;
+ _end_allocated = _data + new_size;
+ if (_end_data != _data)
+ {
+ internal::AlignedVectorMove<T>(new_data, new_data + old_size,
+ _data);
+#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0
+ _mm_free(new_data);
+#else
+ free(new_data);
+#endif
+ }
+ }
+ else if (size_alloc == 0)
+ clear();
+ }
+
+ /**
+ * Releases all previously allocated memory
+ * and leaves the vector in a state equivalent
+ * to the state after the default constructor
+ * has been called.
+ */
+ void clear ()
+ {
+ if (_data != 0)
+ {
+ if (std_cxx1x::is_trivial<T>::value == false)
+ while (_end_data != _data)
+ (--_end_data)->~T();
+
+#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0
+ _mm_free(_data);
+#else
+ free(_data);
+#endif
+ }
+ _data = 0;
+ _end_data = 0;
+ _end_allocated = 0;
+ };
+
+ /**
+ * Inserts an element at the end of the
+ * vector, increasing the vector size by
+ * one. Note that the allocated size will
+ * double whenever the previous space is not
+ * enough to hold the new element.
+ */
+ void push_back (const T in_data)
+ {
+ Assert (_end_data <= _end_allocated, ExcInternalError());
+ if (_end_data == _end_allocated)
+ reserve (std::max(2*capacity(),static_cast<size_type>(16)));
+ if (std_cxx1x::is_trivial<T>::value == false)
+ new (_end_data) T;
+ *_end_data++ = in_data;
+ }
+
+ /**
+ * Returns the last element of the vector
+ * (read and write access).
+ */
+ reference back ()
+ {
+ AssertIndexRange (0, size());
+ T* field = _end_data - 1;
+ return *field;
+ }
+
+ /**
+ * Returns the last element of the vector
+ * (read-only access).
+ */
+ const_reference back () const
+ {
+ AssertIndexRange (0, size());
+ const T* field = _end_data - 1;
+ return *field;
+ }
+
+ /**
+ * Inserts several elements at the end of the
+ * vector given by a range of elements.
+ */
+ template <typename ForwardIterator>
+ void insert_back (ForwardIterator begin,
+ ForwardIterator end)
+ {
+ const unsigned int old_size = size();
+ reserve (old_size + (end-begin));
+ for ( ; begin != end; ++begin, ++_end_data)
+ {
+ if (std_cxx1x::is_trivial<T>::value == false)
+ new (_end_data) T;
+ *_end_data = *begin;
+ }
+ }
+
+ /**
+ * Swaps the given vector with the calling
+ * vector.
+ */
+ void swap (AlignedVector<T> &vec)
+ {
+ std::swap (_data, vec._data);
+ std::swap (_end_data, vec._end_data);
+ std::swap (_end_allocated, vec._end_allocated);
+ }
+
+ /**
+ * Returns the size of the vector.
+ */
+ size_type size () const
+ {
+ return _end_data - _data;
+ }
+
+ /**
+ * Returns the capacity of the vector, i.e.,
+ * the size this vector can hold without
+ * reallocation. Note that capacity() >=
+ * size().
+ */
+ size_type capacity () const
+ {
+ return _end_allocated - _data;
+ }
+
+ /**
+ * Read-write access to entry @p index in the
+ * vector.
+ */
+ reference
+ operator [] (const size_type index)
+ {
+ AssertIndexRange (index, size());
+ return _data[index];
+ };
+
+ /**
+ * Read-only access to entry @p index in the
+ * vector.
+ */
+ const_reference operator [] (const size_type index) const
+ {
+ AssertIndexRange (index, size());
+ return _data[index];
+ };
+
+ /**
+ * Returns a read and write pointer to the
+ * beginning of the data array.
+ */
+ iterator begin ()
+ {
+ return _data;
+ }
+
+ /**
+ * Returns a read and write pointer to the
+ * end of the data array.
+ */
+ iterator end ()
+ {
+ return _end_data;
+ }
+
+ /**
+ * Returns a read-only pointer to the
+ * beginning of the data array.
+ */
+ const_iterator begin () const
+ {
+ return _data;
+ }
+
+ /**
+ * Returns a read-only pointer to the
+ * end of the data array.
+ */
+ const_iterator end () const
+ {
+ return _end_data;
+ }
+
+ /**
+ * Returns the memory consumption of the
+ * allocated memory in this class. If the
+ * underlying type @p T allocates memory by
+ * itself, this memory is not counted.
+ */
+ size_type memory_consumption () const
+ {
+ size_type memory = sizeof(this);
+ memory += sizeof(T) * capacity();
+ return memory;
+ }
+
+ private:
+
+ /**
+ * Pointer to actual class data.
+ */
+ T* _data;
+
+ /**
+ * Pointer to the end of valid data fields.
+ */
+ T* _end_data;
+
+ /**
+ * Pointer to the end of the allocated memory.
+ */
+ T* _end_allocated;
+};
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+
+/**
+ * Implementation of functions from cmath on VectorizedArray. These functions
+ * do not reside in the dealii namespace in order to ensure a similar
+ * interface as for the respective functions in cmath. Instead, call them
+ * using std::sin.
+ */
+namespace std
+{
+ /**
+ * Computes the sine of a vectorized
+ * data field. The result is return as
+ * vectorized array in the form
+ * <tt>{sin(x[0]), sin(x[1]), ...,
+ * sin(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ sin (const ::dealii::VectorizedArray<Number> &x)
+ {
+ ::dealii::VectorizedArray<Number> sin_val;
+ for (unsigned int i=0; i<dealii::VectorizedArray<Number>::n_array_elements; ++i)
+ sin_val[i] = std::sin(x[i]);
+ return sin_val;
+ }
+
+
+
+ /**
+ * Computes the tangent of a vectorized
+ * data field. The result is return as
+ * vectorized array in the form
+ * <tt>{tan(x[0]), tan(x[1]), ...,
+ * tan(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ tan (const ::dealii::VectorizedArray<Number> &x)
+ {
+ ::dealii::VectorizedArray<Number> tan_val;
+ for (unsigned int i=0; i<dealii::VectorizedArray<Number>::n_array_elements; ++i)
+ tan_val[i] = std::tan(x[i]);
+ return tan_val;
+ }
+
+
+ /**
+ * Computes the cosine of a vectorized
+ * data field. The result is return as
+ * vectorized array in the form
+ * <tt>{cos(x[0]), cos(x[1]), ...,
+ * cos(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ cos (const ::dealii::VectorizedArray<Number> &x)
+ {
+ ::dealii::VectorizedArray<Number> cos_val;
+ for (unsigned int i=0; i<dealii::VectorizedArray<Number>::n_array_elements; ++i)
+ cos_val[i] = std::cos(x[i]);
+ return cos_val;
+ }
+
+
+ /**
+ * Computes the exponential of a vectorized
+ * data field. The result is return as
+ * vectorized array in the form
+ * <tt>{exp(x[0]), exp(x[1]), ...,
+ * exp(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ exp (const ::dealii::VectorizedArray<Number> &x)
+ {
+ ::dealii::VectorizedArray<Number> exp_val;
+ for (unsigned int i=0; i<dealii::VectorizedArray<Number>::n_array_elements; ++i)
+ exp_val[i] = std::exp(x[i]);
+ return exp_val;
+ }
+
+
+ /**
+ * Computes the natural logarithm of a
+ * vectorized data field. The result is return
+ * as vectorized array in the form
+ * <tt>{log(x[0]), log(x[1]), ...,
+ * log(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ log (const ::dealii::VectorizedArray<Number> &x)
+ {
+ ::dealii::VectorizedArray<Number> log_val;
+ for (unsigned int i=0; i<dealii::VectorizedArray<Number>::n_array_elements; ++i)
+ log_val[i] = std::log(x[i]);
+ return log_val;
+ }
+
+
+
+ /**
+ * Computes the square root of a vectorized
+ * data field. The result is return as
+ * vectorized array in the form
+ * <tt>{sqrt(x[0]), sqrt(x[1]), ...,
+ * sqrt(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ sqrt (const ::dealii::VectorizedArray<Number> &x)
+ {
+ return x.get_sqrt();
+ }
+
+
+
+ /**
+ * Computes the absolute value (modulus) of a
+ * vectorized data field. The result is return
+ * as vectorized array in the form
+ * <tt>{abs(x[0]), abs(x[1]), ...,
+ * abs(x[n_array_elements-1])}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ abs (const ::dealii::VectorizedArray<Number> &x)
+ {
+ return x.get_abs();
+ }
+
+
+
+ /**
+ * Computes the componentwise maximum of two
+ * vectorized data fields. The result is
+ * return as vectorized array in the form
+ * <tt>{max(x[0],y[0]), max(x[1],y[1]),
+ * ...}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ max (const ::dealii::VectorizedArray<Number> &x,
+ const ::dealii::VectorizedArray<Number> &y)
+ {
+ return x.get_max(y);
+ }
+
+
+
+ /**
+ * Computes the componentwise minimum of two
+ * vectorized data fields. The result is
+ * return as vectorized array in the form
+ * <tt>{min(x[0],y[0]), min(x[1],y[1]),
+ * ...}</tt>.
+ *
+ * @relates VectorizedArray
+ */
+ template <typename Number>
+ inline
+ ::dealii::VectorizedArray<Number>
+ min (const ::dealii::VectorizedArray<Number> &x,
+ const ::dealii::VectorizedArray<Number> &y)
+ {
+ return x.get_min(y);
+ }
+
+}
+
+#endif
*/
void add(const unsigned int row, const unsigned int column, const number value);
+ /**
+ * Add an array of values given by
+ * <tt>values</tt> in the given global
+ * matrix row at columns specified by
+ * col_indices in the full matrix. This
+ * function is present for
+ * compatibility with the various
+ * sparse matrices in deal.II. In
+ * particular, the two boolean fields
+ * @p elide_zero_values and @p
+ * col_indices_are_sorted do not impact
+ * the performance of this routine, as
+ * opposed to the sparse matrix case
+ * and are indeed ignored in the
+ * implementation.
+ */
+ template <typename number2>
+ void add (const unsigned int row,
+ const unsigned int n_cols,
+ const unsigned int *col_indices,
+ const number2 *values,
+ const bool elide_zero_values = true,
+ const bool col_indices_are_sorted = false);
+
/**
* <i>A(i,1...n) +=
* s*A(j,1...n)</i>. Simple
}
+
+template <typename number>
+template <typename number2>
+inline
+void
+FullMatrix<number>::add (const unsigned int row,
+ const unsigned int n_cols,
+ const unsigned int *col_indices,
+ const number2 *values,
+ const bool,
+ const bool)
+{
+ AssertIndexRange(row, this->m());
+ for (unsigned int col=0; col<n_cols; ++col)
+ {
+ AssertIndexRange(col_indices[col], this->n());
+ this->operator()(row,col_indices[col]) += values[col];
+ }
+}
+
+
template <typename number>
template <class STREAM>
inline
{
// if we call Vector::operator=0, we want to
// zero out all the entries plus ghosts.
- vector_view.dealii::template Vector<Number>::operator= (s);
+ if (partitioner->local_size() > 0)
+ vector_view.dealii::template Vector<Number>::operator= (s);
if (s==Number())
zero_out_ghosts();
- template <typename Number>
- inline
- void
- Vector<Number>::swap (Vector<Number> &v)
- {
- std::swap (allocated_size, v.allocated_size);
- std::swap (val, v.val);
- std::swap (import_data, v.import_data);
- std::swap (vector_view, v.vector_view);
-#ifdef DEAL_II_COMPILER_SUPPROTS_MPI
- std::swap (compress_requests, v.compress_requests);
- std::swap (update_ghost_values_requests, v.update_ghost_values_requests);
-#endif
- }
-
-
-
template <typename Number>
inline
bool
+ template <typename Number>
+ void
+ Vector<Number>::swap (Vector<Number> &v)
+ {
+#ifdef DEAL_II_COMPILER_SUPPORTS_MPI
+ // introduce a Barrier over all MPI processes
+ // to make sure that the compress request are
+ // no longer used before changing the owner
+ if (v.partitioner->n_mpi_processes() > 1)
+ MPI_Barrier (v.partitioner->get_communicator());
+ if (partitioner->n_mpi_processes() > 1 &&
+ v.partitioner->n_mpi_processes() !=
+ partitioner->n_mpi_processes())
+ MPI_Barrier (partitioner->get_communicator());
+
+ std::swap (compress_requests, v.compress_requests);
+ std::swap (update_ghost_values_requests, v.update_ghost_values_requests);
+#endif
+
+ std::swap (partitioner, v.partitioner);
+ std::swap (allocated_size, v.allocated_size);
+ std::swap (val, v.val);
+ std::swap (import_data, v.import_data);
+
+ // vector view cannot be swapped so reset it
+ // manually (without touching the vector
+ // elements)
+ vector_view.reinit (partitioner->local_size(), val);
+ v.vector_view.reinit (v.partitioner->local_size(), v.val);
+ }
+
+
+
template <typename Number>
std::size_t
Vector<Number>::memory_consumption () const
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_dof_info_h
+#define __deal2__matrix_free_dof_info_h
+
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/vectorization.h>
+#include <deal.II/base/partitioner.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/matrix_free/helper_functions.h>
+
+#include <memory>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ /**
+ * The class that stores the indices of the degrees of freedom for all the
+ * cells. Essentially, this is a smart number cache in the style of a
+ * DoFHandler that also embeds the description of constraints directly on
+ * the cell level without the need to refer to the external
+ * ConstraintMatrix.
+ *
+ * This class only stores index relations. The weights for hanging node
+ * constraints are stored in a different field. This is because a different
+ * field allows for the same compressed weight data on different DoFHandlers
+ * for vector-valued problems. There, the indices might be constrained
+ * differently on different components (e.g. Dirichlet conditions only on
+ * selected components), whereas the weights from hanging nodes are the same
+ * and need to be stored only once. The combination will be handled in the
+ * MatrixFree class.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+ struct DoFInfo
+ {
+ /**
+ * Default empty constructor.
+ */
+ DoFInfo ();
+
+ /**
+ * Copy constructor.
+ */
+ DoFInfo (const DoFInfo &dof_info);
+
+ /**
+ * Clears all data fields in this class.
+ */
+ void clear ();
+
+ /**
+ * Returns a pointer to the first index in the
+ * DoF row @p row.
+ */
+ const unsigned int * begin_indices (const unsigned int row) const;
+
+ /**
+ * Returns a pointer to the one past the last
+ * DoF index in the row @p row.
+ */
+ const unsigned int * end_indices (const unsigned int row) const;
+
+ /**
+ * Returns the number of entries in the
+ * indices field for the given row.
+ */
+ unsigned int row_length_indices (const unsigned int row) const;
+
+ /**
+ * Returns a pointer to the first constraint
+ * indicator in the row @p row.
+ */
+ const std::pair<unsigned short,unsigned short> *
+ begin_indicators (const unsigned int row) const;
+
+ /**
+ * Returns a pointer to the one past the last
+ * constraint indicator in the row @p row.
+ */
+ const std::pair<unsigned short,unsigned short> *
+ end_indicators (const unsigned int row) const;
+
+ /**
+ * Returns the number of entries in the
+ * constraint indicator field for the given
+ * row.
+ */
+ unsigned int row_length_indicators (const unsigned int row) const;
+
+ /**
+ * Returns a pointer to the first index in the
+ * DoF row @p row for plain indices (i.e., the
+ * entries where constraints are not
+ * embedded).
+ */
+ const unsigned int * begin_indices_plain (const unsigned int row) const;
+
+ /**
+ * Returns a pointer to the one past the last
+ * DoF index in the row @p row (i.e., the
+ * entries where constraints are not
+ * embedded).
+ */
+ const unsigned int * end_indices_plain (const unsigned int row) const;
+
+ /**
+ * Returns the FE index for a given finite
+ * element degree. If not in hp mode, this
+ * function always returns index 0. If an
+ * index is not found in hp mode, it returns
+ * max_fe_degree, i.e., one index past the
+ * last valid one.
+ */
+ unsigned int fe_index_from_degree (const unsigned int fe_degree) const;
+
+
+ /**
+ * Returns the FE index for a given finite
+ * element degree. If not in hp mode or if the
+ * index is not found, this function always
+ * returns index 0. Hence, this function does
+ * not check whether the given degree is
+ * actually present.
+ */
+ unsigned int
+ fe_index_from_dofs_per_cell (const unsigned int dofs_per_cell) const;
+
+ /**
+ * This internal method takes the local
+ * indices on a cell and fills them into this
+ * class. It resolves the constraints and
+ * distributes the results. Ghost indices,
+ * i.e., indices that are located on another
+ * processor, get a temporary number by this
+ * function, and will later be assigned the
+ * correct index after all the ghost indices
+ * have been collected by the call to @p
+ * assign_ghosts.
+ */
+ void read_dof_indices (const std::vector<unsigned int> &local_indices,
+ const std::vector<unsigned int> &lexicographic_inv,
+ const ConstraintMatrix &constraints,
+ const unsigned int cell_number,
+ internal::ConstraintValues<double>&constraint_values,
+ bool &cell_at_boundary);
+
+ /**
+ * This method assigns the correct indices to
+ * ghost indices from the temporary numbering
+ * employed by the @p read_dof_indices
+ * function. The numbers are localized with
+ * respect to the MPI process, and ghosts
+ * start at the end of the locally owned
+ * range. This way, we get direct access to
+ * all vector entries.
+ */
+ void assign_ghosts(const std::vector<unsigned int> &boundary_cells);
+
+ /**
+ * This method reorders the way cells are gone
+ * through based on a given renumbering of the
+ * cells. It also takes @p n_vectors cells
+ * together and interprets them as one cell
+ * only, as is needed for vectorization.
+ */
+ void reorder_cells (const SizeInfo &size_info,
+ const std::vector<unsigned int> &renumbering,
+ const std::vector<unsigned int> &constraint_pool_row_index,
+ const std::vector<unsigned int> &irregular_cells,
+ const unsigned int n_vectors);
+
+ /**
+ * This helper function determines a block
+ * size if the user decided not to force a
+ * block size through
+ * MatrixFree::AdditionalData. This is
+ * computed based on the number of hardware
+ * threads on the system
+ * (multithread_info.n_default_threads) and
+ * the number of macro cells that we should
+ * work on.
+ */
+ void guess_block_size (const SizeInfo &size_info,
+ TaskInfo &task_info);
+
+ /**
+ * This method goes through all cells that
+ * have been filled into @p dof_indices and
+ * finds out which cells can be worked on
+ * independently and which ones are
+ * neighboring and need to be done at
+ * different times when used in parallel.
+ *
+ * The strategy is based on a two-level
+ * approach. The outer level is subdivided
+ * into partitions similar to the type of
+ * neighbors in Cuthill-McKee, and the inner
+ * level is subdivided via colors (for chunks
+ * within the same color, can work
+ * independently). One task is represented by
+ * a chunk of cells. The cell chunks are
+ * formed before subdivision into partitions
+ * and colors.
+ */
+ void
+ make_thread_graph_partition_color (SizeInfo &size_info,
+ TaskInfo &task_info,
+ std::vector<unsigned int> &renumbering,
+ std::vector<unsigned int> &irregular_cells,
+ const bool hp_bool);
+
+ /**
+ * This function goes through all cells that
+ * have been filled into @p dof_indices and
+ * finds out which cells can be worked on
+ * independently and which ones are
+ * neighboring and need to be done at
+ * different times when used in parallel.
+ *
+ * The strategy is based on a two-level
+ * approach. The outer level is subdivided
+ * into partitions similar to the type of
+ * neighbors in Cuthill-McKee, and the inner
+ * level is again subdivided into
+ * Cuthill-McKee-like partitions (partitions
+ * whose level differs by more than 2 can be
+ * worked on independently). One task is
+ * represented by a chunk of cells. The cell
+ * chunks are formed after subdivision into
+ * the two levels of partitions.
+ */
+ void
+ make_thread_graph_partition_partition (SizeInfo &size_info,
+ TaskInfo &task_info,
+ std::vector<unsigned int> &renumbering,
+ std::vector<unsigned int> &irregular_cells,
+ const bool hp_bool);
+
+ /**
+ * This function computes the connectivity of
+ * the currently stored indices and fills the
+ * structure into a sparsity pattern. The
+ * parameter block_size can be used to specify
+ * whether several cells should be treated as
+ * one.
+ */
+ void
+ make_connectivity_graph (const SizeInfo &size_info,
+ const TaskInfo &task_info,
+ const std::vector<unsigned int> &renumbering,
+ const std::vector<unsigned int> &irregular_cells,
+ const bool do_blocking,
+ CompressedSimpleSparsityPattern &connectivity) const;
+
+ /**
+ * Renumbers the degrees of freedom to give
+ * good access for this class.
+ */
+ void renumber_dofs (std::vector<unsigned int> &renumbering);
+
+ /**
+ * Returns the memory consumption in bytes of
+ * this class.
+ */
+ std::size_t memory_consumption() const;
+
+ /**
+ * Prints a detailed summary of memory
+ * consumption in the different structures of
+ * this class to the given output stream.
+ */
+ template <typename STREAM>
+ void print_memory_consumption(STREAM &out,
+ const SizeInfo &size_info) const;
+
+ /**
+ * Returns the memory consumption in bytes of
+ * this class.
+ */
+ template <typename Number>
+ void print (const CompressedMatrix<Number> &constraint_pool,
+ std::ostream &out) const;
+
+ /**
+ * Stores the rowstart indices of the
+ * compressed row storage in the @p
+ * dof_indices and @p constraint_indicator
+ * fields. These two fields are always
+ * accessed together, so it is simpler to keep
+ * just one variable for them. This also
+ * obviates keeping two rowstart vectors in
+ * synch.
+ *
+ * In addition, the third field stores whether
+ * a particular cell has a certain structure
+ * in the indices, like indices for
+ * vector-valued problems or for cells where
+ * not all vector components are filled.
+ */
+ std::vector<std_cxx1x::tuple<unsigned int,
+ unsigned int,
+ unsigned int> > row_starts;
+
+ /**
+ * Stores the indices of the degrees of
+ * freedom for each cell. This array also
+ * includes the indirect contributions from
+ * constraints, which are described by the @p
+ * constraint_indicator field. Because of
+ * variable lengths of rows, this would be a
+ * vector of a vector. However, we use one
+ * contiguous memory region and store the
+ * rowstart in the variable @p row_starts.
+ */
+ std::vector<unsigned int> dof_indices;
+
+ /**
+ * This variable describes the position of
+ * constraints in terms of the local numbering
+ * of degrees of freedom on a cell. The first
+ * number stores the distance from one
+ * constrained degree of freedom to the
+ * next. This allows to identify the position
+ * of constrained DoFs as we loop through the
+ * local degrees of freedom of the cell when
+ * reading from or writing to a vector. The
+ * second number stores the index of the
+ * constraint weights, stored in the variable
+ * constraint_pool.
+ */
+ std::vector<std::pair<unsigned short,unsigned short> > constraint_indicator;
+
+ /**
+ * This stores the parallel partitioning that
+ * can be used to set up vectors. The
+ * partitioner includes the description of the
+ * local range in the vector, and also
+ * includes how the ghosts look like. This
+ * enables initialization of vectors based on
+ * the DoFInfo field.
+ */
+ std_cxx1x::shared_ptr<const Utilities::MPI::Partitioner> vector_partitioner;
+
+ /**
+ * This stores a (sorted) list of all locally
+ * owned degrees of freedom that are
+ * constrained.
+ */
+ std::vector<unsigned int> constrained_dofs;
+
+ /**
+ * Stores the rowstart indices of the
+ * compressed row storage in the @p
+ * dof_indices_plain
+ * fields.
+ */
+ std::vector<unsigned int> row_starts_plain_indices;
+
+ /**
+ * Stores the indices of the degrees of
+ * freedom for each cell. This array does not
+ * include the indirect contributions from
+ * constraints, which are included in @p
+ * dof_indices. Because of variable lengths of
+ * rows, this would be a vector of a
+ * vector. However, we use one contiguous
+ * memory region and store the rowstart in the
+ * variable @p row_starts_plain_indices.
+ */
+ std::vector<unsigned int> plain_dof_indices;
+
+ /**
+ * Stores the number of components in the
+ * DoFHandler where the indices have been read
+ * from.
+ */
+ unsigned int n_components;
+
+ /**
+ * Stores the number of degrees of freedom per
+ * cell.
+ */
+ std::vector<unsigned int> dofs_per_cell;
+
+ /**
+ * Stores the number of degrees of freedom per
+ * face.
+ */
+ std::vector<unsigned int> dofs_per_face;
+
+ /**
+ * Informs on whether plain indices are
+ * cached.
+ */
+ bool store_plain_indices;
+
+ /**
+ * Stores the index of the active
+ * finite element in the hp case.
+ */
+ std::vector<unsigned int> cell_active_fe_index;
+
+ /**
+ * Stores the maximum degree of
+ * different finite elements for the
+ * hp case.
+ */
+ unsigned int max_fe_index;
+
+ /**
+ * This variable stores the dofs per cell and
+ * the finite element degree associated for
+ * all fe indices in the underlying element
+ * for easier access to data in the hp case.
+ */
+ std::vector<std::pair<unsigned int,unsigned int> > fe_index_conversion;
+
+ /**
+ * Temporarily stores the numbers of ghosts
+ * during setup. Cleared when calling @p
+ * assign_ghosts. Then, all information is
+ * collected by the partitioner.
+ */
+ std::vector<unsigned int> ghost_dofs;
+ };
+
+
+/*----------------------- Inline functions ----------------------------------*/
+
+#ifndef DOXYGEN
+
+ inline
+ const unsigned int *
+ DoFInfo::begin_indices (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ const unsigned int index = std_cxx1x::get<0>(row_starts[row]);
+ AssertIndexRange(index, dof_indices.size()+1);
+ return &dof_indices[index];
+ }
+
+
+
+ inline
+ const unsigned int *
+ DoFInfo::end_indices (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ const unsigned int index = std_cxx1x::get<0>(row_starts[row+1]);
+ AssertIndexRange(index, dof_indices.size()+1);
+ return &dof_indices[0] + index;
+ }
+
+
+
+ inline
+ unsigned int
+ DoFInfo::row_length_indices (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ return (std_cxx1x::get<0>(row_starts[row+1]) -
+ std_cxx1x::get<0>(row_starts[row]));
+ }
+
+
+
+ inline
+ const std::pair<unsigned short,unsigned short> *
+ DoFInfo::begin_indicators (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ const unsigned int index = std_cxx1x::get<1>(row_starts[row]);
+ AssertIndexRange (index, constraint_indicator.size()+1);
+ return &constraint_indicator[index];
+ }
+
+
+
+ inline
+ const std::pair<unsigned short,unsigned short> *
+ DoFInfo::end_indicators (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ const unsigned int index = std_cxx1x::get<1>(row_starts[row+1]);
+ AssertIndexRange (index, constraint_indicator.size()+1);
+ return &constraint_indicator[0] + index;
+ }
+
+
+
+ inline
+ unsigned int
+ DoFInfo::row_length_indicators (const unsigned int row) const
+ {
+ AssertIndexRange (row, row_starts.size()-1);
+ return (std_cxx1x::get<1>(row_starts[row+1]) -
+ std_cxx1x::get<1>(row_starts[row]));
+ }
+
+
+
+ inline
+ const unsigned int *
+ DoFInfo::begin_indices_plain (const unsigned int row) const
+ {
+ // if we have no constraints, should take the
+ // data from dof_indices
+ if (row_length_indicators(row) == 0)
+ {
+ Assert (row_starts_plain_indices[row] == numbers::invalid_unsigned_int,
+ ExcInternalError());
+ return begin_indices(row);
+ }
+ else
+ {
+ AssertDimension (row_starts.size(), row_starts_plain_indices.size());
+ const unsigned int index = row_starts_plain_indices[row];
+ AssertIndexRange(index, plain_dof_indices.size()+1);
+ return &plain_dof_indices[index];
+ }
+ }
+
+
+
+ inline
+ const unsigned int *
+ DoFInfo::end_indices_plain (const unsigned int row) const
+ {
+ return begin_indices_plain(row) +
+ dofs_per_cell[(cell_active_fe_index.size()==0)?
+ 0:cell_active_fe_index[row]];
+ }
+
+
+
+ inline
+ unsigned int
+ DoFInfo::fe_index_from_degree (const unsigned int fe_degree) const
+ {
+ const unsigned int n_indices = fe_index_conversion.size();
+ for (unsigned int i=0; i<n_indices; ++i)
+ if (fe_index_conversion[i].first == fe_degree)
+ return i;
+ return n_indices;
+ }
+
+
+
+ inline
+ unsigned int
+ DoFInfo::fe_index_from_dofs_per_cell (const unsigned int dofs_per_cell) const
+ {
+ for (unsigned int i=0; i<fe_index_conversion.size(); ++i)
+ if (fe_index_conversion[i].second == dofs_per_cell)
+ return i;
+ return 0;
+ }
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+#endif // ifndef DOXYGEN
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011, 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/base/multithread_info.h>
+#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
+#include <deal.II/matrix_free/dof_info.h>
+#include <deal.II/matrix_free/helper_functions.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ namespace internal
+ {
+ /**
+ * A struct that takes entries describing
+ * a constraint and puts them into a
+ * sorted list where duplicates are
+ * filtered out
+ */
+ template <typename Number>
+ struct ConstraintValues
+ {
+ ConstraintValues();
+
+ /**
+ * This function inserts some
+ * constrained entries to the
+ * collection of all
+ * values. It stores the
+ * (reordered) numbering of
+ * the dofs (according to the
+ * ordering that matches with
+ * the function) in
+ * new_indices, and returns
+ * the storage position the
+ * double array for access
+ * later on.
+ */
+ unsigned short
+ insert_entries (const std::vector<std::pair<unsigned int,double> > &entries);
+
+ CompressedMatrix<Number> constraint_pool;
+ std::vector<std::pair<unsigned int, unsigned int> > pool_locations;
+ std::vector<std::pair<Number,unsigned int> > constraint_entries;
+ std::vector<unsigned int> constraint_indices;
+ std::vector<double> one_constraint;
+ HashValue hashes;
+ };
+
+
+ template <typename Number>
+ ConstraintValues<Number>::ConstraintValues ()
+ :
+ hashes (1.)
+ {
+ constraint_pool.row_index.push_back (0);
+ }
+
+ template <typename Number>
+ unsigned short
+ ConstraintValues<Number>::
+ insert_entries (const std::vector<std::pair<unsigned int,double> > &entries)
+ {
+ unsigned int insert_position = deal_II_numbers::invalid_unsigned_int;
+
+ typedef std::vector<std::pair<unsigned int, unsigned int> >::iterator iter;
+ constraint_entries.resize(entries.size());
+ one_constraint.resize(entries.size());
+ constraint_indices.resize(entries.size());
+ for (unsigned int j=0;j<entries.size(); j++)
+ {
+ constraint_entries[j].first = entries[j].second;
+ constraint_entries[j].second = entries[j].first;
+ }
+
+ std::sort(constraint_entries.begin(),constraint_entries.end());
+ for (unsigned int j=0;j<entries.size(); j++)
+ {
+ // copy the indices of the constraint entries after
+ // sorting.
+ constraint_indices[j] = constraint_entries[j].second;
+
+ // one_constraint takes the weights of the
+ // constraint
+ one_constraint[j] = constraint_entries[j].first;
+ }
+
+ // check whether or not constraint is already
+ // in pool
+ unsigned int hash_val = hashes(one_constraint);
+ std::pair<unsigned int,unsigned int> test (hash_val, 0);
+
+ // Try to find a constraint in the pool with
+ // the same hash value.
+ iter pos = std::lower_bound (pool_locations.begin(),
+ pool_locations.end(),
+ test);
+
+ // If constraint has to be added, which will
+ // be its no.
+ test.second = constraint_pool.row_index.size()-1;
+
+ // Hash value larger than all the ones
+ // before. We need to add it.
+ if (pos == pool_locations.end())
+ goto insert;
+
+ // A constraint in the pool with the same hash
+ // value identified.
+ else if (pos->first == test.first)
+ {
+ bool is_same = true;
+ while(is_same == true)
+ {
+ if(one_constraint.size()!=
+ (constraint_pool.row_length(pos->second)))
+ // The constraints have different length, and
+ // hence different.
+ is_same = false;
+ else
+ for (unsigned int q=0; q<one_constraint.size(); ++q)
+ // check whether or not all weights are the
+ // same.
+ if (std::fabs(constraint_pool.data[constraint_pool.
+ row_index[pos->second]+q]-
+ one_constraint[q])>hashes.scaling)
+ {
+ is_same = false;
+ break;
+ }
+ if (is_same == false)
+ {
+ // Try if there is another constraint with the
+ // same hash value.
+ ++pos;
+ if (pos != pool_locations.end() && pos->first == test.first)
+ is_same = true;
+ else
+ goto insert;
+ }
+ else
+ {
+ // The constraint is the same as the
+ // (pos->second)th in the pool. Add the
+ // location of constraint in pool
+ insert_position = pos->second;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // A new constraint has been identified. It
+ // needs to be added (at the position
+ // test->second in pool_locations).
+ insert:
+ pool_locations.insert (pos, test);
+
+ // Remember hash value and location of
+ // constraint.
+ constraint_pool.data.insert_back(one_constraint.begin(),
+ one_constraint.end());
+ constraint_pool.complete_last_row();
+
+ // Add the location of constraint in pool.
+ insert_position = test.second;
+ }
+
+ // we want to store the result as a short
+ // variable, so we have to make sure that the
+ // result does not exceed the limits when
+ // casting.
+ Assert(insert_position < (1<<(8*sizeof(unsigned short))),
+ ExcInternalError());
+ return static_cast<unsigned short>(insert_position);
+ }
+ } // end of namespace internal
+
+
+
+ // ----------------- actual DoFInfo functions -----------------------------
+
+ DoFInfo::DoFInfo ()
+ {
+ clear();
+ }
+
+
+ DoFInfo::DoFInfo (const DoFInfo &dof_info_in)
+ :
+ row_starts (dof_info_in.row_starts),
+ dof_indices (dof_info_in.dof_indices),
+ constraint_indicator (dof_info_in.constraint_indicator),
+ vector_partitioner (dof_info_in.vector_partitioner),
+ constrained_dofs (dof_info_in.constrained_dofs),
+ row_starts_plain_indices (dof_info_in.row_starts_plain_indices),
+ plain_dof_indices (dof_info_in.plain_dof_indices),
+ n_components (dof_info_in.n_components),
+ dofs_per_cell (dof_info_in.dofs_per_cell),
+ dofs_per_face (dof_info_in.dofs_per_face),
+ store_plain_indices (dof_info_in.store_plain_indices),
+ ghost_dofs (dof_info_in.ghost_dofs)
+ {}
+
+
+
+ void
+ DoFInfo::clear ()
+ {
+ row_starts.clear();
+ dof_indices.clear();
+ constraint_indicator.clear();
+ vector_partitioner.reset();
+ ghost_dofs.clear();
+ dofs_per_cell.clear();
+ dofs_per_face.clear();
+ row_starts_plain_indices.clear();
+ plain_dof_indices.clear();
+ store_plain_indices = false;
+ }
+
+
+
+ void
+ DoFInfo::read_dof_indices (const std::vector<unsigned int> &local_indices,
+ const std::vector<unsigned int> &lexicographic_inv,
+ const ConstraintMatrix &constraints,
+ const unsigned int cell_number,
+ internal::ConstraintValues<double> &constraint_values,
+ bool &cell_at_boundary)
+ {
+ Assert (vector_partitioner.get() !=0, ExcInternalError());
+ const unsigned int n_mpi_procs = vector_partitioner->n_mpi_processes();
+ const unsigned int first_owned = vector_partitioner->local_range().first;
+ const unsigned int last_owned = vector_partitioner->local_range().second;
+ const unsigned int n_owned = last_owned - first_owned;
+ std::pair<unsigned short,unsigned short> constraint_iterator (0,0);
+
+ unsigned int dofs_this_cell = (cell_active_fe_index.size() == 0) ?
+ dofs_per_cell[0] : dofs_per_cell[cell_active_fe_index[cell_number]];
+ for (unsigned int i=0; i<dofs_this_cell; i++)
+ {
+ unsigned int current_dof =
+ local_indices[lexicographic_inv[i]];
+ const std::vector<std::pair<unsigned int,double> >
+ * entries_ptr =
+ constraints.get_constraint_entries(current_dof);
+
+ // dof is constrained
+ if(entries_ptr != 0)
+ {
+ // in case we want to access plain indices, we
+ // need to know about the location of
+ // constrained indices as well (all the other
+ // indices are collected by the cases below)
+ if (current_dof < first_owned || current_dof >= last_owned)
+ {
+ ghost_dofs.push_back (current_dof);
+ cell_at_boundary = true;
+ }
+
+ // check whether this dof is identity
+ // constrained to another dof. then we can
+ // simply insert that dof and there is no need
+ // to actually resolve the constraint entries
+ const std::vector<std::pair<unsigned int,double> >
+ & entries = *entries_ptr;
+ const unsigned int n_entries = entries.size();
+ if (n_entries == 1 && std::fabs(entries[0].second-1.)<1e-14)
+ {
+ current_dof = entries[0].first;
+ goto no_constraint;
+ }
+
+ // append a new index to the indicators
+ constraint_indicator.push_back (constraint_iterator);
+ constraint_indicator.back().second =
+ constraint_values.insert_entries (entries);
+
+ // reset constraint iterator for next round
+ constraint_iterator.first = 0;
+
+ // add the local_to_global indices computed in
+ // the insert_entries function. transform the
+ // index to local index space or mark it as
+ // ghost if necessary
+ if (n_entries > 0)
+ {
+ const std::vector<unsigned int> &constraint_indices =
+ constraint_values.constraint_indices;
+ for (unsigned int j=0; j<n_entries; ++j)
+ {
+ if (n_mpi_procs > 1 &&
+ (constraint_indices[j] < first_owned ||
+ constraint_indices[j] >= last_owned))
+ {
+ dof_indices.push_back
+ (n_owned + ghost_dofs.size());
+
+ // collect ghosts so that we can later
+ // construct an IndexSet for them. also store
+ // whether the current cell is on the boundary
+ ghost_dofs.push_back(constraint_indices[j]);
+ cell_at_boundary = true;
+ }
+ else
+ // not ghost, so transform to the local index
+ // space directly
+ dof_indices.push_back
+ (constraint_indices[j] - first_owned);
+ }
+ }
+ }
+ else
+ {
+ no_constraint:
+ // Not constrained, we simply have to add the
+ // local index to the indices_local_to_global
+ // list and increment constraint
+ // iterator. transform to local index
+ // space/mark as ghost
+ if (n_mpi_procs > 1 &&
+ (current_dof < first_owned ||
+ current_dof >= last_owned))
+ {
+ ghost_dofs.push_back(current_dof);
+ current_dof = n_owned + ghost_dofs.size()-1;
+ cell_at_boundary = true;
+ }
+ else
+ current_dof -= first_owned;
+
+ dof_indices.push_back (current_dof);
+
+ // make sure constraint_iterator.first is
+ // always within the bounds of unsigned short
+ Assert (constraint_iterator.first <
+ (1<<(8*sizeof(unsigned short)))-1,
+ ExcInternalError());
+ constraint_iterator.first++;
+ }
+ }
+ row_starts[cell_number+1] = std_cxx1x::tuple<unsigned int,unsigned int,
+ unsigned int>
+ (dof_indices.size(), constraint_indicator.size(), 0);
+
+ // now to the plain indices: in case we have
+ // constraints on this cell, store the indices
+ // without the constraints resolve once again
+ if (store_plain_indices == true)
+ {
+ if (cell_number == 0)
+ row_starts_plain_indices.resize (row_starts.size());
+ row_starts_plain_indices[cell_number] = plain_dof_indices.size();
+ bool cell_has_constraints =
+ std_cxx1x::get<1>(row_starts[cell_number+1]) >
+ std_cxx1x::get<1>(row_starts[cell_number]);
+ if (cell_has_constraints == true)
+ {
+ for (unsigned int i=0; i<dofs_this_cell; ++i)
+ {
+ unsigned int current_dof =
+ local_indices[lexicographic_inv[i]];
+ if (n_mpi_procs > 1 &&
+ (current_dof < first_owned ||
+ current_dof >= last_owned))
+ {
+ ghost_dofs.push_back(current_dof);
+ current_dof = n_owned + ghost_dofs.size()-1;
+ cell_at_boundary = true;
+ }
+ else
+ current_dof -= first_owned;
+ plain_dof_indices.push_back (current_dof);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ {
+ unsigned int n_dofs = 0;
+ const std::pair<unsigned short,unsigned short> * blb = begin_indicators(cell_number);
+ for (unsigned int j=0; j<row_length_indicators(cell_number); ++j)
+ {
+ n_dofs += blb[j].first;
+ n_dofs += constraint_values.constraint_pool.row_length(blb[j].second);
+ }
+ n_dofs += constraint_iterator.first;
+ AssertDimension(n_dofs, row_length_indices(cell_number));
+ }
+#endif
+ }
+
+
+
+ void
+ DoFInfo::assign_ghosts (const std::vector<unsigned int> &boundary_cells)
+ {
+ Assert (boundary_cells.size() < row_starts.size(), ExcInternalError());
+
+ // sort ghost dofs and compress out duplicates
+ const unsigned int n_owned = (vector_partitioner->local_range().second-
+ vector_partitioner->local_range().first);
+ const unsigned int n_ghosts = ghost_dofs.size();
+ unsigned int n_unique_ghosts= 0;
+#ifdef DEBUG
+ for (std::vector<unsigned int>::iterator dof = dof_indices.begin();
+ dof!=dof_indices.end(); ++dof)
+ AssertIndexRange (*dof, n_owned+n_ghosts);
+#endif
+
+ std::vector<unsigned int> ghost_numbering (n_ghosts);
+ IndexSet ghost_indices (vector_partitioner->size());
+ if (n_ghosts > 0)
+ {
+ // since we need to go back to the
+ // local_to_global indices and replace the
+ // temporary numbering of ghosts by the real
+ // number in the index set, we need to store
+ // these values
+ std::vector<std::pair<unsigned int,unsigned int> > ghost_origin(n_ghosts);
+ for (unsigned int i=0; i<n_ghosts; ++i)
+ {
+ ghost_origin[i].first = ghost_dofs[i];
+ ghost_origin[i].second = i;
+ }
+ std::sort (ghost_origin.begin(), ghost_origin.end());
+ unsigned int last_contiguous_start = ghost_origin[0].first;
+ ghost_numbering[ghost_origin[0].second] = 0;
+ for (unsigned int i=1; i<n_ghosts; i++)
+ {
+ if (ghost_origin[i].first>ghost_origin[i-1].first+1)
+ {
+ ghost_indices.add_range (last_contiguous_start,
+ ghost_origin[i-1].first+1);
+ last_contiguous_start = ghost_origin[i].first;
+ }
+ if (ghost_origin[i].first>ghost_origin[i-1].first)
+ ++n_unique_ghosts;
+ ghost_numbering[ghost_origin[i].second] = n_unique_ghosts;
+ }
+ ++n_unique_ghosts;
+ ghost_indices.add_range (last_contiguous_start,
+ ghost_origin.back().first+1);
+ ghost_indices.compress();
+
+ // make sure that we got the correct local
+ // numbering of the ghost dofs. the ghost
+ // index set should store the same number
+ {
+ AssertDimension (n_unique_ghosts, ghost_indices.n_elements());
+ for (unsigned int i=0; i<n_ghosts; ++i)
+ Assert (ghost_numbering[i] ==
+ ghost_indices.index_within_set(ghost_dofs[i]),
+ ExcInternalError());
+ }
+
+ // apply correct numbering for ghost indices:
+ // We previously just enumerated them
+ // according to their appearance in the
+ // local_to_global structure. Above, we
+ // derived a relation between this enumeration
+ // and the actual number
+ const unsigned int n_boundary_cells = boundary_cells.size();
+ for (unsigned int i=0; i<n_boundary_cells; ++i)
+ {
+ unsigned int * data_ptr = const_cast<unsigned int*>
+ (begin_indices(boundary_cells[i]));
+ const unsigned int * row_end = end_indices(boundary_cells[i]);
+ for ( ; data_ptr != row_end; ++data_ptr)
+ *data_ptr = ((*data_ptr < n_owned)
+ ?
+ *data_ptr
+ :
+ n_owned +
+ ghost_numbering[*data_ptr - n_owned]);
+
+ // now the same procedure for plain indices
+ if (store_plain_indices == true)
+ {
+ if (row_length_indicators(boundary_cells[i]) > 0)
+ {
+ unsigned int * data_ptr = const_cast<unsigned int*>
+ (begin_indices_plain(boundary_cells[i]));
+ const unsigned int * row_end =
+ (end_indices_plain(boundary_cells[i]));
+ for ( ; data_ptr != row_end; ++data_ptr)
+ *data_ptr = ((*data_ptr < n_owned)
+ ?
+ *data_ptr
+ :
+ n_owned +
+ ghost_numbering[*data_ptr - n_owned]);
+ }
+ }
+ }
+ }
+
+ std::vector<unsigned int> new_ghosts;
+ ghost_dofs.swap(new_ghosts);
+ // set the ghost indices now. need to cast
+ // away constness here, but that is uncritical
+ // since we reset the Partitioner in the same
+ // initialize call as this call here.
+ Utilities::MPI::Partitioner * vec_part =
+ const_cast<Utilities::MPI::Partitioner*>(vector_partitioner.get());
+ vec_part->set_ghost_indices (ghost_indices);
+ }
+
+
+
+ void
+ DoFInfo::reorder_cells (const SizeInfo &size_info,
+ const std::vector<unsigned int> &renumbering,
+ const std::vector<unsigned int> &constraint_pool_row_index,
+ const std::vector<unsigned int> &irregular_cells,
+ const unsigned int n_vectors)
+ {
+ // first reorder the active fe index.
+ if (cell_active_fe_index.size() > 0)
+ {
+ std::vector<unsigned int> new_active_fe_index;
+ new_active_fe_index.reserve (size_info.n_macro_cells);
+ std::vector<unsigned int> fe_indices(n_vectors);
+ unsigned int position_cell = 0;
+ for (unsigned int cell=0; cell<size_info.n_macro_cells; ++cell)
+ {
+ const unsigned int n_comp = (irregular_cells[cell] > 0 ?
+ irregular_cells[cell] : n_vectors);
+ for (unsigned int j=0; j<n_comp; ++j)
+ fe_indices[j]=cell_active_fe_index[renumbering[position_cell+j]];
+
+ // by construction, all cells should
+ // have the same fe index.
+ for (unsigned int j=1; j<n_comp; ++j)
+ Assert (fe_indices[j] == fe_indices[0], ExcInternalError());
+
+ new_active_fe_index.push_back(fe_indices[0]);
+ position_cell += n_comp;
+ }
+ std::swap (new_active_fe_index, cell_active_fe_index);
+ }
+
+ std::vector<std_cxx1x::tuple<unsigned int,unsigned int,
+ unsigned int> > new_row_starts;
+ std::vector<unsigned int> new_dof_indices;
+ std::vector<std::pair<unsigned short,unsigned short> >
+ new_constraint_indicator;
+ std::vector<unsigned int> new_plain_indices, new_rowstart_plain;
+ unsigned int position_cell = 0;
+ new_row_starts.resize (size_info.n_macro_cells + 1);
+ new_dof_indices.reserve (dof_indices.size());
+ new_constraint_indicator.reserve (constraint_indicator.size());
+ if (store_plain_indices == true)
+ {
+ new_rowstart_plain.resize (size_info.n_macro_cells + 1,
+ numbers::invalid_unsigned_int);
+ new_plain_indices.reserve (plain_dof_indices.size());
+ }
+
+ // copy the indices and the constraint
+ // indicators to the new data field: Store the
+ // indices in a way so that adjacent data
+ // fields in local vectors are adjacent, i.e.,
+ // first dof index 0 for all vectors, then dof
+ // index 1 for all vectors, and so on. This
+ // involves some extra resorting.
+ std::vector<const unsigned int*> glob_indices (n_vectors);
+ std::vector<const unsigned int*> plain_glob_indices (n_vectors);
+ std::vector<const std::pair<unsigned short,unsigned short>*>
+ constr_ind(n_vectors), constr_end(n_vectors);
+ std::vector<unsigned int> index(n_vectors);
+ for (unsigned int i=0; i<size_info.n_macro_cells; ++i)
+ {
+ const unsigned int dofs_mcell =
+ dofs_per_cell[cell_active_fe_index.size() == 0 ? 0 :
+ cell_active_fe_index[i]] * n_vectors;
+ new_row_starts[i] =
+ std_cxx1x::tuple<unsigned int,unsigned int,unsigned int>
+ (new_dof_indices.size(), new_constraint_indicator.size(),
+ irregular_cells[i]);
+
+ const unsigned int n_comp = (irregular_cells[i]>0 ?
+ irregular_cells[i] : n_vectors);
+
+ for (unsigned int j=0; j<n_comp; ++j)
+ {
+ glob_indices[j] = begin_indices(renumbering[position_cell+j]);
+ constr_ind[j] = begin_indicators(renumbering[position_cell+j]);
+ constr_end[j] = end_indicators(renumbering[position_cell+j]);
+ index[j] = 0;
+ }
+
+ bool has_constraints = false;
+ if (store_plain_indices == true)
+ {
+ for (unsigned int j=0; j<n_comp; ++j)
+ if (begin_indicators(renumbering[position_cell+j]) <
+ end_indicators(renumbering[position_cell+j]))
+ {
+ plain_glob_indices[j] =
+ begin_indices_plain (renumbering[position_cell+j]);
+ has_constraints = true;
+ }
+ else
+ plain_glob_indices[j] =
+ begin_indices (renumbering[position_cell+j]);
+ if (has_constraints == true)
+ new_rowstart_plain[i] = new_plain_indices.size();
+ }
+
+ unsigned int m_ind_local = 0, m_index = 0;
+ while (m_ind_local < dofs_mcell)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ {
+ // last cell: nothing to do
+ if (j >= n_comp)
+ {
+ ++m_ind_local;
+ continue;
+ }
+
+ // otherwise, check if we are a constrained
+ // dof. The dof is not constrained if we are
+ // at the end of the row for the constraints
+ // (indi[j] == n_indi[j]) or if the local
+ // index[j] is smaller than the next position
+ // for a constraint. Then, just copy
+ // it. otherwise, copy all the entries that
+ // come with this dof
+ if (constr_ind[j] == constr_end[j] ||
+ index[j] < constr_ind[j]->first)
+ {
+ new_dof_indices.push_back (*glob_indices[j]);
+ ++m_index;
+ ++index[j];
+ ++glob_indices[j];
+ }
+ else
+ {
+ const unsigned int constraint_loc = constr_ind[j]->second;
+ new_constraint_indicator.push_back
+ (std::pair<unsigned short,unsigned short> (m_index, constraint_loc));
+ for(unsigned int k=constraint_pool_row_index[constraint_loc];
+ k<constraint_pool_row_index[constraint_loc+1];
+ ++k, ++glob_indices[j])
+ new_dof_indices.push_back (*glob_indices[j]);
+ ++constr_ind[j];
+ m_index = 0;
+ index[j] = 0;
+ }
+ if (store_plain_indices==true && has_constraints==true)
+ new_plain_indices.push_back (*plain_glob_indices[j]++);
+ ++m_ind_local;
+ }
+
+ for (unsigned int j=0; j<n_comp; ++j)
+ Assert (glob_indices[j]==end_indices(renumbering[position_cell+j]),
+ ExcInternalError());
+ position_cell += n_comp;
+ }
+ AssertDimension (position_cell+1, row_starts.size());
+
+ new_row_starts[size_info.n_macro_cells] =
+ std_cxx1x::tuple<unsigned int,unsigned int,unsigned int>
+ (new_dof_indices.size(), new_constraint_indicator.size(), 0);
+
+ AssertDimension(dof_indices.size(), new_dof_indices.size());
+ AssertDimension(constraint_indicator.size(),
+ new_constraint_indicator.size());
+
+ new_row_starts.swap (row_starts);
+ new_dof_indices.swap (dof_indices);
+ new_constraint_indicator.swap (constraint_indicator);
+ new_plain_indices.swap (plain_dof_indices);
+ new_rowstart_plain.swap (row_starts_plain_indices);
+
+#ifdef DEBUG
+ // sanity check 1: all indices should be
+ // smaller than the number of dofs locally
+ // owned plus the number of ghosts
+ const unsigned int index_range = (vector_partitioner->local_range().second-
+ vector_partitioner->local_range().first)
+ + vector_partitioner->ghost_indices().n_elements();
+ for (unsigned int i=0; i<dof_indices.size(); ++i)
+ AssertIndexRange (dof_indices[i], index_range);
+
+ // sanity check 2: for the constraint
+ // indicators, the first index should be
+ // smaller than the number of indices in the
+ // row, and the second index should be smaller
+ // than the number of constraints in the
+ // constraint pool.
+ for (unsigned int row=0; row<size_info.n_macro_cells; ++row)
+ {
+ const unsigned int row_length_ind = row_length_indices(row);
+ const std::pair<unsigned short,unsigned short>
+ * con_it = begin_indicators(row), * end_con = end_indicators(row);
+ for ( ; con_it != end_con; ++con_it)
+ {
+ AssertIndexRange (con_it->first, row_length_ind+1);
+ AssertIndexRange (con_it->second,
+ constraint_pool_row_index.size()-1);
+ }
+ }
+
+ // sanity check 3: all non-boundary cells
+ // should have indices that only refer to the
+ // locally owned range
+ const unsigned int local_size = (vector_partitioner->local_range().second-
+ vector_partitioner->local_range().first);
+ for (unsigned int row=0; row<size_info.boundary_cells_start; ++row)
+ {
+ const unsigned int * ptr = begin_indices(row);
+ const unsigned int * end_ptr = end_indices (row);
+ for ( ; ptr != end_ptr; ++ptr)
+ AssertIndexRange (*ptr, local_size);
+ }
+ for (unsigned int row=size_info.boundary_cells_end;
+ row<size_info.n_macro_cells; ++row)
+ {
+ const unsigned int * ptr = begin_indices(row);
+ const unsigned int * end_ptr = end_indices (row);
+ for ( ; ptr != end_ptr; ++ptr)
+ AssertIndexRange (*ptr, local_size);
+ }
+#endif
+ }
+
+
+
+ void DoFInfo::guess_block_size (const SizeInfo &size_info,
+ TaskInfo &task_info)
+ {
+ // user did not say a positive number, so we
+ // have to guess
+ if (task_info.block_size == 0)
+ {
+ // we would like to have enough work to do, so
+ // as first guess, try to get 50 times as many
+ // chunks as we have threads on the
+ // system.
+ task_info.block_size =
+ size_info.n_macro_cells / (multithread_info.n_default_threads * 50);
+
+ // if there are too few degrees of freedom per
+ // cell, need to increase the block size
+ if (dofs_per_cell[0] * task_info.block_size <
+ internal::minimum_parallel_grain_size)
+ task_info.block_size = (internal::minimum_parallel_grain_size /
+ dofs_per_cell[0] + 1);
+ }
+ if (task_info.block_size > size_info.n_macro_cells)
+ task_info.block_size = size_info.n_macro_cells;
+ }
+
+
+
+ void DoFInfo::make_thread_graph_partition_color
+ (SizeInfo &size_info,
+ TaskInfo &task_info,
+ std::vector<unsigned int> &renumbering,
+ std::vector<unsigned int> &irregular_cells,
+ const bool hp_bool)
+ {
+ if (size_info.n_macro_cells == 0)
+ return;
+
+ const std::size_t n_vectors = size_info.n_vectors;
+ Assert (n_vectors > 0, ExcInternalError());
+
+ guess_block_size (size_info, task_info);
+
+ // set up partitions. if we just use coloring
+ // without partitions, do nothing here, assume
+ // all cells to belong to the zero partition
+ // (that we otherwise use for MPI boundary
+ // cells)
+ unsigned int partition = 0, start_up = 0, counter = 0;
+ unsigned int start_nonboundary = numbers::invalid_unsigned_int;
+ bool work = true;
+ if (task_info.use_coloring_only == false)
+ {
+ start_nonboundary =
+ std::min(((size_info.boundary_cells_end+task_info.block_size-1)/
+ task_info.block_size)*task_info.block_size,
+ size_info.n_macro_cells);
+ start_up = start_nonboundary;
+ size_info.boundary_cells_end = start_nonboundary;
+ }
+ else
+ {
+ start_nonboundary = size_info.n_macro_cells;
+ start_up = size_info.n_macro_cells;
+ size_info.boundary_cells_start = 0;
+ size_info.boundary_cells_end = size_info.n_macro_cells;
+ }
+ if (hp_bool == true)
+ {
+ irregular_cells.resize (0);
+ irregular_cells.resize (size_info.n_macro_cells+2*max_fe_index);
+ std::vector<std::vector<unsigned int> > renumbering_fe_index;
+ renumbering_fe_index.resize(max_fe_index);
+ unsigned int counter,n_macro_cells_before = 0;
+ for(counter=0;counter<start_nonboundary*n_vectors;
+ counter++)
+ {
+ renumbering_fe_index[cell_active_fe_index[renumbering[counter]]].
+ push_back(renumbering[counter]);
+ }
+ counter = 0;
+ for (unsigned int j=0;j<max_fe_index;j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].size();jj++)
+ renumbering[counter++] = renumbering_fe_index[j][jj];
+ irregular_cells[renumbering_fe_index[j].size()/n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].size()+n_vectors-1)/
+ n_vectors;
+ renumbering_fe_index[j].resize(0);
+ }
+
+ unsigned int new_boundary_end = n_macro_cells_before;
+ for(counter=start_nonboundary*n_vectors;
+ counter<size_info.n_active_cells; counter++)
+ {
+ renumbering_fe_index[cell_active_fe_index[renumbering[counter]]].
+ push_back(renumbering[counter]);
+ }
+ counter = start_nonboundary * n_vectors;
+ for (unsigned int j=0;j<max_fe_index;j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].size();jj++)
+ renumbering[counter++] = renumbering_fe_index[j][jj];
+ irregular_cells[renumbering_fe_index[j].size()/n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].size()+n_vectors-1)/
+ n_vectors;
+ }
+ AssertIndexRange (n_macro_cells_before,
+ size_info.n_macro_cells + 2*max_fe_index+1);
+ irregular_cells.resize (n_macro_cells_before);
+ size_info.n_macro_cells = n_macro_cells_before;
+ size_info.boundary_cells_start = 0;
+ size_info.boundary_cells_end = new_boundary_end;
+ task_info.n_blocks = (size_info.n_macro_cells+task_info.block_size-1)
+ /task_info.block_size;
+ task_info.block_size_last = size_info.n_macro_cells%task_info.block_size;
+ if (task_info.block_size_last == 0)
+ task_info.block_size_last = task_info.block_size;
+ }
+
+ // assume that all FEs have the same
+ // connectivity graph, so take the zeroth FE
+ task_info.n_blocks = (size_info.n_macro_cells+task_info.block_size-1)/
+ task_info.block_size;
+ task_info.block_size_last = size_info.n_macro_cells-
+ (task_info.block_size*(task_info.n_blocks-1));
+
+ // create the connectivity graph with internal
+ // blocking
+ CompressedSimpleSparsityPattern connectivity;
+ make_connectivity_graph (size_info, task_info, renumbering,irregular_cells,
+ true, connectivity);
+
+ // Create cell-block partitioning.
+
+ // For each block of cells, this variable
+ // saves to which partitions the block
+ // belongs. Initialize all to n_macro_cells to
+ // mark them as not yet assigned a partition.
+ std::vector<unsigned int> cell_partition(task_info.n_blocks,
+ size_info.n_macro_cells);
+ std::vector<unsigned int> neighbor_list;
+ std::vector<unsigned int> neighbor_neighbor_list;
+
+ // In element j of this variable, one puts the
+ // old number of the block that should be the
+ // jth block in the new numeration.
+ std::vector<unsigned int> partition_list (task_info.n_blocks,0);
+ std::vector<unsigned int> partition_color_list(task_info.n_blocks,0);
+
+ // This vector points to the start of each
+ // partition.
+ std::vector<unsigned int> partition_blocks (2,0);
+ std::vector<unsigned int> cell_color(task_info.n_blocks,
+ size_info.n_macro_cells);
+ std::vector<bool> color_finder;
+
+ while(work)
+ {
+ // put all cells up to begin_inner_cells into
+ // first partition. if the numbers do not add
+ // up exactly, assign an additional block
+ if(start_nonboundary>0 && start_up == start_nonboundary)
+ {
+ unsigned int n_blocks = ((start_nonboundary+task_info.block_size-1)
+ /task_info.block_size);
+ for(unsigned int cell=0; cell<n_blocks; ++cell)
+ {
+ cell_partition[cell] = partition;
+ neighbor_list.push_back(cell);
+ partition_list[counter++] = cell;
+ partition_blocks.back()++;
+ }
+ }
+ else
+ {
+ // To start up, set the start_up cell to
+ // partition and list all its neighbors.
+ cell_partition[start_up] = partition;
+ neighbor_list.push_back(start_up);
+ partition_list[counter++] = start_up;
+ partition_blocks.back()++;
+ }
+
+ while(neighbor_list.size()>0)
+ {
+ partition++;
+ partition_blocks.push_back(partition_blocks.back());
+ for(unsigned int j=0;j<neighbor_list.size();++j)
+ {
+ Assert(cell_partition[neighbor_list[j]]==partition-1,
+ ExcInternalError());
+ CompressedSimpleSparsityPattern::row_iterator neighbor =
+ connectivity.row_begin(neighbor_list[j]),
+ end = connectivity.row_end(neighbor_list[j]);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor]==size_info.n_macro_cells)
+ {
+ partition_blocks.back()++;
+ cell_partition[*neighbor] = partition;
+ neighbor_neighbor_list.push_back(*neighbor);
+ partition_list[counter++] = *neighbor;
+ }
+ }
+ }
+ neighbor_list = neighbor_neighbor_list;
+ neighbor_neighbor_list.resize(0);
+ }
+
+ // One has to check if the graph is not
+ // connected so we have to find another
+ // partition.
+ work = false;
+ for (unsigned int j=start_up;j<task_info.n_blocks;++j)
+ if(cell_partition[j] == size_info.n_macro_cells)
+ {
+ start_up = j;
+ work = true;
+ break;
+ }
+ }
+ AssertDimension (partition_blocks[partition], task_info.n_blocks);
+
+
+ // Color the cells within each partition
+ task_info.partition_color_blocks.row_index.resize(partition+1);
+ unsigned int color_counter = 0, index_counter = 0;
+ for(unsigned int part=0; part<partition; part++)
+ {
+ task_info.partition_color_blocks.row_index[part] = index_counter;
+ unsigned int max_color = 0;
+ for (unsigned int k=partition_blocks[part]; k<partition_blocks[part+1];
+ k++)
+ {
+ unsigned int cell = partition_list[k];
+ unsigned int no_neighbors = connectivity.row_length(cell);
+
+ // In the worst case, each neighbor has a
+ // different color. So we find at least one
+ // available color between 0 and no_neighbors.
+ color_finder.resize(no_neighbors+1);
+ for (unsigned int j=0; j<=no_neighbors; ++j)
+ color_finder[j]=true;
+ CompressedSimpleSparsityPattern::row_iterator
+ neighbor = connectivity.row_begin(cell),
+ end = connectivity.row_end(cell);
+ for(; neighbor!=end ;++neighbor)
+ {
+ // Mark the color that a neighbor within the
+ // partition has as taken
+ if (cell_partition[*neighbor] == part &&
+ cell_color[*neighbor] <= no_neighbors)
+ color_finder[cell_color[*neighbor]] = false;
+ }
+ // Choose the smallest color that is not taken
+ // for the block
+ cell_color[cell]=0;
+ while (color_finder[cell_color[cell]] == false)
+ cell_color[cell]++;
+ if (cell_color[cell] > max_color)
+ max_color = cell_color[cell];
+ }
+ // Reorder within partition: First, all blocks
+ // that belong the 0 and then so on until
+ // those with color max (Note that the smaller
+ // the number the larger the partition)
+ for(unsigned int color=0; color<=max_color; color++)
+ {
+ task_info.partition_color_blocks.data.push_back(color_counter);
+ index_counter++;
+ for (unsigned int k=partition_blocks[part];
+ k<partition_blocks[part+1]; k++)
+ {
+ unsigned int cell=partition_list[k];
+ if (cell_color[cell] == color)
+ {
+ partition_color_list[color_counter++] = cell;
+ }
+ }
+ }
+ }
+ task_info.partition_color_blocks.data.push_back(task_info.n_blocks);
+ task_info.partition_color_blocks.row_index[partition] = index_counter;
+ AssertDimension (color_counter, task_info.n_blocks);
+
+ partition_list = renumbering;
+
+ // in debug mode, check that the partition
+ // color list is one-to-one
+#ifdef DEBUG
+ {
+ std::vector<unsigned int> sorted_pc_list (partition_color_list);
+ std::sort(sorted_pc_list.begin(), sorted_pc_list.end());
+ for (unsigned int i=0; i<sorted_pc_list.size(); ++i)
+ Assert(sorted_pc_list[i] == i, ExcInternalError());
+ }
+#endif
+
+ // set the start list for each block and
+ // compute the renumbering of cells
+ std::vector<unsigned int> block_start(size_info.n_macro_cells+1);
+ std::vector<unsigned int> irregular(size_info.n_macro_cells);
+
+ unsigned int mcell_start=0;
+ block_start[0] = 0;
+ for(unsigned int block=0; block<task_info.n_blocks; block++)
+ {
+ block_start[block+1] = block_start[block];
+ for(unsigned int mcell=mcell_start;mcell<
+ std::min(mcell_start+task_info.block_size,
+ size_info.n_macro_cells);
+ ++mcell)
+ {
+ unsigned int n_comp = (irregular_cells[mcell]>0)
+ ?irregular_cells[mcell]:size_info.n_vectors;
+ block_start[block+1] += n_comp;
+ ++counter;
+ }
+ mcell_start += task_info.block_size;
+ }
+ counter = 0;
+ unsigned int counter_macro = 0;
+ for(unsigned int block=0; block<task_info.n_blocks; block++)
+ {
+ unsigned int present_block = partition_color_list[block];
+ for(unsigned int cell = block_start[present_block];
+ cell<block_start[present_block+1];++cell)
+ renumbering[counter++] = partition_list[cell];
+ unsigned int this_block_size = (present_block == task_info.n_blocks-1)?
+ task_info.block_size_last:task_info.block_size;
+ for(unsigned int j=0;j<this_block_size;j++)
+ irregular[counter_macro++] =
+ irregular_cells[present_block*task_info.block_size+j];
+ if(present_block == task_info.n_blocks-1)
+ task_info.position_short_block = block;
+ }
+ irregular_cells.swap(irregular);
+ AssertDimension (counter, size_info.n_active_cells);
+ AssertDimension (counter_macro, size_info.n_macro_cells);
+
+ // check that the renumbering is one-to-one
+#ifdef DEBUG
+ {
+ std::vector<unsigned int> sorted_renumbering (renumbering);
+ std::sort(sorted_renumbering.begin(), sorted_renumbering.end());
+ for (unsigned int i=0; i<sorted_renumbering.size(); ++i)
+ Assert(sorted_renumbering[i] == i, ExcInternalError());
+ }
+#endif
+ AssertDimension(counter,size_info.n_active_cells);
+ task_info.evens = (partition+1)>>1;
+ task_info.odds = (partition)>>1;
+ task_info.n_blocked_workers = task_info.odds-
+ (task_info.odds+task_info.evens+1)%2;
+ task_info.n_workers = task_info.partition_color_blocks.data.size()-1-
+ task_info.n_blocked_workers;
+ }
+
+
+
+ void
+ DoFInfo::make_thread_graph_partition_partition
+ (SizeInfo &size_info,
+ TaskInfo &task_info,
+ std::vector<unsigned int> &renumbering,
+ std::vector<unsigned int> &irregular_cells,
+ const bool hp_bool)
+ {
+ if (size_info.n_macro_cells == 0)
+ return;
+
+ const std::size_t n_vectors = size_info.n_vectors;
+ Assert (n_vectors > 0, ExcInternalError());
+
+ guess_block_size (size_info, task_info);
+
+ // assume that all FEs have the same
+ // connectivity graph, so take the zeroth FE
+ task_info.n_blocks = (size_info.n_macro_cells+task_info.block_size-1)/
+ task_info.block_size;
+ task_info.block_size_last = size_info.n_macro_cells-
+ (task_info.block_size*(task_info.n_blocks-1));
+ task_info.position_short_block = task_info.n_blocks-1;
+ unsigned int cluster_size = task_info.block_size*n_vectors;
+
+ // create the connectivity graph without
+ // internal blocking
+ CompressedSimpleSparsityPattern connectivity;
+ make_connectivity_graph (size_info, task_info, renumbering,irregular_cells,
+ false, connectivity);
+
+ // Create cell-block partitioning.
+
+ // For each block of cells, this variable
+ // saves to which partitions the block
+ // belongs. Initialize all to n_macro_cells to
+ // mark them as not yet assigned a partition.
+ std::vector<unsigned int> cell_partition (size_info.n_active_cells,
+ size_info.n_active_cells);
+ std::vector<unsigned int> neighbor_list;
+ std::vector<unsigned int> neighbor_neighbor_list;
+
+ // In element j of this variable, one puts the
+ // old number of the block that should be the
+ // jth block in the new numeration.
+ std::vector<unsigned int> partition_list(size_info.n_active_cells,0);
+ std::vector<unsigned int> partition_partition_list(size_info.n_active_cells,0);
+
+ // This vector points to the start of each
+ // partition.
+ std::vector<unsigned int> partition_size(2,0);
+
+ unsigned int partition = 0,start_up=0,counter=0;
+ unsigned int start_nonboundary = n_vectors * size_info.boundary_cells_end;
+ if (start_nonboundary > size_info.n_active_cells)
+ start_nonboundary = size_info.n_active_cells;
+ bool work = true;
+ unsigned int remainder = cluster_size;
+ while (work)
+ {
+ // put the cells with neighbors on remote MPI
+ // processes up front
+ if(start_nonboundary>0)
+ {
+ for(unsigned int cell=0; cell<start_nonboundary; ++cell)
+ {
+ const unsigned int cell_nn = renumbering[cell];
+ cell_partition[cell_nn] = partition;
+ neighbor_list.push_back(cell_nn);
+ partition_list[counter++] = cell_nn;
+ partition_size.back()++;
+ }
+ remainder -= (start_nonboundary%cluster_size);
+ if(remainder == cluster_size)
+ remainder = 0;
+
+ // adjust end of boundary cells to the
+ // remainder
+ size_info.boundary_cells_end += (remainder+n_vectors-1)/n_vectors;
+ }
+ else
+ {
+ // To start up, set the start_up cell to
+ // partition and list all its neighbors.
+ cell_partition[start_up] = partition;
+ neighbor_list.push_back(start_up);
+ partition_list[counter++] = start_up;
+ partition_size.back()++;
+ start_up++;
+ remainder--;
+ if(remainder == cluster_size)
+ remainder = 0;
+ }
+ int index_before = neighbor_list.size(), index = index_before,
+ index_stop = 0;
+ while (remainder>0)
+ {
+ if(index==index_stop)
+ {
+ index = neighbor_list.size();
+ if(index == index_before)
+ {
+ neighbor_list.resize(0);
+ goto not_connect;
+ }
+ index_stop = index_before;
+ index_before = index;
+ }
+ index--;
+ unsigned int additional = neighbor_list[index];
+ CompressedSimpleSparsityPattern::row_iterator neighbor =
+ connectivity.row_begin(additional),
+ end = connectivity.row_end(additional);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor]==size_info.n_active_cells)
+ {
+ partition_size.back()++;
+ cell_partition[*neighbor] = partition;
+ neighbor_list.push_back(*neighbor);
+ partition_list[counter++] = *neighbor;
+ remainder--;
+ if(remainder == 0)
+ break;
+ }
+ }
+ }
+
+ while(neighbor_list.size()>0)
+ {
+ partition++;
+ unsigned int partition_counter = 0;
+ partition_size.push_back(partition_size.back());
+
+ for(unsigned int j=0;j<neighbor_list.size();++j)
+ {
+ Assert(cell_partition[neighbor_list[j]]==partition-1,
+ ExcInternalError());
+ CompressedSimpleSparsityPattern::row_iterator neighbor =
+ connectivity.row_begin(neighbor_list[j]),
+ end = connectivity.row_end(neighbor_list[j]);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor]==size_info.n_active_cells)
+ {
+ partition_size.back()++;
+ cell_partition[*neighbor] = partition;
+ neighbor_neighbor_list.push_back(*neighbor);
+ partition_list[counter++] = *neighbor;
+ partition_counter++;
+ }
+ }
+ }
+ remainder = cluster_size-(partition_counter%cluster_size);
+ if(remainder == cluster_size)
+ remainder = 0;
+ int index_stop = 0;
+ int index_before = neighbor_neighbor_list.size(), index = index_before;
+ while (remainder>0)
+ {
+ if(index==index_stop)
+ {
+ index = neighbor_neighbor_list.size();
+ if(index == index_before)
+ {
+ neighbor_neighbor_list.resize(0);
+ break;
+ }
+ index_stop = index_before;
+ index_before = index;
+ }
+ index--;
+ unsigned int additional = neighbor_neighbor_list[index];
+ CompressedSimpleSparsityPattern::row_iterator neighbor =
+ connectivity.row_begin(additional),
+ end = connectivity.row_end(additional);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor]==size_info.n_active_cells)
+ {
+ partition_size.back()++;
+ cell_partition[*neighbor] = partition;
+ neighbor_neighbor_list.push_back(*neighbor);
+ partition_list[counter++] = *neighbor;
+ remainder--;
+ if(remainder == 0)
+ break;
+ }
+ }
+ }
+
+ neighbor_list = neighbor_neighbor_list;
+ neighbor_neighbor_list.resize(0);
+ }
+ not_connect:
+ // One has to check if the graph is not
+ // connected so we have to find another
+ // partition.
+ work = false;
+ for (unsigned int j=start_up;j<size_info.n_active_cells;++j)
+ if(cell_partition[j] == size_info.n_active_cells)
+ {
+ start_up = j;
+ work = true;
+ if(remainder == 0)
+ remainder = cluster_size;
+ break;
+ }
+ }
+ if(remainder != 0)
+ partition++;
+
+ for(unsigned int j=0;j<renumbering.size();j++)
+ renumbering[j] = 0;
+ irregular_cells.back() = 0;
+ irregular_cells.resize(size_info.n_active_cells);
+ unsigned int n_macro_cells_before = 0;
+ {
+ // Create partitioning within partitions.
+
+ // For each block of cells, this variable
+ // saves to which partitions the block
+ // belongs. Initialize all to n_macro_cells to
+ // mark them as not yet assigned a partition.
+ std::vector<unsigned int> cell_partition_l2(size_info.n_active_cells,
+ size_info.n_active_cells);
+ task_info.partition_color_blocks.row_index.resize(partition+1,0);
+ task_info.partition_color_blocks.data.resize(1,0);
+
+ start_up = 0;
+ counter = 0;
+ unsigned int missing_macros;
+ for (unsigned int part=0;part<partition; ++part)
+ {
+ neighbor_neighbor_list.resize(0);
+ neighbor_list.resize(0);
+ bool work = true;
+ unsigned int partition_l2 = 0;
+ start_up = partition_size[part];
+ unsigned int partition_counter = 0;
+ while(work)
+ {
+ if (neighbor_list.size()==0)
+ {
+ work = false;
+ partition_counter = 0;
+ for (unsigned int j=start_up; j<partition_size[part+1];++j)
+ if(cell_partition[partition_list[j]] == part &&
+ cell_partition_l2[partition_list[j]] == size_info.n_active_cells)
+ {
+ start_up = j;
+ work = true;
+ partition_counter = 1;
+ // To start up, set the start_up cell to
+ // partition and list all its neighbors.
+ AssertIndexRange (start_up, partition_size[part+1]);
+ cell_partition_l2[partition_list[start_up]] =
+ partition_l2;
+ neighbor_neighbor_list.push_back
+ (partition_list[start_up]);
+ partition_partition_list[counter++] =
+ partition_list[start_up];
+ start_up++;
+ break;
+ }
+ }
+ else
+ {
+ partition_counter = 0;
+ for(unsigned int j=0;j<neighbor_list.size();++j)
+ {
+ Assert(cell_partition[neighbor_list[j]]==part,
+ ExcInternalError());
+ Assert(cell_partition_l2[neighbor_list[j]]==partition_l2-1,
+ ExcInternalError());
+ CompressedSimpleSparsityPattern::row_iterator neighbor =
+ connectivity.row_begin(neighbor_list[j]),
+ end = connectivity.row_end(neighbor_list[j]);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor] == part &&
+ cell_partition_l2[*neighbor]==
+ size_info.n_active_cells)
+ {
+ cell_partition_l2[*neighbor] = partition_l2;
+ neighbor_neighbor_list.push_back(*neighbor);
+ partition_partition_list[counter++] = *neighbor;
+ partition_counter++;
+ }
+ }
+ }
+ }
+ if (partition_counter>0)
+ {
+ int index_before = neighbor_neighbor_list.size(),
+ index = index_before;
+ {
+ // put the cells into separate lists for each
+ // FE index within one partition-partition
+ missing_macros = 0;
+ std::vector<unsigned int> remaining_per_macro_cell
+ (max_fe_index);
+ std::vector<std::vector<unsigned int> >
+ renumbering_fe_index;
+ unsigned int cell;
+ bool filled = true;
+ if(hp_bool == true)
+ {
+ renumbering_fe_index.resize(max_fe_index);
+ for(cell=counter-partition_counter; cell<counter; ++cell)
+ {
+ renumbering_fe_index
+ [cell_active_fe_index[partition_partition_list
+ [cell]]].
+ push_back(partition_partition_list[cell]);
+ }
+ // check how many more cells are needed in the
+ // lists
+ for (unsigned int j=0; j<max_fe_index; j++)
+ {
+ remaining_per_macro_cell[j] =
+ renumbering_fe_index[j].size()%n_vectors;
+ if(remaining_per_macro_cell[j] != 0)
+ filled = false;
+ missing_macros += ((renumbering_fe_index[j].size()+
+ n_vectors-1)/n_vectors);
+ }
+ }
+ else
+ {
+ remaining_per_macro_cell.resize(1);
+ remaining_per_macro_cell[0] = partition_counter%
+ n_vectors;
+ missing_macros = partition_counter/n_vectors;
+ if(remaining_per_macro_cell[0] != 0)
+ {
+ filled = false;
+ missing_macros++;
+ }
+ }
+ missing_macros = task_info.block_size -
+ (missing_macros%task_info.block_size);
+
+ // now we realized that there are some cells
+ // missing.
+ while (missing_macros>0 || filled == false)
+ {
+ if(index==0)
+ {
+ index = neighbor_neighbor_list.size();
+ if(index == index_before)
+ {
+ if(missing_macros != 0)
+ {
+ neighbor_neighbor_list.resize(0);
+ }
+ break;//goto not_connected;
+ }
+ index_before = index;
+ }
+ index--;
+ unsigned int additional = neighbor_neighbor_list
+ [index];
+
+ // go through the neighbors of the last cell
+ // in the current partition and check if we
+ // find some to fill up with.
+ CompressedSimpleSparsityPattern::row_iterator
+ neighbor =
+ connectivity.row_begin(additional),
+ end = connectivity.row_end(additional);
+ for(; neighbor!=end ;++neighbor)
+ {
+ if(cell_partition[*neighbor] == part &&
+ cell_partition_l2[*neighbor] ==
+ size_info.n_active_cells)
+ {
+ unsigned int this_index = 0;
+ if(hp_bool == true)
+ this_index = cell_active_fe_index[*neighbor];
+
+ // Only add this cell if we need more macro
+ // cells in the current block or if there is a
+ // macro cell with the FE index that is not
+ // yet fully populated
+ if (missing_macros > 0 ||
+ remaining_per_macro_cell[this_index] > 0)
+ {
+ cell_partition_l2[*neighbor] = partition_l2;
+ neighbor_neighbor_list.push_back(*neighbor);
+ if(hp_bool == true)
+ renumbering_fe_index[this_index].
+ push_back(*neighbor);
+ partition_partition_list[counter] =
+ *neighbor;
+ counter++;
+ partition_counter++;
+ if (remaining_per_macro_cell[this_index]
+ == 0 && missing_macros > 0)
+ missing_macros--;
+ remaining_per_macro_cell[this_index]++;
+ if (remaining_per_macro_cell[this_index]
+ == n_vectors)
+ {
+ remaining_per_macro_cell[this_index] = 0;
+ }
+ if(missing_macros == 0)
+ {
+ filled = true;
+ for(unsigned int fe_ind=0;
+ fe_ind<max_fe_index;++fe_ind)
+ if(remaining_per_macro_cell[fe_ind]!=0)
+ filled = false;
+ }
+ if (filled == true)
+ break;
+ }
+ }
+ }
+ }
+ if(hp_bool == true)
+ {
+ // set the renumbering according to their
+ // active FE index within one
+ // partition-partition which was implicitly
+ // assumed above
+ cell = counter - partition_counter;
+ for (unsigned int j=0; j<max_fe_index; j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].
+ size(); jj++)
+ renumbering[cell++] =
+ renumbering_fe_index[j][jj];
+ if(renumbering_fe_index[j].size()%n_vectors != 0)
+ irregular_cells[renumbering_fe_index[j].size()/
+ n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].
+ size()+n_vectors-1)/
+ n_vectors;
+ renumbering_fe_index[j].resize(0);
+ }
+ }
+ else
+ {
+ n_macro_cells_before += partition_counter/n_vectors;
+ if(partition_counter%n_vectors != 0)
+ {
+ irregular_cells[n_macro_cells_before] =
+ partition_counter%n_vectors;
+ n_macro_cells_before++;
+ }
+ }
+ }
+ task_info.partition_color_blocks.data.
+ push_back(n_macro_cells_before);
+ partition_l2++;
+ }
+ neighbor_list = neighbor_neighbor_list;
+ neighbor_neighbor_list.resize(0);
+ }
+ task_info.partition_color_blocks.row_index[part+1] =
+ task_info.partition_color_blocks.row_index[part] + partition_l2;
+ }
+ }
+
+ if(size_info.boundary_cells_end>0)
+ size_info.boundary_cells_end = task_info.partition_color_blocks.
+ data[task_info.partition_color_blocks.row_index[1]];
+
+ if (hp_bool == false)
+ renumbering.swap(partition_partition_list);
+ irregular_cells.resize(n_macro_cells_before);
+ size_info.n_macro_cells = n_macro_cells_before;
+
+ task_info.evens = (partition+1)/2;
+ task_info.odds = partition/2;
+ task_info.n_blocked_workers =
+ task_info.odds-(task_info.odds+task_info.evens+1)%2;
+ task_info.n_workers = task_info.evens+task_info.odds-
+ task_info.n_blocked_workers;
+ task_info.partition_evens.resize(partition);
+ task_info.partition_odds.resize(partition);
+ task_info.partition_n_blocked_workers.resize(partition);
+ task_info.partition_n_workers.resize(partition);
+ for(unsigned int part=0;part<partition;part++)
+ {
+ task_info.partition_evens[part] =
+ (task_info.partition_color_blocks.row_index[part+1]-
+ task_info.partition_color_blocks.row_index[part]+1)/2;
+ task_info.partition_odds[part] =
+ (task_info.partition_color_blocks.row_index[part+1]-
+ task_info.partition_color_blocks.row_index[part])/2;
+ task_info.partition_n_blocked_workers[part] =
+ task_info.partition_odds[part]-(task_info.partition_odds[part]+
+ task_info.partition_evens[part]+1)%2;
+ task_info.partition_n_workers[part] =
+ task_info.partition_evens[part]+task_info.partition_odds[part]-
+ task_info.partition_n_blocked_workers[part];
+ }
+ }
+
+
+ // internal sparsity pattern structure that is
+ // specialized to the situation in the setup
+ // of the cells the dofs belong to: allows
+ // only insertion of entries at the end
+ namespace internal
+ {
+ struct SimplifiedCompressedSparsity
+ {
+ static const unsigned int short_length = 8;
+ struct LineData
+ {
+ LineData () : n_entries (0) {};
+ void add (unsigned int entry)
+ {
+ if (n_entries < short_length)
+ {
+ if (n_entries == 0 || short_data[n_entries-1] < entry)
+ short_data[n_entries++] = entry;
+#ifdef DEBUG
+ else
+ Assert (short_data[n_entries-1] == entry, ExcInternalError());
+#endif
+ }
+ else
+ {
+ if (short_data[short_length-1] < entry)
+ {
+ Assert (n_entries == short_length, ExcInternalError());
+ if (additional_data.size()==0)
+ {
+#ifdef DEBUG
+ Assert (short_data[n_entries-1] < entry, ExcInternalError());
+#endif
+ additional_data.reserve (16);
+ additional_data.push_back (entry);
+ }
+ else if (additional_data.back() < entry)
+ additional_data.push_back (entry);
+#ifdef DEBUG
+ else
+ Assert (additional_data.back() == entry, ExcInternalError());
+#endif
+ }
+ }
+ }
+
+ unsigned int n_entries;
+ unsigned int short_data[short_length];
+ std::vector<unsigned int> additional_data;
+ };
+
+ SimplifiedCompressedSparsity (const unsigned int n_rows,
+ const unsigned int n_cols)
+ :
+ rows (n_rows),
+ n_cols (n_cols)
+ {}
+
+ void add (unsigned int row, unsigned int entry)
+ {
+ AssertIndexRange (row, rows.size());
+ AssertIndexRange (entry, n_cols);
+ rows[row].add (entry);
+ }
+
+ unsigned int row_length (unsigned int row) const
+ {
+ return rows[row].n_entries + rows[row].additional_data.size();
+ }
+
+ std::vector<LineData> rows;
+ unsigned int n_cols;
+ };
+
+ // rudimentary version of a vector that keeps
+ // entries always ordered
+ class ordered_vector : public std::vector<unsigned int>
+ {
+ public:
+ ordered_vector () { reserve (2000); }
+
+ void reserve (const unsigned int size)
+ {
+ if (size > 0)
+ this->std::vector<unsigned int>::reserve (size);
+ }
+
+
+ // insert a given entry. dat is a pointer
+ // within this vector (make sure that it
+ // really stays there, otherwise there will be
+ // an error)
+ void insert (const unsigned int entry,
+ std::vector<unsigned int>::iterator &dat)
+ {
+ AssertIndexRange (static_cast<unsigned int>(dat - begin()), size()+1);
+ AssertIndexRange (static_cast<unsigned int>(end() - dat), size()+1);
+ AssertIndexRange (size(), capacity());
+ while (dat != end() && *dat < entry)
+ ++dat;
+
+ if (dat == end())
+ {
+ push_back(entry);
+ }
+ else if (*dat > entry)
+ {
+ this->std::vector<unsigned int>::insert (dat, entry);
+ }
+ ++dat;
+ }
+ };
+ }
+
+
+ void
+ DoFInfo::make_connectivity_graph
+ (const SizeInfo &size_info,
+ const TaskInfo &task_info,
+ const std::vector<unsigned int> &renumbering,
+ const std::vector<unsigned int> &irregular_cells,
+ const bool do_blocking,
+ CompressedSimpleSparsityPattern &connectivity) const
+ {
+ AssertDimension (row_starts.size()-1, size_info.n_active_cells);
+ const unsigned int n_rows = (vector_partitioner->local_range().second-
+ vector_partitioner->local_range().first)
+ + vector_partitioner->ghost_indices().n_elements();
+ const unsigned int n_blocks = (do_blocking == true) ?
+ task_info.n_blocks : size_info.n_active_cells;
+
+ internal::SimplifiedCompressedSparsity
+ connectivity_dof (n_rows, n_blocks);
+ unsigned int cell_start = 0,mcell_start = 0;
+ for (unsigned int block = 0; block < n_blocks; ++block)
+ {
+
+ // if we have the blocking variant (used in
+ // the coloring scheme), we want to build a
+ // graph with the blocks with interaction with
+ // remote MPI processes up front. in the
+ // non-blocking variant, we do not do this
+ // here. TODO: unify this approach!!!
+ if (do_blocking == true)
+ {
+ for(unsigned int mcell=mcell_start;mcell<
+ std::min(mcell_start+task_info.block_size,
+ size_info.n_macro_cells);
+ ++mcell)
+ {
+ unsigned int n_comp = (irregular_cells[mcell]>0)
+ ?irregular_cells[mcell]:size_info.n_vectors;
+ for (unsigned int cell = cell_start; cell < cell_start+n_comp;
+ ++cell)
+ {
+ const unsigned int * it = begin_indices
+ (renumbering[cell]),
+ * end_cell = end_indices (renumbering[cell]);
+ for ( ; it != end_cell; ++it)
+ connectivity_dof.add(*it, block);
+ }
+ cell_start += n_comp;
+ }
+ mcell_start += task_info.block_size;
+ }
+ else
+ {
+ const unsigned int * it = begin_indices (block),
+ * end_cell = end_indices (block);
+ for ( ; it != end_cell; ++it)
+ connectivity_dof.add(*it, block);
+ }
+ }
+
+ connectivity.reinit (n_blocks, n_blocks);
+ internal::ordered_vector row_entries;
+ cell_start = 0;
+ mcell_start = 0;
+ for (unsigned int block=0; block < n_blocks; ++block)
+ {
+ row_entries.clear();
+
+ if(do_blocking==true)
+ {
+ for(unsigned int mcell=mcell_start;mcell<
+ std::min(mcell_start+task_info.block_size,
+ size_info.n_macro_cells);
+ ++mcell)
+ {
+ unsigned int n_comp = (irregular_cells[mcell]>0)
+ ?irregular_cells[mcell]:size_info.n_vectors;
+ for (unsigned int cell = cell_start; cell < cell_start+n_comp;
+ ++cell)
+ {
+ // apply renumbering when we do blocking
+ const unsigned int * it = begin_indices (renumbering[cell]),
+ * end_cell = end_indices (renumbering[cell]);
+ row_entries.reserve (row_entries.size() + end_cell - it);
+ for ( ; it != end_cell; ++it)
+ {
+ // the simplified sparsity pattern has two
+ // fields: a short field for dofs that only
+ // belong to a few cells, and a long field for
+ // all dofs with many cells that spills the
+ // short data field. so go them through both
+ const internal::SimplifiedCompressedSparsity::LineData & dat =
+ connectivity_dof.rows[*it];
+ std::vector<unsigned int>::iterator insert_pos = row_entries.begin();
+ for (unsigned int i=0; i<dat.n_entries; ++i)
+ if (dat.short_data[i] >= block)
+ goto end_this;
+ else
+ row_entries.insert (dat.short_data[i], insert_pos);
+ if (dat.n_entries ==
+ internal::SimplifiedCompressedSparsity::short_length)
+ {
+ for (unsigned int i=0; i<dat.additional_data.size(); ++i)
+ if (dat.additional_data[i] >= block)
+ goto end_this;
+ else
+ row_entries.insert (dat.additional_data[i], insert_pos);
+ }
+ end_this:
+ {}
+ }
+ }
+ cell_start +=n_comp;
+ }
+ mcell_start += task_info.block_size;
+ }
+ else
+ {
+ const unsigned int * it = begin_indices (block),
+ * end_cell = end_indices (block);
+ row_entries.reserve (row_entries.size() + end_cell - it);
+ for ( ; it != end_cell; ++it)
+ {
+ // the simplified sparsity pattern has two
+ // fields: a short field for dofs that only
+ // belong to a few cells, and a long field for
+ // all dofs with many cells that spills the
+ // short data field. so go them through both
+ const internal::SimplifiedCompressedSparsity::LineData & dat =
+ connectivity_dof.rows[*it];
+ std::vector<unsigned int>::iterator insert_pos = row_entries.begin();
+ for (unsigned int i=0; i<dat.n_entries; ++i)
+ if (dat.short_data[i] >= block)
+ goto end_this_nb;
+ else
+ row_entries.insert (dat.short_data[i], insert_pos);
+ if (dat.n_entries ==
+ internal::SimplifiedCompressedSparsity::short_length)
+ {
+ for (unsigned int i=0; i<dat.additional_data.size(); ++i)
+ if (dat.additional_data[i] >= block)
+ goto end_this_nb;
+ else
+ row_entries.insert (dat.additional_data[i], insert_pos);
+ }
+ end_this_nb:
+ {}
+ }
+ }
+ connectivity.add_entries (block, row_entries.begin(), row_entries.end());
+ }
+ connectivity.symmetrize ();
+ }
+
+
+
+ void DoFInfo::renumber_dofs (std::vector<unsigned int> &renumbering)
+ {
+ // first renumber all locally owned degrees of
+ // freedom
+ AssertDimension (vector_partitioner->local_size(),
+ vector_partitioner->size());
+ const unsigned int local_size = vector_partitioner->local_size();
+ renumbering.resize (0);
+ renumbering.resize (local_size, numbers::invalid_unsigned_int);
+ unsigned int counter = 0;
+ std::vector<unsigned int>::iterator dof_ind = dof_indices.begin(),
+ end_ind = dof_indices.end();
+ for ( ; dof_ind != end_ind; ++dof_ind)
+ {
+ if (*dof_ind < local_size)
+ {
+ if (renumbering[*dof_ind] == numbers::invalid_unsigned_int)
+ renumbering[*dof_ind] = counter++;
+ *dof_ind = renumbering[*dof_ind];
+ }
+ }
+
+ AssertIndexRange (counter, local_size+1);
+ for (unsigned int i=0; i<renumbering.size(); ++i)
+ if (renumbering[i] == numbers::invalid_unsigned_int)
+ renumbering[i] = counter++;
+
+ // adjust the constrained DoFs
+ std::vector<unsigned int> new_constrained_dofs (constrained_dofs.size());
+ for (unsigned int i=0; i<constrained_dofs.size(); ++i)
+ new_constrained_dofs[i] = renumbering[constrained_dofs[i]];
+
+ // the new constrained DoFs should be sorted
+ // already as they are not contained in
+ // dof_indices and then get contiguous numbers
+#ifdef DEBUG
+ for (unsigned int i=1; i<new_constrained_dofs.size(); ++i)
+ Assert (new_constrained_dofs[i] > new_constrained_dofs[i-1], ExcInternalError());
+#endif
+ std::swap (constrained_dofs, new_constrained_dofs);
+
+ AssertDimension (counter, renumbering.size());
+ }
+
+
+
+ std::size_t
+ DoFInfo::memory_consumption () const
+ {
+ std::size_t memory = sizeof(*this);
+ memory += (row_starts.capacity()*sizeof(std_cxx1x::tuple<unsigned int,
+ unsigned int, unsigned int>));
+ memory += MemoryConsumption::memory_consumption (dof_indices);
+ memory += MemoryConsumption::memory_consumption (row_starts_plain_indices);
+ memory += MemoryConsumption::memory_consumption (plain_dof_indices);
+ memory += MemoryConsumption::memory_consumption (constraint_indicator);
+ memory += MemoryConsumption::memory_consumption (*vector_partitioner);
+ return memory;
+ }
+
+
+
+ template <typename STREAM>
+ void
+ DoFInfo::print_memory_consumption (STREAM &out,
+ const SizeInfo &size_info) const
+ {
+ out << " Memory row starts indices: ";
+ size_info.print_mem (out, (row_starts.capacity()*
+ sizeof(std_cxx1x::tuple<unsigned int,
+ unsigned int, unsigned int>)));
+ out << " Memory dof indices: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (dof_indices));
+ out << " Memory constraint indicators: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (constraint_indicator));
+ out << " Memory plain indices: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (row_starts_plain_indices)+
+ MemoryConsumption::memory_consumption (plain_dof_indices));
+ out << " Memory vector partitioner: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (*vector_partitioner));
+ }
+
+
+
+ template <typename Number>
+ void
+ DoFInfo::print (const CompressedMatrix<Number> &constraint_pool,
+ std::ostream &out) const
+ {
+ const unsigned int n_rows = row_starts.size() - 1;
+ for (unsigned int row=0 ; row<n_rows ; ++row)
+ {
+ out << "Entries row " << row << ": ";
+ const unsigned int
+ *glob_indices = begin_indices(row),
+ *end_row = end_indices(row);
+ unsigned int index = 0;
+ const std::pair<unsigned short,unsigned short>
+ * con_it = begin_indicators(row),
+ * end_con = end_indicators(row);
+ for ( ; con_it != end_con; ++con_it)
+ {
+ for( ; index<con_it->first; index++)
+ {
+ Assert (glob_indices+index != end_row, ExcInternalError());
+ out << glob_indices[index] << " ";
+ }
+
+ out << "[ ";
+ for(unsigned int k=constraint_pool.row_index[con_it->second];
+ k<constraint_pool.row_index[con_it->second+1];
+ k++,index++)
+ {
+ Assert (glob_indices+index != end_row, ExcInternalError());
+ out << glob_indices[index] << "/"
+ << constraint_pool.data[k];
+ if (k<constraint_pool.row_index[con_it->second+1]-1)
+ out << " ";
+ }
+ out << "] ";
+ }
+ glob_indices += index;
+ for(; glob_indices != end_row; ++glob_indices)
+ out << *glob_indices << " ";
+ out << std::endl;
+ }
+ }
+
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+DEAL_II_NAMESPACE_CLOSE
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011, 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_fe_evaluation_h
+#define __deal2__matrix_free_fe_evaluation_h
+
+
+#include <deal.II/base/config.h>
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/template_constraints.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/vectorization.h>
+#include <deal.II/matrix_free/matrix_free.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace parallel
+{
+ namespace distributed
+ {
+ template <typename> class Vector;
+ }
+}
+
+
+
+namespace internal
+{
+ DeclException0 (ExcAccessToUninitializedField);
+}
+
+
+
+/**
+ * This is the base class for the FEEvaluation classes. This class is a base
+ * class and needs usually not be called in user code. Use one of the derived
+ * classes instead. It implements access functions to vectors for the @p
+ * read_dof_values, @p set_dof_values, and @p distributed_local_to_global
+ * functions, as well as the @p reinit method.
+ *
+ * This class has five template arguments:
+ *
+ * @param dim Dimension in which this class is to be used
+ *
+ * @param dofs_per_cell Number of degrees of freedom of the FE per cell,
+ * usually (fe_degree+1)^dim for elements based on a tensor
+ * product
+ *
+ * @param n_q_points Number of points in the quadrature formula, usually
+ * (fe_degree+1)^dim for tensor-product quadrature formulas
+ *
+ * @param n_components Number of vector components when solving a system of
+ * PDEs. If the same operation is applied to several
+ * components of a PDE (e.g. a vector Laplace equation), they
+ * can be applied simultaneously with one call (and often
+ * more efficiently)
+ *
+ * @param Number Number format, usually @p double or @p float
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+class FEEvaluationBase
+{
+public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors =
+ VectorizedArray<Number>::n_array_elements;
+ static const unsigned int dofs_per_cell = dofs_per_cell_;
+ static const unsigned int n_q_points = n_q_points_;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationBase (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Initializes the operation pointer to the
+ * current cell. Unlike the FEValues::reinit
+ * function, where the information related to
+ * a particular cell is generated in the
+ * reinit call, this function is very cheap
+ * since all data is pre-computed in @p
+ * matrix_free, and only a few indices
+ * have to be set appropriately.
+ */
+ void reinit (const unsigned int cell);
+
+ /**
+ * For the transformation information stored
+ * in MappingInfo, this function returns the
+ * index which belongs to the current cell as
+ * specified in @p reinit. Note that
+ * MappingInfo has different fields for
+ * Cartesian cells, cells with linear mapping
+ * and with general mappings, so in order to
+ * access the correct data, this interface
+ * must be used together with get_cell_type.
+ */
+ unsigned int get_cell_data_number() const;
+
+ /**
+ * Returns the type of the cell the @p reinit
+ * function has been called for. 0 means
+ * Cartesian cells (which allows for
+ * considerable data compression), 1 means
+ * cells with linear mappings, and 2 means
+ * general cells without any compressed
+ * storage applied.
+ */
+ unsigned int get_cell_type() const;
+
+ /**
+ * Returns a read-only pointer to the first
+ * field of function values on quadrature
+ * points. First come the function values on
+ * all quadrature points for the first
+ * component, then all values for the second
+ * component, and so on. This is related to
+ * the internal data structures used in this
+ * class. The raw data after a call to @p
+ * evaluate only contains unit cell
+ * operations, so possible transformations,
+ * quadrature weights etc. must be applied
+ * manually. In general, it is safer to use
+ * the get_value() function instead, which
+ * does all the transformation internally.
+ */
+ const vector_t * begin_values () const;
+
+ /**
+ * Returns a read and write pointer to the
+ * first field of function values on
+ * quadrature points. First come the function
+ * values on all quadrature points for the
+ * first component, then all values for the
+ * second component, and so on. This is
+ * related to the internal data structures
+ * used in this class. The raw data after a
+ * call to @p evaluate only contains unit
+ * cell operations, so possible
+ * transformations, quadrature weights
+ * etc. must be applied manually. In general,
+ * it is safer to use the get_value() function
+ * instead, which does all the transformation
+ * internally.
+ */
+ vector_t * begin_values ();
+
+ /**
+ * Returns a read-only pointer to the first
+ * field of function gradients on quadrature
+ * points. First comes the x-component of the
+ * gradient for the first component on all
+ * quadrature points, then the y-component,
+ * and so on. Next comes the x-component of
+ * the second component, and so on. This is
+ * related to the internal data structures
+ * used in this class. The raw data after a
+ * call to @p evaluate only contains unit
+ * cell operations, so possible
+ * transformations, quadrature weights
+ * etc. must be applied manually. In general,
+ * it is safer to use the get_gradient() function
+ * instead, which does all the transformation
+ * internally.
+ */
+ const vector_t * begin_gradients () const;
+
+ /**
+ * Returns a read and write pointer to the
+ * first field of function gradients on
+ * quadrature points. First comes the
+ * x-component of the gradient for the first
+ * component on all quadrature points, then
+ * the y-component, and so on. Next comes the
+ * x-component of the second component, and so
+ * on. This is related to the internal data
+ * structures used in this class. The raw data
+ * after a call to @p evaluate only
+ * contains unit cell operations, so possible
+ * transformations, quadrature weights
+ * etc. must be applied manually. In general,
+ * it is safer to use the get_gradient()
+ * function instead, which does all the
+ * transformation internally.
+ */
+ vector_t * begin_gradients ();
+
+ /**
+ * Returns a read-only pointer to the first
+ * field of function hessians on quadrature
+ * points. First comes the xx-component of the
+ * hessian for the first component on all
+ * quadrature points, then the yy-component,
+ * zz-component in (3D), then the
+ * xy-component, and so on. Next comes the
+ * xx-component of the second component, and
+ * so on. This is related to the internal data
+ * structures used in this class. The raw data
+ * after a call to @p evaluate only
+ * contains unit cell operations, so possible
+ * transformations, quadrature weights
+ * etc. must be applied manually. In general,
+ * it is safer to use the get_laplacian() or
+ * get_hessian() functions instead, which does
+ * all the transformation internally.
+ */
+ const vector_t * begin_hessians () const;
+
+ /**
+ * Returns a read and write pointer to the
+ * first field of function hessians on
+ * quadrature points. First comes the
+ * xx-component of the hessian for the first
+ * component on all quadrature points, then
+ * the yy-component, zz-component in (3D),
+ * then the xy-component, and so on. Next
+ * comes the xx-component of the second
+ * component, and so on. This is related to
+ * the internal data structures used in this
+ * class. The raw data after a call to @p
+ * evaluate only contains unit cell
+ * operations, so possible transformations,
+ * quadrature weights etc. must be applied
+ * manually. In general, it is safer to use
+ * the get_laplacian() or get_hessian()
+ * functions instead, which does all the
+ * transformation internally.
+ */
+ vector_t * begin_hessians ();
+
+ /**
+ * For the vector @p src, read out the values
+ * on the degrees of freedom of the current
+ * cell, and store them internally. Similar
+ * functionality as the function
+ * DoFAccessor::get_interpolated_dof_values
+ * when no constraints are present, but it
+ * also includes constraints from hanging
+ * nodes, so one can see it as a similar
+ * function to
+ * ConstraintMatrix::read_dof_values as
+ * well. Note that if vectorization is
+ * enabled, the DoF values for several cells
+ * are set.
+ *
+ * If some constraints on the vector are
+ * inhomogeneous, use the function
+ * read_dof_values_plain instead and provide
+ * the vector with useful data also in
+ * constrained positions by calling
+ * ConstraintMatrix::distribute. When
+ * accessing vector entries during the
+ * solution of linear systems, the temporary
+ * solution should always have homogeneous
+ * constraints and this method is the correct
+ * one.
+ */
+ template <typename VectorType>
+ void read_dof_values (const VectorType &src);
+
+ /**
+ * For a collection of several vector @p src,
+ * read out the values on the degrees of
+ * freedom of the current cell for @p
+ * n_components (template argument), starting
+ * at @p first_index, and store them
+ * internally. Similar functionality as the
+ * function ConstraintMatrix::read_dof_values.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are set.
+ */
+ template <typename VectorType>
+ void read_dof_values (const std::vector<VectorType> &src,
+ const unsigned int first_index=0);
+
+ /**
+ * Reads data from several vectors. Same as
+ * other function with std::vector, but
+ * accepts a vector of pointers to vectors.
+ */
+ template <typename VectorType>
+ void read_dof_values (const std::vector<VectorType*> &src,
+ const unsigned int first_index=0);
+
+ /**
+ * For a collection of several vector @p src,
+ * read out the values on the degrees of
+ * freedom of the current cell for @p
+ * n_components (template argument), and store
+ * them internally. Similar functionality as
+ * the function
+ * ConstraintMatrix::read_dof_values. Note
+ * that if vectorization is enabled, the DoF
+ * values for several cells are set.
+ */
+ template<typename VectorType>
+ void read_dof_values (const VectorType * src_data[]);
+
+ /**
+ * For the vector @p src, read out the values
+ * on the degrees of freedom of the current
+ * cell, and store them internally. Similar
+ * functionality as the function
+ * DoFAccessor::get_interpolated_dof_values. As
+ * opposed to the read_dof_values function,
+ * this function reads out the plain entries
+ * from vectors, without taking stored
+ * constraints into account. This way of
+ * access is appropriate when the constraints
+ * have been distributed on the vector by a
+ * call to ConstraintMatrix::distribute
+ * previously. This function is also necessary
+ * when inhomogeneous constraints are to be
+ * used, as MatrixFree can only handle
+ * homogeneous constraints. Note that if
+ * vectorization is enabled, the DoF values
+ * for several cells are set.
+ */
+ template <typename VectorType>
+ void read_dof_values_plain (const VectorType &src);
+
+ /**
+ * For a collection of several vector @p src,
+ * read out the values on the degrees of
+ * freedom of the current cell for @p
+ * n_components (template argument), starting
+ * at @p first_index, and store them
+ * internally. Similar functionality as the
+ * function DoFAccessor::read_dof_values.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are set.
+ */
+ template <typename VectorType>
+ void read_dof_values_plain (const std::vector<VectorType> &src,
+ const unsigned int first_index=0);
+
+ /**
+ * Reads data from several vectors without
+ * resolving constraints. Same as other
+ * function with std::vector, but accepts a
+ * vector of pointers to vectors.
+ */
+ template <typename VectorType>
+ void read_dof_values_plain (const std::vector<VectorType*> &src,
+ const unsigned int first_index=0);
+
+ /**
+ * For a collection of several vector @p src,
+ * read out the values on the degrees of
+ * freedom of the current cell for @p
+ * n_components (template argument), and store
+ * them internally. Similar functionality as
+ * the function
+ * DoFAccessor::read_dof_values. Note
+ * that if vectorization is enabled, the DoF
+ * values for several cells are set.
+ */
+ template<typename VectorType>
+ void read_dof_values_plain (const VectorType * src_data[]);
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell and sums them
+ * into the vector @p dst. The function also
+ * applies constraints during the write
+ * operation. The functionality is hence
+ * similar to the function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void distribute_local_to_global (VectorType &dst) const;
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell for a
+ * vector-valued problem consisting of @p
+ * n_components (template argument) and sums
+ * them into the collection of vectors vector
+ * @p dst, starting at index @p
+ * first_index. The function also applies
+ * constraints during the write operation. The
+ * functionality is hence similar to the
+ * function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void distribute_local_to_global (std::vector<VectorType> &dst,
+ const unsigned int first_index=0) const;
+
+ /**
+ * Writes data to several vectors. Same as
+ * other function with std::vector, but
+ * accepts a vector of pointers to vectors.
+ */
+ template<typename VectorType>
+ void distribute_local_to_global (std::vector<VectorType*> &dst,
+ const unsigned int first_index=0) const;
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell for a
+ * vector-valued problem consisting of @p
+ * n_components (template argument) and sums
+ * them into the collection of vectors vector
+ * @p dst. The function also applies
+ * constraints during the write operation. The
+ * functionality is hence similar to the
+ * function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void distribute_local_to_global (VectorType * dst_data[]) const;
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell and sums them
+ * into the vector @p dst. The function also
+ * applies constraints during the write
+ * operation. The functionality is hence
+ * similar to the function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void set_dof_values (VectorType &dst) const;
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell for a
+ * vector-valued problem consisting of @p
+ * n_components (template argument) and sums
+ * them into the collection of vectors vector
+ * @p dst, starting at index @p
+ * first_index. The function also applies
+ * constraints during the write operation. The
+ * functionality is hence similar to the
+ * function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void set_dof_values (std::vector<VectorType> &dst,
+ const unsigned int first_index=0) const;
+
+ /**
+ * Writes data to several vectors. Same as
+ * other function with std::vector, but
+ * accepts a vector of pointers to vectors.
+ */
+ template<typename VectorType>
+ void set_dof_values (std::vector<VectorType*> &dst,
+ const unsigned int first_index=0) const;
+
+ /**
+ * Takes the values stored internally on dof
+ * values of the current cell for a
+ * vector-valued problem consisting of @p
+ * n_components (template argument) and sums
+ * them into the collection of vectors vector
+ * @p dst. The function also applies
+ * constraints during the write operation. The
+ * functionality is hence similar to the
+ * function
+ * ConstraintMatrix::distribute_local_to_global.
+ * Note that if vectorization is enabled, the
+ * DoF values for several cells are used.
+ */
+ template<typename VectorType>
+ void set_dof_values (VectorType * dst_data[]) const;
+
+ /**
+ * Returns the value stored for the local
+ * degree of freedom with index @p dof. If the
+ * object is vector-valued, a vector-valued
+ * return argument is given. Note that when
+ * vectorization is enabled, values from
+ * several cells are grouped together. If @p
+ * set_dof_values was called last, the value
+ * corresponds to the one set there. If @p
+ * integrate was called last, it instead
+ * corresponds to the value of the integrated
+ * function with the test function of the
+ * given index.
+ */
+ Tensor<1,n_components,vector_t>
+ get_dof_value (unsigned int dof) const;
+
+ /**
+ * Write a value to the field containing the
+ * degrees of freedom with component @p
+ * dof. Access to the same field as through @p
+ * get_dof_value.
+ */
+ void submit_dof_value (Tensor<1,n_components,vector_t> val_in,
+ unsigned int dof);
+
+ /**
+ * Returns the value of a finite
+ * element function at quadrature
+ * point number @p q_point after a
+ * call to @p evaluate(true,...), or
+ * the value that has been stored
+ * there with a call to @p
+ * submit_value. If the object is
+ * vector-valued, a vector-valued
+ * return argument is given. Note that
+ * when vectorization is enabled,
+ * values from several cells are
+ * grouped together.
+ */
+ Tensor<1,n_components,vector_t>
+ get_value (unsigned int q_point) const;
+
+ /**
+ * Write a value to the field containing the
+ * values on quadrature points with component
+ * @p q_point. Access to the same field as
+ * through @p get_value. If applied before the
+ * function @p integrate(true,...) is
+ * called, this specifies the value which is
+ * tested by all basis function on the current
+ * cell and integrated over.
+ */
+ void submit_value (Tensor<1,n_components,vector_t> val_in,
+ unsigned int q_point);
+
+ /**
+ * Returns the gradient of a finite element
+ * function at quadrature point number @p
+ * q_point after a call to @p
+ * evaluate(...,true,...), or the value
+ * that has been stored there with a call to
+ * @p submit_gradient.
+ */
+ Tensor<1,n_components,Tensor<1,dim,vector_t> >
+ get_gradient (unsigned int q_point) const;
+
+ /**
+ * Write a gradient to the field containing
+ * the values on quadrature points with
+ * component @p q_point. Access to the same
+ * field as through @p get_gradient. If
+ * applied before the function @p
+ * integrate(...,true) is called,
+ * this specifies the gradient which is tested
+ * by all basis function gradients on the
+ * current cell and integrated over.
+ */
+ void submit_gradient(Tensor<1,n_components,Tensor<1,dim,vector_t> >grad_in,
+ unsigned int q_point);
+
+ /**
+ * Returns the Hessian of a finite element
+ * function at quadrature point number @p
+ * q_point after a call to @p
+ * evaluate(...,true). If only the
+ * diagonal or even the trace of the Hessian,
+ * the Laplacian, is needed, use the other
+ * functions below.
+ */
+ Tensor<1,n_components,Tensor<2,dim,vector_t> >
+ get_hessian (unsigned int q_point) const;
+
+ /**
+ * Returns the diagonal of the Hessian of a
+ * finite element function at quadrature point
+ * number @p q_point after a call to @p
+ * evaluate(...,true).
+ */
+ Tensor<1,n_components,Tensor<1,dim,vector_t> >
+ get_hessian_diagonal (unsigned int q_point) const;
+
+ /**
+ * Returns the Laplacian of a finite element
+ * function at quadrature point number @p
+ * q_point after a call to @p
+ * evaluate(...,true).
+ */
+ Tensor<1,n_components,vector_t>
+ get_laplacian (unsigned int q_point) const;
+
+ /**
+ * Takes values on quadrature points,
+ * multiplies by the Jacobian determinant and
+ * quadrature weights (JxW) and sums the
+ * values for all quadrature points on the
+ * cell. The result is a scalar, representing
+ * the integral over the function over the
+ * cell. If a vector-element is used, the
+ * resulting components are still
+ * separated. Moreover, if vectorization is
+ * enabled, the integral values of several
+ * cells are represented together.
+ */
+ Tensor<1,n_components,vector_t>
+ integrate_value ();
+
+ /**
+ * Stores a reference to the underlying data.
+ */
+ const MatrixFree<dim,Number> &matrix_info;
+
+ /**
+ * Stores a reference to the underlying DoF
+ * indices and constraint description for the
+ * component specified at construction. Also
+ * contained in matrix_info, but it simplifies
+ * code if we store a reference to it.
+ */
+ const internal::MatrixFreeFunctions::DoFInfo &dof_info;
+
+ /**
+ * Stores the constraints weights that
+ * supplement DoFInfo. Also contained in
+ * matrix_info, but it simplifies code if we
+ * store a reference to it.
+ */
+ const internal::MatrixFreeFunctions::CompressedMatrix<Number> &constraint_pool;
+
+ /**
+ * Stores a reference to the underlying
+ * transformation data from unit to real cells
+ * for the given quadrature formula specified
+ * at construction. Also contained in
+ * matrix_info, but it simplifies code if we
+ * store a reference to it.
+ */
+ const internal::MatrixFreeFunctions::MappingInfo<dim,Number> &mapping_info;
+
+ /**
+ * Stores the active fe index for this class
+ * for efficient indexing in the hp case.
+ */
+ const unsigned int active_fe_index;
+
+ /**
+ * Stores the active quadrature index for this
+ * class for efficient indexing in the hp
+ * case.
+ */
+ const unsigned int active_quad_index;
+
+ /**
+ * Stores a reference to the unit cell data,
+ * i.e., values, gradients and Hessians in 1D
+ * at the quadrature points that constitute
+ * the tensor product. Also contained in
+ * matrix_info, but it simplifies code if we
+ * store a reference to it.
+ */
+ const internal::MatrixFreeFunctions::FEEvaluationData<Number> &data;
+
+protected:
+ /**
+ * Internal data fields that store the
+ * values. Since all array lengths are known
+ * at compile time and since they are rarely
+ * more than a few kilobytes, allocate them on
+ * the stack. This makes it possible to
+ * cheaply set up a FEEvaluation object and
+ * write thread-safe programs by letting each
+ * thread own a private object of this type.
+ */
+ vector_t values_dofs[n_components][dofs_per_cell>0?dofs_per_cell:1];
+ vector_t values_quad[n_components][n_q_points>0?n_q_points:1];
+ vector_t gradients_quad[n_components][dim][n_q_points>0?n_q_points:1];
+ vector_t hessians_quad[n_components][(dim*(dim+1))/2][n_q_points>0?n_q_points:1];
+
+ /**
+ * Stores the indices of the current cell.
+ */
+ const unsigned int quad_no;
+ const unsigned int n_fe_components;
+ unsigned int cell;
+ unsigned int cell_type;
+ unsigned int cell_data_number;
+ bool at_irregular_cell;
+ unsigned int n_irreg_components_filled;
+
+ /**
+ * A pointer to the Cartesian Jacobian
+ * information of the present cell. Only set
+ * to a useful value if on a Cartesian cell,
+ * otherwise zero.
+ */
+ const Tensor<1,dim,vector_t> * cartesian;
+
+ /**
+ * A pointer to the Jacobian information of
+ * the present cell. Only set to a useful
+ * value if on a non-Cartesian cell.
+ */
+ const Tensor<2,dim,vector_t> * jacobian;
+
+ /**
+ * A pointer to the Jacobian determinant of
+ * the present cell. If on a Cartesian cell or
+ * on a cell with constant Jacobian, this is
+ * just the Jacobian determinant, otherwise
+ * the Jacobian determinant times the
+ * quadrature weight.
+ */
+ const vector_t * J_value;
+
+ /**
+ * A pointer to the quadrature weights of the
+ * underlying quadrature formula.
+ */
+ const vector_t * quadrature_weights;
+
+ /**
+ * A pointer to the quadrature points on the
+ * present cell.
+ */
+ const Point<dim,vector_t> * quadrature_points;
+
+ /**
+ * A pointer to the diagonal part of the
+ * Jacobian gradient on the present
+ * cell. Only set to a useful value if on a
+ * general cell with non-constant Jacobian.
+ */
+ const Tensor<2,dim,vector_t> * jacobian_grad;
+
+ /**
+ * A pointer to the upper diagonal part of the
+ * Jacobian gradient on the present cell. Only
+ * set to a useful value if on a general cell
+ * with non-constant Jacobian.
+ */
+ const Tensor<1,(dim>1?dim*(dim-1)/2:1),Tensor<1,dim,vector_t> > * jacobian_grad_upper;
+
+ /**
+ * Debug information to track whether we
+ * uninitialized fields are accessed.
+ */
+ bool dof_values_initialized;
+ bool values_quad_initialized;
+ bool gradients_quad_initialized;
+ bool hessians_quad_initialized;
+ bool values_quad_submitted;
+ bool gradients_quad_submitted;
+};
+
+
+
+/**
+ * This class provides access to the data fields of the FEEvaluation
+ * classes. Generic access is achieved through the base class, and
+ * specializations for scalar and vector-valued elements are defined
+ * separately.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+class FEEvaluationAccess :
+ public FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef Tensor<1,n_components,vector_t> value_type;
+ typedef Tensor<1,n_components,Tensor<1,dim,vector_t> > gradient_type;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ static const unsigned int dofs_per_cell = dofs_per_cell_;
+ static const unsigned int n_q_points = n_q_points_;
+ typedef FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,
+ Number> BaseClass;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationAccess (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+};
+
+
+
+
+/**
+ * This class provides access to the data fields of the FEEvaluation
+ * classes. Partial specialization for scalar fields that defines access with
+ * simple data fields, i.e., scalars for the values and Tensor<1,dim> for the
+ * gradients.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+class FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number> :
+ public FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,1,Number>
+{
+ public:
+ typedef Number number_type;
+ typedef VectorizedArray<Number> vector_t;
+ typedef VectorizedArray<Number> value_type;
+ typedef Tensor<1,dim,vector_t> gradient_type;
+ static const unsigned int dimension = dim;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ static const unsigned int dofs_per_cell = dofs_per_cell_;
+ static const unsigned int n_q_points = n_q_points_;
+ typedef FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,1,Number> BaseClass;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationAccess (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Returns the value stored for the local
+ * degree of freedom with index @p dof. If the
+ * object is vector-valued, a vector-valued
+ * return argument is given. Note that when
+ * vectorization is enabled, values from
+ * several cells are grouped together. If @p
+ * set_dof_values was called last, the value
+ * corresponds to the one set there. If @p
+ * integrate was called last, it instead
+ * corresponds to the value of the integrated
+ * function with the test function of the
+ * given index.
+ */
+ vector_t
+ get_dof_value (unsigned int dof) const;
+
+ /**
+ * Write a value to the field containing the
+ * degrees of freedom with component @p
+ * dof. Access to the same field as through @p
+ * get_dof_value.
+ */
+ void submit_dof_value (vector_t val_in,
+ unsigned int dof);
+
+ /**
+ * Returns the value of a finite element
+ * function at quadrature point number @p
+ * q_point after a call to @p
+ * evaluate(true,...), or the value that
+ * has been stored there with a call to @p
+ * submit_value. If the object is
+ * vector-valued, a vector-valued return
+ * argument is given. Note that when
+ * vectorization is enabled, values from
+ * several cells are grouped together.
+ */
+ vector_t
+ get_value (unsigned int q_point) const;
+
+ /**
+ * Write a value to the field
+ * containing the values on quadrature
+ * points with component @p
+ * q_point. Access to the same field
+ * as through @p get_value. If applied
+ * before the function @p
+ * integrate(true,...) is called, this
+ * specifies the value which is tested
+ * by all basis function on the
+ * current cell and integrated over.
+ */
+ void submit_value (vector_t val_in,
+ unsigned int q_point);
+
+ /**
+ * Returns the gradient of a finite
+ * element function at quadrature
+ * point number @p q_point after a
+ * call to @p evaluate(...,true,...),
+ * or the value that has been stored
+ * there with a call to @p
+ * submit_gradient.
+ */
+ gradient_type
+ get_gradient (unsigned int q_point) const;
+
+ /**
+ * Write a gradient to the field
+ * containing the values on quadrature
+ * points with component @p
+ * q_point. Access to the same field
+ * as through @p get_gradient. If
+ * applied before the function @p
+ * integrate(...,true) is called, this
+ * specifies the gradient which is
+ * tested by all basis function
+ * gradients on the current cell and
+ * integrated over.
+ */
+ void submit_gradient(gradient_type grad_in,
+ unsigned int q_point);
+
+ /**
+ * Returns the Hessian of a finite
+ * element function at quadrature
+ * point number @p q_point after a
+ * call to @p evaluate(...,true). If
+ * only the diagonal part of the
+ * Hessian or its trace, the
+ * Laplacian, are needed, use the
+ * respective functions below.
+ */
+ Tensor<2,dim,vector_t>
+ get_hessian (unsigned int q_point) const;
+
+ /**
+ * Returns the diagonal of the Hessian
+ * of a finite element function at
+ * quadrature point number @p q_point
+ * after a call to @p
+ * evaluate(...,true).
+ */
+ gradient_type
+ get_hessian_diagonal (unsigned int q_point) const;
+
+ /**
+ * Returns the Laplacian of a finite
+ * element function at quadrature
+ * point number @p q_point after a
+ * call to @p evaluate(...,true).
+ */
+ value_type
+ get_laplacian (unsigned int q_point) const;
+
+ /**
+ * Takes values on quadrature points,
+ * multiplies by the Jacobian determinant and
+ * quadrature weights (JxW) and sums the
+ * values for all quadrature points on the
+ * cell. The result is a scalar, representing
+ * the integral over the function over the
+ * cell. If a vector-element is used, the
+ * resulting components are still
+ * separated. Moreover, if vectorization is
+ * enabled, the integral values of several
+ * cells are represented together.
+ */
+ value_type
+ integrate_value ();
+};
+
+
+
+/**
+ * This class provides access to the data fields of the FEEvaluation
+ * classes. Partial specialization for fields with as many components as the
+ * underlying space dimension, i.e., values are of type Tensor<1,dim> and
+ * gradients of type Tensor<2,dim>. Provides some additional functions for
+ * access, like the symmetric gradient and divergence.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+class FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number> :
+ public FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,dim,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef Tensor<1,dim,vector_t> value_type;
+ typedef Tensor<2,dim,vector_t> gradient_type;
+ typedef SymmetricTensor<2,dim,vector_t> sym_gradient_type;
+ typedef Tensor<1,dim==2?1:dim,vector_t> curl_type;
+
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ static const unsigned int dofs_per_cell = dofs_per_cell_;
+ static const unsigned int n_q_points = n_q_points_;
+ typedef FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,dim,Number> BaseClass;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationAccess (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Returns the gradient of a finite element
+ * function at quadrature point number @p
+ * q_point after a call to @p
+ * evaluate(...,true,...).
+ */
+ gradient_type
+ get_gradient (unsigned int q_point) const;
+
+ /**
+ * Returns the divergence of a vector-valued
+ * finite element at quadrature point number
+ * @p q_point after a call to @p
+ * evaluate(...,true,...).
+ */
+ vector_t
+ get_divergence (unsigned int q_point) const;
+
+ /**
+ * Returns the symmetric gradient of a
+ * vector-valued finite element at
+ * quadrature point number @p q_point
+ * after a call to @p
+ * evaluate(...,true,...). It
+ * corresponds to <tt>0.5
+ * (grad+grad<sup>T</sup>)</tt>.
+ */
+ sym_gradient_type
+ get_symmetric_gradient (unsigned int q_point) const;
+
+ /**
+ * Returns the curl of the vector field,
+ * $nabla \times v$ after a call to @p
+ * evaluate(...,true,...).
+ */
+ curl_type
+ get_curl (unsigned int q_point) const;
+
+ /**
+ * Returns the Hessian of a finite
+ * element function at quadrature
+ * point number @p q_point after a
+ * call to @p evaluate(...,true). If
+ * only the diagonal of the Hessian or
+ * its trace, the Laplacian, is
+ * needed, use the respective
+ * functions.
+ */
+ Tensor<3,dim,vector_t>
+ get_hessian (unsigned int q_point) const;
+
+ /**
+ * Returns the diagonal of the Hessian
+ * of a finite element function at
+ * quadrature point number @p q_point
+ * after a call to @p
+ * evaluate(...,true).
+ */
+ gradient_type
+ get_hessian_diagonal (unsigned int q_point) const;
+
+ /**
+ * Write a gradient to the field containing
+ * the values on quadrature points with
+ * component @p q_point. Access to the same
+ * field as through @p get_gradient. If
+ * applied before the function @p
+ * integrate(...,true) is called,
+ * this specifies the gradient which is tested
+ * by all basis function gradients on the
+ * current cell and integrated over.
+ */
+ void submit_gradient(gradient_type grad_in,
+ unsigned int q_point);
+
+ /**
+ * Write a gradient to the field containing
+ * the values on quadrature points with
+ * component @p q_point. This function is an
+ * alternative to the other submit_gradient
+ * function when using a system of fixed
+ * number of equations which happens to
+ * coincide with the dimension for some
+ * dimensions, but not all. To allow for
+ * dimension-independent programming, this
+ * function can be used instead.
+ */
+ void submit_gradient(Tensor<1,dim,Tensor<1,dim,vector_t> > grad_in,
+ unsigned int q_point);
+
+ /**
+ * Write a gradient to the field containing
+ * the values on quadrature points with
+ * component @p q_point. Access to the same
+ * field as through @p get_gradient. If
+ * applied before the function @p
+ * integrate(...,true) is called,
+ * this specifies the gradient which is tested
+ * by all basis function gradients on the
+ * current cell and integrated over.
+ */
+ void submit_symmetric_gradient(sym_gradient_type grad_in,
+ unsigned int q_point);
+
+ /**
+ * Write the components of a curl containing
+ * the values on quadrature point @p
+ * q_point. Access to the same data field as
+ * through @p get_gradient.
+ */
+ void submit_curl (curl_type curl_in,
+ unsigned int q_point);
+};
+
+
+
+/**
+ * The class that provides all functions necessary to evaluate functions at
+ * quadrature points and cell integrations. In functionality, this class is
+ * similar to FEValues<dim>, however, it includes a lot of specialized
+ * functions that make it much faster (between 5 and 500 times as fast,
+ * depending on the polynomial order). Access to the data fields is provided
+ * through functionality in the class FEEvaluationAccess.
+ *
+ * This class is designed for general local finite element operations based on
+ * tensor products of 1D polynomials and quadrature points. Often, there are
+ * some symmetries or zeros in the unit cell data that allow for a more
+ * efficient operator application. FEEvaluation is specialized to standard
+ * FE_Q/FE_DGQ elements and quadrature points symmetric around 0.5 (like Gauss
+ * quadrature), and hence the most common situation. FEEvaluationGL is a
+ * specialization for elements where quadrature formula and support points are
+ * chosen so that a orthogonal relation between the values holds. This is the
+ * case for FE_Q based on Gauss-Lobatto support points with Gauss-Lobatto
+ * quadrature formula of the same order.
+ *
+ * This class has five template arguments:
+ *
+ * @param dim Dimension in which this class is to be used
+ *
+ * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually
+ * fe_degree+1, for elements based on a tensor product
+ *
+ * @param n_q_points_1d Number of points in the quadrature formula in 1D,
+ * usually chosen as fe_degree+1
+ *
+ * @param n_components Number of vector components when solving a system of
+ * PDEs. If the same operation is applied to several
+ * components of a PDE (e.g. a vector Laplace equation), they
+ * can be applied simultaneously with one call (and often
+ * more efficiently)
+ *
+ * @param Number Number format, usually @p double or @p float
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d,
+ int n_components=1, typename Number=double >
+class FEEvaluationGeneral :
+ public FEEvaluationAccess<dim,
+ (n_dofs_1d*(dim>1?n_dofs_1d:1)*(dim>2?n_dofs_1d:1)),
+ (n_q_points_1d*(dim>1?n_q_points_1d:1)*(dim>2?n_q_points_1d:1)),
+ n_components,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ typedef FEEvaluationAccess<dim,(n_dofs_1d*(dim>1?n_dofs_1d:1)*
+ (dim>2?n_dofs_1d:1)),
+ (n_q_points_1d*(dim>1?n_q_points_1d:1)*(dim>2?n_q_points_1d:1)),
+ n_components, Number> BaseClass;
+ static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell;
+ static const unsigned int n_q_points = BaseClass::n_q_points;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationGeneral (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Evaluates the function values, the
+ * gradients, and the Laplacians of the FE
+ * function given at the DoF values in the
+ * input vector at the quadrature points. The
+ * function arguments specify which parts
+ * shall actually be computed. Needs to be
+ * called before the functions @p get_value(),
+ * @p get_gradient() or @p get_laplacian
+ * return useful information.
+ */
+ void evaluate (bool evaluate_val, bool evaluate_grad,
+ bool evaluate_hess=false);
+
+ /**
+ * This function takes the values and/or
+ * gradients that are stored on quadrature
+ * points, tests them by all the basis
+ * functions/gradients on the cell and
+ * performs the cell integration. The two
+ * function arguments @p integrate_val and @p
+ * integrate_grad are used to enable/disable
+ * some of values or gradients.
+ */
+ void integrate (bool integrate_val, bool integrate_grad);
+
+ /**
+ * Returns the q-th quadrature point stored in
+ * MappingInfo.
+ */
+ Point<dim,vector_t> quadrature_point (const unsigned int q_point) const;
+
+protected:
+
+ /**
+ * Internal function that applies the shape
+ * function data of the tensor product in a
+ * given coordinate direction (first template
+ * argument), from polynomials to values on
+ * quadrature points (second flag set to true)
+ * or in an integration loop from values on
+ * quadrature points to values tested by
+ * different test function (second flag set to
+ * false), and if the result is to be added to
+ * some previous results or not.
+ */
+ template <int direction, bool dof_to_quad, bool add>
+ void apply_tensor_prod (const vector_t * shape_data,
+ const vector_t in [],
+ vector_t out []);
+};
+
+
+
+/**
+ * The class that provides all functions necessary to evaluate functions at
+ * quadrature points and cell integrations. In functionality, this class is
+ * similar to FEValues<dim>, however, it includes a lot of specialized
+ * functions that make it much faster (between 5 and 500, depending on the
+ * polynomial order).
+ *
+ * This class is a specialization of FEEvaluationGeneral designed for standard
+ * FE_Q or FE_DGQ elements and quadrature points symmetric around 0.5 (like
+ * Gauss quadrature), and hence the most common situation.
+ *
+ * This class has five template arguments:
+ *
+ * @param dim Dimension in which this class is to be used
+ *
+ * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually
+ * fe_degree+1, for elements based on a tensor product
+ *
+ * @param n_q_points_1d Number of points in the quadrature formula in 1D,
+ * usually chosen as fe_degree+1
+ *
+ * @param n_components Number of vector components when solving a system of
+ * PDEs. If the same operation is applied to several
+ * components of a PDE (e.g. a vector Laplace equation), they
+ * can be applied simultaneously with one call (and often
+ * more efficiently)
+ *
+ * @param Number Number format, usually @p double or @p float
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d,
+ int n_components=1, typename Number=double >
+class FEEvaluation :
+ public FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ typedef FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number> BaseClass;
+ static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell;
+ static const unsigned int n_q_points = BaseClass::n_q_points;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluation (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Evaluates the function values, the
+ * gradients, and the Laplacians of the FE
+ * function given at the DoF values in the
+ * input vector at the quadrature points on
+ * the unit cell. The function arguments
+ * specify which parts shall actually be
+ * computed. Needs to be called before the
+ * functions @p get_value(), @p get_gradient()
+ * or @p get_laplacian give useful information
+ * (unless these values have been set
+ * manually).
+ */
+ void evaluate (bool evaluate_val, bool evaluate_grad,
+ bool evaluate_hess=false);
+
+ /**
+ * This function takes the values and/or
+ * gradients that are stored on quadrature
+ * points, tests them by all the basis
+ * functions/gradients on the cell and
+ * performs the cell integration. The two
+ * function arguments @p integrate_val and @p
+ * integrate_grad are used to enable/disable
+ * some of values or gradients.
+ */
+ void integrate (bool integrate_val, bool integrate_grad);
+
+protected:
+ /**
+ * Internal function that applies the function
+ * values of the tensor product in a given
+ * coordinate direction (first template
+ * argument), from polynomials to values on
+ * quadrature points (second flag set to true)
+ * or in an integration loop from values on
+ * quadrature points to values tested by
+ * different test function (second flag set to
+ * false), and if the result is to be added to
+ * previous content in the data fields or
+ * not.
+ */
+ template <int direction, bool dof_to_quad, bool add>
+ void apply_values (const vector_t in [], vector_t out []);
+
+ /**
+ * Internal function that applies the gradient
+ * operation of the tensor product in a given
+ * coordinate direction (first template
+ * argument), from polynomials to values on
+ * quadrature points (second flag set to true)
+ * or in an integration loop from values on
+ * quadrature points to values tested by
+ * different test function (second flag set to
+ * false), and if the result is to be added to
+ * previous content in the data fields or
+ * not.
+ */
+ template <int direction, bool dof_to_quad, bool add>
+ void apply_gradients (const vector_t in [], vector_t out []);
+
+ /**
+ * Internal function that applies the second
+ * derivative operation (Hessian) of the
+ * tensor product in a given coordinate
+ * direction (first template argument), from
+ * polynomials to values on quadrature points
+ * (second flag set to true) or in an
+ * integration loop from values on quadrature
+ * points to values tested by different test
+ * function (second flag set to false), and if
+ * the result is to be added to previous
+ * content in the data fields or not.
+ */
+ template <int direction, bool dof_to_quad, bool add>
+ void apply_hessians (const vector_t in [], vector_t out []);
+};
+
+
+
+/**
+ * The class that provides all functions necessary to evaluate functions at
+ * quadrature points and cell integrations. In functionality, this class is
+ * similar to FEValues<dim>, however, it includes a lot of specialized
+ * functions that make it much faster (between 5 and 500, depending on the
+ * polynomial order).
+ *
+ * This class is a specialization of FEEvaluation for elements where
+ * quadrature formula and support points are chosen so that a orthonormal
+ * relation between the values holds. This is the case for FE_Q based on
+ * Gauss-Lobatto support points with Gauss-Lobatto quadrature formula of the
+ * same order (QGaussLobatto). In that case, application of values is trivial
+ * (as the transformation is the identity matrix), and application of
+ * gradients is considerably simpler (since all value applications in
+ * directions other than the gradient direction are again identity
+ * operations).
+ *
+ * This class has five template arguments:
+ *
+ * @param dim Dimension in which this class is to be used
+ *
+ * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually
+ * fe_degree+1, for elements based on a tensor product
+ *
+ * @param n_q_points_1d Number of points in the quadrature formula in 1D,
+ * usually chosen as fe_degree+1
+ *
+ * @param n_components Number of vector components when solving a system of
+ * PDEs. If the same operation is applied to several
+ * components of a PDE (e.g. a vector Laplace equation), they
+ * can be applied simultaneously with one call (and often
+ * more efficiently)
+ *
+ * @param Number Number format, usually @p double or @p float
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+template <int dim, int n_points_1d, int n_components=1, typename Number=double >
+class FEEvaluationGL :
+ public FEEvaluation<dim,n_points_1d,n_points_1d,n_components,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+ typedef FEEvaluation<dim,n_points_1d,n_points_1d,n_components,Number> BaseClass;
+ static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell;
+ static const unsigned int n_q_points = BaseClass::n_q_points;
+
+ /**
+ * Constructor. Takes all data stored in
+ * MatrixFree. If applied to problems with
+ * more than one finite element or more than
+ * one quadrature formula selected during
+ * construction of @p matrix_free, @p
+ * fe_no and @p quad_no allow to select the
+ * appropriate components.
+ */
+ FEEvaluationGL (const MatrixFree<dim,Number> &matrix_free,
+ const unsigned int fe_no = 0,
+ const unsigned int quad_no = 0);
+
+ /**
+ * Evaluates the function values, the
+ * gradients, and the Hessians of the FE
+ * function given at the DoF values in the
+ * input vector at the quadrature points of
+ * the unit cell. The function arguments
+ * specify which parts shall actually be
+ * computed. Needs to be called before the
+ * functions @p get_value(), @p get_gradient()
+ * or @p get_laplacian give useful information
+ * (unless these values have been set
+ * manually).
+ */
+ void evaluate (bool evaluate_val, bool evaluate_grad,
+ bool evaluate_lapl=false);
+
+ /**
+ * This function takes the values and/or
+ * gradients that are stored on quadrature
+ * points, tests them by all the basis
+ * functions/gradients on the cell and
+ * performs the cell integration. The two
+ * function arguments @p integrate_val and @p
+ * integrate_grad are used to enable/disable
+ * some of values or gradients.
+ */
+ void integrate (bool integrate_val, bool integrate_grad);
+
+protected:
+ /**
+ * Internal function that applies the gradient
+ * operation of the tensor product in a given
+ * coordinate direction (first template
+ * argument), from polynomials to values on
+ * quadrature points (second flag set to true)
+ * or in an integration loop from values on
+ * quadrature points to values tested by
+ * different test function (second flag set to
+ * false), and if the result is to be added to
+ * some previous results or not.
+ */
+ template <int direction, bool dof_to_quad, bool add>
+ void apply_gradients (const vector_t in [], vector_t out []);
+};
+
+
+
+
+/*----------------------- Inline functions ----------------------------------*/
+
+#ifndef DOXYGEN
+
+
+/*----------------------- FEEvaluationBase -------------------------------*/
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+FEEvaluationBase (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no_in,
+ const unsigned int quad_no_in)
+ :
+ matrix_info (data_in),
+ dof_info (data_in.get_dof_info(fe_no_in)),
+ constraint_pool (data_in.get_constraint_pool()),
+ mapping_info (data_in.get_mapping_info()),
+ active_fe_index (dof_info.fe_index_from_dofs_per_cell
+ (dofs_per_cell_ * dof_info.n_components)),
+ active_quad_index (mapping_info.
+ mapping_data_gen[quad_no_in].
+ quad_index_from_n_q_points(n_q_points_)),
+ data (data_in.get_fe_evaluation
+ (fe_no_in, quad_no_in, active_fe_index,
+ active_quad_index)),
+ quad_no (quad_no_in),
+ n_fe_components (dof_info.n_components),
+ cell (numbers::invalid_unsigned_int),
+ cell_type (numbers::invalid_unsigned_int),
+ cartesian (0),
+ jacobian (0),
+ J_value (0),
+ quadrature_weights (mapping_info.mapping_data_gen[quad_no].
+ quadrature_weights[active_quad_index].begin()),
+ quadrature_points (0),
+ jacobian_grad (0),
+ jacobian_grad_upper(0)
+{
+ Assert (matrix_info.indices_initialized() == true,
+ ExcNotInitialized());
+ Assert (matrix_info.mapping_initialized() == true,
+ ExcNotInitialized());
+ AssertDimension (matrix_info.get_size_info().n_vectors, n_vectors);
+ Assert (n_fe_components == 1 ||
+ n_components == n_fe_components,
+ ExcMessage ("The underlying FE is vector-valued. In this case, the "
+ "template argument n_components must be a the same "
+ "as the number of underlying vector components."));
+
+
+ // do not check for correct dimensions of data
+ // fields here, should be done in derived
+ // classes
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+reinit (const unsigned int cell_in)
+{
+ AssertIndexRange (cell_in, dof_info.row_starts.size()-1);
+ AssertDimension (((dof_info.cell_active_fe_index.size() > 0) ?
+ dof_info.cell_active_fe_index[cell_in] : 0),
+ active_fe_index);
+ cell = cell_in;
+ cell_type = mapping_info.get_cell_type(cell);
+ cell_data_number = mapping_info.get_cell_data_index(cell);
+
+ if (mapping_info.quadrature_points_initialized == true)
+ {
+ AssertIndexRange (cell_data_number, mapping_info.
+ mapping_data_gen[quad_no].rowstart_q_points.size());
+ const unsigned int index = mapping_info.mapping_data_gen[quad_no].
+ rowstart_q_points[cell];
+ AssertIndexRange (index, mapping_info.mapping_data_gen[quad_no].
+ quadrature_points.size());
+ quadrature_points =
+ &mapping_info.mapping_data_gen[quad_no].quadrature_points[index];
+ }
+
+ if (cell_type == 0)
+ {
+ cartesian = &mapping_info.cartesian[cell_data_number].first;
+ J_value = &mapping_info.cartesian[cell_data_number].second;
+ }
+ else if (cell_type == 1)
+ {
+ jacobian = &mapping_info.linear[cell_data_number].first;
+ J_value = &mapping_info.linear[cell_data_number].second;
+ }
+ else
+ {
+ const unsigned int rowstart = mapping_info.
+ mapping_data_gen[quad_no].rowstart_jacobians[cell_data_number];
+ AssertIndexRange (rowstart, mapping_info.
+ mapping_data_gen[quad_no].jacobians.size());
+ jacobian =
+ &mapping_info.mapping_data_gen[quad_no].jacobians[rowstart];
+ if (mapping_info.JxW_values_initialized == true)
+ {
+ AssertIndexRange (rowstart, mapping_info.
+ mapping_data_gen[quad_no].JxW_values.size());
+ J_value = &(mapping_info.mapping_data_gen[quad_no].
+ JxW_values[rowstart]);
+ }
+ if (mapping_info.second_derivatives_initialized == true)
+ {
+ AssertIndexRange(rowstart, mapping_info.
+ mapping_data_gen[quad_no].jacobians_grad_diag.size());
+ jacobian_grad = &mapping_info.mapping_data_gen[quad_no].
+ jacobians_grad_diag[rowstart];
+ AssertIndexRange(rowstart, mapping_info.
+ mapping_data_gen[quad_no].jacobians_grad_upper.size());
+ jacobian_grad_upper = &mapping_info.mapping_data_gen[quad_no].
+ jacobians_grad_upper[rowstart];
+ }
+ }
+
+ n_irreg_components_filled =
+ std_cxx1x::get<2>(dof_info.row_starts[cell_in]);
+ at_irregular_cell = n_irreg_components_filled > 0;
+#ifdef DEBUG
+ dof_values_initialized = false;
+ values_quad_initialized = false;
+ gradients_quad_initialized = false;
+ hessians_quad_initialized = false;
+#endif
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+unsigned int
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_cell_data_number () const
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ return cell_data_number;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+unsigned int
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_cell_type () const
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ return cell_type;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+const VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_values () const
+{
+ Assert (values_quad_initialized || values_quad_submitted,
+ ExcNotInitialized());
+ return &values_quad[0][0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_values ()
+{
+#ifdef DEBUG
+ values_quad_submitted = true;
+#endif
+ return &values_quad[0][0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+const VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_gradients () const
+{
+ Assert (gradients_quad_initialized || gradients_quad_submitted,
+ ExcNotInitialized());
+ return &gradients_quad[0][0][0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_gradients ()
+{
+#ifdef DEBUG
+ gradients_quad_submitted = true;
+#endif
+ return &gradients_quad[0][0][0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+const VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_hessians () const
+{
+ Assert (hessians_quad_initialized, ExcNotInitialized());
+ return &hessians_quad[0][0][0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+VectorizedArray<Number> *
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+begin_hessians ()
+{
+ return &hessians_quad[0][0][0];
+}
+
+
+
+namespace internal
+{
+ // write access to generic vectors that have
+ // operator ().
+ template <typename VectorType>
+ inline
+ typename VectorType::value_type &
+ vector_access (VectorType &vec,
+ const unsigned int entry)
+ {
+ return vec(entry);
+ }
+
+
+
+ // read access to generic vectors that have
+ // operator ().
+ template <typename VectorType>
+ inline
+ typename VectorType::value_type
+ vector_access (const VectorType &vec,
+ const unsigned int entry)
+ {
+ return vec(entry);
+ }
+
+
+
+ // write access to distributed MPI vectors
+ // that have operator [] to access data in
+ // local index space, which is what we use in
+ // DoFInfo and hence in read_dof_values etc.
+ template <typename Number>
+ inline
+ Number &
+ vector_access (parallel::distributed::Vector<Number> &vec,
+ const unsigned int entry)
+ {
+ return vec.local_element(entry);
+ }
+
+
+
+ // read access to distributed MPI vectors that
+ // have operator [] to access data in local
+ // index space, which is what we use in
+ // DoFInfo and hence in read_dof_values etc.
+ template <typename Number>
+ inline
+ Number
+ vector_access (const parallel::distributed::Vector<Number> &vec,
+ const unsigned int entry)
+ {
+ return vec.local_element(entry);
+ }
+
+
+
+ // this is to make sure that the parallel
+ // partitioning in the
+ // parallel::distributed::Vector is really the
+ // same as stored in MatrixFree
+ template <typename VectorType>
+ inline
+ void check_vector_compatibility (const VectorType &vec,
+ const internal::MatrixFreeFunctions::DoFInfo &dof_info)
+ {
+ AssertDimension (vec.size(),
+ dof_info.vector_partitioner->size());
+ }
+
+ template <typename Number>
+ inline
+ void check_vector_compatibility (const parallel::distributed::Vector<Number> &vec,
+ const internal::MatrixFreeFunctions::DoFInfo &dof_info)
+ {
+ Assert (vec.partitioners_are_compatible(*dof_info.vector_partitioner),
+ ExcMessage("The parallel layout of the given vector is not "
+ "compatible with the parallel partitioning in MatrixFree. "
+ "Use MatrixFree::initialize_dof_vector to get a "
+ "compatible vector."));
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values (const VectorType &src)
+{
+ AssertDimension (n_components, n_fe_components);
+ // only need one component, but to avoid
+ // compiler warnings, use n_components copies
+ // here (but these will not be used)
+ const VectorType * src_data[n_components];
+ for (unsigned int d=0; d<n_components; ++d)
+ src_data[d] = &src;
+ read_dof_values (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values (const std::vector<VectorType> &src,
+ const unsigned int first_index)
+{
+ AssertIndexRange (first_index, src.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= src.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, src.size()));
+ const VectorType * src_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ src_data[comp] = &src[comp+first_index];
+ read_dof_values (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values (const std::vector<VectorType*> &src,
+ const unsigned int first_index)
+{
+ AssertIndexRange (first_index, src.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= src.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, src.size()));
+ const VectorType * src_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ src_data[comp] = src[comp+first_index];
+ read_dof_values (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values (const VectorType * src[])
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+
+ // loop over all local dofs. ind_local holds
+ // local number on cell, index iterates over
+ // the elements of index_local_to_global and
+ // dof_indices points to the global indices
+ // stored in index_local_to_global
+ const unsigned int * dof_indices = dof_info.begin_indices(cell);
+ const std::pair<unsigned short,unsigned short> * indicators =
+ dof_info.begin_indicators(cell);
+ const std::pair<unsigned short,unsigned short> * indicators_end =
+ dof_info.end_indicators(cell);
+ unsigned int ind_local = 0;
+
+ // scalar case (or case when all components
+ // have the same degrees of freedom and sit on
+ // a different vector each)
+ if (n_fe_components == 1)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::check_vector_compatibility (*src[comp], dof_info);
+ Number * local_src_number [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp] = reinterpret_cast<Number*>(values_dofs[comp]);
+
+ // standard case where there are sufficiently
+ // many cells to fill all vectors
+ if (at_irregular_cell == false)
+ {
+ // check whether there is any constraint on
+ // the current cell
+ if (indicators != indicators_end)
+ {
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // run through values up to next constraint
+ for (unsigned int j=0; j<indicators->first; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local+j] =
+ internal::vector_access (*src[comp], dof_indices[j]);
+ ind_local += indicators->first;
+ dof_indices += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraints
+ Number value [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ value[comp] = 0;
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ value[comp] +=
+ (internal::vector_access (*src[comp], dof_indices[k]) *
+ data_val[k]);
+ dof_indices += row_length;
+
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = value[comp];
+ ind_local++;
+ }
+
+ // get the dof values past the last
+ // constraint
+ for(; ind_local<n_vectors*dofs_per_cell; ++dof_indices, ++ind_local)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] =
+ internal::vector_access (*src[comp], *dof_indices);
+ }
+ }
+ else
+ {
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ n_vectors*dofs_per_cell);
+ for (unsigned int j=0; j<dofs_per_cell*n_vectors; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][j] =
+ internal::vector_access (*src[comp], dof_indices[j]);
+ }
+ }
+
+ // non-standard case: need to fill in zeros
+ // for those components that are not present
+ // (a bit more expensive), but there is not
+ // more than one such cell
+ else
+ {
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_src.
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] =
+ internal::vector_access (*src[comp], dof_indices[j]);
+
+ // here we jump over all the components that
+ // are artificial
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ dof_indices += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraint
+ Number value [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ value[comp] = 0.;
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ value[comp] +=
+ internal::vector_access (*src[comp], dof_indices[k]) * data_val[k];
+ dof_indices += row_length;
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = value[comp];
+ ind_local++;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ for(; ind_local<n_vectors*dofs_per_cell; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_dst.
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] =
+ internal::vector_access (*src[comp], *dof_indices);
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ }
+ }
+ else
+ // case with vector-valued finite elements
+ // where all components are included in one
+ // single vector. Assumption: first come all
+ // entries to the first component, then all
+ // entries to the second one, and so on. This
+ // is ensured by the way MatrixFree reads
+ // out the indices.
+ {
+ internal::check_vector_compatibility (*src[0], dof_info);
+ Assert (n_fe_components == n_components, ExcNotImplemented());
+ const unsigned int total_dofs_per_cell =
+ dofs_per_cell * n_vectors * n_components;
+ Number * local_src_number = reinterpret_cast<Number*>(values_dofs[0]);
+ if (at_irregular_cell == false)
+ {
+ // check whether there is any constraint on
+ // the current cell
+ if (indicators != indicators_end)
+ {
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // run through values up to next constraint
+ for (unsigned int j=0; j<indicators->first; ++j)
+ local_src_number[ind_local+j] =
+ internal::vector_access (*src[0], dof_indices[j]);
+ ind_local += indicators->first;
+ dof_indices += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraints
+ Number value = 0;
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ value +=
+ (internal::vector_access (*src[0], dof_indices[k]) *
+ data_val[k]);
+ dof_indices += row_length;
+
+ local_src_number[ind_local] = value;
+ ind_local++;
+ }
+
+ // get the dof values past the last
+ // constraint
+ for(; ind_local<total_dofs_per_cell; ++dof_indices, ++ind_local)
+ local_src_number[ind_local] =
+ internal::vector_access (*src[0], *dof_indices);
+ Assert (dof_indices == dof_info.end_indices(cell),
+ ExcInternalError());
+ }
+ else
+ {
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ static_cast<int>(total_dofs_per_cell));
+ for (unsigned int j=0; j<total_dofs_per_cell; ++j)
+ local_src_number[j] =
+ internal::vector_access (*src[0], dof_indices[j]);
+ }
+ }
+
+ // non-standard case: need to fill in zeros
+ // for those components that are not present
+ // (a bit more expensive), but there is not
+ // more than one such cell
+ else
+ {
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_src.
+ local_src_number[ind_local] =
+ internal::vector_access (*src[0], dof_indices[j]);
+
+ // here we jump over all the components that
+ // are artificial
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ local_src_number[ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ dof_indices += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraint
+ Number value = 0;
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ value +=
+ internal::vector_access (*src[0], dof_indices[k]) * data_val[k];
+ dof_indices += row_length;
+ local_src_number[ind_local] = value;
+ ind_local++;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ local_src_number[ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ for(; ind_local<total_dofs_per_cell; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_dst.
+ local_src_number[ind_local] =
+ internal::vector_access (*src[0], *dof_indices);
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ local_src_number[ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ dof_values_initialized = true;
+#endif
+}
+
+
+
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values_plain (const VectorType &src)
+{
+ AssertDimension (n_components, n_fe_components);
+ // only need one component, but to avoid
+ // compiler warnings, use n_components copies
+ // here (but these will not be used)
+ const VectorType * src_data[n_components];
+ for (unsigned int d=0; d<n_components; ++d)
+ src_data[d] = &src;
+ read_dof_values_plain (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values_plain (const std::vector<VectorType> &src,
+ const unsigned int first_index)
+{
+ AssertIndexRange (first_index, src.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= src.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, src.size()));
+ const VectorType * src_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ src_data[comp] = &src[comp+first_index];
+ read_dof_values_plain (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values_plain (const std::vector<VectorType*> &src,
+ const unsigned int first_index)
+{
+ AssertIndexRange (first_index, src.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= src.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, src.size()));
+ const VectorType * src_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ src_data[comp] = src[comp+first_index];
+ read_dof_values_plain (src_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+read_dof_values_plain (const VectorType * src[])
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ Assert (dof_info.store_plain_indices == true, ExcNotInitialized());
+
+ // loop over all local dofs. ind_local holds
+ // local number on cell, index iterates over
+ // the elements of index_local_to_global and
+ // dof_indices points to the global indices
+ // stored in index_local_to_global
+ const unsigned int * dof_indices = dof_info.begin_indices_plain(cell);
+
+ // scalar case (or case when all components
+ // have the same degrees of freedom and sit on
+ // a different vector each)
+ if (n_fe_components == 1)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::check_vector_compatibility (*src[comp], dof_info);
+ Number * local_src_number [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp] = reinterpret_cast<Number*>(values_dofs[comp]);
+
+ // standard case where there are sufficiently
+ // many cells to fill all vectors
+ if (at_irregular_cell == false)
+ {
+ for (unsigned int j=0; j<dofs_per_cell*n_vectors; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][j] =
+ internal::vector_access (*src[comp], dof_indices[j]);
+ }
+
+ // non-standard case: need to fill in zeros
+ // for those components that are not present
+ // (a bit more expensive), but there is not
+ // more than one such cell
+ else
+ {
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for(unsigned int ind_local=0; ind_local<n_vectors*dofs_per_cell;
+ ++dof_indices)
+ {
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_dst.
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] =
+ internal::vector_access (*src[comp], *dof_indices);
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_src_number[comp][ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ }
+ }
+ else
+ // case with vector-valued finite elements
+ // where all components are included in one
+ // single vector. Assumption: first come all
+ // entries to the first component, then all
+ // entries to the second one, and so on. This
+ // is ensured by the way MatrixFree reads
+ // out the indices.
+ {
+ internal::check_vector_compatibility (*src[0], dof_info);
+ Assert (n_fe_components == n_components, ExcNotImplemented());
+ const unsigned int total_dofs_per_cell =
+ dofs_per_cell * n_vectors * n_components;
+ Number * local_src_number = reinterpret_cast<Number*>(values_dofs[0]);
+ if (at_irregular_cell == false)
+ {
+ for (unsigned int j=0; j<total_dofs_per_cell; ++j)
+ local_src_number[j] =
+ internal::vector_access (*src[0], dof_indices[j]);
+ }
+
+ // non-standard case: need to fill in zeros
+ // for those components that are not present
+ // (a bit more expensive), but there is not
+ // more than one such cell
+ else
+ {
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for(unsigned int ind_local=0; ind_local<total_dofs_per_cell; ++dof_indices)
+ {
+ // non-constrained case: copy the data from
+ // the global vector, src, to the local one,
+ // local_dst.
+ local_src_number[ind_local] =
+ internal::vector_access (*src[0], *dof_indices);
+ ++ind_local;
+ while (ind_local % n_vectors >= n_irreg_components_filled)
+ {
+ local_src_number[ind_local] = 0.;
+ ++ind_local;
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ dof_values_initialized = true;
+#endif
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+distribute_local_to_global (VectorType &dst) const
+{
+ AssertDimension (n_components, n_fe_components);
+ // only need one component, but to avoid
+ // compiler warnings, use n_components copies
+ // here (but these will not be used)
+ VectorType * dst_data [n_components];
+ for (unsigned int d=0; d<n_components; ++d)
+ dst_data[d] = &dst;
+ distribute_local_to_global (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+distribute_local_to_global (std::vector<VectorType> &dst,
+ const unsigned int first_index) const
+{
+ AssertIndexRange (first_index, dst.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= dst.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, dst.size()));
+
+ VectorType * dst_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ dst_data[comp] = &dst[comp+first_index];
+ distribute_local_to_global (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+distribute_local_to_global (std::vector<VectorType*> &dst,
+ const unsigned int first_index) const
+{
+ AssertIndexRange (first_index, dst.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= dst.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, dst.size()));
+
+ VectorType * dst_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ dst_data[comp] = dst[comp+first_index];
+ distribute_local_to_global (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+distribute_local_to_global (VectorType * dst[]) const
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ Assert (dof_values_initialized==true,
+ internal::ExcAccessToUninitializedField());
+
+ // loop over all local dofs. ind_local holds
+ // local number on cell, index iterates over
+ // the elements of index_local_to_global and
+ // dof_indices points to the global indices
+ // stored in index_local_to_global
+ const unsigned int * dof_indices = dof_info.begin_indices(cell);
+ const std::pair<unsigned short,unsigned short> * indicators =
+ dof_info.begin_indicators(cell);
+ const std::pair<unsigned short,unsigned short> * indicators_end =
+ dof_info.end_indicators(cell);
+ unsigned int ind_local = 0;
+
+ // scalar case (or case when all components
+ // have the same degrees of freedom and sit on
+ // a different vector each)
+ if (n_fe_components == 1)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::check_vector_compatibility (*dst[comp], dof_info);
+
+ const Number * local_dst_number [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_dst_number[comp] =
+ reinterpret_cast<const Number*>(values_dofs[comp]);
+ if (at_irregular_cell == false)
+ {
+ // check whether there is no constraint at all
+ if (indicators != indicators_end)
+ {
+ // run from one constraint to the next
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // distribute values up to the constraint
+ // (values not constrained)
+ for (unsigned int j=0; j<indicators->first; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ += local_dst_number[comp][ind_local+j];
+ dof_indices += indicators->first;
+ ind_local += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraint
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[k])
+ += local_dst_number[comp][ind_local] * data_val[k];
+ dof_indices += row_length;
+ ++ind_local;
+ }
+ // distribute values after the last constraint
+ // (values not constrained)
+ for(; ind_local<dofs_per_cell*n_vectors; ++dof_indices, ++ind_local)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], *dof_indices)
+ += local_dst_number[comp][ind_local];
+ }
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ else
+ {
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ n_vectors * dofs_per_cell);
+ for (unsigned int j=0; j<dofs_per_cell*n_vectors; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ += local_dst_number[comp][j];
+ }
+ return;
+ }
+
+ // irregular case
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case: copy the data from
+ // the local vector to the global one.
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ += local_dst_number[comp][ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ dof_indices += indicators->first;
+
+ // constrained case: distribute according to
+ // the constraint
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[k])
+ += local_dst_number[comp][ind_local] * data_val[k];
+ }
+ dof_indices += row_length;
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ for(; ind_local<dofs_per_cell*n_vectors; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+
+ // non-constrained case
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], *dof_indices)
+ += local_dst_number[comp][ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ }
+ else
+ // case with vector-valued finite elements
+ // where all components are included in one
+ // single vector. Assumption: first come all
+ // entries to the first component, then all
+ // entries to the second one, and so on. This
+ // is ensured by the way MatrixFree reads
+ // out the indices.
+ {
+ internal::check_vector_compatibility (*dst[0], dof_info);
+ Assert (n_fe_components == n_components, ExcNotImplemented());
+ const unsigned int total_dofs_per_cell =
+ dofs_per_cell * n_vectors * n_components;
+ const Number * local_dst_number =
+ reinterpret_cast<const Number*>(values_dofs[0]);
+ if (at_irregular_cell == false)
+ {
+ // check whether there is no constraint at all
+ if (indicators != indicators_end)
+ {
+ // run from one constraint to the next
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // distribute values up to the constraint
+ // (values not constrained)
+ for (unsigned int j=0; j<indicators->first; ++j)
+ internal::vector_access (*dst[0], dof_indices[j])
+ += local_dst_number[ind_local+j];
+ dof_indices += indicators->first;
+ ind_local += indicators->first;
+
+ // constrained case: build the local value as
+ // a linear combination of the global value
+ // according to constraint
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ internal::vector_access (*dst[0], dof_indices[k])
+ += local_dst_number[ind_local] * data_val[k];
+ dof_indices += row_length;
+ ++ind_local;
+ }
+ // distribute values after the last constraint
+ // (values not constrained)
+ for(; ind_local<total_dofs_per_cell; ++dof_indices, ++ind_local)
+ internal::vector_access (*dst[0], *dof_indices)
+ += local_dst_number[ind_local];
+ }
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ else
+ {
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ static_cast<int>(total_dofs_per_cell));
+ for (unsigned int j=0; j<total_dofs_per_cell; ++j)
+ internal::vector_access (*dst[0], dof_indices[j])
+ += local_dst_number[j];
+ }
+ return;
+ }
+
+ // irregular case
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case: copy the data from
+ // the local vector to the global one.
+ internal::vector_access (*dst[0], dof_indices[j])
+ += local_dst_number[ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ dof_indices += indicators->first;
+
+ // constrained case: distribute according to
+ // the constraint
+ const Number * data_val =
+ constraint_pool.begin(indicators->second);
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ for (unsigned int k=0; k<row_length; ++k)
+ {
+ internal::vector_access (*dst[0], dof_indices[k])
+ += local_dst_number[ind_local] * data_val[k];
+ }
+ dof_indices += row_length;
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ for(; ind_local<total_dofs_per_cell; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+
+ // non-constrained case
+ internal::vector_access (*dst[0], *dof_indices)
+ += local_dst_number[ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ Assert (dof_indices == dof_info.end_indices(cell),
+ ExcInternalError());
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+set_dof_values (VectorType &dst) const
+{
+ AssertDimension (n_components, n_fe_components);
+ // only need one component, but to avoid
+ // compiler warnings, use n_components copies
+ // here (but these will not be used)
+ VectorType * dst_data [n_components];
+ for (unsigned int d=0; d<n_components; ++d)
+ dst_data[d] = &dst;
+ set_dof_values (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+set_dof_values (std::vector<VectorType> &dst,
+ const unsigned int first_index) const
+{
+ AssertIndexRange (first_index, dst.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= dst.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, dst.size()));
+
+ VectorType * dst_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ dst_data[comp] = &dst[comp+first_index];
+ set_dof_values (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+set_dof_values (std::vector<VectorType*> &dst,
+ const unsigned int first_index) const
+{
+ AssertIndexRange (first_index, dst.size());
+ Assert (n_fe_components == 1, ExcNotImplemented());
+ Assert ((n_fe_components == 1 ?
+ (first_index+n_components <= dst.size()) : true),
+ ExcIndexRange (first_index + n_components, 0, dst.size()));
+
+ VectorType * dst_data [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ dst_data[comp] = dst[comp+first_index];
+ set_dof_values (dst_data);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+template<typename VectorType>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+set_dof_values (VectorType * dst[]) const
+{
+ Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ Assert (dof_values_initialized==true,
+ internal::ExcAccessToUninitializedField());
+
+ // loop over all local dofs. ind_local holds
+ // local number on cell, index iterates over
+ // the elements of index_local_to_global and
+ // glob_indices points to the global indices
+ // stored in index_local_to_global
+ const unsigned int * dof_indices = dof_info.begin_indices(cell);
+ const std::pair<unsigned short,unsigned short> * indicators =
+ dof_info.begin_indicators(cell);
+ const std::pair<unsigned short,unsigned short> * indicators_end =
+ dof_info.end_indicators(cell);
+ unsigned int ind_local = 0;
+
+ if (n_fe_components == 1)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ AssertDimension (dst[comp]->size(),
+ dof_info.vector_partitioner->size());
+
+ const Number * local_dst_number [n_components];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ local_dst_number[comp] =
+ reinterpret_cast<const Number*>(values_dofs[comp]);
+ if (at_irregular_cell == false)
+ {
+ // check whether there is no constraint at all
+ if (indicators != indicators_end)
+ {
+ // run from one constraint to the next
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // distribute values up to the constraint
+ // (values not constrained)
+ for (unsigned int j=0; j<indicators->first; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ = local_dst_number[comp][ind_local+j];
+ dof_indices += indicators->first;
+ ind_local += indicators->first;
+
+ // jump over constraints
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ dof_indices += row_length;
+ ++ind_local;
+ }
+ // distribute values after the last constraint
+ // (values not constrained)
+ for(; ind_local<dofs_per_cell*n_vectors; ++dof_indices, ++ind_local)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], *dof_indices)
+ = local_dst_number[comp][ind_local];
+ }
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ else
+ {
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ n_vectors * dofs_per_cell);
+ for (unsigned int j=0; j<dofs_per_cell*n_vectors; ++j)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ = local_dst_number[comp][j];
+ }
+ return;
+ }
+
+ // irregular case
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], dof_indices[j])
+ = local_dst_number[comp][ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ dof_indices += indicators->first;
+
+ // jump over constraint
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ dof_indices += row_length;
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ for(; ind_local<dofs_per_cell*n_vectors; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+ // non-constrained case
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ internal::vector_access (*dst[comp], *dof_indices)
+ = local_dst_number[comp][ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ }
+ else
+ // case with vector-valued finite elements
+ // where all components are included in one
+ // single vector. Assumption: first come all
+ // entries to the first component, then all
+ // entries to the second one, and so on. This
+ // is ensured by the way MatrixFree reads
+ // out the indices.
+ {
+ AssertDimension (dst[0]->size(),
+ dof_info.vector_partitioner->size());
+ Assert (n_fe_components == n_components, ExcNotImplemented());
+ const unsigned int total_dofs_per_cell =
+ dofs_per_cell * n_vectors * n_components;
+ const Number * local_dst_number =
+ reinterpret_cast<const Number*>(values_dofs[0]);
+
+ if (at_irregular_cell == false)
+ {
+ // check whether there is no constraint at all
+ if (indicators != indicators_end)
+ {
+ // run from one constraint to the next
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ // distribute values up to the constraint
+ // (values not constrained)
+ for (unsigned int j=0; j<indicators->first; ++j)
+ internal::vector_access (*dst[0], dof_indices[j])
+ = local_dst_number[ind_local+j];
+ dof_indices += indicators->first;
+ ind_local += indicators->first;
+
+ // jump over constraints
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ dof_indices += row_length;
+ ++ind_local;
+ }
+ // distribute values after the last constraint
+ // (values not constrained)
+ for(; ind_local<total_dofs_per_cell; ++dof_indices, ++ind_local)
+ internal::vector_access (*dst[0], *dof_indices)
+ = local_dst_number[ind_local];
+ }
+ // no constraint at all: loop bounds are
+ // known, compiler can unroll without checks
+ else
+ {
+ AssertDimension (dof_info.end_indices(cell)-dof_indices,
+ total_dofs_per_cell);
+ for (unsigned int j=0; j<total_dofs_per_cell; ++j)
+ internal::vector_access (*dst[0], dof_indices[j])
+ = local_dst_number[j];
+ }
+ return;
+ }
+
+ // irregular case
+ Assert (n_irreg_components_filled > 0, ExcInternalError());
+ for ( ; indicators != indicators_end; ++indicators)
+ {
+ for(unsigned int j=0; j<indicators->first; ++j)
+ {
+ // non-constrained case
+ internal::vector_access (*dst[0], dof_indices[j])
+ = local_dst_number[ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ dof_indices += indicators->first;
+
+ // jump over constraint
+ const unsigned int row_length =
+ constraint_pool.row_length(indicators->second);
+ dof_indices += row_length;
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ for(; ind_local<total_dofs_per_cell; ++dof_indices)
+ {
+ Assert (dof_indices != dof_info.end_indices(cell),
+ ExcInternalError());
+ // non-constrained case
+ internal::vector_access (*dst[0], *dof_indices)
+ = local_dst_number[ind_local];
+ ++ind_local;
+ if (ind_local % n_vectors == n_irreg_components_filled)
+ ind_local += n_vectors-n_irreg_components_filled;
+ }
+ Assert (dof_indices == dof_info.end_indices (cell),
+ ExcInternalError());
+ }
+}
+
+
+
+// ------------------------------ access to data fields ---------------------
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,VectorizedArray<Number> >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_dof_value (unsigned int dof) const
+{
+ AssertIndexRange (dof, dofs_per_cell);
+ Tensor<1,n_components,vector_t> return_value (false);
+ for(unsigned int comp=0;comp<n_components;comp++)
+ return_value[comp] = this->values_dofs[comp][dof];
+ return return_value;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,VectorizedArray<Number> >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_value (unsigned int q_point) const
+{
+ Assert (this->values_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+ Tensor<1,n_components,vector_t> return_value (false);
+ for(unsigned int comp=0;comp<n_components;comp++)
+ return_value[comp] = this->values_quad[comp][q_point];
+ return return_value;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,Tensor<1,dim,VectorizedArray<Number> > >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_gradient (unsigned int q_point) const
+{
+ Assert (this->gradients_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ Tensor<1,n_components,Tensor<1,dim,vector_t> > grad_out (false);
+
+ // Cartesian cell
+ if (this->cell_type == 0)
+ {
+ for (unsigned int comp=0;comp<n_components;comp++)
+ for (unsigned int d=0; d<dim; ++d)
+ grad_out[comp][d] = (this->gradients_quad[comp][d][q_point] *
+ cartesian[0][d]);
+ }
+ // cell with general Jacobian
+ else if (this->cell_type == 2)
+ {
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ grad_out[comp][d] = (jacobian[q_point][d][0] *
+ this->gradients_quad[comp][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ grad_out[comp][d] += (jacobian[q_point][d][e] *
+ this->gradients_quad[comp][e][q_point]);
+ }
+ }
+ }
+ // cell with general Jacobian, but constant
+ // within the cell
+ else // if (this->cell_type == 1)
+ {
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ grad_out[comp][d] = (jacobian[0][d][0] *
+ this->gradients_quad[comp][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ grad_out[comp][d] += (jacobian[0][d][e] *
+ this->gradients_quad[comp][e][q_point]);
+ }
+ }
+ }
+ return grad_out;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,Tensor<2,dim,VectorizedArray<Number> > >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_hessian (unsigned int q_point) const
+{
+ Assert (this->hessians_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ Tensor<2,dim,vector_t> hessian_out [n_components];
+
+ // Cartesian cell
+ if (this->cell_type == 0)
+ {
+ const Tensor<1,dim,vector_t> &jac = cartesian[0];
+ for (unsigned int comp=0;comp<n_components;comp++)
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ hessian_out[comp][d][d] = (this->hessians_quad[comp][d][q_point] *
+ jac[d] * jac[d]);
+ switch (dim)
+ {
+ case 1: break;
+ case 2:
+ hessian_out[comp][0][1] = (this->hessians_quad[comp][2][q_point] *
+ jac[0] * jac[1]);
+ break;
+ case 3:
+ hessian_out[comp][0][1] = (this->hessians_quad[comp][3][q_point] *
+ jac[0] * jac[1]);
+ hessian_out[comp][0][2] = (this->hessians_quad[comp][4][q_point] *
+ jac[0] * jac[2]);
+ hessian_out[comp][1][2] = (this->hessians_quad[comp][5][q_point] *
+ jac[1] * jac[2]);
+ break;
+ default: Assert (false, ExcNotImplemented());
+ }
+ for (unsigned int e=d+1; e<dim; ++e)
+ hessian_out[comp][e][d] = hessian_out[comp][d][e];
+ }
+ }
+ // cell with general Jacobian
+ else if (this->cell_type == 2)
+ {
+ Assert (this->mapping_info.second_derivatives_initialized == true,
+ ExcNotInitialized());
+ const Tensor<2,dim,vector_t> & jac = jacobian[q_point];
+ const Tensor<2,dim,vector_t> & jac_grad = jacobian_grad[q_point];
+ const typename internal::MatrixFreeFunctions::MappingInfo<dim,Number>::tensorUT
+ & jac_grad_UT = jacobian_grad_upper[q_point];
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ // compute laplacian before the gradient
+ // because it needs to access unscaled
+ // gradient data
+ vector_t tmp[dim][dim];
+
+ // compute tmp = hess_unit(u) * J^T. do this
+ // manually because we do not store the lower
+ // diagonal because of symmetry
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ switch (dim)
+ {
+ case 1:
+ tmp[0][0] = jac[0][0] * this->hessians_quad[comp][0][q_point];
+ break;
+ case 2:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][2][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point]);
+ break;
+ case 3:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][3][q_point] +
+ jac[d][2] * this->hessians_quad[comp][4][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point] +
+ jac[d][2] * this->hessians_quad[comp][5][q_point]);
+ tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] +
+ jac[d][1] * this->hessians_quad[comp][5][q_point] +
+ jac[d][2] * this->hessians_quad[comp][2][q_point]);
+ break;
+ default: Assert (false, ExcNotImplemented());
+ }
+ }
+ // compute first part of hessian,
+ // J * tmp = J * hess_unit(u) * J^T
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=d; e<dim; ++e)
+ {
+ hessian_out[comp][d][e] = jac[d][0] * tmp[0][e];
+ for (unsigned int f=1; f<dim; ++f)
+ hessian_out[comp][d][e] += jac[d][f] * tmp[f][e];
+ }
+
+ // add diagonal part of J' * grad(u)
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ hessian_out[comp][d][d] += (jac_grad[d][e] *
+ this->gradients_quad[comp][e][q_point]);
+
+ // add off-diagonal part of J' * grad(u)
+ for (unsigned int d=0, count=0; d<dim; ++d)
+ for (unsigned int e=d+1; e<dim; ++e, ++count)
+ for (unsigned int f=0; f<dim; ++f)
+ hessian_out[comp][d][e] += (jac_grad_UT[count][f] *
+ this->gradients_quad[comp][f][q_point]);
+
+ // take symmetric part
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=d+1; e<dim; ++e)
+ hessian_out[comp][e][d] = hessian_out[comp][d][e];
+ }
+ }
+ // cell with general Jacobian, but constant
+ // within the cell
+ else // if (this->cell_type == 1)
+ {
+ const Tensor<2,dim,vector_t> &jac = jacobian[0];
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ // compute laplacian before the gradient
+ // because it needs to access unscaled
+ // gradient data
+ vector_t tmp[dim][dim];
+
+ // compute tmp = hess_unit(u) * J^T. do this
+ // manually because we do not store the lower
+ // diagonal because of symmetry
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ switch (dim)
+ {
+ case 1:
+ tmp[0][0] = jac[0][0] * this->hessians_quad[comp][0][q_point];
+ break;
+ case 2:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][2][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point]);
+ break;
+ case 3:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][3][q_point] +
+ jac[d][2] * this->hessians_quad[comp][4][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point] +
+ jac[d][2] * this->hessians_quad[comp][5][q_point]);
+ tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] +
+ jac[d][1] * this->hessians_quad[comp][5][q_point] +
+ jac[d][2] * this->hessians_quad[comp][2][q_point]);
+ break;
+ default: Assert (false, ExcNotImplemented());
+ }
+ }
+ // compute first part of hessian,
+ // J * tmp = J * hess_unit(u) * J^T
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=d; e<dim; ++e)
+ {
+ hessian_out[comp][d][e] = jac[d][0] * tmp[0][e];
+ for (unsigned int f=1; f<dim; ++f)
+ hessian_out[comp][d][e] += jac[d][f] * tmp[f][e];
+ }
+
+ // no J' * grad(u) part here because the
+ // Jacobian is constant throughout the cell
+ // and hence, its derivative is zero
+
+ // take symmetric part
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=d+1; e<dim; ++e)
+ hessian_out[comp][e][d] = hessian_out[comp][d][e];
+ }
+ }
+ return Tensor<1,n_components,Tensor<2,dim,vector_t> >(hessian_out);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,Tensor<1,dim,VectorizedArray<Number> > >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_hessian_diagonal (unsigned int q_point) const
+{
+ Assert (this->hessians_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ Tensor<1,n_components,Tensor<1,dim,vector_t> > hessian_out (false);
+
+ // Cartesian cell
+ if (this->cell_type == 0)
+ {
+ const Tensor<1,dim,vector_t> &jac = cartesian[0];
+ for (unsigned int comp=0;comp<n_components;comp++)
+ for (unsigned int d=0; d<dim; ++d)
+ hessian_out[comp][d] = (this->hessians_quad[comp][d][q_point] *
+ jac[d] * jac[d]);
+ }
+ // cell with general Jacobian
+ else if (this->cell_type == 2)
+ {
+ Assert (this->mapping_info.second_derivatives_initialized == true,
+ ExcNotInitialized());
+ const Tensor<2,dim,vector_t> &jac = jacobian[q_point];
+ const Tensor<2,dim,vector_t> &jac_grad = jacobian_grad[q_point];
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ // compute laplacian before the gradient
+ // because it needs to access unscaled
+ // gradient data
+ vector_t tmp[dim][dim];
+
+ // compute tmp = hess_unit(u) * J^T. do this
+ // manually because we do not store the lower
+ // diagonal because of symmetry
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ switch (dim)
+ {
+ case 1:
+ tmp[0][0] = jac[0][0] * this->hessians_quad[comp][0][q_point];
+ break;
+ case 2:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][2][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point]);
+ break;
+ case 3:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][3][q_point] +
+ jac[d][2] * this->hessians_quad[comp][4][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point] +
+ jac[d][2] * this->hessians_quad[comp][5][q_point]);
+ tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] +
+ jac[d][1] * this->hessians_quad[comp][5][q_point] +
+ jac[d][2] * this->hessians_quad[comp][2][q_point]);
+ break;
+ default: Assert (false, ExcNotImplemented());
+ }
+ }
+ // compute only the trace part of hessian,
+ // J * tmp = J * hess_unit(u) * J^T
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ hessian_out[comp][d] = jac[d][0] * tmp[0][d];
+ for (unsigned int f=1; f<dim; ++f)
+ hessian_out[comp][d] += jac[d][f] * tmp[f][d];
+ }
+
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ hessian_out[comp][d] += (jac_grad[d][e] *
+ this->gradients_quad[comp][e][q_point]);
+ }
+ }
+ // cell with general Jacobian, but constant
+ // within the cell
+ else // if (this->cell_type == 1)
+ {
+ const Tensor<2,dim,vector_t> & jac = jacobian[0];
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ // compute laplacian before the gradient
+ // because it needs to access unscaled
+ // gradient data
+ vector_t tmp[dim][dim];
+
+ // compute tmp = hess_unit(u) * J^T. do this
+ // manually because we do not store the lower
+ // diagonal because of symmetry
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ switch (dim)
+ {
+ case 1:
+ tmp[0][0] = jac[0][0] * this->hessians_quad[comp][0][q_point];
+ break;
+ case 2:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][2][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point]);
+ break;
+ case 3:
+ tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] +
+ jac[d][1] * this->hessians_quad[comp][3][q_point] +
+ jac[d][2] * this->hessians_quad[comp][4][q_point]);
+ tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] +
+ jac[d][1] * this->hessians_quad[comp][1][q_point] +
+ jac[d][2] * this->hessians_quad[comp][5][q_point]);
+ tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] +
+ jac[d][1] * this->hessians_quad[comp][5][q_point] +
+ jac[d][2] * this->hessians_quad[comp][2][q_point]);
+ break;
+ default: Assert (false, ExcNotImplemented());
+ }
+ }
+ // compute only the trace part of hessian,
+ // J * tmp = J * hess_unit(u) * J^T
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ hessian_out[comp][d] = jac[d][0] * tmp[0][d];
+ for (unsigned int f=1; f<dim; ++f)
+ hessian_out[comp][d] += jac[d][f] * tmp[f][d];
+ }
+ }
+ }
+ return hessian_out;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,VectorizedArray<Number> >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+get_laplacian (unsigned int q_point) const
+{
+ Assert (this->hessians_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+ Tensor<1,n_components,vector_t> laplacian_out (false);
+ const Tensor<1,n_components,Tensor<1,dim,vector_t> > hess_diag
+ = get_hessian_diagonal(q_point);
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ {
+ laplacian_out[comp] = hess_diag[comp][0];
+ for (unsigned int d=1; d<dim; ++d)
+ laplacian_out[comp] += hess_diag[comp][d];
+ }
+ return laplacian_out;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+submit_dof_value (Tensor<1,n_components,VectorizedArray<Number> > val_in,
+ unsigned int dof)
+{
+#ifdef DEBUG
+ this->dof_values_initialized = true;
+#endif
+ AssertIndexRange (dof, dofs_per_cell);
+ for (unsigned int comp=0;comp<n_components;comp++)
+ this->values_dofs[comp][dof] = val_in[comp];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+submit_value (Tensor<1,n_components,VectorizedArray<Number> > val_in,
+ unsigned int q_point)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ AssertIndexRange (q_point, n_q_points);
+ this->values_quad_submitted = true;
+#endif
+ if (this->cell_type == 2)
+ {
+ const vector_t JxW = J_value[q_point];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ this->values_quad[comp][q_point] = val_in[comp] * JxW;
+ }
+ else //if (this->cell_type < 2)
+ {
+ const vector_t JxW = J_value[0] * quadrature_weights[q_point];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ this->values_quad[comp][q_point] = val_in[comp] * JxW;
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+void
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+submit_gradient (Tensor<1,n_components,
+ Tensor<1,dim,VectorizedArray<Number> > > grad_in,
+ unsigned int q_point)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ AssertIndexRange (q_point, n_q_points);
+ this->gradients_quad_submitted = true;
+#endif
+ if (this->cell_type == 0)
+ {
+ const vector_t JxW = J_value[0] * quadrature_weights[q_point];
+ for (unsigned int comp=0;comp<n_components;comp++)
+ for (unsigned int d=0; d<dim; ++d)
+ this->gradients_quad[comp][d][q_point] = (grad_in[comp][d] *
+ cartesian[0][d] * JxW);
+ }
+ else if (this->cell_type == 2)
+ {
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = jacobian[q_point][0][d] * grad_in[comp][0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += jacobian[q_point][e][d] * grad_in[comp][e];
+ this->gradients_quad[comp][d][q_point] = new_val * J_value[q_point];
+ }
+ }
+ else //if (this->cell_type == 1)
+ {
+ const vector_t JxW = J_value[0] * quadrature_weights[q_point];
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = jacobian[0][0][d] * grad_in[comp][0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += jacobian[0][e][d] * grad_in[comp][e];
+ this->gradients_quad[comp][d][q_point] = new_val * JxW;
+ }
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+Tensor<1,n_components,VectorizedArray<Number> >
+FEEvaluationBase<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+integrate_value ()
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ Assert (this->values_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+#endif
+ Tensor<1,n_components,vector_t> return_value (false);
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ return_value[comp] = this->values_quad[comp][0];
+ for (unsigned int q=0; q<n_q_points; ++q)
+ for (unsigned int comp=0; comp<n_components; ++comp)
+ return_value[comp] += this->values_quad[comp][q];
+ return (return_value);
+}
+
+
+
+/*----------------------- FEEvaluationAccess -------------------------------*/
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_,
+ int n_components, typename Number>
+inline
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,n_components,Number>::
+FEEvaluationAccess (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no_in)
+ :
+ FEEvaluationBase <dim,dofs_per_cell_,n_q_points_,n_components,Number>
+ (data_in, fe_no, quad_no_in)
+{}
+
+
+
+
+/*-------------------- FEEvaluationAccess scalar --------------------------*/
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+FEEvaluationAccess (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no_in)
+ :
+ FEEvaluationBase <dim,dofs_per_cell_,n_q_points_,1,Number>
+ (data_in, fe_no, quad_no_in)
+{}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+VectorizedArray<Number>
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_dof_value (unsigned int dof) const
+{
+ AssertIndexRange (dof, dofs_per_cell);
+ return this->values_dofs[0][dof];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+VectorizedArray<Number>
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_value (unsigned int q_point) const
+{
+ Assert (this->values_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+ return this->values_quad[0][q_point];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<1,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_gradient (unsigned int q_point) const
+{
+ // could use the base class gradient, but that
+ // involves too many inefficient
+ // initialization
+
+ Assert (this->gradients_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ Tensor<1,dim,vector_t> grad_out (false);
+
+ // Cartesian cell
+ if (this->cell_type == 0)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ grad_out[d] = (this->gradients_quad[0][d][q_point] *
+ this->cartesian[0][d]);
+ }
+ // cell with general Jacobian
+ else if (this->cell_type == 2)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ grad_out[d] = (this->jacobian[q_point][d][0] *
+ this->gradients_quad[0][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ grad_out[d] += (this->jacobian[q_point][d][e] *
+ this->gradients_quad[0][e][q_point]);
+ }
+ }
+ // cell with general Jacobian, but constant
+ // within the cell
+ else // if (this->cell_type == 1)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ grad_out[d] = (this->jacobian[0][d][0] *
+ this->gradients_quad[0][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ grad_out[d] += (this->jacobian[0][d][e] *
+ this->gradients_quad[0][e][q_point]);
+ }
+ }
+ return grad_out;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<2,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_hessian (unsigned int q_point) const
+{
+ return BaseClass::get_hessian(q_point)[0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<1,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_hessian_diagonal (unsigned int q_point) const
+{
+ return BaseClass::get_hessian_diagonal(q_point)[0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+VectorizedArray<Number>
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+get_laplacian (unsigned int q_point) const
+{
+ return BaseClass::get_laplacian(q_point)[0];
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+submit_dof_value (VectorizedArray<Number> val_in,
+ unsigned int dof)
+{
+#ifdef DEBUG
+ this->dof_values_initialized = true;
+ AssertIndexRange (dof, dofs_per_cell);
+#endif
+ this->values_dofs[0][dof] = val_in;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+submit_value (VectorizedArray<Number> val_in,
+ unsigned int q_point)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ AssertIndexRange (q_point, n_q_points);
+ this->values_quad_submitted = true;
+#endif
+ if (this->cell_type == 2)
+ {
+ const vector_t JxW = this->J_value[q_point];
+ this->values_quad[0][q_point] = val_in * JxW;
+ }
+ else //if (this->cell_type < 2)
+ {
+ const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point];
+ this->values_quad[0][q_point] = val_in * JxW;
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+submit_gradient (Tensor<1,dim,VectorizedArray<Number> > grad_in,
+ unsigned int q_point)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ AssertIndexRange (q_point, n_q_points);
+ this->gradients_quad_submitted = true;
+#endif
+ if (this->cell_type == 0)
+ {
+ const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point];
+ for (unsigned int d=0; d<dim; ++d)
+ this->gradients_quad[0][d][q_point] = (grad_in[d] *
+ this->cartesian[0][d] * JxW);
+ }
+ else if (this->cell_type == 2)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = this->jacobian[q_point][0][d] * grad_in[0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += this->jacobian[q_point][e][d] * grad_in[e];
+ this->gradients_quad[0][d][q_point] = new_val * this->J_value[q_point];
+ }
+ }
+ else //if (this->cell_type == 1)
+ {
+ const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point];
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = this->jacobian[0][0][d] * grad_in[0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += this->jacobian[0][e][d] * grad_in[e];
+ this->gradients_quad[0][d][q_point] = new_val * JxW;
+ }
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+VectorizedArray<Number>
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,1,Number>::
+integrate_value ()
+{
+ return BaseClass::integrate_value()[0];
+}
+
+
+
+
+/*----------------- FEEvaluationAccess vector-valued ----------------------*/
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+FEEvaluationAccess (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no_in)
+ :
+ FEEvaluationBase <dim,dofs_per_cell_,n_q_points_,dim,Number>
+ (data_in, fe_no, quad_no_in)
+{}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<2,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_gradient (unsigned int q_point) const
+{
+ return BaseClass::get_gradient (q_point);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+VectorizedArray<Number>
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_divergence (unsigned int q_point) const
+{
+ Assert (this->gradients_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ vector_t divergence;
+
+ // Cartesian cell
+ if (this->cell_type == 0)
+ {
+ divergence = (this->gradients_quad[0][0][q_point] *
+ this->cartesian[0][0]);
+ for (unsigned int d=1; d<dim; ++d)
+ divergence += (this->gradients_quad[d][d][q_point] *
+ this->cartesian[0][d]);
+ }
+ // cell with general Jacobian
+ else if (this->cell_type == 2)
+ {
+ divergence = (this->jacobian[q_point][0][0] *
+ this->gradients_quad[0][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ divergence += (this->jacobian[q_point][0][e] *
+ this->gradients_quad[0][e][q_point]);
+ for (unsigned int d=1; d<dim; ++d)
+ for (unsigned e=0; e<dim; ++e)
+ divergence += (this->jacobian[q_point][d][e] *
+ this->gradients_quad[d][e][q_point]);
+ }
+ // cell with general Jacobian, but constant
+ // within the cell
+ else // if (this->cell_type == 1)
+ {
+ divergence = (this->jacobian[0][0][0] *
+ this->gradients_quad[0][0][q_point]);
+ for (unsigned e=1; e<dim; ++e)
+ divergence += (this->jacobian[0][0][e] *
+ this->gradients_quad[0][e][q_point]);
+ for (unsigned int d=1; d<dim; ++d)
+ for (unsigned e=0; e<dim; ++e)
+ divergence += (this->jacobian[0][d][e] *
+ this->gradients_quad[d][e][q_point]);
+ }
+ return divergence;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+SymmetricTensor<2,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_symmetric_gradient (unsigned int q_point) const
+{
+ // copy from generic function into
+ // dim-specialization function
+ const Tensor<2,dim,vector_t> grad = get_gradient(q_point);
+ vector_t symmetrized [(dim*dim+dim)/2];
+ vector_t half = make_vectorized_array (0.5);
+ for (unsigned int d=0; d<dim; ++d)
+ symmetrized[d] = grad[d][d];
+ switch (dim)
+ {
+ case 1:
+ break;
+ case 2:
+ symmetrized[2] = grad[0][1] + grad[1][0];
+ symmetrized[2] *= half;
+ break;
+ case 3:
+ symmetrized[3] = grad[0][1] + grad[1][0];
+ symmetrized[3] *= half;
+ symmetrized[4] = grad[0][2] + grad[2][0];
+ symmetrized[4] *= half;
+ symmetrized[5] = grad[1][2] + grad[2][1];
+ symmetrized[5] *= half;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ return SymmetricTensor<2,dim,vector_t> (symmetrized);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+typename FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::curl_type
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_curl (unsigned int q_point) const
+{
+ // copy from generic function into
+ // dim-specialization function
+ const Tensor<2,dim,vector_t> grad = get_gradient(q_point);
+ curl_type curl;
+ switch (dim)
+ {
+ case 1:
+ Assert (false,
+ ExcMessage("Computing the curl in 1d is not a useful operation"));
+ break;
+ case 2:
+ curl[0] = grad[1][0] - grad[0][1];
+ break;
+ case 3:
+ curl[0] = grad[2][1] - grad[1][2];
+ curl[1] = grad[0][2] - grad[2][0];
+ curl[2] = grad[1][0] - grad[0][1];
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ return curl;
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<2,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_hessian_diagonal (unsigned int q_point) const
+{
+ Assert (this->hessians_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+
+ return BaseClass::get_hessian_diagonal (q_point);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+Tensor<3,dim,VectorizedArray<Number> >
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+get_hessian (unsigned int q_point) const
+{
+ Assert (this->hessians_quad_initialized==true,
+ internal::ExcAccessToUninitializedField());
+ AssertIndexRange (q_point, n_q_points);
+ return BaseClass::get_hessian(q_point);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+submit_gradient (Tensor<2,dim,VectorizedArray<Number> > grad_in,
+ unsigned int q_point)
+{
+ BaseClass::submit_gradient (grad_in, q_point);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+submit_gradient (Tensor<1,dim,Tensor<1,dim,VectorizedArray<Number> > > grad_in,
+ unsigned int q_point)
+{
+ BaseClass::submit_gradient(grad_in, q_point);
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+submit_symmetric_gradient (SymmetricTensor<2,dim,VectorizedArray<Number> >
+ sym_grad,
+ unsigned int q_point)
+{
+ // could have used base class operator, but
+ // that involves some overhead which is
+ // inefficient. it is nice to have the
+ // symmetric tensor because that saves some
+ // operations
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ AssertIndexRange (q_point, n_q_points);
+ this->gradients_quad_submitted = true;
+#endif
+ if (this->cell_type == 0)
+ {
+ const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point];
+ for (unsigned int d=0; d<dim; ++d)
+ this->gradients_quad[d][d][q_point] = (sym_grad.access_raw_entry(d) *
+ JxW *
+ this->cartesian[0][d]);
+ for (unsigned int e=0, counter=dim; e<dim; ++e)
+ for (unsigned int d=e+1; d<dim; ++d, ++counter)
+ {
+ const vector_t value = sym_grad.access_raw_entry(counter) * JxW;
+ this->gradients_quad[e][d][q_point] = (value *
+ this->cartesian[0][d]);
+ this->gradients_quad[d][e][q_point] = (value *
+ this->cartesian[0][e]);
+ }
+ }
+ else if (this->cell_type == 2)
+ {
+ vector_t weighted [dim][dim];
+ {
+ const vector_t JxW = this->J_value[q_point];
+ for (unsigned int i=0; i<dim; ++i)
+ weighted[i][i] = sym_grad.access_raw_entry(i) * JxW;
+ for (unsigned int i=0, counter=dim; i<dim; ++i)
+ for (unsigned int j=i+1; j<dim; ++j, ++counter)
+ {
+ const vector_t value = sym_grad.access_raw_entry(counter) * JxW;
+ weighted[i][j] = value;
+ weighted[j][i] = value;
+ }
+ }
+ for (unsigned int comp=0; comp<dim; ++comp)
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = this->jacobian[q_point][0][d] * weighted[comp][0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += this->jacobian[q_point][e][d] * weighted[comp][e];
+ this->gradients_quad[comp][d][q_point] = new_val;
+ }
+ }
+ else //if (this->cell_type == 1)
+ {
+ vector_t weighted [dim][dim];
+ {
+ const vector_t JxW = (this->J_value[0] *
+ this->quadrature_weights[q_point]);
+ for (unsigned int i=0; i<dim; ++i)
+ weighted[i][i] = sym_grad.access_raw_entry(i) * JxW;
+ for (unsigned int i=0, counter=dim; i<dim; ++i)
+ for (unsigned int j=i+1; j<dim; ++j, ++counter)
+ {
+ const vector_t value = sym_grad.access_raw_entry(counter) * JxW;
+ weighted[i][j] = value;
+ weighted[j][i] = value;
+ }
+ }
+ for (unsigned int comp=0; comp<dim; ++comp)
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ vector_t new_val = this->jacobian[q_point][0][d] * weighted[comp][0];
+ for (unsigned e=1; e<dim; ++e)
+ new_val += this->jacobian[q_point][e][d] * weighted[comp][e];
+ this->gradients_quad[comp][d][q_point] = new_val;
+ }
+ }
+}
+
+
+
+template <int dim, int dofs_per_cell_, int n_q_points_, typename Number>
+inline
+void
+FEEvaluationAccess<dim,dofs_per_cell_,n_q_points_,dim,Number>::
+submit_curl (curl_type curl,
+ unsigned int q_point)
+{
+ Tensor<2,dim,vector_t> grad;
+ switch (dim)
+ {
+ case 1:
+ Assert (false,
+ ExcMessage("Testing by the curl in 1d is not a useful operation"));
+ break;
+ case 2:
+ grad[1][0] = curl[0];
+ grad[0][1] = -curl[0];
+ break;
+ case 3:
+ grad[2][1] = curl[0];
+ grad[1][2] = -curl[0];
+ grad[0][2] = curl[1];
+ grad[2][0] = -curl[1];
+ grad[1][0] = curl[2];
+ grad[0][1] = -curl[2];
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ submit_gradient (grad, q_point);
+}
+
+
+
+/*----------------------- FEEvaluationGeneral -------------------------------*/
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+FEEvaluationGeneral (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no_in)
+ :
+ BaseClass (data_in, fe_no, quad_no_in)
+{
+#ifdef DEBUG
+ // print error message when the dimensions do
+ // not match. Propose a possible fix
+ if (dofs_per_cell != this->data.dofs_per_cell ||
+ n_q_points != this->data.n_q_points)
+ {
+ std::string message =
+ "-------------------------------------------------------\n";
+ message += "Illegal arguments in constructor/wrong template arguments!\n";
+ message += " Called --> FEEvaluation<dim,";
+ message += Utilities::int_to_string(n_dofs_1d) + ",";
+ message += Utilities::int_to_string(n_q_points_1d) + ",Number>(data, ";
+ message += Utilities::int_to_string(fe_no) + ", ";
+ message += Utilities::int_to_string(quad_no_in) + ")\n";
+
+ // check whether some other vector component
+ // has the correct number of points
+ unsigned int proposed_dof_comp = numbers::invalid_unsigned_int,
+ proposed_quad_comp = numbers::invalid_unsigned_int;
+ for (unsigned int no=0; no<this->matrix_info.n_components(); ++no)
+ if (this->matrix_info.get_dof_info(no).dofs_per_cell[0] ==
+ dofs_per_cell)
+ {
+ proposed_dof_comp = no;
+ break;
+ }
+ for (unsigned int no=0; no<this->mapping_info.mapping_data_gen.size(); ++no)
+ if (this->mapping_info.mapping_data_gen[no].n_q_points[this->active_quad_index] == n_q_points)
+ {
+ proposed_quad_comp = no;
+ break;
+ }
+ if (proposed_dof_comp != numbers::invalid_unsigned_int &&
+ proposed_quad_comp != numbers::invalid_unsigned_int)
+ {
+ message += "Wrong vector component selection:\n";
+ message += " Did you mean FEEvaluation<dim,Number,";
+ message += Utilities::int_to_string(n_dofs_1d) + ",";
+ message += Utilities::int_to_string(n_q_points_1d) + ">(data, ";
+ message += Utilities::int_to_string(proposed_dof_comp) + ", ";
+ message += Utilities::int_to_string(proposed_quad_comp) + ")?\n";
+ std::string correct_pos;
+ if (proposed_dof_comp != fe_no)
+ correct_pos = " ^ ";
+ else
+ correct_pos = " ";
+ if (proposed_quad_comp != quad_no_in)
+ correct_pos += " ^\n";
+ else
+ correct_pos += " \n";
+ message += " " + correct_pos;
+ }
+ // ok, did not find the numbers specified by
+ // the template arguments in the given
+ // list. Suggest correct template arguments
+ const unsigned int proposed_n_dofs_1d = static_cast<unsigned int>(std::pow(1.001*this->data.dofs_per_cell,1./dim));
+ const unsigned int proposed_n_q_points_1d = static_cast<unsigned int>(std::pow(1.001*this->data.n_q_points,1./dim));
+ message += "Wrong template arguments:\n";
+ message += " Did you mean FEEvaluation<dim,";
+ message += Utilities::int_to_string(proposed_n_dofs_1d) + ",";
+ message += Utilities::int_to_string(proposed_n_q_points_1d);
+ message += ",Number>(data, ";
+ message += Utilities::int_to_string(fe_no) + ", ";
+ message += Utilities::int_to_string(quad_no_in) + ")?\n";
+ std::string correct_pos;
+ if (proposed_n_dofs_1d != n_dofs_1d)
+ correct_pos = " ^";
+ else
+ correct_pos = " ";
+ if (proposed_n_q_points_1d != n_q_points_1d)
+ correct_pos += " ^\n";
+ else
+ correct_pos += " \n";
+ message += " " + correct_pos;
+
+ Assert (dofs_per_cell == this->data.dofs_per_cell &&
+ n_q_points == this->data.n_q_points,
+ ExcMessage(message));
+ }
+ AssertDimension (n_q_points,
+ this->mapping_info.mapping_data_gen[this->quad_no].
+ n_q_points[this->active_quad_index]);
+ AssertDimension (dofs_per_cell * this->n_fe_components,
+ this->dof_info.dofs_per_cell[this->active_fe_index]);
+#endif
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+void
+FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+evaluate (bool evaluate_val, bool evaluate_grad, bool evaluate_lapl)
+{
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ Assert (this->dof_values_initialized == true,
+ internal::ExcAccessToUninitializedField());
+
+ const vector_t * val = this->data.shape_values.begin();
+ const vector_t * grad = this->data.shape_gradients.begin();
+ const vector_t * hess = this->data.shape_hessians.begin();
+
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ vector_t temp1[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+ vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+
+ if (dim == 3)
+ {
+ if (evaluate_grad == true)
+ {
+ // grad x
+ apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (val, temp1, temp2);
+ apply_tensor_prod<2,true,false> (val, temp2, this->gradients_quad[comp][0]);
+ }
+
+ if (evaluate_lapl == true)
+ {
+ // grad xz
+ if (evaluate_grad == false)
+ {
+ apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (val, temp1, temp2);
+ }
+ apply_tensor_prod<2,true,false>(grad, temp2, this->hessians_quad[comp][4]);
+
+ // grad xy
+ apply_tensor_prod<1,true,false>(grad, temp1, temp2);
+ apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][3]);
+
+ // grad xx
+ apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (val, temp1, temp2);
+ apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][0]);
+ }
+
+ // grad y
+ apply_tensor_prod<0,true,false> (val, this->values_dofs[comp], temp1);
+ if (evaluate_grad == true)
+ {
+ apply_tensor_prod<1,true,false> (grad, temp1, temp2);
+ apply_tensor_prod<2,true,false> (val, temp2, this->gradients_quad[comp][1]);
+ }
+
+ if (evaluate_lapl == true)
+ {
+ // grad yz
+ if (evaluate_grad == false)
+ apply_tensor_prod<1,true,false> (grad, temp1, temp2);
+ apply_tensor_prod<2,true,false> (grad, temp2, this->hessians_quad[comp][5]);
+
+ // grad yy
+ apply_tensor_prod<1,true,false> (hess, temp1, temp2);
+ apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][1]);
+ }
+
+ // grad z: can use the values applied in x direction stored in temp1
+ apply_tensor_prod<1,true,false> (val, temp1, temp2);
+ if (evaluate_grad == true)
+ apply_tensor_prod<2,true,false> (grad, temp2, this->gradients_quad[comp][2]);
+
+ // grad zz: can use the values applied in x and y direction stored in temp2
+ if (evaluate_lapl == true)
+ apply_tensor_prod<2,true,false> (hess, temp2, this->hessians_quad[comp][2]);
+
+ // val: can use the values applied in x & y direction stored in temp2
+ if (evaluate_val == true)
+ apply_tensor_prod<2,true,false> (val, temp2, this->values_quad[comp]);
+ }
+ else if (dim == 2)
+ {
+ // grad x
+ if (evaluate_grad == true)
+ {
+ apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (val, temp1, this->gradients_quad[comp][0]);
+ }
+ if (evaluate_lapl == true)
+ {
+ // grad xy
+ if (evaluate_grad == false)
+ apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (grad, temp1, this->hessians_quad[comp][2]);
+
+ // grad xx
+ apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp], temp1);
+ apply_tensor_prod<1,true,false> (val, temp1, this->hessians_quad[comp][0]);
+ }
+
+ // grad y
+ apply_tensor_prod<0,true,false> (val, this->values_dofs[comp], temp1);
+ if (evaluate_grad == true)
+ apply_tensor_prod<1,true,false> (grad, temp1, this->gradients_quad[comp][1]);
+
+ // grad yy
+ if (evaluate_lapl == true)
+ apply_tensor_prod<1,true,false> (hess, temp1, this->hessians_quad[comp][1]);
+
+ // val: can use values applied in x
+ if (evaluate_val == true)
+ apply_tensor_prod<1,true,false> (val, temp1, this->values_quad[comp]);
+ }
+ else if (dim == 1)
+ {
+ if (evaluate_val == true)
+ apply_tensor_prod<0,true,false> (val, this->values_dofs[comp],
+ this->values_quad[comp]);
+ if (evaluate_grad == true)
+ apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp],
+ this->gradients_quad[comp][0]);
+ if (evaluate_lapl == true)
+ apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp],
+ this->hessians_quad[comp][0]);
+ }
+ }
+
+#ifdef DEBUG
+ if (evaluate_val == true)
+ this->values_quad_initialized = true;
+ if (evaluate_grad == true)
+ this->gradients_quad_initialized = true;
+ if (evaluate_lapl == true)
+ this->hessians_quad_initialized = true;
+#endif
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+void
+FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+integrate (bool integrate_val,bool integrate_grad)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized());
+ if (integrate_val == true)
+ Assert (this->values_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+ if (integrate_grad == true)
+ Assert (this->gradients_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+#endif
+
+ const vector_t * val = this->data.shape_values.begin();
+ const vector_t * grad = this->data.shape_gradients.begin();
+
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ vector_t temp1[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+ vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+
+ if (dim == 3)
+ {
+ if (integrate_val == true)
+ {
+ // val
+ apply_tensor_prod<0,false,false> (val, this->values_quad[comp], temp1);
+ }
+ if (integrate_grad == true)
+ {
+ // grad x: can sum to temporary value in temp1
+ if (integrate_val == true)
+ apply_tensor_prod<0,false,true>
+ (grad, this->gradients_quad[comp][0],temp1);
+ else
+ apply_tensor_prod<0,false,false>
+ (grad, this->gradients_quad[comp][0],temp1);
+ }
+ apply_tensor_prod<1,false,false> (val, temp1, temp2);
+ if (integrate_grad == true)
+ {
+ // grad y: can sum to temporary x value in temp2
+ apply_tensor_prod<0,false,false> (val, this->gradients_quad[comp][1], temp1);
+ apply_tensor_prod<1,false,true> (grad, temp1, temp2);
+ }
+ apply_tensor_prod<2,false,false> (val, temp2, this->values_dofs[comp]);
+ if (integrate_grad == true)
+ {
+ // grad z: can sum to temporary x and y value in output
+ apply_tensor_prod<0,false,false> (val, this->gradients_quad[comp][2], temp1);
+ apply_tensor_prod<1,false,false> (val, temp1, temp2);
+ apply_tensor_prod<2,false,true> (grad, temp2, this->values_dofs[comp]);
+ }
+ }
+ else if (dim == 2)
+ {
+ // val
+ if (integrate_val == true)
+ apply_tensor_prod<0,false,false> (val, this->values_quad[comp], temp1);
+ if (integrate_grad == true)
+ {
+ //grad x
+ if (integrate_val == true)
+ apply_tensor_prod<0,false,true>
+ (grad, this->gradients_quad[comp][0],temp1);
+ else
+ apply_tensor_prod<0,false,false>
+ (grad, this->gradients_quad[comp][0],temp1);
+ }
+ apply_tensor_prod<1,false,false> (val, temp1, this->values_dofs[comp]);
+ if (integrate_grad == true)
+ {
+ // grad y
+ apply_tensor_prod<0,false,false> (grad, this->gradients_quad[comp][1], temp1);
+ apply_tensor_prod<1,false,true> (val, temp1, this->values_dofs[comp]);
+ }
+ }
+ else if (dim == 1)
+ {
+ if (integrate_grad == true)
+ apply_tensor_prod<0,false,false> (grad, this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+ if (integrate_val == true)
+ {
+ if (integrate_grad == true)
+ apply_tensor_prod<0,false,true> (val, this->values_quad[comp],
+ this->values_dofs[comp]);
+ else
+ apply_tensor_prod<0,false,false> (val, this->values_quad[comp],
+ this->values_dofs[comp]);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ this->dof_values_initialized = true;
+#endif
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+Point<dim,VectorizedArray<Number> >
+FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+quadrature_point (const unsigned int q) const
+{
+ Assert (this->mapping_info.quadrature_points_initialized == true,
+ ExcNotInitialized());
+ AssertIndexRange (q, n_q_points);
+
+ // Cartesian mesh: not all quadrature points
+ // are stored, only the diagonal. Hence, need
+ // to find the tensor product index and
+ // retrieve the value from that
+ if (this->cell_type == 0)
+ {
+ Point<dim,vector_t> point (false);
+ switch (dim)
+ {
+ case 1:
+ return this->quadrature_points[q];
+ case 2:
+ point[0] = this->quadrature_points[q%n_q_points_1d][0];
+ point[1] = this->quadrature_points[q/n_q_points_1d][1];
+ return point;
+ case 3:
+ point[0] = this->quadrature_points[q%n_q_points_1d][0];
+ point[1] = this->quadrature_points[(q/n_q_points_1d)%n_q_points_1d][1];
+ point[2] = this->quadrature_points[q/(n_q_points_1d*n_q_points_1d)][2];
+ return point;
+ default:
+ Assert (false, ExcNotImplemented());
+ return point;
+ }
+ }
+ // all other cases: just return the respective
+ // data as it is fully stored
+ else
+ return this->quadrature_points[q];
+}
+
+
+ // General tensor product application for up
+ // to three spatial dimensions. Does not
+ // assume any symmetry in the shape values
+ // field
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+template <int direction, bool dof_to_quad, bool add>
+inline
+void
+FEEvaluationGeneral<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+apply_tensor_prod (const VectorizedArray<Number>*shape_data,
+ const VectorizedArray<Number> input [],
+ VectorizedArray<Number> output [])
+{
+ AssertIndexRange (direction, dim);
+ const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d,
+ nn = dof_to_quad ? n_q_points_1d : n_dofs_1d;
+
+ const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
+ const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
+ const int stride = ((direction > 0 ? nn : 1 ) *
+ (direction > 1 ? nn : 1));
+
+ const vector_t * in = &input[0];
+ vector_t * out = &output[0];
+ for (int i2=0; i2<n_blocks2; ++i2)
+ {
+ for (int i1=0; i1<n_blocks1; ++i1)
+ {
+ for (int col=0; col<nn; ++col)
+ {
+ vector_t val0;
+ if (dof_to_quad == true)
+ val0 = shape_data[col];
+ else
+ val0 = shape_data[col*n_q_points_1d];
+ vector_t res0 = val0 * in[0];
+ for (int ind=1; ind<mm; ++ind)
+ {
+ if (dof_to_quad == true)
+ val0 = shape_data[ind*n_q_points_1d+col];
+ else
+ val0 = shape_data[col*n_q_points_1d+ind];
+ res0 += val0 * in[stride*ind];
+ }
+ if (add == false)
+ out[stride*col] = res0;
+ else
+ out[stride*col] += res0;
+ }
+
+ // increment: in regular case, just go to the
+ // next point in x-direction. If we are at the
+ // end of one chunk in x-dir, need to jump
+ // over to the next layer in z-direction
+ switch (direction)
+ {
+ case 0:
+ in += mm;
+ out += nn;
+ break;
+ case 1:
+ case 2:
+ ++in;
+ ++out;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+ if (direction == 1)
+ {
+ in += nn*(mm-1);
+ out += nn*(nn-1);
+ }
+ }
+}
+
+
+/*----------------------- FEEvaluation -------------------------------*/
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+FEEvaluation (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no)
+ :
+ BaseClass (data_in, fe_no, quad_no)
+{
+ // check whether element is appropriate
+#ifdef DEBUG
+ const double zero_tol =
+ types_are_equal<Number,double>::value==true?1e-8:1e-7;
+ std::string error_message = "FEEvaluation not appropriate.\n";
+ error_message += " It assumes symmetry of quadrature points w.r.t. 0.5 \n";
+ error_message += " and the basis functions starting from left and right.\n";
+ error_message += "Try FEEvaluationGeneral<...> instead!";
+
+ // symmetry for values
+ for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i)
+ for (unsigned int j=0; j<n_q_points_1d; ++j)
+ Assert (std::fabs(this->data.shape_values[i*n_q_points_1d+j][0] -
+ this->data.shape_values[(n_dofs_1d-i)*n_q_points_1d
+ -j-1][0]) < zero_tol,
+ ExcMessage(error_message));
+
+ // shape values should be zero at for all
+ // basis functions except for one where they
+ // are one in the middle
+ if (n_q_points_1d%2 == 1 && n_dofs_1d%2 == 1)
+ {
+ for (int i=0; i<static_cast<int>(n_dofs_1d/2); ++i)
+ Assert (std::fabs(this->data.shape_values[i*n_q_points_1d+
+ n_q_points_1d/2][0]) < zero_tol,
+ ExcMessage(error_message));
+ Assert (std::fabs(this->data.shape_values[(n_dofs_1d/2)*n_q_points_1d+
+ n_q_points_1d/2][0]-1.)< zero_tol,
+ ExcMessage(error_message));
+ }
+
+ // skew-symmetry for gradient, zero of middle
+ // basis function in middle quadrature point
+ for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i)
+ for (unsigned int j=0; j<n_q_points_1d; ++j)
+ Assert (std::fabs(this->data.shape_gradients[i*n_q_points_1d+j][0] +
+ this->data.shape_gradients[(n_dofs_1d-i)*n_q_points_1d-
+ j-1][0]) < zero_tol,
+ ExcMessage(error_message));
+ if (n_dofs_1d%2 == 1 && n_q_points_1d%2 == 1)
+ Assert (std::fabs(this->data.shape_gradients[(n_dofs_1d/2)*n_q_points_1d+
+ (n_q_points_1d/2)][0]) < zero_tol,
+ ExcMessage(error_message));
+
+
+ // symmetry for Laplacian
+ for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i)
+ for (unsigned int j=0; j<n_q_points_1d; ++j)
+ Assert (std::fabs(this->data.shape_hessians[i*n_q_points_1d+j][0] -
+ this->data.shape_hessians[(n_dofs_1d-i)*n_q_points_1d-
+ j-1][0]) < zero_tol,
+ ExcMessage(error_message));
+#endif
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+void
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+evaluate (bool evaluate_val, bool evaluate_grad, bool evaluate_lapl)
+{
+ Assert (this->cell != numbers::invalid_unsigned_int,
+ ExcNotInitialized());
+ Assert (this->dof_values_initialized == true,
+ internal::ExcAccessToUninitializedField());
+
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ vector_t temp1[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+ vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+
+ if (dim == 3)
+ {
+ if (evaluate_grad == true)
+ {
+ // grad x
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_values<1,true,false> (temp1, temp2);
+ apply_values<2,true,false> (temp2, this->gradients_quad[comp][0]);
+ }
+
+ if (evaluate_lapl == true)
+ {
+ // grad xz
+ if (evaluate_grad == false)
+ {
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_values<1,true,false> (temp1, temp2);
+ }
+ apply_gradients<2,true,false>(temp2, this->hessians_quad[comp][4]);
+
+ // grad xy
+ apply_gradients<1,true,false>(temp1, temp2);
+ apply_values<2,true,false> (temp2, this->hessians_quad[comp][3]);
+
+ // grad xx
+ apply_hessians<0,true,false> (this->values_dofs[comp], temp1);
+ apply_values<1,true,false> (temp1, temp2);
+ apply_values<2,true,false> (temp2, this->hessians_quad[comp][0]);
+ }
+
+ // grad y
+ apply_values<0,true,false> (this->values_dofs[comp], temp1);
+ if (evaluate_grad == true)
+ {
+ apply_gradients<1,true,false> (temp1, temp2);
+ apply_values<2,true,false> (temp2, this->gradients_quad[comp][1]);
+ }
+
+ if (evaluate_lapl == true)
+ {
+ // grad yz
+ if (evaluate_grad == false)
+ apply_gradients<1,true,false> (temp1, temp2);
+ apply_gradients<2,true,false> (temp2, this->hessians_quad[comp][5]);
+
+ // grad yy
+ apply_hessians<1,true,false> (temp1, temp2);
+ apply_values<2,true,false> (temp2, this->hessians_quad[comp][1]);
+ }
+
+ // grad z: can use the values applied in x direction stored in temp1
+ apply_values<1,true,false> (temp1, temp2);
+ if (evaluate_grad == true)
+ apply_gradients<2,true,false> (temp2, this->gradients_quad[comp][2]);
+
+ // grad zz: can use the values applied in x and y direction stored in temp2
+ if (evaluate_lapl == true)
+ apply_hessians<2,true,false> (temp2, this->hessians_quad[comp][2]);
+
+ // val: can use the values applied in x & y direction stored in temp2
+ if (evaluate_val == true)
+ apply_values<2,true,false> (temp2, this->values_quad[comp]);
+ }
+ else if (dim == 2)
+ {
+ // grad x
+ if (evaluate_grad == true)
+ {
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_values<1,true,false> (temp1, this->gradients_quad[comp][0]);
+ }
+ if (evaluate_lapl == true)
+ {
+ // grad xy
+ if (evaluate_grad == false)
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][2]);
+
+ // grad xx
+ apply_hessians<0,true,false> (this->values_dofs[comp], temp1);
+ apply_values<1,true,false> (temp1, this->hessians_quad[comp][0]);
+ }
+
+ // grad y
+ apply_values<0,true,false> (this->values_dofs[comp], temp1);
+ if (evaluate_grad == true)
+ apply_gradients<1,true,false> (temp1, this->gradients_quad[comp][1]);
+
+ // grad yy
+ if (evaluate_lapl == true)
+ apply_hessians<1,true,false> (temp1, this->hessians_quad[comp][1]);
+
+ // val: can use values applied in x
+ if (evaluate_val == true)
+ apply_values<1,true,false> (temp1, this->values_quad[comp]);
+ }
+ else if (dim == 1)
+ {
+ if (evaluate_val == true)
+ apply_values<0,true,false> (this->values_dofs[comp],
+ this->values_quad[comp]);
+ if (evaluate_grad == true)
+ apply_gradients<0,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][0]);
+ if (evaluate_lapl == true)
+ apply_hessians<0,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][0]);
+ }
+ }
+
+#ifdef DEBUG
+ if (evaluate_val == true)
+ this->values_quad_initialized = true;
+ if (evaluate_grad == true)
+ this->gradients_quad_initialized = true;
+ if (evaluate_lapl == true)
+ this->hessians_quad_initialized = true;
+#endif
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+inline
+void
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+integrate (bool integrate_val,bool integrate_grad)
+{
+#ifdef DEBUG
+ Assert (this->cell != numbers::invalid_unsigned_int,
+ ExcNotInitialized());
+ if (integrate_val == true)
+ Assert (this->values_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+ if (integrate_grad == true)
+ Assert (this->gradients_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+#endif
+
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ vector_t temp1[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+ vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points];
+
+ if (dim == 3)
+ {
+ if (integrate_val == true)
+ {
+ // val
+ apply_values<0,false,false> (this->values_quad[comp], temp1);
+ }
+ if (integrate_grad == true)
+ {
+ // grad x: can sum to temporary value in temp1
+ if (integrate_val == true)
+ apply_gradients<0,false,true> (this->gradients_quad[comp][0],
+ temp1);
+ else
+ apply_gradients<0,false,false> (this->gradients_quad[comp][0],
+ temp1);
+ }
+ apply_values<1,false,false> (temp1, temp2);
+ if (integrate_grad == true)
+ {
+ // grad y: can sum to temporary x value in temp2
+ apply_values<0,false,false> (this->gradients_quad[comp][1], temp1);
+ apply_gradients<1,false,true> (temp1, temp2);
+ }
+ apply_values<2,false,false> (temp2, this->values_dofs[comp]);
+ if (integrate_grad == true)
+ {
+ // grad z: can sum to temporary x and y value in output
+ apply_values<0,false,false> (this->gradients_quad[comp][2], temp1);
+ apply_values<1,false,false> (temp1, temp2);
+ apply_gradients<2,false,true> (temp2, this->values_dofs[comp]);
+ }
+ }
+ else if (dim == 2)
+ {
+ // val
+ if (integrate_val == true)
+ apply_values<0,false,false> (this->values_quad[comp], temp1);
+ if (integrate_grad == true)
+ {
+ //grad x
+ if (integrate_val == true)
+ apply_gradients<0,false,true> (this->gradients_quad[comp][0],
+ temp1);
+ else
+ apply_gradients<0,false,false> (this->gradients_quad[comp][0],
+ temp1);
+ }
+ apply_values<1,false,false> (temp1, this->values_dofs[comp]);
+ if (integrate_grad == true)
+ {
+ // grad y
+ apply_values<0,false,false> (this->gradients_quad[comp][1], temp1);
+ apply_gradients<1,false,true> (temp1, this->values_dofs[comp]);
+ }
+ }
+ else if (dim == 1)
+ {
+ if (integrate_grad == true)
+ apply_gradients<0,false,false> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+ if (integrate_val == true)
+ {
+ if (integrate_grad == true)
+ apply_values<0,false,true> (this->values_quad[comp],
+ this->values_dofs[comp]);
+ else
+ apply_values<0,false,false> (this->values_quad[comp],
+ this->values_dofs[comp]);
+ }
+ }
+ }
+#ifdef DEBUG
+ this->dof_values_initialized = true;
+#endif
+}
+
+
+
+// ----------------- optimized implementation tensor product symmetric case
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+template <int direction, bool dof_to_quad, bool add>
+inline
+void
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+apply_values (const VectorizedArray<Number> input [],
+ VectorizedArray<Number> output [])
+{
+ AssertIndexRange (direction, dim);
+ const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d,
+ nn = dof_to_quad ? n_q_points_1d : n_dofs_1d;
+ const int n_cols = nn / 2;
+ const int mid = mm / 2;
+
+ const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
+ const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
+ const int stride = ((direction > 0 ? nn : 1 ) *
+ (direction > 1 ? nn : 1));
+
+ const vector_t * in = &input[0];
+ vector_t * out = &output[0];
+ for (int i2=0; i2<n_blocks2; ++i2)
+ {
+ for (int i1=0; i1<n_blocks1; ++i1)
+ {
+ for (int col=0; col<n_cols; ++col)
+ {
+ vector_t val0, val1, res0, res1;
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_values[col];
+ val1 = this->data.shape_values[nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_values[col*n_q_points_1d];
+ val1 = this->data.shape_values[(col+1)*n_q_points_1d-1];
+ }
+ if (mid > 0)
+ {
+ res0 = val0 * in[0];
+ res1 = val1 * in[0];
+ res0 += val1 * in[stride*(mm-1)];
+ res1 += val0 * in[stride*(mm-1)];
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_values[ind*n_q_points_1d+col];
+ val1 = this->data.shape_values[ind*n_q_points_1d+nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_values[col*n_q_points_1d+ind];
+ val1 = this->data.shape_values[(col+1)*n_q_points_1d-1-ind];
+ }
+ res0 += val0 * in[stride*ind];
+ res1 += val1 * in[stride*ind];
+ res0 += val1 * in[stride*(mm-1-ind)];
+ res1 += val0 * in[stride*(mm-1-ind)];
+ }
+ }
+ else
+ res0 = res1 = vector_t();
+ if (dof_to_quad == true)
+ {
+ if (mm % 2 == 1)
+ {
+ val0 = this->data.shape_values[mid*n_q_points_1d+col];
+ val1 = val0 * in[stride*mid];
+ res0 += val1;
+ res1 += val1;
+ }
+ }
+ else
+ {
+ if (mm % 2 == 1 && nn % 2 == 0)
+ {
+ val0 = this->data.shape_values[col*n_q_points_1d+mid];
+ val1 = val0 * in[stride*mid];
+ res0 += val1;
+ res1 += val1;
+ }
+ }
+ if (add == false)
+ {
+ out[stride*col] = res0;
+ out[stride*(nn-1-col)] = res1;
+ }
+ else
+ {
+ out[stride*col] += res0;
+ out[stride*(nn-1-col)] += res1;
+ }
+ }
+ if ( dof_to_quad == true && nn%2==1 && mm%2==1 )
+ {
+ if (add==false)
+ out[stride*n_cols] = in[stride*mid];
+ else
+ out[stride*n_cols] += in[stride*mid];
+ }
+ else if (dof_to_quad == true && nn%2==1)
+ {
+ vector_t res0;
+ vector_t val0 = this->data.shape_values[n_cols];
+ if (mid > 0)
+ {
+ res0 = in[0] + in[stride*(mm-1)];
+ res0 *= val0;
+ for (int ind=1; ind<mid; ++ind)
+ {
+ val0 = this->data.shape_values[ind*n_q_points_1d+n_cols];
+ vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)];
+ val1 *= val0;
+ res0 += val1;
+ }
+ }
+ else
+ res0 = vector_t();
+ if (mm % 2 == 1)
+ {
+ val0 = this->data.shape_values[mid*n_q_points_1d+n_cols];
+ res0 += val0 * in[stride*mid];
+ }
+ if (add == false)
+ out[stride*n_cols] = res0;
+ else
+ out[stride*n_cols] += res0;
+ }
+ else if (dof_to_quad == false && nn%2 == 1)
+ {
+ vector_t res0;
+ if (mid > 0)
+ {
+ vector_t val0 = this->data.shape_values[n_cols*n_q_points_1d];
+ res0 = in[0] + in[stride*(mm-1)];
+ res0 *= val0;
+ for (int ind=1; ind<mid; ++ind)
+ {
+ val0 = this->data.shape_values[n_cols*n_q_points_1d+ind];
+ vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)];
+ val1 *= val0;
+ res0 += val1;
+ }
+ if (mm % 2)
+ res0 += in[stride*mid];
+ }
+ else
+ res0 = in[0];
+ if (add == false)
+ out[stride*n_cols] = res0;
+ else
+ out[stride*n_cols] += res0;
+ }
+
+ // increment: in regular case, just go to the
+ // next point in x-direction. If we are at the
+ // end of one chunk in x-dir, need to jump
+ // over to the next layer in z-direction
+ switch (direction)
+ {
+ case 0:
+ in += mm;
+ out += nn;
+ break;
+ case 1:
+ case 2:
+ ++in;
+ ++out;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+ if (direction == 1)
+ {
+ in += nn*(mm-1);
+ out += nn*(nn-1);
+ }
+ }
+}
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+template <int direction, bool dof_to_quad, bool add>
+inline
+void
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+apply_gradients (const VectorizedArray<Number> input [],
+ VectorizedArray<Number> output [])
+{
+ AssertIndexRange (direction, dim);
+ const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d,
+ nn = dof_to_quad ? n_q_points_1d : n_dofs_1d;
+ const int n_cols = nn / 2;
+ const int mid = mm / 2;
+
+ const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
+ const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
+ const int stride = ((direction > 0 ? nn : 1 ) *
+ (direction > 1 ? nn : 1));
+
+ const vector_t * in = &input[0];
+ vector_t * out = &output[0];
+ for (int i2=0; i2<n_blocks2; ++i2)
+ {
+ for (int i1=0; i1<n_blocks1; ++i1)
+ {
+ for (int col=0; col<n_cols; ++col)
+ {
+ vector_t val0, val1, res0, res1;
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_gradients[col];
+ val1 = this->data.shape_gradients[nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_gradients[col*n_q_points_1d];
+ val1 = this->data.shape_gradients[(nn-col-1)*n_q_points_1d];
+ }
+ if (mid > 0)
+ {
+ res0 = val0 * in[0];
+ res1 = val1 * in[0];
+ res0 -= val1 * in[stride*(mm-1)];
+ res1 -= val0 * in[stride*(mm-1)];
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_gradients[ind*n_q_points_1d+col];
+ val1 = this->data.shape_gradients[ind*n_q_points_1d+nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_gradients[col*n_q_points_1d+ind];
+ val1 = this->data.shape_gradients[(nn-col-1)*n_q_points_1d+ind];
+ }
+ res0 += val0 * in[stride*ind];
+ res1 += val1 * in[stride*ind];
+ res0 -= val1 * in[stride*(mm-1-ind)];
+ res1 -= val0 * in[stride*(mm-1-ind)];
+ }
+ }
+ else
+ res0 = res1 = vector_t();
+ if (mm % 2 == 1)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[mid*n_q_points_1d+col];
+ else
+ val0 = this->data.shape_gradients[col*n_q_points_1d+mid];
+ val1 = val0 * in[stride*mid];
+ res0 += val1;
+ res1 -= val1;
+ }
+ if (add == false)
+ {
+ out[stride*col] = res0;
+ out[stride*(nn-1-col)] = res1;
+ }
+ else
+ {
+ out[stride*col] += res0;
+ out[stride*(nn-1-col)] += res1;
+ }
+ }
+ if ( nn%2 == 1 )
+ {
+ vector_t val0, res0;
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[n_cols];
+ else
+ val0 = this->data.shape_gradients[n_cols*n_q_points_1d];
+ res0 = in[0] - in[stride*(mm-1)];
+ res0 *= val0;
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[ind*n_q_points_1d+n_cols];
+ else
+ val0 = this->data.shape_gradients[n_cols*n_q_points_1d+ind];
+ vector_t val1 = in[stride*ind] - in[stride*(mm-1-ind)];
+ val1 *= val0;
+ res0 += val1;
+ }
+ if (add == false)
+ out[stride*n_cols] = res0;
+ else
+ out[stride*n_cols] += res0;
+ }
+
+ // increment: in regular case, just go to the
+ // next point in x-direction. for y-part in 3D
+ // and if we are at the end of one chunk in
+ // x-dir, need to jump over to the next layer
+ // in z-direction
+ switch (direction)
+ {
+ case 0:
+ in += mm;
+ out += nn;
+ break;
+ case 1:
+ case 2:
+ ++in;
+ ++out;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+
+ if (direction == 1)
+ {
+ in += nn * (mm-1);
+ out += nn * (nn-1);
+ }
+ }
+}
+
+
+
+ // Laplacian operator application. Very
+ // similar to value application because the
+ // same symmetry relations hold. However, it
+ // is not possible to omit some values that
+ // are zero for the values
+template <int dim, int n_dofs_1d, int n_q_points_1d, int n_components,
+ typename Number>
+template <int direction, bool dof_to_quad, bool add>
+inline
+void
+FEEvaluation<dim,n_dofs_1d,n_q_points_1d,n_components,Number>::
+apply_hessians (const VectorizedArray<Number> input [],
+ VectorizedArray<Number> output [])
+{
+ AssertIndexRange (direction, dim);
+ const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d,
+ nn = dof_to_quad ? n_q_points_1d : n_dofs_1d;
+ const int n_cols = nn / 2;
+ const int mid = mm / 2;
+
+ const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
+ const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
+ const int stride = ((direction > 0 ? nn : 1 ) *
+ (direction > 1 ? nn : 1));
+
+ const vector_t * in = &input[0];
+ vector_t * out = &output[0];
+ for (int i2=0; i2<n_blocks2; ++i2)
+ {
+ for (int i1=0; i1<n_blocks1; ++i1)
+ {
+ for (int col=0; col<n_cols; ++col)
+ {
+ vector_t val0, val1, res0, res1;
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_hessians[col];
+ val1 = this->data.shape_hessians[nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_hessians[col*n_q_points_1d];
+ val1 = this->data.shape_hessians[(col+1)*n_q_points_1d-1];
+ }
+ if (mid > 0)
+ {
+ res0 = val0 * in[0];
+ res1 = val1 * in[0];
+ res0 += val1 * in[stride*(mm-1)];
+ res1 += val0 * in[stride*(mm-1)];
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_hessians[ind*n_q_points_1d+col];
+ val1 = this->data.shape_hessians[ind*n_q_points_1d+nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_hessians[col*n_q_points_1d+ind];
+ val1 = this->data.shape_hessians[(col+1)*n_q_points_1d-1-ind];
+ }
+ res0 += val0 * in[stride*ind];
+ res1 += val1 * in[stride*ind];
+ res0 += val1 * in[stride*(mm-1-ind)];
+ res1 += val0 * in[stride*(mm-1-ind)];
+ }
+ }
+ else
+ res0 = res1 = vector_t();
+ if (mm % 2 == 1)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_hessians[mid*n_q_points_1d+col];
+ else
+ val0 = this->data.shape_hessians[col*n_q_points_1d+mid];
+ val1 = val0 * in[stride*mid];
+ res0 += val1;
+ res1 += val1;
+ }
+ if (add == false)
+ {
+ out[stride*col] = res0;
+ out[stride*(nn-1-col)] = res1;
+ }
+ else
+ {
+ out[stride*col] += res0;
+ out[stride*(nn-1-col)] += res1;
+ }
+ }
+ if ( nn%2 == 1 )
+ {
+ vector_t val0, res0;
+ if (dof_to_quad == true)
+ val0 = this->data.shape_hessians[n_cols];
+ else
+ val0 = this->data.shape_hessians[n_cols*n_q_points_1d];
+ if (mid > 0)
+ {
+ res0 = in[0] + in[stride*(mm-1)];
+ res0 *= val0;
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_hessians[ind*n_q_points_1d+n_cols];
+ else
+ val0 = this->data.shape_hessians[n_cols*n_q_points_1d+ind];
+ vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)];
+ val1 *= val0;
+ res0 += val1;
+ }
+ }
+ else
+ res0 = vector_t();
+ if (mm % 2 == 1)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_hessians[mid*n_q_points_1d+n_cols];
+ else
+ val0 = this->data.shape_hessians[n_cols*n_q_points_1d+mid];
+ res0 += val0 * in[stride*mid];
+ }
+ if (add == false)
+ out[stride*n_cols] = res0;
+ else
+ out[stride*n_cols] += res0;
+ }
+
+ // increment: in regular case, just go to the
+ // next point in x-direction. If we are at the
+ // end of one chunk in x-dir, need to jump
+ // over to the next layer in z-direction
+ switch (direction)
+ {
+ case 0:
+ in += mm;
+ out += nn;
+ break;
+ case 1:
+ case 2:
+ ++in;
+ ++out;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+ if (direction == 1)
+ {
+ in += nn*(mm-1);
+ out += nn*(nn-1);
+ }
+ }
+}
+
+
+/*----------------------- FEEvaluationGL -------------------------------*/
+
+
+template <int dim, int n_points_1d, int n_components, typename Number>
+inline
+FEEvaluationGL<dim,n_points_1d,n_components,Number>::
+FEEvaluationGL (const MatrixFree<dim,Number> &data_in,
+ const unsigned int fe_no,
+ const unsigned int quad_no)
+ :
+ BaseClass (data_in, fe_no, quad_no)
+{
+#ifdef DEBUG
+ std::string error_mess = "FEEvaluationGL not appropriate. It assumes:\n";
+ error_mess += " - identity operation for shape values\n";
+ error_mess += " - zero diagonal at interior points for gradients\n";
+ error_mess += "Try FEEvaluation<...> instead!";
+
+ const double zero_tol =
+ types_are_equal<Number,double>::value==true?1e-9:1e-7;
+
+ for (unsigned int i=0; i<n_points_1d; ++i)
+ for (unsigned int j=0; j<n_points_1d; ++j)
+ if (i!=j)
+ {
+ Assert (std::fabs(this->data.shape_values[i*n_points_1d+j][0])<zero_tol,
+ ExcMessage (error_mess.c_str()));
+ }
+ else
+ {
+ Assert (std::fabs(this->data.shape_values[i*n_points_1d+
+ j][0]-1.)<zero_tol,
+ ExcMessage (error_mess.c_str()));
+ }
+ for (unsigned int i=1; i<n_points_1d-1; ++i)
+ Assert (std::fabs(this->data.shape_gradients[i*n_points_1d+i][0])<zero_tol,
+ ExcMessage (error_mess.c_str()));
+#endif
+}
+
+
+
+template <int dim, int n_points_1d, int n_components, typename Number>
+inline
+void
+FEEvaluationGL<dim,n_points_1d,n_components,Number>::
+evaluate (bool evaluate_val,bool evaluate_grad,bool evaluate_lapl)
+{
+ Assert (this->cell != numbers::invalid_unsigned_int,
+ ExcNotInitialized());
+ Assert (this->dof_values_initialized == true,
+ internal::ExcAccessToUninitializedField());
+
+ if (evaluate_val == true)
+ {
+ std::memcpy (&this->values_quad[0][0], &this->values_dofs[0][0],
+ dofs_per_cell * n_components *
+ sizeof (this->values_dofs[0][0]));
+#ifdef DEBUG
+ this->values_quad_initialized = true;
+#endif
+ }
+ if (evaluate_grad == true)
+ {
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ if (dim == 3)
+ {
+ // grad x
+ apply_gradients<0,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][0]);
+ // grad y
+ apply_gradients<1,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][1]);
+ // grad y
+ apply_gradients<2,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][2]);
+ }
+ else if (dim == 2)
+ {
+ // grad x
+ apply_gradients<0,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][0]);
+ // grad y
+ apply_gradients<1,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][1]);
+ }
+ else if (dim == 1)
+ apply_gradients<0,true,false> (this->values_dofs[comp],
+ this->gradients_quad[comp][0]);
+ }
+#ifdef DEBUG
+ this->gradients_quad_initialized = true;
+#endif
+ }
+ if (evaluate_lapl == true)
+ {
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ if (dim == 3)
+ {
+ // grad x
+ this->template apply_hessians<0,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][0]);
+ // grad y
+ this->template apply_hessians<1,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][1]);
+ // grad y
+ this->template apply_hessians<2,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][2]);
+
+ vector_t temp1[n_q_points];
+ // grad xy
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][3]);
+ // grad xz
+ apply_gradients<2,true,false> (temp1, this->hessians_quad[comp][4]);
+ // grad yz
+ apply_gradients<1,true,false> (this->values_dofs[comp], temp1);
+ apply_gradients<2,true,false> (temp1, this->hessians_quad[comp][5]);
+ }
+ else if (dim == 2)
+ {
+ // grad x
+ this->template apply_hessians<0,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][0]);
+ // grad y
+ this->template apply_hessians<1,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][1]);
+ vector_t temp1[n_q_points];
+ // grad xy
+ apply_gradients<0,true,false> (this->values_dofs[comp], temp1);
+ apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][2]);
+ }
+ else if (dim == 1)
+ this->template apply_hessians<0,true,false> (this->values_dofs[comp],
+ this->hessians_quad[comp][0]);
+ }
+#ifdef DEBUG
+ this->hessians_quad_initialized = true;
+#endif
+ }
+}
+
+
+
+template <int dim, int n_points_1d, int n_components, typename Number>
+inline
+void
+FEEvaluationGL<dim,n_points_1d,n_components,Number>::
+integrate (bool integrate_val, bool integrate_grad)
+{
+ Assert (this->cell != numbers::invalid_unsigned_int,
+ ExcNotInitialized());
+ if (integrate_val == true)
+ Assert (this->values_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+ if (integrate_grad == true)
+ Assert (this->gradients_quad_submitted == true,
+ internal::ExcAccessToUninitializedField());
+ if (integrate_val == true)
+ std::memcpy (&this->values_dofs[0][0], &this->values_quad[0][0],
+ dofs_per_cell * n_components *
+ sizeof (this->values_dofs[0][0]));
+ if (integrate_grad == true)
+ {
+ for(unsigned int comp=0;comp<n_components;comp++)
+ {
+ if (dim == 3)
+ {
+ // grad x: If integrate_val == true we have to add to the previous output
+ if (integrate_val == true)
+ apply_gradients<0, false, true> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+ else
+ apply_gradients<0, false, false> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+
+ // grad y: can sum to temporary x value in temp2
+ apply_gradients<1, false, true> (this->gradients_quad[comp][1],
+ this->values_dofs[comp]);
+
+ // grad z: can sum to temporary x and y value in output
+ apply_gradients<2, false, true> (this->gradients_quad[comp][2],
+ this->values_dofs[comp]);
+ }
+ else if (dim == 2)
+ {
+ // grad x: If integrate_val == true we have to add to the previous output
+ if (integrate_val == true)
+ apply_gradients<0, false, true> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+ else
+ apply_gradients<0, false, false> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+
+ // grad y: can sum to temporary x value in temp2
+ apply_gradients<1, false, true> (this->gradients_quad[comp][1],
+ this->values_dofs[comp]);
+ }
+ else if (dim == 1)
+ {
+ if (integrate_val == true)
+ apply_gradients<0, false, true> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+ else
+ apply_gradients<0, false, false> (this->gradients_quad[comp][0],
+ this->values_dofs[comp]);
+
+ }
+ }
+ }
+
+#ifdef DEBUG
+ this->dof_values_initialized = true;
+#endif
+}
+
+
+
+template <int dim, int n_points_1d, int n_components, typename Number>
+template <int direction, bool dof_to_quad, bool add>
+inline
+void
+FEEvaluationGL<dim,n_points_1d,n_components,Number>::
+apply_gradients (const VectorizedArray<Number> input [],
+ VectorizedArray<Number> output [])
+{
+ AssertIndexRange (direction, dim);
+ const int mm = n_points_1d;
+ const int nn = n_points_1d;
+ const int n_cols = nn / 2;
+ const int mid = mm / 2;
+
+ const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
+ const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
+ const int stride = ((direction > 0 ? nn : 1 ) *
+ (direction > 1 ? nn : 1));
+
+ const vector_t * in = &input[0];
+ vector_t * out = &output[0];
+ for (int i2=0; i2<n_blocks2; ++i2)
+ {
+ for (int i1=0; i1<n_blocks1; ++i1)
+ {
+ for (int col=0; col<n_cols; ++col)
+ {
+ vector_t val0, val1, res0, res1;
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_gradients[col];
+ val1 = this->data.shape_gradients[nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_gradients[col*n_points_1d];
+ val1 = this->data.shape_gradients[(nn-col-1)*n_points_1d];
+ }
+ if (mid > 0)
+ {
+ res0 = val0 * in[0];
+ res1 = val1 * in[0];
+ res0 -= val1 * in[stride*(mm-1)];
+ res1 -= val0 * in[stride*(mm-1)];
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ {
+ val0 = this->data.shape_gradients[ind*n_points_1d+col];
+ val1 = this->data.shape_gradients[ind*n_points_1d+nn-1-col];
+ }
+ else
+ {
+ val0 = this->data.shape_gradients[col*n_points_1d+ind];
+ val1 = this->data.shape_gradients[(nn-col-1)*n_points_1d+ind];
+ }
+
+ // at inner points, the gradient is zero for
+ // ind==col
+ if (ind == col)
+ {
+ res1 += val1 * in[stride*ind];
+ res0 -= val1 * in[stride*(mm-1-ind)];
+ }
+ else
+ {
+ res0 += val0 * in[stride*ind];
+ res1 += val1 * in[stride*ind];
+ res0 -= val1 * in[stride*(mm-1-ind)];
+ res1 -= val0 * in[stride*(mm-1-ind)];
+ }
+ }
+ }
+ else
+ res0 = res1 = vector_t();
+ if (mm % 2 == 1)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[mid*n_points_1d+col];
+ else
+ val0 = this->data.shape_gradients[col*n_points_1d+mid];
+ val1 = val0 * in[stride*mid];
+ res0 += val1;
+ res1 -= val1;
+ }
+ if (add == false)
+ {
+ out[stride*col] = res0;
+ out[stride*(nn-1-col)] = res1;
+ }
+ else
+ {
+ out[stride*col] += res0;
+ out[stride*(nn-1-col)] += res1;
+ }
+ }
+ if ( nn%2 == 1 )
+ {
+ vector_t val0, res0;
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[n_cols];
+ else
+ val0 = this->data.shape_gradients[n_cols*n_points_1d];
+ if (mid > 0)
+ {
+ res0 = in[0] - in[stride*(mm-1)];
+ res0 *= val0;
+ for (int ind=1; ind<mid; ++ind)
+ {
+ if (dof_to_quad == true)
+ val0 = this->data.shape_gradients[ind*n_points_1d+n_cols];
+ else
+ val0 = this->data.shape_gradients[n_cols*n_points_1d+ind];
+ vector_t val1 = in[stride*ind] - in[stride*(mm-1-ind)];
+ val1 *= val0;
+ res0 += val1;
+ }
+ }
+ else
+ res0 = vector_t();
+ if (add == false)
+ out[stride*n_cols] = res0;
+ else
+ out[stride*n_cols] += res0;
+ }
+
+ // increment: in regular case, just go to the
+ // next point in x-direction. for y-part in 3D
+ // and if we are at the end of one chunk in
+ // x-dir, need to jump over to the next layer
+ // in z-direction
+ switch (direction)
+ {
+ case 0:
+ in += mm;
+ out += nn;
+ break;
+ case 1:
+ case 2:
+ ++in;
+ ++out;
+ break;
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+
+ if (direction == 1)
+ {
+ in += nn * (mm-1);
+ out += nn * (nn-1);
+ }
+ }
+
+#endif // ifndef DOXYGEN
+
+}
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_fe_evaluation_data_h
+#define __deal2__matrix_free_fe_evaluation_data_h
+
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/vectorization.h>
+#include <deal.II/fe/fe.h>
+
+#include <deal.II/matrix_free/helper_functions.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ /**
+ * The class that stores the shape functions, gradients and Hessians
+ * evaluated for a tensor product finite element and tensor product
+ * quadrature formula on the unit cell. Because of this structure, only
+ * one-dimensional data is stored.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+ template <typename Number>
+ struct FEEvaluationData
+ {
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ /**
+ * Empty constructor. Does nothing.
+ */
+ FEEvaluationData ();
+
+ /**
+ * Initializes the data fields. Takes a
+ * one-dimensional quadrature formula and a
+ * finite element as arguments and evaluates
+ * the shape functions, gradients and Hessians
+ * on the one-dimensional unit cell. This
+ * function assumes that the finite element is
+ * derived from a one-dimensional element by a
+ * tensor product. It uses FETools::get_name()
+ * and FETools::get_fe_from_name() to find the
+ * one-dimensional element corresponding to
+ * the input element in @p dim dimensions.
+ */
+ template <int dim>
+ void reinit (const Quadrature<1> &quad,
+ const FiniteElement<dim> &fe_dim);
+
+ /**
+ * Internal helper function for initialization
+ * that does the main work.
+ */
+ void do_initialize (const Quadrature<1> &quad,
+ const FiniteElement<1> &fe,
+ const unsigned int dim);
+
+ /**
+ * Returns the memory consumption of this
+ * class in bytes.
+ */
+ std::size_t memory_consumption () const;
+
+ /**
+ * Stores the shape values of the 1D finite
+ * element evaluated on all 1D quadrature
+ * points in vectorized format, i.e., as an
+ * array of
+ * VectorizedArray<dim>::n_array_elements
+ * equal elements. The length of this array is
+ * <tt>n_dofs_1d * n_q_points_1d</tt> and
+ * quadrature points are the index running
+ * fastest.
+ */
+ AlignedVector<vector_t> shape_values;
+
+ /**
+ * Stores the shape gradients of the 1D finite
+ * element evaluated on all 1D quadrature
+ * points in vectorized format, i.e., as an
+ * array of
+ * VectorizedArray<dim>::n_array_elements
+ * equal elements. The length of this array is
+ * <tt>n_dofs_1d * n_q_points_1d</tt> and
+ * quadrature points are the index running
+ * fastest.
+ */
+ AlignedVector<vector_t> shape_gradients;
+
+ /**
+ * Stores the shape Hessians of the 1D finite
+ * element evaluated on all 1D quadrature
+ * points in vectorized format, i.e., as an
+ * array of
+ * VectorizedArray<dim>::n_array_elements
+ * equal elements. The length of this array is
+ * <tt>n_dofs_1d * n_q_points_1d</tt> and
+ * quadrature points are the index running
+ * fastest.
+ */
+ AlignedVector<vector_t> shape_hessians;
+
+ /**
+ * Stores the indices from cell DoFs to face
+ * DoFs. The rows go through the
+ * <tt>2*dim</tt> faces, and the columns the
+ * DoFs on the faces.
+ */
+ Table<2,unsigned int> face_indices;
+
+ /**
+ * Stores one-dimensional values of shape
+ * functions on subface. Since there are two
+ * subfaces, store two variants. Not
+ * vectorized.
+ */
+ std::vector<Number> face_value[2];
+
+ /**
+ * Stores one-dimensional gradients of shape
+ * functions on subface. Since there are two
+ * subfaces, store two variants. Not
+ * vectorized.
+ */
+ std::vector<Number> face_gradient[2];
+
+ /**
+ * Non-vectorized version of shape
+ * values. Needed when evaluating face info.
+ */
+ std::vector<Number> shape_values_number;
+
+ /**
+ * Non-vectorized version of shape
+ * gradients. Needed when evaluating face
+ * info.
+ */
+ std::vector<Number> shape_gradient_number;
+
+ /**
+ * Stores the number of quadrature points in
+ * @p dim dimensions for a cell.
+ */
+ unsigned int n_q_points;
+
+ /**
+ * Stores the number of DoFs per cell in @p
+ * dim dimensions.
+ */
+ unsigned int dofs_per_cell;
+
+ /**
+ * Stores the number of quadrature points per
+ * face in @p dim dimensions.
+ */
+ unsigned int n_q_points_face;
+
+ /**
+ * Stores the number of DoFs per face in @p
+ * dim dimensions.
+ */
+ unsigned int dofs_per_face;
+ };
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/base/polynomial.h>
+#include <deal.II/base/tensor_product_polynomials.h>
+#include <deal.II/fe/fe_poly.h>
+#include <deal.II/fe/fe_tools.h>
+
+#include <deal.II/matrix_free/fe_evaluation_data.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ namespace internal
+ {
+
+ // ----------------- FE_PolyAccess -----------------------------------
+
+ // in order to read out the 1D info from a
+ // finite element and use the tensor product
+ // structure easily, we need to be able to
+ // access the numbering in the polynomial
+ // space of the finite element. that
+ // information is not public, but we can get
+ // access to that information by creating a
+ // new dummy class that is based on
+ // FE_Poly<TensorProductPolynomials<dim>,dim,spacedim>
+ template <int dim, int spacedim>
+ class FE_PolyAccess : public FE_Poly<TensorProductPolynomials<dim>, dim, spacedim>
+ {
+ public:
+ FE_PolyAccess (const FE_Poly<TensorProductPolynomials<dim>,dim,spacedim> &fe)
+ :
+ FE_Poly<TensorProductPolynomials<dim>,dim,spacedim>(fe)
+ {}
+
+ virtual std::string get_name() const
+ {
+ Assert (false, ExcNotImplemented());
+ return 0;
+ }
+ virtual FiniteElement<dim,spacedim> * clone() const
+ {
+ Assert (false, ExcNotImplemented());
+ return 0;
+ }
+
+ const std::vector<unsigned int> get_numbering () const
+ {
+ return this->poly_space.get_numbering();
+ }
+
+ const std::vector<unsigned int> get_numbering_inverse () const
+ {
+ return this->poly_space.get_numbering_inverse();
+ }
+ };
+
+ } // end of namespace internal
+
+
+
+ // ----------------- actual FEEvaluationData functions --------------------
+
+ template <typename Number>
+ FEEvaluationData<Number>::FEEvaluationData ()
+ :
+ n_q_points (0),
+ dofs_per_cell (0)
+ {}
+
+
+
+ template <typename Number>
+ template <int dim>
+ void
+ FEEvaluationData<Number>::reinit (const Quadrature<1> &quad,
+ const FiniteElement<dim> &fe_dim)
+ {
+ Assert (fe_dim.n_components() == 1,
+ ExcMessage("FEEvaluation only works for scalar finite elements."));
+
+ // take the name of the finite element
+ // and generate a 1d element. read the
+ // name, change the template argument
+ // to one and construct an element
+ std::string fe_name = fe_dim.get_name();
+ const std::size_t template_starts = fe_name.find_first_of('<');
+ Assert (fe_name[template_starts+1] == (dim==1?'1':(dim==2?'2':'3')),
+ ExcInternalError());
+ fe_name[template_starts+1] = '1';
+ std_cxx1x::shared_ptr<FiniteElement<1> > fe_1d
+ (FETools::get_fe_from_name<1>(fe_name));
+ const FiniteElement<1> & fe = *fe_1d;
+ do_initialize (quad, fe, dim);
+ }
+
+
+ template <typename Number>
+ void
+ FEEvaluationData<Number>::do_initialize (const Quadrature<1> &quad,
+ const FiniteElement<1> &fe,
+ const unsigned int dim)
+ {
+ const unsigned int n_dofs_1d = fe.dofs_per_cell,
+ n_q_points_1d = quad.size();
+ std::vector<unsigned int> lexicographic (n_dofs_1d);
+
+ // renumber (this is necessary for FE_Q, for
+ // example, since there the vertex DoFs come
+ // first, which is incompatible with the
+ // lexicographic ordering necessary to apply
+ // tensor products efficiently)
+ {
+ const FE_Poly<TensorProductPolynomials<1>,1,1> *cast_fe =
+ dynamic_cast<const FE_Poly<TensorProductPolynomials<1>,1,1>*>(&fe);
+ Assert (cast_fe != 0, ExcNotImplemented());
+ const internal::FE_PolyAccess<1,1> & fe_acc =
+ static_cast< const internal::FE_PolyAccess<1,1> &>(*cast_fe);
+ lexicographic = fe_acc.get_numbering();
+ }
+
+ n_q_points = 1;
+ dofs_per_cell = 1;
+ n_q_points_face = 1;
+ dofs_per_face = 1;
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ n_q_points *= n_q_points_1d;
+ dofs_per_cell *= n_dofs_1d;
+ }
+ for (int d=0; d<static_cast<int>(dim)-1; ++d)
+ {
+ n_q_points_face *= n_q_points_1d;
+ dofs_per_face *= n_dofs_1d;
+ }
+
+ const unsigned int array_size = n_dofs_1d*n_q_points_1d;
+ this->shape_gradients.resize_fast (array_size);
+ this->shape_values.resize_fast (array_size);
+ this->shape_hessians.resize_fast (array_size);
+
+ this->face_gradient[0].resize(n_dofs_1d);
+ this->face_value[0].resize(array_size);
+ this->face_gradient[1].resize(n_dofs_1d);
+ this->face_value[1].resize(array_size);
+ this->shape_values_number.resize (array_size);
+ this->shape_gradient_number.resize (array_size);
+
+ for (unsigned int i=0; i<n_dofs_1d; ++i)
+ {
+ // need to reorder from hierarchical to
+ // lexicographic to get the DoFs correct
+ const unsigned int my_i = lexicographic[i];
+ for (unsigned int q=0; q<n_q_points_1d; ++q)
+ {
+ // fill both vectors with n_vectors copies for
+ // the shape information and non-vectorized
+ // fields
+ const Point<1> q_point = quad.get_points()[q];
+ shape_values_number[my_i*n_q_points_1d+q] = fe.shape_value(i,q_point);
+ shape_gradient_number[my_i*n_q_points_1d+q] = fe.shape_grad (i,q_point)[0];
+ shape_values [my_i*n_q_points_1d+q] =
+ shape_values_number [my_i*n_q_points_1d+q];
+ shape_gradients[my_i*n_q_points_1d+q] =
+ shape_gradient_number[my_i*n_q_points_1d+q];
+ shape_hessians[my_i*n_q_points_1d+q] =
+ fe.shape_grad_grad(i,q_point)[0][0];
+ face_value[0][my_i*n_q_points_1d+q] = fe.shape_value(i,q_point*0.5);
+ face_value[1][my_i*n_q_points_1d+q] = fe.shape_value(i,Point<1>(0.5)+q_point*0.5);
+ }
+ this->face_gradient[0][my_i] = fe.shape_grad(i,Point<1>(0.))[0];
+ this->face_gradient[1][my_i] = fe.shape_grad(i,Point<1>(1.))[0];
+ }
+
+ // face information
+ unsigned int n_faces = 1;
+ for (unsigned int d=0; d<dim; ++d)
+ n_faces *= 2;
+ this->face_indices.reinit(n_faces, this->dofs_per_face);
+ switch (dim)
+ {
+ case 3:
+ {
+ for (unsigned int i=0; i<this->dofs_per_face; i++)
+ {
+ const unsigned int jump_term =
+ this->dofs_per_face*((i*n_dofs_1d)/this->dofs_per_face);
+ this->face_indices(0,i) = i*n_dofs_1d;
+ this->face_indices(1,i) = i*n_dofs_1d + n_dofs_1d-1;
+ this->face_indices(2,i) = i%n_dofs_1d + jump_term;
+ this->face_indices(3,i) = (i%n_dofs_1d + jump_term +
+ (n_dofs_1d-1)*n_dofs_1d);
+ this->face_indices(4,i) = i;
+ this->face_indices(5,i) = (n_dofs_1d-1)*this->dofs_per_face+i;
+ }
+ break;
+ }
+ case 2:
+ {
+ for (unsigned int i=0; i<n_dofs_1d; i++)
+ {
+ this->face_indices(0,i) = n_dofs_1d*i;
+ this->face_indices(1,i) = n_dofs_1d*i + n_dofs_1d-1;
+ this->face_indices(2,i) = i;
+ this->face_indices(3,i) = (n_dofs_1d-1)*n_dofs_1d+i;
+ }
+ break;
+ }
+ case 1:
+ {
+ this->face_indices(0,0) = 0;
+ this->face_indices(1,0) = n_dofs_1d-1;
+ break;
+ }
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ }
+
+
+
+ template <typename Number>
+ std::size_t
+ FEEvaluationData<Number>::memory_consumption () const
+ {
+ std::size_t memory = sizeof(*this);
+ memory += MemoryConsumption::memory_consumption(shape_values);
+ memory += MemoryConsumption::memory_consumption(shape_gradients);
+ memory += MemoryConsumption::memory_consumption(shape_hessians);
+ memory += face_indices.memory_consumption();
+ for (unsigned int i=0; i<2; ++i)
+ {
+ memory += MemoryConsumption::memory_consumption(face_value[i]);
+ memory += MemoryConsumption::memory_consumption(face_gradient[i]);
+ }
+ memory += MemoryConsumption::memory_consumption(shape_values_number);
+ memory += MemoryConsumption::memory_consumption(shape_gradient_number);
+ return memory;
+ }
+
+ // end of functions for FEEvaluationData
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+
+DEAL_II_NAMESPACE_CLOSE
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_helper_functions_h
+#define __deal2__matrix_free_helper_functions_h
+
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/base/index_set.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/vectorization.h>
+
+#include <boost/functional/hash.hpp>
+
+DEAL_II_NAMESPACE_OPEN
+
+
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ // forward declaration of internal data structure
+ namespace internal
+ {
+ template <typename Number> struct ConstraintValues;
+ }
+
+
+ // set minimum grain size for parallel
+ // computations
+ namespace internal
+ {
+ const unsigned int minimum_parallel_grain_size = 500;
+ }
+
+
+ /*
+ * Compressed data type to store a two
+ * dimensional array. The data is stored in
+ * a single standard vector. In a second
+ * vector, the first element belonging to
+ * each row is stored.
+ */
+ template<typename T>
+ struct CompressedMatrix
+ {
+ AlignedVector<T> data;
+ std::vector<unsigned int> row_index;
+ T* operator[] (const unsigned int row) {
+ return begin(row);
+ };
+ const T* operator[] (const unsigned int row) const {
+ return begin(row);
+ };
+ const T* begin(const unsigned int row) const {
+ AssertIndexRange (row, row_index.size()-1);
+ return data.begin() + row_index[row];
+ };
+ const T* end(const unsigned int row) const {
+ AssertIndexRange (row, row_index.size()-1);
+ return data.begin() + row_index[row+1];
+ };
+ unsigned int row_length (const unsigned int row) const {
+ AssertIndexRange (row, row_index.size()-1);
+ return row_index[row+1] - row_index[row];
+ };
+ T* begin(const unsigned int row) {
+ AssertIndexRange (row, row_index.size()-1);
+ return data.begin() + row_index[row];
+ };
+ T* end(const unsigned int row) {
+ AssertIndexRange (row, row_index.size()-1);
+ return data.begin() + row_index[row+1];
+ };
+ void complete_last_row() {
+ row_index.push_back (data.size());
+ }
+ void swap (CompressedMatrix<T> &other) {
+ data.swap (other.data);
+ row_index.swap (other.row_index);
+ }
+ void print (std::ostream &out) const
+ {
+ for (unsigned int row=0; row<row_index.size(); ++row)
+ {
+ for (const T* iterator=begin(row); iterator != end(row); ++iterator)
+ out << *iterator << " ";
+ out << std::endl;
+ }
+ };
+ void clear()
+ {
+ data.clear();
+ row_index.clear();
+ }
+ unsigned int memory_consumption() const
+ {
+ return MemoryConsumption::memory_consumption(data)+
+ MemoryConsumption::memory_consumption(row_index);
+ };
+ };
+
+ /**
+ * A struct that collects all information
+ * related to parallelization with threads:
+ * The work is subdivided into tasks that can
+ * be done independently.
+ */
+ struct TaskInfo
+ {
+ /**
+ * Constructor.
+ */
+ TaskInfo ()
+ {
+ clear();
+ }
+
+ /**
+ * Clears all the data fields and resets them
+ * to zero.
+ */
+ void clear ()
+ {
+ block_size = 0;
+ n_blocks = 0;
+ block_size_last = 0;
+ position_short_block = 0;
+ use_multithreading = false;
+ use_partition_partition = false;
+ use_coloring_only = false;
+ partition_color_blocks.clear();
+ evens = 0;
+ odds = 0;
+ n_blocked_workers = 0;
+ n_workers = 0;
+ partition_evens.clear();
+ partition_odds.clear();
+ partition_n_blocked_workers.clear();
+ partition_n_workers.clear();
+ }
+
+ std::size_t memory_consumption () const
+ {
+ return (MemoryConsumption::memory_consumption (partition_color_blocks) +
+ MemoryConsumption::memory_consumption (partition_evens) +
+ MemoryConsumption::memory_consumption (partition_odds) +
+ MemoryConsumption::memory_consumption (partition_n_blocked_workers) +
+ MemoryConsumption::memory_consumption (partition_n_workers));
+ }
+
+ unsigned int block_size;
+ unsigned int n_blocks;
+ unsigned int block_size_last;
+ unsigned int position_short_block;
+ bool use_multithreading;
+ bool use_partition_partition;
+ bool use_coloring_only;
+
+ CompressedMatrix<unsigned int> partition_color_blocks;
+ unsigned int evens;
+ unsigned int odds;
+ unsigned int n_blocked_workers;
+ unsigned int n_workers;
+
+ std::vector<unsigned int> partition_evens;
+ std::vector<unsigned int> partition_odds;
+ std::vector<unsigned int> partition_n_blocked_workers;
+ std::vector<unsigned int> partition_n_workers;
+ };
+
+
+
+ /**
+ * A struct that collects all information
+ * related to the size of the problem and MPI
+ * parallelization.
+ */
+ struct SizeInfo
+ {
+ /**
+ * Constructor.
+ */
+ SizeInfo ()
+ {
+ clear();
+ }
+
+ /**
+ * Clears all data fields and resets the sizes
+ * to zero.
+ */
+ void clear()
+ {
+ n_active_cells = 0;
+ n_macro_cells = 0;
+ boundary_cells_start = 0;
+ boundary_cells_end = 0;
+ n_vectors = 0;
+ locally_owned_cells = IndexSet();
+ ghost_cells = IndexSet();
+ communicator = MPI_COMM_SELF;
+ my_pid = 0;
+ n_procs = 0;
+ }
+
+ template <typename STREAM>
+ void print_mem (STREAM &out,
+ std::size_t data_length) const
+ {
+ Utilities::MPI::MinMaxAvg memory_c;
+ if (Utilities::System::job_supports_mpi() == true)
+ {
+ memory_c = Utilities::MPI::min_max_avg (1e-6*data_length,
+ communicator);
+ }
+ else
+ {
+ memory_c.sum = 1e-6*data_length;
+ memory_c.min = memory_c.sum;
+ memory_c.max = memory_c.sum;
+ memory_c.avg = memory_c.sum;
+ memory_c.min_index = 0;
+ memory_c.max_index = 0;
+ }
+ if (n_procs < 2)
+ out << memory_c.min;
+ else
+ out << memory_c.min << "/" << memory_c.avg << "/" << memory_c.max;
+ out << " MB" << std::endl;
+ }
+
+ void make_layout (const unsigned int n_active_cells_in,
+ const unsigned int n_boundary_cells,
+ const unsigned int n_vectors_in,
+ std::vector<unsigned int> &irregular_cells)
+ {
+ n_vectors = n_vectors_in;
+ n_active_cells = n_active_cells_in;
+
+ // check that number of boundary cells is
+ // divisible by n_vectors or that it contains
+ // all cells
+ Assert (n_boundary_cells % n_vectors == 0 ||
+ n_boundary_cells == n_active_cells, ExcInternalError());
+ n_macro_cells = (n_active_cells+n_vectors-1)/n_vectors;
+ irregular_cells.resize (n_macro_cells);
+ if (n_macro_cells*n_vectors > n_active_cells)
+ {
+ irregular_cells[n_macro_cells-1] =
+ n_vectors - (n_macro_cells*n_vectors - n_active_cells);
+ }
+ if (n_procs > 1)
+ {
+ const unsigned int n_macro_boundary_cells =
+ (n_boundary_cells+n_vectors-1)/n_vectors;
+ boundary_cells_start = (n_macro_cells-n_macro_boundary_cells)/2;
+ boundary_cells_end = boundary_cells_start + n_macro_boundary_cells;
+ }
+ else
+ boundary_cells_start = boundary_cells_end = n_macro_cells;
+ }
+
+ unsigned int n_active_cells;
+ unsigned int n_macro_cells;
+ unsigned int boundary_cells_start;
+ unsigned int boundary_cells_end;
+ unsigned int n_vectors;
+
+ /**
+ * index sets to describe the layout of cells:
+ * locally owned cells and locally active
+ * cells
+ */
+ IndexSet locally_owned_cells;
+ IndexSet ghost_cells;
+
+ /**
+ * MPI communicator
+ */
+ MPI_Comm communicator;
+ unsigned int my_pid;
+ unsigned int n_procs;
+ };
+
+
+
+ namespace internal
+ {
+ // ----------------- hash structure --------------------------------
+
+ /**
+ * A class that is
+ * used to quickly find out whether two
+ * vectors of floating point numbers are the
+ * same without going through all the
+ * elements: store a hash value for each
+ * vector. Generate the
+ * hash value by a sum of all values
+ * multiplied by random numbers (cast to
+ * int). Of course, this is not a sure
+ * criterion and one must manually check for
+ * equality before this hash is telling
+ * something useful. However, inequalities are
+ * easily detected (unless roundoff spoils the
+ * hash function)
+ */
+ struct HashValue
+ {
+ // Constructor: sets the size of Number values
+ // with the typical magnitude that is to be
+ // expected.
+ HashValue (const double element_size = 1.)
+ :
+ scaling (element_size * std::numeric_limits<double>::epsilon() *
+ 1024.)
+ {};
+
+ // get hash value for a vector of floating
+ // point numbers (which are assumed to be of
+ // order of magnitude one). Do this by first
+ // truncating everything that is smaller than
+ // the scaling (in order to eliminate noise
+ // from roundoff errors) and then calling the
+ // boost hash function
+ unsigned int operator ()(const std::vector<double> &vec)
+ {
+ std::vector<double> mod_vec(vec);
+ for (unsigned int i=0; i<mod_vec.size(); ++i)
+ mod_vec[i] -= fmod (mod_vec[i], scaling);
+ return static_cast<unsigned int>(boost::hash_range (mod_vec.begin(), mod_vec.end()));
+ };
+
+ // get hash value for a tensor of rank
+ // two where the magnitude of the
+ // entries is given by the parameter
+ // weight
+ template <int dim, typename number>
+ unsigned int operator ()(const Tensor<2,dim,VectorizedArray<number> > &input,
+ const bool is_diagonal)
+ {
+ const unsigned int n_vectors = VectorizedArray<number>::n_array_elements;
+
+ if (is_diagonal)
+ {
+ number mod_tensor [dim][n_vectors];
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ mod_tensor[i][j] = input[i][i][j] - fmod (input[i][i][j],
+ number(scaling));
+ return static_cast<unsigned int>(boost::hash_range
+ (&mod_tensor[0][0],
+ &mod_tensor[0][0]+dim*n_vectors));
+ }
+ else
+ {
+ number mod_tensor [dim][dim][n_vectors];
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ mod_tensor[i][d][j] = input[i][d][j] - fmod (input[i][d][j],
+ number(scaling));
+ return static_cast<unsigned int>(boost::hash_range
+ (&mod_tensor[0][0][0],
+ &mod_tensor[0][0][0]+
+ dim*dim*n_vectors));
+ }
+ };
+
+ const double scaling;
+ };
+
+ } // end of namespace internal
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_mapping_info_h
+#define __deal2__matrix_free_mapping_info_h
+
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/vectorization.h>
+#include <deal.II/hp/q_collection.h>
+#include <deal.II/fe/fe.h>
+#include <deal.II/fe/mapping.h>
+#include <deal.II/matrix_free/helper_functions.h>
+
+#include <memory>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ /**
+ * The class that stores all geometry-dependent data related with cell
+ * interiors for use in the matrix-free class.
+ *
+ * @author Katharina Kormann and Martin Kronbichler, 2010, 2011
+ */
+ template <int dim, typename Number>
+ struct MappingInfo
+ {
+ typedef VectorizedArray<Number> vector_t;
+ typedef Point<dim,vector_t> point;
+ typedef Tensor<1,dim,vector_t> tensor1;
+ typedef Tensor<2,dim,vector_t> tensor2;
+ typedef Tensor<3,dim,vector_t> tensor3;
+ typedef Tensor<1,(dim>1?dim*(dim-1)/2:1),Tensor<1,dim,vector_t> > tensorUT;
+ static const std::size_t n_vectors
+ = VectorizedArray<Number>::n_array_elements;
+
+ /**
+ * Determines how many bits of an unsigned int
+ * are used to distinguish the cell types
+ * (Cartesian, with constant Jacobian, or
+ * general)
+ */
+ static const std::size_t n_cell_type_bits = 2;
+
+ /**
+ * Determines how many types of different
+ * cells can be detected at most. Corresponds
+ * to the number of bits we reserved for it.
+ */
+ static const unsigned int n_cell_types = 1U<<n_cell_type_bits;
+
+ /**
+ * Empty constructor.
+ */
+ MappingInfo();
+
+ /**
+ * Computes the information in the given
+ * cells. The cells are specified by the level
+ * and the index within the level (as given by
+ * CellIterator::level() and
+ * CellIterator::index(), in order to allow
+ * for different kinds of iterators,
+ * e.g. standard DoFHandler, multigrid, etc.)
+ * on a fixed Triangulation. In addition, a
+ * mapping and several quadrature formulas are
+ * given.
+ */
+ void initialize (const dealii::Triangulation<dim> &tria,
+ const std::vector<std::pair<unsigned int,unsigned int> > &cells,
+ const std::vector<unsigned int> &active_fe_index,
+ const Mapping<dim> &mapping,
+ const std::vector<dealii::hp::QCollection<1> > &quad,
+ const UpdateFlags update_flags);
+
+ /**
+ * Helper function to determine which update
+ * flags must be set in the internal functions
+ * to initialize all data as requested by the
+ * user.
+ */
+ UpdateFlags
+ compute_update_flags (const UpdateFlags update_flags,
+ const std::vector<dealii::hp::QCollection<1> > &quad) const;
+
+ /**
+ * Returns the type of a given cell as
+ * detected during initialization.
+ */
+ unsigned int get_cell_type (const unsigned int cell_chunk_no) const
+ {
+ AssertIndexRange (cell_chunk_no, cell_type.size());
+ return cell_type[cell_chunk_no] % n_cell_types;
+ };
+
+ /**
+ * Returns the type of a given cell as
+ * detected during initialization.
+ */
+ unsigned int get_cell_data_index (const unsigned int cell_chunk_no) const
+ {
+ AssertIndexRange (cell_chunk_no, cell_type.size());
+ return cell_type[cell_chunk_no] >> n_cell_type_bits;
+ };
+
+ /**
+ * Clears all data fields in this class.
+ */
+ void clear ();
+
+ /**
+ * Returns the memory consumption of this
+ * class in bytes.
+ */
+ std::size_t memory_consumption() const;
+
+ /**
+ * Prints a detailed summary of memory
+ * consumption in the different structures of
+ * this class to the given output stream.
+ */
+ template <typename STREAM>
+ void print_memory_consumption(STREAM &out,
+ const SizeInfo &size_info) const;
+
+ /**
+ * Stores whether a cell is Cartesian, has
+ * constant transform data (Jacobians) or is
+ * general. cell_type % 4 gives this
+ * information (0: Cartesian, 1: constant
+ * Jacobian throughout cell, 2: general cell),
+ * and cell_type / 4 gives the index in the
+ * data field of where to find the information
+ * in the fields Jacobian and JxW values
+ * (except for quadrature points, for which
+ * the index runs as usual).
+ */
+ std::vector<unsigned int> cell_type;
+
+ /**
+ * The first field stores the inverse Jacobian
+ * for Cartesian cells: There, it is a
+ * diagonal rank-2 tensor, so we actually just
+ * store a rank-1 tensor. It is the same on
+ * all cells, therefore we only store it once
+ * per cell, and use similarities from one
+ * cell to another, too (on structured meshes,
+ * there are usually many cells with the same
+ * Jacobian).
+ *
+ * The second field stores the Jacobian
+ * determinant for Cartesian cells (without
+ * the quadrature weight, which depends on the
+ * quadrature point, whereas the determinant
+ * is the same on each quadrature point).
+ */
+ AlignedVector<std::pair<tensor1,vector_t> > cartesian;
+
+ /**
+ * The first field stores the Jacobian for
+ * non-Cartesian cells where all the Jacobians
+ * on the cell are the same (i.e., constant,
+ * which comes from a linear transformation
+ * from unit to real cell). Also use
+ * similarities from one cell to another (on
+ * structured meshes, there are usually many
+ * cells with the same Jacobian).
+ *
+ * The second field stores the Jacobian
+ * determinant for non-Cartesian cells with
+ * constant Jacobian throughout the cell
+ * (without the quadrature weight, which
+ * depends on the quadrature point, whereas
+ * the determinant is the same on each
+ * quadrature point).
+ */
+ AlignedVector<std::pair<tensor2,vector_t> > linear;
+
+ /**
+ * Definition of a structure that stores data
+ * that depends on the quadrature formula (if
+ * we have more than one quadrature formula on
+ * a given problem, these fields will be
+ * different)
+ */
+ struct MappingInfoDependent
+ {
+ /**
+ * This field stores the row starts for the
+ * inverse Jacobian transformations,
+ * quadrature weights and second derivatives.
+ */
+ std::vector<unsigned int> rowstart_jacobians;
+
+ /**
+ * This field stores the inverse Jacobian
+ * transformation from unit to real cell,
+ * which is needed for most gradient
+ * transformations (corresponds to
+ * FEValues::inverse_jacobian) for general
+ * cells.
+ */
+ AlignedVector<tensor2> jacobians;
+
+ /**
+ * This field stores the Jacobian
+ * determinant times the quadrature weights
+ * (JxW in deal.II speak) for general cells.
+ */
+ AlignedVector<vector_t> JxW_values;
+
+ /**
+ * Stores the diagonal part of the gradient of
+ * the inverse Jacobian transformation. The
+ * first index runs over the derivatives
+ * $\partial^2/\partial x_i^2$, the second
+ * over the space coordinate. Needed for
+ * computing the Laplacian of FE functions on
+ * the real cell. Uses a separate storage from
+ * the off-diagonal part $\partial^2/\partial
+ * x_i \partial x_j, i\neq j$ because that is
+ * only needed for computing a full Hessian.
+ */
+ AlignedVector<tensor2> jacobians_grad_diag;
+
+ /**
+ * Stores the off-diagonal part of the
+ * gradient of the inverse Jacobian
+ * transformation. Because of symmetry, only
+ * the upper diagonal part is needed. The
+ * first index runs through the derivatives
+ * row-wise, i.e., $\partial^2/\partial x_1
+ * \partial x_2$ first, then
+ * $\partial^2/\partial x_1 \partial x_3$, and
+ * so on. The second index is the spatial
+ * coordinate. Not filled currently.
+ */
+ AlignedVector<tensorUT> jacobians_grad_upper;
+
+ /**
+ * Stores the row start for quadrature points
+ * in real coordinates for both types of
+ * cells. Note that Cartesian cells will have
+ * shorter fields (length is @p n_q_points_1d)
+ * than non-Cartesian cells (length is @p
+ * n_q_points).
+ */
+ std::vector<unsigned int> rowstart_q_points;
+
+ /**
+ * Stores the quadrature points in real
+ * coordinates for Cartesian cells (does not
+ * need to store the full data on all points)
+ */
+ AlignedVector<point> quadrature_points;
+
+ /**
+ * The dim-dimensional quadrature formula
+ * underlying the problem (constructed from a
+ * 1D tensor product quadrature formula).
+ */
+ dealii::hp::QCollection<dim> quadrature_formula;
+
+ /**
+ * The (dim-1)-dimensional quadrature formula
+ * underlying the problem (constructed from a
+ * 1D tensor product quadrature formula).
+ */
+ dealii::hp::QCollection<dim-1> quadrature_formula_faces;
+
+ /**
+ * The number of quadrature points for the
+ * current quadrature formula.
+ */
+ std::vector<unsigned int> n_q_points;
+
+ /**
+ * The number of quadrature points for the
+ * current quadrature formula when applied to
+ * a face. Only set if the quadrature formula
+ * is derived from a tensor product, since it
+ * is not defined from the full quadrature
+ * formula otherwise.
+ */
+ std::vector<unsigned int> n_q_points_face;
+
+ /**
+ * The quadrature weights (vectorized data
+ * format) on the unit cell.
+ */
+ std::vector<AlignedVector<vector_t> > quadrature_weights;
+
+ /**
+ * This variable stores the number of
+ * quadrature points for all quadrature
+ * indices in the underlying element for
+ * easier access to data in the hp case.
+ */
+ std::vector<unsigned int> quad_index_conversion;
+
+ /**
+ * Returns the quadrature index for a given
+ * number of quadrature points. If not in hp
+ * mode or if the index is not found, this
+ * function always returns index 0. Hence,
+ * this function does not check whether the
+ * given degree is actually present.
+ */
+ unsigned int
+ quad_index_from_n_q_points (const unsigned int n_q_points) const
+ {
+ for (unsigned int i=0; i<quad_index_conversion.size(); ++i)
+ if (n_q_points == quad_index_conversion[i])
+ return i;
+ return 0;
+ }
+
+
+ /**
+ * Prints a detailed summary of memory
+ * consumption in the different structures of
+ * this class to the given output stream.
+ */
+ template <typename STREAM>
+ void print_memory_consumption(STREAM &out,
+ const SizeInfo &size_info) const;
+
+ /**
+ * Returns the memory consumption in bytes.
+ */
+ std::size_t memory_consumption () const;
+ };
+
+ /**
+ * Contains all the stuff that depends on the
+ * quadrature formula
+
+ */
+ std::vector<MappingInfoDependent> mapping_data_gen;
+
+ /**
+ * Stores whether JxW values have been
+ * initialized
+ */
+ bool JxW_values_initialized;
+
+ /**
+ * Stores whether we computed second
+ * derivatives.
+ */
+ bool second_derivatives_initialized;
+
+ /**
+ * Stores whether we computed quadrature
+ * points.
+ */
+ bool quadrature_points_initialized;
+
+ /**
+ * Internal temporary data used for the
+ * initialization.
+ */
+ struct CellData
+ {
+ CellData (const double jac_size_in) :
+ jac_size (jac_size_in) {}
+
+ void resize (const unsigned int size)
+ {
+ if (general_jac.size() != size)
+ {
+ quadrature_points.resize(size);
+ general_jac.resize(size);
+ general_jac_grad.resize(size);
+ }
+ }
+
+ AlignedVector<tensor1> quadrature_points;
+ AlignedVector<tensor2> general_jac;
+ AlignedVector<tensor3> general_jac_grad;
+ tensor2 const_jac;
+ const double jac_size;
+ };
+
+ /**
+ * Helper function called internally during
+ * the initialize function.
+ */
+ void evaluate_on_cell (const dealii::Triangulation<dim> &tria,
+ const std::pair<unsigned int,unsigned int> *cells,
+ const unsigned int cell,
+ const unsigned int my_q,
+ unsigned int (&cell_t_prev)[n_vectors],
+ unsigned int (&cell_t)[n_vectors],
+ FEValues<dim,dim> &fe_values,
+ CellData &cell_data) const;
+ };
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/fe/fe_nothing.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/mapping_q1.h>
+
+#include <deal.II/matrix_free/mapping_info.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+namespace internal
+{
+namespace MatrixFreeFunctions
+{
+ // ----------------- actual MappingInfo functions -------------------------
+
+ template <int dim, typename Number>
+ MappingInfo<dim,Number>::MappingInfo()
+ :
+ JxW_values_initialized (false),
+ second_derivatives_initialized (false),
+ quadrature_points_initialized (false)
+ {}
+
+
+
+ template <int dim, typename Number>
+ void
+ MappingInfo<dim,Number>::clear ()
+ {
+ JxW_values_initialized = false;
+ quadrature_points_initialized = false;
+ second_derivatives_initialized = false;
+ mapping_data_gen.clear();
+ cell_type.clear();
+ cartesian.clear();
+ linear.clear();
+ }
+
+
+
+ template <int dim, typename Number>
+ UpdateFlags
+ MappingInfo<dim,Number>::
+ compute_update_flags (const UpdateFlags update_flags,
+ const std::vector<dealii::hp::QCollection<1> > &quad) const
+ {
+ // this class is build around the evaluation
+ // this class is build around the evaluation
+ // of inverse gradients, so compute them in
+ // any case
+ UpdateFlags new_flags = update_inverse_jacobians;
+
+ // if the user requested gradients, need
+ // inverse Jacobians
+ if (update_flags & update_gradients || update_flags & update_inverse_jacobians)
+ new_flags |= update_inverse_jacobians;
+
+ // for JxW, would only need JxW values.
+ if (update_flags & update_JxW_values)
+ new_flags |= update_JxW_values;
+
+ // for Hessian information, need inverse
+ // Jacobians and the derivative of Jacobians
+ // (these two together will give use the
+ // gradients of the inverse Jacobians, which
+ // is what we need)
+ if (update_flags & update_hessians || update_flags & update_jacobian_grads)
+ new_flags |= update_jacobian_grads;
+
+ if (update_flags & update_quadrature_points)
+ new_flags |= update_quadrature_points;
+
+ // there is one more thing: if we have a
+ // quadrature formula with only one quadrature
+ // point on the first component, but more
+ // points on later components, we need to have
+ // Jacobian gradients anyway in order to
+ // determine whether the Jacobian is constant
+ // throughout a cell
+ bool formula_with_one_point = false;
+ for (unsigned int i=0; i<quad[0].size(); ++i)
+ if (quad[0][i].size() == 1)
+ {
+ formula_with_one_point = true;
+ break;
+ }
+ if (formula_with_one_point == true)
+ for (unsigned int comp=1; comp<quad.size(); ++comp)
+ for (unsigned int i=0; i<quad[comp].size(); ++i)
+ if (quad[comp][i].size() > 1)
+ {
+ new_flags |= update_jacobian_grads;
+ goto end_set;
+ }
+ end_set:
+ return new_flags;
+ }
+
+
+
+ namespace internal
+ {
+ template <int dim>
+ double get_jacobian_size (const dealii::Triangulation<dim> &tria)
+ {
+ if (tria.n_cells() == 0)
+ return 1;
+ else return tria.begin()->diameter();
+ }
+ }
+
+
+
+ template <int dim, typename Number>
+ void
+ MappingInfo<dim,Number>::initialize
+ (const dealii::Triangulation<dim> &tria,
+ const std::vector<std::pair<unsigned int,unsigned int> > &cells,
+ const std::vector<unsigned int> &active_fe_index,
+ const Mapping<dim> &mapping,
+ const std::vector<dealii::hp::QCollection<1> > &quad,
+ const UpdateFlags update_flags_input)
+ {
+ clear();
+ const unsigned int n_quads = quad.size();
+ const unsigned int n_cells = cells.size();
+ Assert (n_cells%n_vectors == 0, ExcInternalError());
+ const unsigned int n_macro_cells = n_cells/n_vectors;
+ mapping_data_gen.resize (n_quads);
+ cell_type.resize (n_macro_cells);
+
+ // dummy FE that is used to set up an FEValues
+ // object. Do not need the actual finite
+ // element because we will only evaluate
+ // quantities for the mapping that are
+ // independent of the FE
+ FE_Nothing<dim> dummy_fe;
+ UpdateFlags update_flags = compute_update_flags (update_flags_input, quad);
+
+ if (update_flags & update_JxW_values)
+ JxW_values_initialized = true;
+ if (update_flags & update_jacobian_grads)
+ second_derivatives_initialized = true;
+ if (update_flags & update_quadrature_points)
+ quadrature_points_initialized = true;
+
+ // when we make comparisons about the size of
+ // Jacobians we need to know the approximate
+ // size of typical entries in Jacobians. We
+ // need to fix the Jacobian size once and for
+ // all. We choose the diameter of the first
+ // cell (on level zero, which is the best
+ // accuracy we can hope for, since diameters
+ // on finer levels are computed by differences
+ // of nearby cells). If the mesh extends over
+ // a certain domain, the precision of double
+ // values is essentially limited by this
+ // precision.
+ const double jacobian_size = internal::get_jacobian_size(tria);
+
+ // objects that hold the data for up to
+ // n_vectors cells while we fill them up. Only
+ // after all n_vectors cells have been
+ // processed, we can insert the data into the
+ // data structures of this class
+ CellData data (jacobian_size);
+
+ for (unsigned int my_q=0; my_q<n_quads; ++my_q)
+ {
+ MappingInfoDependent & current_data = mapping_data_gen[my_q];
+ const unsigned int n_hp_quads = quad[my_q].size();
+ AssertIndexRange (0, n_hp_quads);
+ current_data.n_q_points.reserve (n_hp_quads);
+ current_data.n_q_points_face.reserve (n_hp_quads);
+ current_data.quadrature_weights.resize (n_hp_quads);
+ std::vector<unsigned int> n_q_points_1d (n_hp_quads),
+ step_size_cartesian (n_hp_quads);
+ if (n_hp_quads > 1)
+ current_data.quad_index_conversion.resize(n_hp_quads);
+ for (unsigned int q=0; q<n_hp_quads; ++q)
+ {
+ n_q_points_1d[q] = quad[my_q][q].size();
+ const unsigned int n_q_points =
+ Utilities::fixed_power<dim>(n_q_points_1d[q]);
+ current_data.n_q_points.push_back (n_q_points);
+
+ current_data.n_q_points_face.push_back
+ (Utilities::fixed_power<dim-1>(n_q_points_1d[q]));
+ current_data.quadrature_formula.push_back
+ (Quadrature<dim>(quad[my_q][q]));
+ current_data.quadrature_formula_faces.push_back
+ (Quadrature<dim-1>(quad[my_q][q]));
+
+ // set quadrature weights in vectorized form
+ current_data.quadrature_weights[q].resize(n_q_points);
+ for (unsigned int i=0; i<n_q_points; ++i)
+ current_data.quadrature_weights[q][i] =
+ current_data.quadrature_formula[q].get_weights()[i];
+
+ if (n_hp_quads > 1)
+ current_data.quad_index_conversion[q] = n_q_points;
+
+ // To walk on the diagonal for lexicographic
+ // ordering, we have to jump one index ahead
+ // in each direction. For direction 0, this is
+ // just the next point, for direction 1, it
+ // means adding n_q_points_1d, and so on.
+ step_size_cartesian[q] = 0;
+ unsigned int factor = 1;
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ step_size_cartesian[q] += factor;
+ factor *= n_q_points_1d[q];
+ }
+ }
+
+ // if there are no cells, there is nothing to
+ // do
+ if (cells.size() == 0)
+ continue;
+
+ tensor3 jac_grad, grad_jac_inv;
+ tensor1 tmp;
+
+ // encodes the cell types of the current
+ // cell. Since several cells must be
+ // considered together, this variable holds
+ // the individual info of the last chunk of
+ // cells
+ unsigned int cell_t [n_vectors], cell_t_prev [n_vectors];
+ for (unsigned int j=0; j<n_vectors; ++j)
+ cell_t_prev[j] = numbers::invalid_unsigned_int;
+
+ // fe_values object that is used to compute
+ // the mapping data. for the hp case there
+ // might be more than one finite
+ // element. since we manually select the
+ // active FE index and not via a
+ // hp::DoFHandler<dim>::active_cell_iterator,
+ // we need to manually select the correct
+ // finite element, so just hold a vector of
+ // FEValues
+ std::vector<std_cxx1x::shared_ptr<FEValues<dim> > >
+ fe_values (current_data.quadrature_formula.size());
+ UpdateFlags update_flags_feval =
+ (update_flags & update_inverse_jacobians ? update_jacobians : update_default) |
+ (update_flags & update_jacobian_grads ? update_jacobian_grads : update_default) |
+ (update_flags & update_quadrature_points ? update_quadrature_points : update_default);
+
+ // resize the fields that have fixed size or
+ // for which we know something from an earlier
+ // loop
+ current_data.rowstart_q_points.resize (n_macro_cells+1);
+ if (my_q > 0)
+ {
+ const unsigned int n_cells_var =
+ mapping_data_gen[0].rowstart_jacobians.size()-1;
+ current_data.rowstart_jacobians.reserve (n_cells_var+1);
+ const unsigned int reserve_size = n_cells_var *
+ current_data.n_q_points[0];
+ if (mapping_data_gen[0].jacobians.size() > 0)
+ current_data.jacobians.reserve (reserve_size);
+ if (mapping_data_gen[0].JxW_values.size() > 0)
+ current_data.jacobians.reserve (reserve_size);
+ if (mapping_data_gen[0].jacobians_grad_diag.size() > 0)
+ current_data.jacobians_grad_diag.reserve (reserve_size);
+ if (mapping_data_gen[0].jacobians_grad_upper.size() > 0)
+ current_data.jacobians_grad_upper.reserve (reserve_size);
+ }
+
+ // a hash structure that is used to detect
+ // similarities between mapping data from one
+ // cell to the next.
+ std::vector<std::pair<unsigned int, int> > hash_collection;
+ internal::HashValue hash_value (jacobian_size);
+
+ // loop over all cells
+ for (unsigned int cell=0; cell<n_macro_cells; ++cell)
+ {
+ // GENERAL OUTLINE: First generate the data in
+ // format "number" for n_vectors cells, and
+ // then find the most general type of cell for
+ // appropriate vectorized formats. then fill
+ // this data in
+ const unsigned int fe_index = active_fe_index.size() > 0 ?
+ active_fe_index[cell] : 0;
+ const unsigned int n_q_points = current_data.n_q_points[fe_index];
+ if (fe_values[fe_index].get() == 0)
+ fe_values[fe_index].reset
+ (new FEValues<dim> (mapping, dummy_fe,
+ current_data.quadrature_formula[fe_index],
+ update_flags_feval));
+ FEValues<dim> &fe_val = *fe_values[fe_index];
+ data.resize (n_q_points);
+
+ // if the fe index has changed from the
+ // previous cell, set the old cell type to
+ // invalid (otherwise, we might detect
+ // similarity due to some cells further ahead)
+ if (cell > 0 && active_fe_index.size() > 0 &&
+ active_fe_index[cell] != active_fe_index[cell-1])
+ cell_t_prev[n_vectors-1] = numbers::invalid_unsigned_int;
+ evaluate_on_cell (tria, &cells[cell*n_vectors],
+ cell, my_q, cell_t_prev, cell_t, fe_val, data);
+
+ // now reorder the data into vectorized
+ // types. if we are here for the first time,
+ // we need to find out whether the Jacobian
+ // allows for some simplification (Cartesian,
+ // linear) taking n_vectors cell together and
+ // we have to insert that data into the
+ // respective fields. Also, we have to
+ // compress different cell indicators into one
+ // structure.
+
+ if (my_q == 0)
+ {
+ // find the most general cell type (most
+ // general type is 2 (general cell))
+ unsigned int most_general_type = 0;
+ for (unsigned int j=0; j<n_vectors; ++j)
+ if (cell_t[j] > most_general_type)
+ most_general_type = cell_t[j];
+ AssertIndexRange (most_general_type, 3);
+
+ // Cartesian cell with diagonal Jacobian: only
+ // insert the diagonal of the inverse and the
+ // Jacobian determinant
+ unsigned int insert_position = numbers::invalid_unsigned_int;
+ typedef std::vector<std::pair<unsigned int,int> >::iterator iter;
+ if (most_general_type == 0)
+ {
+ std::pair<tensor1,vector_t> new_entry;
+ for (unsigned int d=0; d<dim; ++d)
+ new_entry.first[d] = data.const_jac[d][d];
+ insert_position = cartesian.size();
+
+ // check whether everything is the same as on
+ // another cell before. find an insertion point
+ // in the list of keys that we have
+ // collected. put negative position so that
+ // the insertion position from lower_bound is
+ // that with the same key
+ const unsigned int hash = hash_value.template operator()<dim,Number> (data.const_jac, true);
+ std::pair<unsigned int,int> insertion (hash, -insert_position);
+ iter pos = std::lower_bound (hash_collection.begin(),
+ hash_collection.end(),
+ insertion);
+
+ // ok, found a data field with the same
+ // key. check whether we really hit a
+ // duplicate, i.e., whether the hash really
+ // was effective
+ bool duplicate = true;
+ if (pos != hash_collection.end() &&
+ pos->first == hash)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ if (std::fabs(data.const_jac[d][d][j]-
+ cartesian[-pos->second].first[d][j])>
+ hash_value.scaling)
+ duplicate = false;
+ }
+ else
+ duplicate = false;
+
+ // if no duplicate, insert the data
+ if (duplicate == false)
+ {
+ hash_collection.insert (pos, insertion);
+ cartesian.push_back (new_entry);
+ }
+ // else, remember the position
+ else
+ insert_position = -pos->second;
+ }
+
+ // Constant Jacobian case. same strategy as
+ // before, but with other data fields
+ else if (most_general_type == 1)
+ {
+ insert_position = linear.size();
+
+ // check whether everything is the same as on
+ // the previous cell
+ const unsigned int hash = hash_value.template operator()<dim,Number> (data.const_jac, false);
+ std::pair<unsigned int,int> insertion (hash, -insert_position);
+ iter pos = std::lower_bound (hash_collection.begin(),
+ hash_collection.end(),
+ insertion);
+
+ // ok, found a data field with the same
+ // key. check whether we really hit a
+ // duplicate
+ bool duplicate = true;
+ if (pos != hash_collection.end() &&
+ pos->first == hash)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ if (std::fabs(data.const_jac[d][e][j]-
+ linear[-pos->second].first[d][e][j])>
+ hash_value.scaling)
+ duplicate = false;
+ }
+ else
+ duplicate = false;
+
+ if (duplicate == false)
+ {
+ hash_collection.insert (pos, insertion);
+ linear.push_back (std::pair<tensor2,vector_t>(data.const_jac,
+ make_vectorized_array (Number(0.))));
+ }
+ else
+ insert_position = -pos->second;
+ }
+
+ // general cell case: first resize the data
+ // field to fit the new data. if we are here
+ // the first time, assume that there are many
+ // general cells to come, so reserve some
+ // memory in order to not have too many
+ // reallocations and memcpy's. The scheme used
+ // here involves at most one reallocation.
+ else
+ {
+ Assert (most_general_type == 2, ExcInternalError());
+ insert_position = current_data.rowstart_jacobians.size();
+ if (current_data.rowstart_jacobians.size() == 0)
+ {
+ unsigned int reserve_size = (n_macro_cells-cell+1)/2;
+ current_data.rowstart_jacobians.reserve
+ (reserve_size);
+ reserve_size *= n_q_points;
+ current_data.jacobians.reserve (reserve_size);
+ if (update_flags & update_JxW_values)
+ current_data.JxW_values.reserve (reserve_size);
+ if (update_flags & update_jacobian_grads)
+ current_data.jacobians_grad_diag.reserve (reserve_size);
+ if (update_flags & update_jacobian_grads)
+ current_data.jacobians_grad_upper.reserve (reserve_size);
+ }
+ }
+
+ cell_type[cell] = ((insert_position << n_cell_type_bits) +
+ most_general_type);
+
+ } // end if (my_q == 0)
+
+ // general cell case: now go through all
+ // quadrature points and collect the
+ // data. done for all different quadrature
+ // formulas, so do it outside the above loop.
+ if (get_cell_type(cell) == 2)
+ {
+ const unsigned int previous_size =
+ current_data.jacobians.size();
+ current_data.rowstart_jacobians.push_back (previous_size);
+ if (update_flags & update_JxW_values)
+ {
+ AssertDimension (previous_size,
+ current_data.JxW_values.size());
+ }
+ if (update_flags & update_jacobian_grads)
+ {
+ AssertDimension (previous_size,
+ current_data.jacobians_grad_diag.size());
+ AssertDimension (previous_size,
+ current_data.jacobians_grad_upper.size());
+ }
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ tensor2 &jac = data.general_jac[q];
+ tensor3 &jacobian_grad = data.general_jac_grad[q];
+ for (unsigned int j=0; j<n_vectors; ++j)
+ if (cell_t[j] < 2)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ {
+ jac[d][e][j] = data.const_jac[d][e][j];
+ for (unsigned int f=0; f<dim; ++f)
+ jacobian_grad[d][e][f][j] = 0.;
+ }
+ }
+
+ const vector_t det = determinant (jac);
+ current_data.jacobians.push_back (transpose(invert(jac)));
+ const tensor2 &inv_jac = current_data.jacobians.back();
+
+ // TODO: deal.II does not use abs on
+ // determinants. Is there an assumption
+ // somewhere that negative determinants are
+ // not allowed?
+ if (update_flags & update_JxW_values)
+ current_data.JxW_values.push_back
+ (std::abs(det) * current_data.quadrature_weights[fe_index][q]);
+
+ if (update_flags & update_jacobian_grads)
+ {
+ // for second derivatives on the real cell,
+ // need also the gradient of the inverse
+ // Jacobian J. This involves some calculus and
+ // is done vectorized. This is very cheap
+ // compared to what fe_values does (in early
+ // 2011). If L is the gradient of the jacobian
+ // on the unit cell, the gradient of the
+ // inverse is given by (multidimensional
+ // calculus) - J * (J * L) * J (the third J is
+ // because we need to transform the gradient L
+ // from the unit to the real cell, and then
+ // apply the inverse Jacobian). Compare this
+ // with 1D with j(x) = 1/k(phi(x)), where j =
+ // phi' is the inverse of the jacobian and k
+ // is the derivative of the jacobian on the
+ // unit cell. Then j' = phi' k'/k^2 = j k'
+ // j^2.
+
+ // compute: jac_grad = J*grad_unit(J^-1)
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ for (unsigned int f=0; f<dim; ++f)
+ {
+ jac_grad[f][e][d] = (inv_jac[f][0] *
+ jacobian_grad[d][e][0]);
+ for (unsigned int g=1; g<dim; ++g)
+ jac_grad[f][e][d] += (inv_jac[f][g] *
+ jacobian_grad[d][e][g]);
+ }
+
+ // compute: transpose (-jac * jac_grad[d] * jac)
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ {
+ for (unsigned int f=0; f<dim; ++f)
+ {
+ tmp[f] = vector_t();
+ for (unsigned int g=0; g<dim; ++g)
+ tmp[f] -= jac_grad[d][f][g] * inv_jac[g][e];
+ }
+
+ // needed for non-diagonal part of Jacobian
+ // grad
+ for (unsigned int f=0; f<dim; ++f)
+ {
+ grad_jac_inv[f][d][e] = inv_jac[f][0] * tmp[0];
+ for (unsigned int g=1; g<dim; ++g)
+ grad_jac_inv[f][d][e] += inv_jac[f][g] * tmp[g];
+ }
+ }
+
+ {
+ vector_t grad_diag[dim][dim];
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ grad_diag[d][e] = grad_jac_inv[d][d][e];
+ current_data.jacobians_grad_diag.push_back
+ (Tensor<2,dim,vector_t>(grad_diag));
+ }
+
+ // sets upper-diagonal part of Jacobian
+ tensorUT grad_upper;
+ for (unsigned int d=0, count=0; d<dim; ++d)
+ for (unsigned int e=d+1; e<dim; ++e, ++count)
+ for (unsigned int f=0; f<dim; ++f)
+ grad_upper[count][f] = grad_jac_inv[d][e][f];
+ current_data.jacobians_grad_upper.push_back(grad_upper);
+ }
+ }
+ }
+
+ if (update_flags & update_quadrature_points)
+ {
+ // eventually we turn to the quadrature points
+ // that we can compress in case we have
+ // Cartesian cells. we also need to reorder
+ // them into arrays of vectorized data types.
+ // first go through the cells and find out how
+ // much memory we need to allocate for the
+ // quadrature points. We store n_q_points for
+ // all cells but Cartesian cells. For
+ // Cartesian cells, only need to store the
+ // values on a diagonal through the cell
+ // (n_q_points_1d). This will give (with some
+ // little indexing) the location of all
+ // quadrature points.
+ const unsigned int old_size =
+ current_data.quadrature_points.size();
+ current_data.rowstart_q_points[cell] = old_size;
+
+ tensor1 quad_point;
+
+ if (get_cell_type(cell) == 0)
+ {
+ current_data.quadrature_points.resize (old_size+
+ n_q_points_1d[fe_index]);
+ for (unsigned int q=0; q<n_q_points_1d[fe_index]; ++q)
+ for (unsigned int d=0; d<dim; ++d)
+ current_data.quadrature_points[old_size+q][d] =
+ data.quadrature_points[q*step_size_cartesian[fe_index]][d];
+ }
+ else
+ {
+ current_data.quadrature_points.resize (old_size + n_q_points);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ for (unsigned int d=0; d<dim; ++d)
+ current_data.quadrature_points[old_size+q][d] =
+ data.quadrature_points[q][d];
+ }
+ }
+ } // end for ( cell < n_macro_cells )
+ current_data.rowstart_jacobians.push_back
+ (current_data.jacobians.size());
+ current_data.rowstart_q_points[n_macro_cells] =
+ current_data.quadrature_points.size();
+
+ // finally, need to invert and transpose the
+ // Jacobians in the cartesian and linear
+ // fields and compute the JxW value.
+ if (my_q == 0)
+ {
+ for (unsigned int i=0; i<cartesian.size(); ++i)
+ {
+ vector_t det = cartesian[i].first[0];
+ for (unsigned int d=1; d<dim; ++d)
+ det *= cartesian[i].first[d];
+ for (unsigned int d=0; d<dim; ++d)
+ cartesian[i].first[d] = 1./cartesian[i].first[d];
+ cartesian[i].second = std::abs(det);
+ }
+ for (unsigned int i=0; i<linear.size(); ++i)
+ {
+ vector_t det = determinant(linear[i].first);
+ linear[i].first = transpose(invert(linear[i].first));
+ linear[i].second = std::abs(det);
+ }
+ }
+ }
+ }
+
+
+
+ template<int dim, typename Number>
+ void
+ MappingInfo<dim,Number>::evaluate_on_cell (const dealii::Triangulation<dim> &tria,
+ const std::pair<unsigned int,unsigned int> *cells,
+ const unsigned int cell,
+ const unsigned int my_q,
+ unsigned int (&cell_t_prev)[n_vectors],
+ unsigned int (&cell_t)[n_vectors],
+ FEValues<dim,dim> &fe_val,
+ CellData &data) const
+ {
+ const unsigned int n_q_points = fe_val.n_quadrature_points;
+ const UpdateFlags update_flags = fe_val.get_update_flags();
+
+ // this should be the same value as
+ // used in HashValue::scaling (but we
+ // not have that field here)
+ const double zero_tolerance_double = data.jac_size *
+ std::numeric_limits<double>::epsilon() * 1024.;
+ for (unsigned int j=0; j<n_vectors; ++j)
+ {
+ typename dealii::Triangulation<dim>::cell_iterator
+ cell_it (&tria, cells[j].first, cells[j].second);
+ fe_val.reinit(cell_it);
+ cell_t[j] = numbers::invalid_unsigned_int;
+
+ // extract quadrature points and store them
+ // temporarily. if we have Cartesian cells, we
+ // can compress the indices
+ if (update_flags & update_quadrature_points)
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ const Point<dim> &point = fe_val.quadrature_point(q);
+ for (unsigned int d=0; d<dim; ++d)
+ data.quadrature_points[q][d][j] = point[d];
+ }
+
+ // if this is not the first quadrature formula
+ // and we already have determined that this
+ // cell is either Cartesian or with constant
+ // Jacobian, we have nothing more to do.
+ if (my_q > 0 && get_cell_type(cell) < 2)
+ continue;
+
+ // first round: if the transformation is
+ // detected to be the same as on the old cell,
+ // we only need to copy over the data.
+ if (fe_val.get_cell_similarity() == CellSimilarity::translation
+ &&
+ my_q == 0)
+ {
+ if (j==0)
+ {
+ Assert (cell>0, ExcInternalError());
+ cell_t[j] = cell_t_prev[n_vectors-1];
+ }
+ else
+ cell_t[j] = cell_t[j-1];
+ }
+
+ const DerivativeForm<1,dim,dim> & jac_0 = fe_val.jacobian(0);
+
+ if (my_q == 0)
+ {
+ // check whether the Jacobian is constant on
+ // this cell the first time we come around
+ // here
+ if (cell_t[j] == numbers::invalid_unsigned_int)
+ {
+ bool jacobian_constant = true;
+ for (unsigned int q=1; q<n_q_points; ++q)
+ {
+ const DerivativeForm<1,dim,dim> &jac = fe_val.jacobian(q);
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ if (std::fabs(jac_0[d][e]-jac[d][e]) >
+ zero_tolerance_double)
+ jacobian_constant = false;
+ if (jacobian_constant == false)
+ break;
+ }
+
+ // check whether the Jacobian is diagonal to
+ // machine accuracy
+ bool cell_cartesian = jacobian_constant;
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ if (d!=e)
+ if (std::fabs(jac_0[d][e]) >
+ zero_tolerance_double)
+ {
+ cell_cartesian=false;
+ break;
+ }
+
+ // in case we have only one quadrature point,
+ // we can have non-constant Jacobians, but we
+ // cannot detect it by comparison from one
+ // quadrature point to the next: in that case,
+ // need to look at second derivatives and see
+ // whether there are some non-zero entries
+ // (this is necessary since we determine the
+ // constness of the Jacobian for the first
+ // quadrature formula and might not look at
+ // them any more for the second, third
+ // quadrature formula
+ if (cell_cartesian == false && n_q_points == 1)
+ {
+ Assert (update_flags & update_jacobian_grads,
+ ExcInternalError());
+ const DerivativeForm<1,dim,dim> &jac = fe_val.jacobian(0);
+ const DerivativeForm<2,dim,dim> &jacobian_grad =
+ fe_val.jacobian_grad(0);
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ for (unsigned int f=0; f<dim; ++f)
+ {
+ double jac_grad_comp = (jac[f][0] *
+ jacobian_grad[d][e][0]);
+ for (unsigned int g=1; g<dim; ++g)
+ jac_grad_comp += (jac[f][g] *
+ jacobian_grad[d][e][g]);
+ if (std::fabs(jac_grad_comp) >
+ zero_tolerance_double)
+ jacobian_constant = false;
+ }
+ }
+ // set cell type
+ if (cell_cartesian == true)
+ cell_t[j] = 0;
+ else if (jacobian_constant == true)
+ cell_t[j] = 1;
+ else
+ cell_t[j] = 2;
+ }
+
+ // Cartesian cell
+ if (cell_t[j] == 0)
+ {
+ // set Jacobian into diagonal and clear
+ // off-diagonal part
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ data.const_jac[d][d][j] = jac_0[d][d];
+ for (unsigned int e=d+1; e<dim; ++e)
+ {
+ data.const_jac[d][e][j] = 0.;
+ data.const_jac[e][d][j] = 0.;
+ }
+ }
+ continue;
+ }
+
+ // cell with linear mapping
+ else if (cell_t[j] == 1)
+ {
+ // compress out very small values
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ data.const_jac[d][e][j] =
+ std::fabs(jac_0[d][e]) < zero_tolerance_double ?
+ 0 : jac_0[d][e];
+ continue;
+ }
+ }
+
+ // general cell case
+
+ // go through all quadrature points and fill
+ // in the data into the temporary data
+ // structures with slots for the vectorized
+ // data types
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+
+ // compress out very small numbers which are
+ // only noise. Then it is cleaner to use zero
+ // straight away (though it does not save any
+ // memory)
+ const DerivativeForm<1,dim,dim> &jac = fe_val.jacobian(q);
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ data.general_jac[q][d][e][j] =
+ std::fabs(jac[d][e]) < zero_tolerance_double ? 0. : jac[d][e];
+
+ // need to do some calculus based on the
+ // gradient of the Jacobian, in order to find
+ // the gradient of the inverse Jacobian which
+ // is needed in user code. however, we would
+ // like to perform that on vectorized data
+ // types instead of doubles or floats. to this
+ // end, copy the gradients first
+ if (update_flags & update_jacobian_grads)
+ {
+ const DerivativeForm<2,dim,dim> &jacobian_grad = fe_val.jacobian_grad(q);
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ for (unsigned int f=0; f<dim; ++f)
+ data.general_jac_grad[q][d][e][f][j] = jacobian_grad[d][e][f];
+ }
+ }
+ } // end loop over all entries in vectorization (n_vectors cells)
+
+ // set information for next cell
+ for (unsigned int j=0; j<n_vectors; ++j)
+ cell_t_prev[j] = cell_t[j];
+ }
+
+
+
+
+ template <int dim, typename Number>
+ std::size_t MappingInfo<dim,Number>::MappingInfoDependent::memory_consumption() const
+ {
+ std::size_t
+ memory = MemoryConsumption::memory_consumption (jacobians);
+ memory += MemoryConsumption::memory_consumption (JxW_values);
+ memory += MemoryConsumption::memory_consumption (jacobians_grad_diag);
+ memory += MemoryConsumption::memory_consumption (jacobians_grad_upper);
+ memory += MemoryConsumption::memory_consumption (rowstart_q_points);
+ memory += MemoryConsumption::memory_consumption (quadrature_points);
+ memory += MemoryConsumption::memory_consumption (quadrature_formula);
+ memory += MemoryConsumption::memory_consumption (quadrature_formula_faces);
+ memory += MemoryConsumption::memory_consumption (quadrature_weights);
+ memory += MemoryConsumption::memory_consumption (n_q_points);
+ memory += MemoryConsumption::memory_consumption (n_q_points_face);
+ memory += MemoryConsumption::memory_consumption (quad_index_conversion);
+ return memory;
+ }
+
+
+
+ template <int dim, typename Number>
+ std::size_t MappingInfo<dim,Number>::memory_consumption() const
+ {
+ std::size_t
+ memory= MemoryConsumption::memory_consumption (mapping_data_gen);
+ memory += MemoryConsumption::memory_consumption (linear);
+ memory += MemoryConsumption::memory_consumption (cartesian);
+ memory += MemoryConsumption::memory_consumption (cell_type);
+ memory += sizeof (this);
+ return memory;
+ }
+
+
+
+ template <int dim, typename Number>
+ template <typename STREAM>
+ void MappingInfo<dim,Number>::MappingInfoDependent::print_memory_consumption
+ (STREAM &out,
+ const SizeInfo &size_info) const
+ {
+ // print_mem involves global communication, so
+ // we can disable the check here only if no
+ // processor has any such data
+#if DEAL_II_COMPILER_SUPPORTS_MPI
+ unsigned int general_size_glob = 0, general_size_loc = jacobians.size();
+ MPI_Allreduce (&general_size_loc, &general_size_glob, 1, MPI_UNSIGNED,
+ MPI_MAX, size_info.communicator);
+#else
+ unsigned int general_size_glob = jacobians.size();
+#endif
+ if (general_size_glob > 0)
+ {
+ out << " Memory Jacobian data: ";
+ size_info.print_mem (out,
+ MemoryConsumption::memory_consumption (jacobians)
+ +
+ MemoryConsumption::memory_consumption (JxW_values));
+ out << " Memory second derivative data: ";
+ size_info.print_mem (out,
+ MemoryConsumption::memory_consumption (jacobians_grad_diag)
+ +
+ MemoryConsumption::memory_consumption (jacobians_grad_upper));
+ }
+
+#if DEAL_II_COMPILER_SUPPORTS_MPI
+ unsigned int quad_size_glob = 0, quad_size_loc = quadrature_points.size();
+ MPI_Allreduce (&quad_size_loc, &quad_size_glob, 1, MPI_UNSIGNED,
+ MPI_MAX, size_info.communicator);
+#else
+ unsigned int quad_size_glob = quadrature_points.size();
+#endif
+ if (quad_size_glob > 0)
+ {
+ out << " Memory quadrature points: ";
+ size_info.print_mem (out,
+ MemoryConsumption::memory_consumption (rowstart_q_points)
+ +
+ MemoryConsumption::memory_consumption (quadrature_points));
+ }
+ }
+
+
+
+ template <int dim, typename Number>
+ template <typename STREAM>
+ void MappingInfo<dim,Number>::print_memory_consumption(STREAM &out,
+ const SizeInfo &size_info) const
+ {
+ out << " Cell types: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (cell_type));
+ out << " Memory transformations compr: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (linear) +
+ MemoryConsumption::memory_consumption (cartesian));
+ for (unsigned int j=0; j<mapping_data_gen.size(); ++j)
+ {
+ out << " Data component " << j << std::endl;
+ mapping_data_gen[j].print_memory_consumption(out, size_info);
+ }
+ }
+
+} // end of namespace MatrixFreeFunctions
+} // end of namespace internal
+
+
+DEAL_II_NAMESPACE_CLOSE
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011, 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __deal2__matrix_free_h
+#define __deal2__matrix_free_h
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/parallel.h>
+#include <deal.II/base/quadrature.h>
+#include <deal.II/base/vectorization.h>
+#include <deal.II/fe/fe.h>
+#include <deal.II/fe/mapping.h>
+#include <deal.II/fe/mapping_q1.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/lac/parallel_vector.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/multigrid/mg_dof_handler.h>
+#include <deal.II/hp/dof_handler.h>
+#include <deal.II/hp/q_collection.h>
+#include <deal.II/matrix_free/helper_functions.h>
+#include <deal.II/matrix_free/fe_evaluation_data.h>
+#include <deal.II/matrix_free/dof_info.h>
+#include <deal.II/matrix_free/mapping_info.h>
+
+#if DEAL_II_USE_MT==1
+#include <tbb/task.h>
+#include <tbb/task_scheduler_init.h>
+#include <tbb/parallel_for.h>
+#include <tbb/blocked_range.h>
+#endif
+
+#include <stdlib.h>
+#include <memory>
+#include <limits>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+
+/**
+ * This class collects all the data that is stored for the matrix free
+ * implementation. The storage scheme is tailored towards several
+ * loops performed with the same data, i.e., typically doing many
+ * matrix-vector products or residual computations on the same
+ * mesh. The class is used in step-37 and step-48.
+ *
+ * The stored data can be subdivided into three main components:
+ *
+ * - DoFInfo: It stores how local degrees of freedom relate to global degrees
+ * of freedom. It includes a description of constraints that are evaluated
+ * as going through all local degrees of freedom on a cell.
+ *
+ * - MappingInfo: It stores the transformations from real to unit cells that
+ * are necessary in order to build derivatives of finite element functions
+ * and find location of quadrature weights in physical space.
+ *
+ * - FEEvaluationData: It contains the shape functions of the finite element,
+ * evaluated on the unit cell.
+ *
+ * Besides the initialization routines, this class implements only a
+ * single operation, namely a loop over all cells (cell_loop()). This
+ * loop is scheduled in such a way that cells that share degrees of
+ * freedom are not worked on simultaneously, which implies that it is
+ * possible to write to vectors (or matrices) in parallel without
+ * having to explicitly synchronize access to these vectors and
+ * matrices. This class does not implement any shape values, all it
+ * does is to cache the respective data. To implement finite element
+ * operations, use the class FEEvaluation (or some of the related
+ * classes).
+ *
+ * This class traverses the cells in a different order than the usual
+ * Triangulation class in deal.II, in order to be flexible with respect to
+ * parallelization in shared memory and vectorization.
+ *
+ * Vectorization is implemented by merging several topological cells into one
+ * so-called macro cell. This enables the application of all cell-related
+ * operations for several cells with one CPU instruction and is one of the
+ * main features of this framework.
+ *
+ * @author Katharina Kormann, Martin Kronbichler, 2010, 2011
+ */
+
+template <int dim, typename Number=double>
+class MatrixFree
+{
+public:
+
+ /**
+ * Collects the options for initialization of
+ * the MatrixFree class. The first
+ * parameter specifies the MPI communicator to
+ * be used, the second the parallelization
+ * options in shared memory (task-based
+ * parallelism, where one can choose between
+ * no parallelism and three schemes that avoid
+ * that cells with access to the same vector
+ * entries are accessed simultaneously), the
+ * third with the block size for task parallel
+ * scheduling, the fourth the update flags
+ * that should be stored by this class.
+ *
+ * The fifth parameter specifies the level in
+ * the triangulation from which the indices
+ * are to be used. If the level is set to
+ * numbers::invalid_unsigned_int, the active
+ * cells are traversed, and otherwise the
+ * cells in the given level. This option has
+ * no effect in case a DoFHandler or
+ * hp::DoFHandler is given.
+ *
+ * The parameter @p initialize_plain_indices
+ * indicates whether the DoFInfo class should
+ * also allow for access to vectors without
+ * resolving constraints.
+ *
+ * The last two parameters allow the user to
+ * disable some of the initialization
+ * processes. For example, if only the
+ * scheduling that avoids touching the same
+ * vector/matrix indices simultaneously is to
+ * be found, the mapping needs not be
+ * initialized. Likewise, if the mapping has
+ * changed from one iteration to the next but
+ * the topology has not (like when using a
+ * deforming mesh with MappingQEulerian), it
+ * suffices to initialize the mapping only.
+ */
+ struct AdditionalData
+ {
+ /**
+ * Collects options for task parallelism.
+ */
+ enum TasksParallelScheme {none, partition_partition, partition_color, color};
+
+ /**
+ * Constructor for AdditionalData.
+ */
+ AdditionalData (const MPI_Comm mpi_communicator = MPI_COMM_SELF,
+ const TasksParallelScheme tasks_parallel_scheme = partition_partition,
+ const unsigned int tasks_block_size = 0,
+ const UpdateFlags mapping_update_flags = update_gradients | update_JxW_values,
+ const unsigned int level_mg_handler = numbers::invalid_unsigned_int,
+ const bool store_plain_indices = true,
+ const bool initialize_indices = true,
+ const bool initialize_mapping = true)
+ :
+ mpi_communicator (mpi_communicator),
+ tasks_parallel_scheme (tasks_parallel_scheme),
+ tasks_block_size (tasks_block_size),
+ mapping_update_flags (mapping_update_flags),
+ level_mg_handler (level_mg_handler),
+ store_plain_indices (store_plain_indices),
+ initialize_indices (initialize_indices),
+ initialize_mapping (initialize_mapping)
+ {};
+
+ /**
+ * Sets the MPI communicator that the parallel
+ * layout of the operator should be based
+ * upon. Defaults to MPI_COMM_SELF, but should
+ * be set to a communicator similar to the one
+ * used for a distributed triangulation in
+ * order to inform this class over all cells
+ * that are present.
+ */
+ MPI_Comm mpi_communicator;
+
+ /**
+ * Sets the scheme for task parallelism. There
+ * are four options available. If set to @p
+ * none, the operator application is done in
+ * serial without shared memory
+ * parallelism. If this class is used together
+ * with MPI and MPI is also used for
+ * parallelism within the nodes, this flag
+ * should be set to @p none. The default value
+ * is @p partition_partition, i.e. we actually
+ * use multithreading with the first option
+ * described below.
+ *
+ * The first option @p partition_partition is
+ * to partition the cells on two levels in
+ * onion-skin-like partitions and forming
+ * chunks of tasks_block_size after the
+ * partitioning. The partitioning finds sets
+ * of independent cells that enable working in
+ * parallel without accessing the same vector
+ * entries at the same time.
+ *
+ * The second option @p partition_color is to
+ * use a partition on the global level and
+ * color cells within the partitions (where
+ * all chunks within a color are
+ * independent). Here, the subdivision into
+ * chunks of cells is done before the
+ * partitioning, which might give worse
+ * partitions but better cache performance if
+ * degrees of freedom are not renumbered.
+ *
+ * The third option @p color is to use a
+ * traditional algorithm of coloring on the
+ * global level. This scheme is a special case
+ * of the second option where only one
+ * partition is present. Note that for
+ * problems with hanging nodes, there are
+ * quite many colors (50 or more in 3D), which
+ * might degrade parallel performance (bad
+ * cache behavior, many synchronization
+ * points).
+ */
+ TasksParallelScheme tasks_parallel_scheme;
+
+ /**
+ * Sets the number of so-called macro cells
+ * that should form one partition. If zero
+ * size is given, the class tries to find a
+ * good size for the blocks based on the
+ * number of threads stored in
+ * multithread_info.n_default_threads and the
+ * number of cells present. Otherwise, the
+ * given number is used. If the given number
+ * is larger than one third of the number of
+ * total cells, this means no
+ * parallelism. Note that in the case
+ * vectorization is used, a macro cell
+ * consists of more than one physical cell.
+ */
+ unsigned int tasks_block_size;
+
+ /**
+ * This flag is used to determine which
+ * quantities should be cached. This class can
+ * cache data needed for gradient computations
+ * (inverse Jacobians), Jacobian determinants
+ * (JxW), quadrature points as well as data
+ * for Hessians (derivative of Jacobians). By
+ * default, only data for gradients and
+ * Jacobian determinants times quadrature
+ * weights, JxW, are cached. If quadrature
+ * points or second derivatives are needed,
+ * they must be specified by this field (even
+ * though second derivatives might still be
+ * evaluated on Cartesian cells without this
+ * option set here, since there the Jacobian
+ * describes the mapping completely).
+ */
+ UpdateFlags mapping_update_flags;
+
+ /**
+ * If working on a MGDoFHandler, this option
+ * can be used to define whether we work on a
+ * certain level of the mesh, and not the
+ * active cells. If set to
+ * invalid_unsigned_int (which is the default
+ * value), the active cells are gone through,
+ * otherwise the level given by this
+ * parameter.
+ */
+ unsigned int level_mg_handler;
+
+ /**
+ * Controls whether to allow reading from
+ * vectors without resolving constraints,
+ * i.e., just read the local values of the
+ * vector. By default, this option is
+ * disabled, so if you want to use
+ * FEEvaluationBase::read_dof_values_plain,
+ * this flag needs to be set.
+ */
+ bool store_plain_indices;
+
+ /**
+ * Option to control whether the indices
+ * stored in the DoFHandler should be read and
+ * the pattern for task parallelism should be
+ * set up in the initialize method of
+ * MatrixFree. Defaults to true. Can be
+ * disabled in case the mapping should be
+ * recomputed (e.g. when using a deforming
+ * mesh described through MappingEulerian) but
+ * the topology of cells has remained the
+ * same.
+ */
+ bool initialize_indices;
+
+ /**
+ * Option to control whether the mapping
+ * information should be computed in the
+ * initialize method of
+ * MatrixFree. Defaults to true. Can be
+ * disabled when only some indices should be
+ * set up (e.g. when only a set of independent
+ * cells should be computed).
+ */
+ bool initialize_mapping;
+ };
+
+ /**
+ * Default empty constructor. Does
+ * nothing.
+ */
+ MatrixFree ();
+
+ /**
+ * Destructor.
+ */
+ ~MatrixFree();
+
+ /**
+ * Extracts the information needed to
+ * perform loops over cells. The
+ * DoFHandler and ConstraintMatrix
+ * describe the layout of degrees of
+ * freedom, the DoFHandler and the
+ * mapping describe the transformations
+ * from unit to real cell, and the finite
+ * element underlying the DoFHandler
+ * together with the quadrature formula
+ * describe the local operations. Note
+ * that the finite element underlying the
+ * DoFHandler must either be scalar or
+ * contain several copies of the same
+ * element. Mixing several different
+ * elements into one FESystem is not
+ * allowed. In that case, use the
+ * initialization function with several
+ * DoFHandler arguments.
+ *
+ * The @p IndexSet @p locally_owned_dofs
+ * is used to specify the parallel
+ * partitioning with MPI. Usually, this
+ * needs not be specified, and the other
+ * initialization function without and @p
+ * IndexSet description can be used,
+ * which gets the partitioning
+ * information builtin into the
+ * DoFHandler.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const Mapping<dim> &mapping,
+ const DH &dof_handler,
+ const ConstraintMatrix &constraint,
+ const IndexSet &locally_owned_dofs,
+ const Quadrature &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as above, but with index set stored in
+ * the DoFHandler for describing the
+ * locally owned degrees of freedom.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const Mapping<dim> &mapping,
+ const DH &dof_handler,
+ const ConstraintMatrix &constraint,
+ const Quadrature &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as above, but with mapping @p
+ * MappingQ1.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const DH &dof_handler,
+ const ConstraintMatrix &constraint,
+ const Quadrature &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Extracts the information needed to
+ * perform loops over cells. The
+ * DoFHandler and ConstraintMatrix
+ * describe the layout of degrees of
+ * freedom, the DoFHandler and the
+ * mapping describe the transformations
+ * from unit to real cell, and the finite
+ * element underlying the DoFHandler
+ * together with the quadrature formula
+ * describe the local operations. As
+ * opposed to the scalar case treated
+ * with the other initialization
+ * functions, this function allows for
+ * problems with two or more different
+ * finite elements. The DoFHandlers to
+ * each element must be passed as
+ * pointers to the initialization
+ * function. Note that the finite element
+ * underlying an DoFHandler must either
+ * be scalar or contain several copies of
+ * the same element. Mixing several
+ * different elements into one @p
+ * FE_System is not allowed.
+ *
+ * This function also allows for using
+ * several quadrature formulas, e.g. when
+ * the description contains independent
+ * integrations of elements of different
+ * degrees. However, the number of
+ * different quadrature formulas can be
+ * sets independently from the number of
+ * DoFHandlers, when several elements are
+ * always integrated with the same
+ * quadrature formula.
+ *
+ * The @p IndexSet @p locally_owned_dofs
+ * is used to specify the parallel
+ * partitioning with MPI. Usually, this
+ * needs not be specified, and the other
+ * initialization function without and @p
+ * IndexSet description can be used,
+ * which gets the partitioning
+ * information from the DoFHandler. This
+ * is the most general initialization
+ * function.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<Quadrature> &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as before, but now the index set
+ * description of the locally owned range
+ * of degrees of freedom is taken from
+ * the DoFHandler.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<Quadrature> &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as above, but with mapping @p
+ * MappingQ1.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<Quadrature> &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as before, but now the index set
+ * description of the locally owned range
+ * of degrees of freedom is taken from
+ * the DoFHandler. Moreover, only a
+ * single quadrature formula is used, as
+ * might be necessary when several
+ * components in a vector-valued problem
+ * are integrated together based on the
+ * same quadrature formula.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const Quadrature &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Initializes the data structures. Same
+ * as above, but with mapping @p
+ * MappingQ1.
+ */
+ template <typename DH, typename Quadrature>
+ void reinit (const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const Quadrature &quad,
+ const AdditionalData additional_data = AdditionalData());
+
+ /**
+ * Copy function. Creates a deep copy of all
+ * data structures. It is usually enough to
+ * keep the data for different operations
+ * once, so this function should not be needed
+ * very often.
+ */
+ void copy_from (const MatrixFree<dim,Number> &matrix_free_base);
+
+ /**
+ * Clears all data fields and brings the class
+ * into a condition similar to after having
+ * called the default constructor.
+ */
+ void clear();
+
+ /**
+ * This method runs the loop over all
+ * cells (in parallel) and performs
+ * the MPI data exchange on the source
+ * vector and destination vector. The
+ * first argument indicates a function
+ * object that has the following
+ * signature: <code>cell_operation
+ * (const MatrixFree<dim,Number>
+ * &, OutVector &, InVector &,
+ * std::pair<unsigned int,unsigned
+ * int> &)</code>, where the first
+ * argument passes the data of the
+ * calling class and the last argument
+ * defines the range of cells which
+ * should be worked on (typically more
+ * than one cell should be worked on
+ * in order to reduce overheads). One
+ * can pass a pointer to an object in
+ * this place if it has an
+ * <code>operator()</code> with the
+ * correct set of arguments since such
+ * a pointer can be converted to the
+ * function object.
+ */
+ template <typename OutVector, typename InVector>
+ void cell_loop (const std_cxx1x::function<void (const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)> &cell_operation,
+ OutVector &dst,
+ const InVector &src) const;
+
+ /**
+ * This is the second variant to run the loop
+ * over all cells, now providing a function
+ * pointer to a member function of class @p
+ * CLASS with the signature
+ * <code>cell_operation (const
+ * MatrixFree<dim,Number> &, OutVector &,
+ * InVector &, std::pair<unsigned int,unsigned
+ * int>&)const</code>. This method obviates the need
+ * to call std_cxx1x::bind to bind the class
+ * into the given function in case the local
+ * function needs to access data in the class
+ * (i.e., it is a non-static member function).
+ */
+ template <typename CLASS, typename OutVector, typename InVector>
+ void cell_loop (void (CLASS::*function_pointer)(const MatrixFree &,
+ OutVector&,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)const,
+ const CLASS *owning_class,
+ OutVector &dst,
+ const InVector &src) const;
+
+ /**
+ * Same as above, but for class member
+ * functions which are non-const.
+ */
+ template <typename CLASS, typename OutVector, typename InVector>
+ void cell_loop (void (CLASS::*function_pointer)(const MatrixFree &,
+ OutVector&,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &),
+ CLASS *owning_class,
+ OutVector &dst,
+ const InVector &src) const;
+
+ /**
+ * Returns an approximation of the memory
+ * consumption of this class in bytes.
+ */
+ std::size_t memory_consumption() const;
+
+ /**
+ * Prints a detailed summary of memory
+ * consumption in the different structures of
+ * this class to the given output stream.
+ */
+ template <typename STREAM>
+ void print_memory_consumption(STREAM &out) const;
+
+ /**
+ * Prints a summary of this class to the given
+ * output stream. It is focused on the
+ * indices, and does not print all the data
+ * stored.
+ */
+ void print (std::ostream &out) const;
+
+ /**
+ * Initialize function for a general
+ * vector. The length of the vector is equal
+ * to the total number of degrees in the
+ * DoFHandler. If the vector is of class
+ * parallel::distributed::Vector<Number>, the ghost
+ * entries are set accordingly. For
+ * vector-valued problems with several
+ * DoFHandlers underlying this class, the
+ * parameter @p vector_component defines which
+ * component is to be used.
+ */
+ template <typename VectorType>
+ void initialize_dof_vector(VectorType &vec,
+ const unsigned int vector_component=0) const;
+
+ /**
+ * Initialize function for a distributed
+ * vector. The length of the vector is equal
+ * to the total number of degrees in the
+ * DoFHandler. If the vector is of class
+ * parallel::distributed::Vector<Number>, the ghost
+ * entries are set accordingly. For
+ * vector-valued problems with several
+ * DoFHandlers underlying this class, the
+ * parameter @p vector_component defines which
+ * component is to be used.
+ */
+ template <typename Number2>
+ void initialize_dof_vector(parallel::distributed::Vector<Number2> &vec,
+ const unsigned int vector_component=0) const;
+
+ /**
+ * Returns the partitioner that represents the
+ * locally owned data and the ghost indices
+ * where access is needed to for the cell
+ * loop.
+ */
+ const std_cxx1x::shared_ptr<const Utilities::MPI::Partitioner>&
+ get_vector_partitioner (const unsigned int vector_component=0) const;
+
+ /**
+ * Returns a list of all degrees of freedom
+ * that are constrained. The list is returned
+ * in local index space for the locally owned
+ * range of the vector, not in global
+ * numbers. In addition, it only returns the
+ * indices for degrees of freedom that are
+ * owned locally, not for ghosts.
+ */
+ const std::vector<unsigned int> &
+ get_constrained_dofs (const unsigned int fe_component = 0) const;
+
+ /**
+ * In the hp adaptive case, a subrange of
+ * cells as computed during the cell loop
+ * might contain elements of different
+ * degrees. Use this function to compute what
+ * the subrange for an individual finite
+ * element degree is. The finite element
+ * degree is associated to the vector
+ * component given in the function call.
+ */
+ std::pair<unsigned int,unsigned int>
+ create_cell_subrange_hp (const std::pair<unsigned int,unsigned int> &range,
+ const unsigned int fe_degree,
+ const unsigned int vector_component = 0) const;
+
+ /**
+ * In the hp adaptive case, a subrange of
+ * cells as computed during the cell loop
+ * might contain elements of different
+ * degrees. Use this function to compute what
+ * the subrange for a given index the hp
+ * finite element, as opposed to the finite
+ * element degree in the other function.
+ */
+ std::pair<unsigned int,unsigned int>
+ create_cell_subrange_hp_by_index (const std::pair<unsigned int,unsigned int> &range,
+ const unsigned int fe_index,
+ const unsigned int vector_component = 0) const;
+ /**
+ * Calls renumber_dofs function in dof
+ * info which renumbers the the
+ * degrees of freedom according to the
+ * ordering for parallelization.
+ */
+ void renumber_dofs (std::vector<unsigned int> &renumbering,
+ const unsigned int vector_component = 0);
+
+ unsigned int n_components () const;
+
+ /**
+ * Returns information on task graph.
+ */
+ const internal::MatrixFreeFunctions::TaskInfo &
+ get_task_info () const;
+
+ /**
+ * Returns information on system size.
+ */
+ const internal::MatrixFreeFunctions::SizeInfo &
+ get_size_info () const;
+
+ /**
+ * Returns the number of cells this structure
+ * is based on. If you are using a usual
+ * DoFHandler, it corresponds to the number of
+ * (locally owned) active cells. Note that
+ * most data structures in this class do not
+ * directly act on this number but rather on
+ * n_macro_cells() which gives the number of
+ * cells as seen when lumping several cells
+ * together with vectorization.
+ */
+ unsigned int n_physical_cells () const;
+
+ /**
+ * Returns the number of macro cells that this
+ * structure works on, i.e., the number of
+ * cell chunks that are worked on after the
+ * application of vectorization which in
+ * general works on several cells at once. The
+ * cell range in @p cell_loop runs from zero
+ * to n_macro_cells() (exclusive), so this is
+ * the appropriate size if you want to store
+ * arrays of data for all cells to be worked
+ * on. This number is approximately
+ * n_physical_cells()/VectorizedArray<Number>::n_array_elements
+ * (depending on how many cells are not filled
+ * up completely).
+ */
+ unsigned int n_macro_cells () const;
+
+ /*
+ * Returns geometry-dependent
+ * information on the cells.
+ */
+
+ const internal::MatrixFreeFunctions::MappingInfo<dim,Number> &
+ get_mapping_info () const;
+
+ /**
+ * Returns information on indexation
+ * degrees of freedom.
+ */
+
+ const internal::MatrixFreeFunctions::DoFInfo &
+ get_dof_info (const unsigned int fe_component = 0) const;
+
+ /*
+ * Returns the constraint pool holding
+ * all the constraints in the mesh.
+ */
+
+ const internal::MatrixFreeFunctions::CompressedMatrix<Number> &
+ get_constraint_pool () const;
+
+ /**
+ * In case this structure was built based on a
+ * DoFHandler, this returns the
+ * DoFHandler. Note that this function returns
+ * an exception in case the structure was
+ * based on MGDoFHandler and a level has been
+ * specified in InitializationOption.
+ */
+ const DoFHandler<dim> &
+ get_dof_handler (const unsigned int fe_component = 0) const;
+
+ /**
+ * In case this structure was built based on a
+ * DoFHandler, this returns the
+ * DoFHandler. Note that this function returns
+ * an exception in case the structure was
+ * based on MGDoFHandler and a level has been
+ * specified in InitializationOption.
+ */
+ const MGDoFHandler<dim> &
+ get_mg_dof_handler (const unsigned int fe_component = 0) const;
+
+ /**
+ * This returns the cell iterator in deal.II
+ * speak to a given cell in the renumbering of
+ * this structure. This function returns an
+ * exception in case the structure was
+ * constructed based on an MGDoFHandler with
+ * level specified, as these cells are in
+ * general not active.
+ *
+ * Note that the cell iterators in deal.II go
+ * through cells differently to what the cell
+ * loop of this class does. This is because
+ * several cells are worked on together
+ * (vectorization), and since cells with
+ * neighbors on different MPI processors need
+ * to be accessed at a certain time when
+ * accessing remote data and overlapping
+ * communication with computation.
+ */
+ typename DoFHandler<dim>::active_cell_iterator
+ get_cell_iterator (const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int fe_component = 0) const;
+
+ /**
+ * This returns the cell iterator in deal.II
+ * speak to a given cell in the renumbering of
+ * this structure. This function returns an
+ * exception in case the structure was
+ * constructed based on a DoFHandler and not
+ * MGDoFHandler.
+ *
+ * Note that the cell iterators in deal.II go
+ * through cells differently to what the cell
+ * loop of this class does. This is because
+ * several cells are worked on together
+ * (vectorization), and since cells with
+ * neighbors on different MPI processors need
+ * to be accessed at a certain time when
+ * accessing remote data and overlapping
+ * communication with computation.
+ */
+ typename MGDoFHandler<dim>::cell_iterator
+ get_mg_cell_iterator (const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int fe_component = 0) const;
+
+ /**
+ * This returns the cell iterator in deal.II
+ * speak to a given cell in the renumbering of
+ * this structure. This function returns an
+ * exception in case the structure was
+ * not constructed based on an hp::DoFHandler.
+ *
+ * Note that the cell iterators in deal.II go
+ * through cells differently to what the cell
+ * loop of this class does. This is because
+ * several cells are worked on together
+ * (vectorization), and since cells with
+ * neighbors on different MPI processors need
+ * to be accessed at a certain time when
+ * accessing remote data and overlapping
+ * communication with computation.
+ */
+ typename hp::DoFHandler<dim>::active_cell_iterator
+ get_hp_cell_iterator (const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int fe_component = 0) const;
+
+ /**
+ * Since this class uses vectorized
+ * data types with usually more than
+ * one value in the data field, a
+ * situation might occur when some
+ * components of the vector type do
+ * not correspond to an actual cell in
+ * the mesh. When using only this
+ * class, one usually does not need to
+ * bother about that fact since the
+ * values are padded with
+ * zeros. However, when this class is
+ * mixed with deal.II access to cells,
+ * care needs to be taken. This
+ * function returns @p true if not all
+ * @p n_vectors cells for the given @p
+ * macro_cell are real cells. To find
+ * out how many cells are actually
+ * used, use the function @p
+ * n_components_filled.
+ */
+ bool
+ at_irregular_cell (const unsigned int macro_cell_number) const;
+
+ /**
+ * Use this function to find out how
+ * many cells over the length of
+ * vectorization data types correspond
+ * to real cells in the mesh. For most
+ * given @p macro_cells, this is just
+ * @p n_vectors many, but there might
+ * be one or a few meshes (where the
+ * numbers do not add up) where there
+ * are less such components filled,
+ * indicated by the function @p
+ * at_irregular_cell.
+ */
+ unsigned int
+ n_components_filled (const unsigned int macro_cell_number) const;
+
+ /**
+ * Returns the number of degrees of
+ * freedom per cell for a given hp
+ * index.
+ */
+ unsigned int
+ get_dofs_per_cell (const unsigned int fe_component = 0,
+ const unsigned int hp_active_fe_index = 0) const;
+
+ /**
+ * Returns the number of quadrature
+ * points per cell for a given hp
+ * index.
+ */
+ unsigned int
+ get_n_q_points (const unsigned int quad_index = 0,
+ const unsigned int hp_active_fe_index = 0) const;
+
+ /**
+ * Returns the number of degrees of
+ * freedom on each face of the cell
+ * for given hp index.
+ */
+ unsigned int
+ get_dofs_per_face (const unsigned int fe_component = 0,
+ const unsigned int hp_active_fe_index = 0) const;
+
+ /**
+ * Returns the number of quadrature
+ * points on each face of the cell for
+ * given hp index.
+ */
+ unsigned int
+ get_n_q_points_face (const unsigned int quad_index = 0,
+ const unsigned int hp_active_fe_index = 0) const;
+
+ /**
+ * Returns the set of cells that are
+ * oned by the processor.
+ */
+ const IndexSet &
+ get_locally_owned_set (const unsigned int fe_component = 0) const;
+
+ /**
+ * Returns the set of ghost cells
+ * needed but not owned by the
+ * processor.
+ */
+ const IndexSet &
+ get_ghost_set (const unsigned int fe_component = 0) const;
+
+ /**
+ * Returns the unit cell information
+ * for given hp index.
+ */
+ const internal::MatrixFreeFunctions::FEEvaluationData<Number> &
+ get_fe_evaluation (const unsigned int fe_component = 0,
+ const unsigned int quad_index = 0,
+ const unsigned int hp_active_fe_index = 0,
+ const unsigned int hp_active_quad_index = 0) const;
+
+ /**
+ * Returns the quadrature rule for
+ * given hp index.
+ */
+ const Quadrature<dim> &
+ get_quad (const unsigned int quad_index = 0,
+ const unsigned int hp_active_fe_index = 0) const;
+
+ /**
+ * Queries whether or not the
+ * indexation has been set.
+ */
+ bool indices_initialized () const;
+
+ /**
+ * Queries whether or not the
+ * geometry-related information for
+ * the cells has been set.
+ */
+
+ bool mapping_initialized () const;
+
+private:
+
+ /**
+ * This is the actual reinit function
+ * that sets up the indices for the
+ * DoFHandler and MGDoFHandler case.
+ */
+ template <typename DoFHandler>
+ void internal_reinit (const Mapping<dim> &mapping,
+ const std::vector<const DoFHandler*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<hp::QCollection<1> > &quad,
+ const AdditionalData additional_data);
+
+ /**
+ * Same as before but for hp::DoFHandler
+ * instead of generic DoFHandler type.
+ */
+ void internal_reinit (const Mapping<dim> &mapping,
+ const std::vector<const hp::DoFHandler<dim>*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<hp::QCollection<1> > &quad,
+ const AdditionalData additional_data);
+
+ /**
+ * Initializes the fields in DoFInfo together
+ * with @p constraint_pool.
+ */
+ void
+ initialize_indices (const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set);
+
+ /**
+ * Initializes the DoFHandlers based on a
+ * DoFHandler<dim> argument.
+ */
+ void initialize_dof_handlers (const std::vector<const DoFHandler<dim>*> &dof_handlers,
+ const unsigned int level);
+
+ /**
+ * Initializes the DoFHandlers based on a
+ * DoFHandler<dim> argument.
+ */
+ void initialize_dof_handlers (const std::vector<const MGDoFHandler<dim>*> &dof_handlers,
+ const unsigned int level);
+
+ /**
+ * Initializes the DoFHandlers based on a
+ * DoFHandler<dim> argument.
+ */
+ void initialize_dof_handlers (const std::vector<const hp::DoFHandler<dim>*> &dof_handlers,
+ const unsigned int level);
+
+ /**
+ * This struct defines which DoFHandler has
+ * actually been given at construction, in
+ * order to define the correct behavior when
+ * querying the underlying DoFHandler.
+ */
+ struct DoFHandlers
+ {
+ DoFHandlers () : n_dof_handlers (0), level (numbers::invalid_unsigned_int) {};
+ std::vector<SmartPointer<const DoFHandler<dim> > > dof_handler;
+ std::vector<SmartPointer<const MGDoFHandler<dim> > > mg_dof_handler;
+ std::vector<SmartPointer<const hp::DoFHandler<dim> > > hp_dof_handler;
+ enum ActiveDoFHandler { usual, multigrid, hp } active_dof_handler;
+ unsigned int n_dof_handlers;
+ unsigned int level;
+ };
+
+ /**
+ * Pointers to the DoFHandlers underlying the
+ * current problem.
+ */
+ DoFHandlers dof_handlers;
+
+ /**
+ * Contains the information about degrees of
+ * freedom on the individual cells and
+ * constraints.
+ */
+ std::vector<internal::MatrixFreeFunctions::DoFInfo> dof_info;
+
+ /**
+ * Contains the weights for constraints stored
+ * in DoFInfo. Filled into a separate field
+ * since several vector components might share
+ * similar weights, which reduces memory
+ * consumption. Moreover, it obviates template
+ * arguments on DoFInfo and keeps it a plain
+ * field of indices only.
+ */
+ internal::MatrixFreeFunctions::CompressedMatrix<Number> constraint_pool;
+
+ /**
+ * Holds information on transformation of
+ * cells from reference cell to real cell that
+ * is needed for evaluating integrals.
+ */
+ internal::MatrixFreeFunctions::MappingInfo<dim,Number> mapping_info;
+
+ /**
+ * Contains shape value information on the
+ * unit cell.
+ */
+ Table<4,internal::MatrixFreeFunctions::FEEvaluationData<Number> > fe_evaluation_data;
+
+ /**
+ * Describes how the cells are gone
+ * through. With the cell level (first index
+ * in this field) and the index within the
+ * level, one can reconstruct a deal.II
+ * cell iterator and use all the traditional
+ * things deal.II offers to do with cell
+ * iterators.
+ */
+ std::vector<std::pair<unsigned int,unsigned int> > cell_level_index;
+
+ /**
+ * Stores how many cells we have, how many
+ * cells that we see after applying
+ * vectorization (i.e., the number of macro
+ * cells), and MPI-related stuff.
+ */
+ internal::MatrixFreeFunctions::SizeInfo size_info;
+
+ /**
+ * Information regarding the shared memory
+ * parallelization.
+ */
+ internal::MatrixFreeFunctions::TaskInfo task_info;
+
+ /**
+ * Stores whether indices have been
+ * initialized.
+ */
+ bool indices_are_initialized;
+
+ /**
+ * Stores whether indices have been
+ * initialized.
+ */
+ bool mapping_is_initialized;
+};
+
+
+
+/*----------------------- Inline functions ----------------------------------*/
+
+#ifndef DOXYGEN
+
+
+template <int dim, typename Number>
+template <typename VectorType>
+inline
+void
+MatrixFree<dim,Number>::initialize_dof_vector(VectorType &vec,
+ const unsigned int comp) const
+{
+ AssertIndexRange (comp, n_components());
+ vec.reinit(dof_info[comp].vector_partitioner->global_size);
+}
+
+
+
+template <int dim, typename Number>
+template <typename Number2>
+inline
+void
+MatrixFree<dim,Number>::initialize_dof_vector(parallel::distributed::Vector<Number2> &vec,
+ const unsigned int comp) const
+{
+ AssertIndexRange (comp, n_components());
+ vec.reinit(dof_info[comp].vector_partitioner);
+}
+
+
+
+template <int dim, typename Number>
+inline
+const std_cxx1x::shared_ptr<const Utilities::MPI::Partitioner>&
+MatrixFree<dim,Number>::get_vector_partitioner (const unsigned int comp) const
+{
+ AssertIndexRange (comp, n_components());
+ return dof_info[comp].vector_partitioner;
+}
+
+
+
+template <int dim, typename Number>
+inline
+const std::vector<unsigned int> &
+MatrixFree<dim,Number>::get_constrained_dofs (const unsigned int comp) const
+{
+ AssertIndexRange (comp, n_components());
+ return dof_info[comp].constrained_dofs;
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::n_components () const
+{
+ AssertDimension (dof_handlers.n_dof_handlers, dof_info.size());
+ return dof_handlers.n_dof_handlers;
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::TaskInfo &
+MatrixFree<dim,Number>::get_task_info () const
+{
+ return task_info;
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::SizeInfo &
+MatrixFree<dim,Number>::get_size_info () const
+{
+ return size_info;
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::n_macro_cells () const
+{
+ return size_info.n_macro_cells;
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::n_physical_cells () const
+{
+ return size_info.n_active_cells;
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::MappingInfo<dim,Number> &
+MatrixFree<dim,Number>::get_mapping_info () const
+{
+ return mapping_info;
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::DoFInfo &
+MatrixFree<dim,Number>::get_dof_info (unsigned int dof_index) const
+{
+ AssertIndexRange (dof_index, n_components());
+ return dof_info[dof_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::CompressedMatrix<Number> &
+MatrixFree<dim,Number>::get_constraint_pool () const
+{
+ return constraint_pool;
+}
+
+
+
+template <int dim, typename Number>
+inline
+std::pair<unsigned int,unsigned int>
+MatrixFree<dim,Number>::create_cell_subrange_hp
+(const std::pair<unsigned int,unsigned int> &range,
+ const unsigned int degree,
+ const unsigned int vector_component) const
+{
+ if (dof_info[vector_component].cell_active_fe_index.size() == 0)
+ return range;
+
+ const unsigned int fe_index =
+ dof_info[vector_component].fe_index_from_degree(degree);
+ if (fe_index >= dof_info[vector_component].max_fe_index)
+ return std::pair<unsigned int,unsigned int>(range.second, range.second);
+ else
+ return create_cell_subrange_hp_by_index (range, fe_index, vector_component);
+}
+
+
+
+template <int dim, typename Number>
+inline
+std::pair<unsigned int,unsigned int>
+MatrixFree<dim,Number>::create_cell_subrange_hp_by_index
+(const std::pair<unsigned int,unsigned int> &range,
+ const unsigned int fe_index,
+ const unsigned int vector_component) const
+{
+ AssertIndexRange (fe_index, dof_info[vector_component].max_fe_index);
+ const std::vector<unsigned int> &fe_indices =
+ dof_info[vector_component].cell_active_fe_index;
+ if (fe_indices.size() == 0)
+ return range;
+ else
+ {
+ // the range over which we are searching must be ordered,
+ // otherwise we got a range that spans over too many cells
+#ifdef DEBUG
+ for (unsigned int i=range.first+1; i<range.second; ++i)
+ Assert (fe_indices[i] >= fe_indices[i-1],
+ ExcMessage ("Cell range must be over sorted range of fe indices in hp case!"));
+ AssertIndexRange(range.first,fe_indices.size()+1);
+ AssertIndexRange(range.second,fe_indices.size()+1);
+#endif
+ std::pair<unsigned int,unsigned int> return_range;
+ return_range.first =
+ std::lower_bound(&fe_indices[range.first], &fe_indices[range.second],
+ fe_index)
+ -&fe_indices[0] ;
+ return_range.second =
+ std::lower_bound(&fe_indices[return_range.first],
+ &fe_indices[range.second],
+ fe_index+1)-&fe_indices[0];
+ Assert(return_range.first >= range.first &&
+ return_range.second <= range.second, ExcInternalError());
+ return return_range;
+ }
+}
+
+
+
+template <int dim, typename Number>
+inline
+void
+MatrixFree<dim,Number>::renumber_dofs (std::vector<unsigned int> &renumbering,
+ const unsigned int vector_component)
+{
+ AssertIndexRange(vector_component, dof_info.size());
+ dof_info[vector_component].renumber_dofs (renumbering);
+}
+
+
+
+template <int dim, typename Number>
+inline
+const DoFHandler<dim> &
+MatrixFree<dim,Number>::get_dof_handler (const unsigned int dof_index) const
+{
+ AssertIndexRange (dof_index, n_components());
+ if (dof_handlers.active_dof_handler == DoFHandlers::usual)
+ {
+ AssertDimension (dof_handlers.dof_handler.size(),
+ dof_handlers.n_dof_handlers);
+ return *dof_handlers.dof_handler[dof_index];
+ }
+ else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid)
+ {
+ Assert (dof_handlers.level == numbers::invalid_unsigned_int,
+ ExcMessage("Cannot return DoFHandler when MGDoFHandler and "
+ "level are specified."));
+ AssertDimension (dof_handlers.mg_dof_handler.size(),
+ dof_handlers.n_dof_handlers);
+ return *dof_handlers.mg_dof_handler[dof_index];
+ }
+ else
+ {
+ Assert (false, ExcNotImplemented());
+ // put pseudo return argument to avoid
+ // compiler error, but trigger a segfault in
+ // case this is only run in optimized mode
+ return *dof_handlers.dof_handler[numbers::invalid_unsigned_int];
+ }
+}
+
+
+
+template <int dim, typename Number>
+inline
+const MGDoFHandler<dim> &
+MatrixFree<dim,Number>::get_mg_dof_handler (const unsigned int dof_index) const
+{
+ AssertIndexRange (dof_index, dof_handlers.n_dof_handlers);
+ Assert (dof_handlers.active_dof_handler == DoFHandlers::multigrid,
+ ExcNotImplemented());
+ return *dof_handlers.mg_dof_handler[dof_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+typename DoFHandler<dim>::active_cell_iterator
+MatrixFree<dim,Number>::get_cell_iterator(const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int dof_index) const
+{
+ const unsigned int n_vectors=VectorizedArray<Number>::n_array_elements;
+#ifdef DEBUG
+ AssertIndexRange (dof_index, dof_handlers.n_dof_handlers);
+ AssertIndexRange (macro_cell_number, size_info.n_macro_cells);
+ AssertIndexRange (vector_number, n_vectors);
+ const unsigned int irreg_filled =
+ std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]);
+ if (irreg_filled > 0)
+ AssertIndexRange (vector_number, irreg_filled);
+#endif
+
+ const DoFHandler<dim>* dofh = 0;
+ if (dof_handlers.active_dof_handler == DoFHandlers::usual)
+ {
+ AssertDimension (dof_handlers.dof_handler.size(),
+ dof_handlers.n_dof_handlers);
+ dofh = dof_handlers.dof_handler[dof_index];
+ }
+ else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid)
+ {
+ Assert (dof_handlers.level == numbers::invalid_unsigned_int,
+ ExcMessage("Cannot return DoFHandler when MGDoFHandler and "
+ "level are specified."));
+ AssertDimension (dof_handlers.mg_dof_handler.size(),
+ dof_handlers.n_dof_handlers);
+ dofh = dof_handlers.mg_dof_handler[dof_index];
+ }
+ else
+ {
+ Assert (false, ExcMessage ("Cannot return DoFHandler<dim>::cell_iterator "
+ "for underlying DoFHandler!"));
+ }
+
+ std::pair<unsigned int,unsigned int> index =
+ cell_level_index[macro_cell_number*n_vectors+vector_number];
+ return typename DoFHandler<dim>::active_cell_iterator
+ (&dofh->get_tria(), index.first, index.second, dofh);
+}
+
+
+
+template <int dim, typename Number>
+inline
+typename MGDoFHandler<dim>::cell_iterator
+MatrixFree<dim,Number>::get_mg_cell_iterator(const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int dof_index) const
+{
+ const unsigned int n_vectors=VectorizedArray<Number>::n_array_elements;
+#ifdef DEBUG
+ AssertIndexRange (dof_index, dof_handlers.n_dof_handlers);
+ AssertIndexRange (macro_cell_number, size_info.n_macro_cells);
+ AssertIndexRange (vector_number, n_vectors);
+ const unsigned int irreg_filled =
+ std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]);
+ if (irreg_filled > 0)
+ AssertIndexRange (vector_number, irreg_filled);
+#endif
+
+ Assert (dof_handlers.active_dof_handler == DoFHandlers::multigrid,
+ ExcNotImplemented());
+ const MGDoFHandler<dim> * dofh = dof_handlers.mg_dof_handler[dof_index];
+
+ std::pair<unsigned int,unsigned int> index =
+ cell_level_index[macro_cell_number*n_vectors+vector_number];
+ return typename MGDoFHandler<dim>::cell_iterator
+ (&dofh->get_tria(), index.first, index.second, dofh);
+}
+
+
+
+template <int dim, typename Number>
+inline
+typename hp::DoFHandler<dim>::active_cell_iterator
+MatrixFree<dim,Number>::get_hp_cell_iterator(const unsigned int macro_cell_number,
+ const unsigned int vector_number,
+ const unsigned int dof_index) const
+{
+ const unsigned int n_vectors=VectorizedArray<Number>::n_array_elements;
+#ifdef DEBUG
+ AssertIndexRange (dof_index, dof_handlers.n_dof_handlers);
+ AssertIndexRange (macro_cell_number, size_info.n_macro_cells);
+ AssertIndexRange (vector_number, n_vectors);
+ const unsigned int irreg_filled =
+ std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]);
+ if (irreg_filled > 0)
+ AssertIndexRange (vector_number, irreg_filled);
+#endif
+
+ Assert (dof_handlers.active_dof_handler == DoFHandlers::hp,
+ ExcNotImplemented());
+ const hp::DoFHandler<dim> * dofh = dof_handlers.hp_dof_handler[dof_index];
+ std::pair<unsigned int,unsigned int> index =
+ cell_level_index[macro_cell_number*n_vectors+vector_number];
+ return typename hp::DoFHandler<dim>::cell_iterator
+ (&dofh->get_tria(), index.first, index.second, dofh);
+}
+
+
+
+template <int dim, typename Number>
+inline
+bool
+MatrixFree<dim,Number>::at_irregular_cell (const unsigned int macro_cell) const
+{
+ AssertIndexRange (macro_cell, size_info.n_macro_cells);
+ return std_cxx1x::get<2>(dof_info[0].row_starts[macro_cell]) > 0;
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::n_components_filled (const unsigned int macro_cell) const
+{
+ AssertIndexRange (macro_cell, size_info.n_macro_cells);
+ const unsigned int n_filled =
+ std_cxx1x::get<2>(dof_info[0].row_starts[macro_cell]);
+ if (n_filled == 0)
+ return VectorizedArray<Number>::n_array_elements;
+ else
+ return n_filled;
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::get_dofs_per_cell(const unsigned int dof_index,
+ const unsigned int active_fe_index) const
+{
+ AssertIndexRange (dof_index, dof_info.size());
+ return dof_info[dof_index].dofs_per_cell[active_fe_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::get_n_q_points(const unsigned int quad_index,
+ const unsigned int active_fe_index) const
+{
+ AssertIndexRange (quad_index,
+ mapping_info.mapping_data_gen.size());
+ return mapping_info.mapping_data_gen[quad_index].n_q_points[active_fe_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::get_dofs_per_face(const unsigned int dof_index,
+ const unsigned int active_fe_index) const
+{
+ AssertIndexRange (dof_index, dof_info.size());
+ return dof_info[dof_index].dofs_per_face[active_fe_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+unsigned int
+MatrixFree<dim,Number>::get_n_q_points_face(const unsigned int quad_index,
+ const unsigned int active_fe_index) const
+{
+ AssertIndexRange (quad_index,
+ mapping_info.mapping_data_gen.size());
+ return mapping_info.mapping_data_gen[quad_index].n_q_points_face[active_fe_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+const IndexSet &
+MatrixFree<dim,Number>::get_locally_owned_set(const unsigned int dof_index) const
+{
+ AssertIndexRange (dof_index, dof_info.size());
+ return dof_info[dof_index].vector_partitioner->locally_owned_range();
+}
+
+
+
+template <int dim, typename Number>
+inline
+const IndexSet &
+MatrixFree<dim,Number>::get_ghost_set(const unsigned int dof_index) const
+{
+ AssertIndexRange (dof_index, dof_info.size());
+ return dof_info[dof_index].vector_partitioner->ghost_indices();
+}
+
+
+
+template <int dim, typename Number>
+inline
+const internal::MatrixFreeFunctions::FEEvaluationData<Number> &
+MatrixFree<dim,Number>::get_fe_evaluation(const unsigned int index_fe,
+ const unsigned int index_quad,
+ const unsigned int active_fe_index,
+ const unsigned int active_quad_index) const
+{
+ AssertIndexRange (index_fe, fe_evaluation_data.size(0));
+ AssertIndexRange (index_quad, fe_evaluation_data.size(1));
+ AssertIndexRange (active_fe_index, fe_evaluation_data.size(2));
+ AssertIndexRange (active_quad_index, fe_evaluation_data.size(3));
+ return fe_evaluation_data(index_fe, index_quad,
+ active_fe_index, active_quad_index);
+}
+
+
+
+template <int dim, typename Number>
+inline
+const Quadrature<dim> &
+MatrixFree<dim,Number>::get_quad (const unsigned int quad_index,
+ const unsigned int active_fe_index) const
+{
+ AssertIndexRange (quad_index, mapping_info.mapping_data_gen.size());
+ return mapping_info.mapping_data_gen[quad_index].
+ quadrature_formula[active_fe_index];
+}
+
+
+
+template <int dim, typename Number>
+inline
+bool
+MatrixFree<dim,Number>::indices_initialized () const
+{
+ return indices_are_initialized;
+}
+
+
+
+template <int dim, typename Number>
+inline
+bool
+MatrixFree<dim,Number>::mapping_initialized () const
+{
+ return mapping_is_initialized;
+}
+
+
+
+// ------------------------------ reinit functions ---------------------------
+
+namespace internal
+{
+ template <typename DH>
+ std::vector<IndexSet>
+ extract_locally_owned_index_sets (const std::vector<const DH*> &dofh,
+ const unsigned int)
+ {
+ std::vector<IndexSet> locally_owned_set;
+ locally_owned_set.reserve (dofh.size());
+ for(unsigned int j=0;j<dofh.size();j++)
+ locally_owned_set.push_back(dofh[j]->locally_owned_dofs());
+ return locally_owned_set;
+ }
+
+
+
+ template <int dim>
+ std::vector<IndexSet>
+ extract_locally_owned_index_sets (const std::vector<const dealii::MGDoFHandler<dim>*> &dofh,
+ const unsigned int level)
+ {
+ std::vector<IndexSet> locally_owned_set;
+ locally_owned_set.reserve (dofh.size());
+ for(unsigned int j=0;j<dofh.size();j++)
+ if (level == numbers::invalid_unsigned_int)
+ locally_owned_set.push_back(dofh[j]->locally_owned_dofs());
+ else
+ {
+ IndexSet new_set (dofh[j]->n_dofs(level));
+ new_set.add_range (0, dofh[j]->n_dofs(level));
+ locally_owned_set.push_back(new_set);
+ }
+ return locally_owned_set;
+ }
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const DH &dof_handler,
+ const ConstraintMatrix &constraints_in,
+ const Quad &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ MappingQ1<dim> mapping;
+ std::vector<const DH*> dof_handlers;
+ std::vector<const ConstraintMatrix*> constraints;
+ std::vector<Quad> quads;
+
+ dof_handlers.push_back(&dof_handler);
+ constraints.push_back (&constraints_in);
+ quads.push_back (quad);
+
+ std::vector<IndexSet> locally_owned_sets =
+ internal::extract_locally_owned_index_sets (dof_handlers,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handlers,constraints, locally_owned_sets, quads,
+ additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const Mapping<dim> &mapping,
+ const DH &dof_handler,
+ const ConstraintMatrix &constraints_in,
+ const Quad &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ std::vector<const DH*> dof_handlers;
+ std::vector<const ConstraintMatrix*> constraints;
+ std::vector<Quad> quads;
+
+ dof_handlers.push_back(&dof_handler);
+ constraints.push_back (&constraints_in);
+ quads.push_back (quad);
+
+ std::vector<IndexSet> locally_owned_sets =
+ internal::extract_locally_owned_index_sets (dof_handlers,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handlers,constraints,locally_owned_sets, quads,
+ additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<Quad> &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ MappingQ1<dim> mapping;
+ std::vector<IndexSet> locally_owned_set =
+ internal::extract_locally_owned_index_sets (dof_handler,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handler,constraint,locally_owned_set,
+ static_cast<const std::vector<Quadrature<1> >&>(quad),
+ additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const Quad &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ MappingQ1<dim> mapping;
+ std::vector<Quad> quads;
+ quads.push_back(quad);
+ std::vector<IndexSet> locally_owned_set =
+ internal::extract_locally_owned_index_sets (dof_handler,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handler,constraint,locally_owned_set, quads,
+ additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const Quad &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ std::vector<Quad> quads;
+ quads.push_back(quad);
+ std::vector<IndexSet> locally_owned_set =
+ internal::extract_locally_owned_index_sets (dof_handler,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handler,constraint,locally_owned_set, quads,
+ additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const Mapping<dim> &mapping,
+ const std::vector<const DH *> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<Quad> &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ std::vector<IndexSet> locally_owned_set =
+ internal::extract_locally_owned_index_sets (dof_handler,
+ additional_data.level_mg_handler);
+ reinit(mapping, dof_handler,constraint,locally_owned_set,
+ quad, additional_data);
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH, typename Quad>
+void MatrixFree<dim,Number>::
+reinit(const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<Quad> &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ // find out whether we use a hp Quadrature or
+ // a standard quadrature
+ std::vector<hp::QCollection<1> > quad_hp;
+ for (unsigned int q=0; q<quad.size(); ++q)
+ quad_hp.push_back (hp::QCollection<1>(quad[q]));
+ internal_reinit (mapping, dof_handler, constraint, locally_owned_set,
+ quad_hp, additional_data);
+}
+
+
+
+// ------------------------------ implementation of cell_loop ---------------
+
+ // internal helper functions that define how
+ // to call MPI data exchange functions: for
+ // generic vectors, do nothing at all. For
+ // distributed vectors, can call
+ // update_ghost_values_start function and so on. If we
+ // have collections of vectors, just do the
+ // individual functions of the components
+namespace internal
+{
+ template<typename VectorStruct>
+ inline
+ void update_ghost_values_start (const VectorStruct &,
+ const unsigned int channel = 0)
+ {
+ (void)channel;
+ }
+
+
+
+ template<typename Number>
+ inline
+ void update_ghost_values_start (const parallel::distributed::Vector<Number> &src,
+ const unsigned int channel = 0)
+ {
+ src.update_ghost_values_start(channel);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void update_ghost_values_start (const std::vector<VectorStruct> &src)
+ {
+ for(unsigned int comp=0;comp<src.size();comp++)
+ update_ghost_values_start(src[comp], comp);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void update_ghost_values_start (const std::vector<VectorStruct*> &src)
+ {
+ for(unsigned int comp=0;comp<src.size();comp++)
+ update_ghost_values_start(*src[comp], comp);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void update_ghost_values_finish (const VectorStruct &)
+ {}
+
+
+
+ template <typename Number>
+ inline
+ void update_ghost_values_finish (const parallel::distributed::Vector<Number> &src)
+ {
+ src.update_ghost_values_finish();
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void update_ghost_values_finish (const std::vector<VectorStruct> &src)
+ {
+ for(unsigned int comp=0;comp<src.size();comp++)
+ update_ghost_values_finish(src[comp]);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void update_ghost_values_finish (const std::vector<VectorStruct*> &src)
+ {
+ for(unsigned int comp=0;comp<src.size();comp++)
+ update_ghost_values_finish(*src[comp]);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_start (VectorStruct &,
+ const unsigned int channel = 0)
+ {
+ (void)channel;
+ }
+
+
+
+ template <typename Number>
+ inline
+ void compress_start (parallel::distributed::Vector<Number> &dst,
+ const unsigned int channel = 0)
+ {
+ dst.compress_start(channel);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_start (std::vector<VectorStruct> &dst)
+ {
+ for(unsigned int comp=0;comp<dst.size();comp++)
+ compress_start (dst[comp], comp);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_start (std::vector<VectorStruct*> &dst)
+ {
+ for(unsigned int comp=0;comp<dst.size();comp++)
+ compress_start (*dst[comp], comp);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_finish (VectorStruct &)
+ {}
+
+
+
+ template <typename Number>
+ inline
+ void compress_finish (parallel::distributed::Vector<Number> &dst)
+ {
+ dst.compress_finish();
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_finish (std::vector<VectorStruct> &dst)
+ {
+ for(unsigned int comp=0;comp<dst.size();comp++)
+ compress_finish(dst[comp]);
+ }
+
+
+
+ template <typename VectorStruct>
+ inline
+ void compress_finish (std::vector<VectorStruct*> &dst)
+ {
+ for(unsigned int comp=0;comp<dst.size();comp++)
+ compress_finish(*dst[comp]);
+ }
+
+
+#if DEAL_II_USE_MT==1
+
+ // This defines the TBB data structures that
+ // are needed to schedule the
+ // partition-partition variant
+
+ namespace partition
+ {
+ template<typename Worker, bool blocked=false>
+ class CellWork : public tbb::task
+ {
+ public:
+ CellWork (const Worker &worker_in,
+ const unsigned int partition_in,
+ const internal::MatrixFreeFunctions::TaskInfo &task_info_in)
+ :
+ worker (worker_in),
+ partition (partition_in),
+ task_info (task_info_in)
+ {};
+ tbb::task* execute ()
+ {
+ std::pair<unsigned int, unsigned int> cell_range
+ (task_info.partition_color_blocks.data[partition],
+ task_info.partition_color_blocks.data[partition+1]);
+ worker(cell_range);
+ if(blocked==true)
+ dummy->spawn (*dummy);
+ return NULL;
+ }
+
+ tbb::empty_task* dummy;
+
+ private:
+ const Worker &worker;
+ const unsigned int partition;
+ const internal::MatrixFreeFunctions::TaskInfo &task_info;
+ };
+
+
+
+ template<typename Worker, bool blocked=false>
+ class PartitionWork : public tbb::task
+ {
+ public:
+ PartitionWork (const Worker &function_in,
+ const unsigned int partition_in,
+ const internal::MatrixFreeFunctions::TaskInfo &task_info_in)
+ :
+ function (function_in),
+ partition (partition_in),
+ task_info (task_info_in)
+ {};
+ tbb::task* execute ()
+ {
+ if(false)
+ {
+ std::pair<unsigned int, unsigned int> cell_range
+ (task_info.partition_color_blocks.data
+ [task_info.partition_color_blocks.row_index[partition]],
+ task_info.partition_color_blocks.data
+ [task_info.partition_color_blocks.row_index[partition+1]]);
+ function(cell_range);
+ }
+ else
+ {
+ tbb::empty_task* root = new( tbb::task::allocate_root() )
+ tbb::empty_task;
+ unsigned int evens = task_info.partition_evens[partition];
+ unsigned int odds = task_info.partition_odds[partition];
+ unsigned int n_blocked_workers =
+ task_info.partition_n_blocked_workers[partition];
+ unsigned int n_workers = task_info.partition_n_workers[partition];
+ std::vector<CellWork<Worker,false>*> worker(n_workers);
+ std::vector<CellWork<Worker,true>*> blocked_worker(n_blocked_workers);
+
+ root->set_ref_count(evens+1);
+ for(unsigned int j=0;j<evens;j++)
+ {
+ worker[j] = new(root->allocate_child())
+ CellWork<Worker,false>(function,task_info.
+ partition_color_blocks.
+ row_index[partition]+
+ 2*j,task_info);
+ if(j>0)
+ {
+ worker[j]->set_ref_count(2);
+ blocked_worker[j-1]->dummy = new(worker[j]->allocate_child())
+ tbb::empty_task;
+ worker[j-1]->spawn(*blocked_worker[j-1]);
+ }
+ else
+ worker[j]->set_ref_count(1);
+ if(j<evens-1)
+ {
+ blocked_worker[j] = new(worker[j]->allocate_child())
+ CellWork<Worker,true>(function,task_info.
+ partition_color_blocks.
+ row_index[partition]+
+ 2*j+1,task_info);
+ }
+ else
+ {
+ if(odds==evens)
+ {
+ worker[evens] = new(worker[j]->allocate_child())
+ CellWork<Worker,false>(function,
+ task_info.
+ partition_color_blocks.
+ row_index[partition]+
+ 2*j+1,task_info);
+ worker[j]->spawn(*worker[evens]);
+ }
+ else
+ {
+ tbb::empty_task *child = new(worker[j]->allocate_child())
+ tbb::empty_task();
+ worker[j]->spawn(*child);
+ }
+ }
+ }
+
+ root->wait_for_all();
+ root->destroy(*root);
+ }
+ if(blocked==true)
+ dummy->spawn (*dummy);
+ return NULL;
+ }
+
+ tbb::empty_task* dummy;
+
+ private:
+ const Worker &function;
+ const unsigned int partition;
+ const internal::MatrixFreeFunctions::TaskInfo &task_info;
+ };
+
+ } // end of namespace partition
+
+
+
+ namespace color
+ {
+ template <typename Worker>
+ class CellWork
+ {
+ public:
+ CellWork (const Worker &worker_in,
+ const internal::MatrixFreeFunctions::TaskInfo &task_info_in)
+ :
+ worker (worker_in),
+ task_info (task_info_in)
+ {};
+ void operator()(const tbb::blocked_range<unsigned int> &r) const
+ {
+ for(unsigned int block=r.begin();block<r.end();block++)
+ {
+ std::pair<unsigned int,unsigned int> cell_range;
+ if(task_info.position_short_block<block)
+ {
+ cell_range.first = (block-1)*task_info.block_size+
+ task_info.block_size_last;
+ cell_range.second = cell_range.first + task_info.block_size;
+ }
+ else
+ {
+ cell_range.first = block*task_info.block_size;
+ cell_range.second = cell_range.first +
+ ((block == task_info.position_short_block)?
+ (task_info.block_size_last):(task_info.block_size));
+ }
+ worker (cell_range);
+ }
+ }
+ private:
+ const Worker &worker;
+ const internal::MatrixFreeFunctions::TaskInfo &task_info;
+ };
+
+
+ template<typename Worker, bool blocked=false>
+ class PartitionWork : public tbb::task
+ {
+ public:
+ PartitionWork (const Worker &worker_in,
+ const unsigned int partition_in,
+ const internal::MatrixFreeFunctions::TaskInfo &task_info_in)
+ :
+ worker (worker_in),
+ partition (partition_in),
+ task_info (task_info_in)
+ {};
+ tbb::task* execute ()
+ {
+ unsigned int lower = task_info.partition_color_blocks.data[partition],
+ upper = task_info.partition_color_blocks.data[partition+1];
+ parallel_for(tbb::blocked_range<unsigned int>(lower,upper,1),
+ CellWork<Worker> (worker,task_info));
+ if(blocked==true)
+ dummy->spawn (*dummy);
+ return NULL;
+ }
+
+ tbb::empty_task* dummy;
+
+ private:
+ const Worker &worker;
+ const unsigned int partition;
+ const internal::MatrixFreeFunctions::TaskInfo &task_info;
+ };
+
+ } // end of namespace color
+
+
+ template<typename VectorStruct>
+ class MPIComDistribute : public tbb::task
+ {
+ public:
+ MPIComDistribute (const VectorStruct &src_in)
+ :
+ src(src_in)
+ {};
+
+ tbb::task* execute ()
+ {
+ internal::update_ghost_values_finish(src);
+ return 0;
+ }
+
+ private:
+ const VectorStruct &src;
+ };
+
+
+
+ template<typename VectorStruct>
+ class MPIComCompress : public tbb::task
+ {
+ public:
+ MPIComCompress (VectorStruct &dst_in)
+ :
+ dst(dst_in)
+ {};
+
+ tbb::task* execute ()
+ {
+ internal::compress_start(dst);
+ return 0;
+ }
+
+ private:
+ VectorStruct &dst;
+ };
+
+#endif // #if DEAL_II_USE_MT==1
+
+} // end of namespace internal
+
+
+
+template <int dim, typename Number>
+template <typename OutVector, typename InVector>
+inline
+void
+MatrixFree<dim, Number>::cell_loop
+(const std_cxx1x::function<void (const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)> &cell_operation,
+ OutVector &dst,
+ const InVector &src) const
+{
+#if DEAL_II_USE_MT==1
+
+ // Use multithreading if so requested and if
+ // there is enough work to do in parallel (the
+ // code might hang if there are less than two
+ // chunks!)
+ if (task_info.use_multithreading == true && task_info.n_blocks > 3)
+ {
+ // to simplify the function calls, bind away
+ // all arguments except the cell range
+ typedef
+ std_cxx1x::function<void (const std::pair<unsigned int,unsigned int> &range)>
+ Worker;
+
+ const Worker func = std_cxx1x::bind (std_cxx1x::ref(cell_operation),
+ std_cxx1x::cref(*this),
+ std_cxx1x::ref(dst),
+ std_cxx1x::cref(src),
+ std_cxx1x::_1);
+
+ if(task_info.use_partition_partition == true)
+ {
+ internal::update_ghost_values_start(src);
+ tbb::empty_task* root = new( tbb::task::allocate_root() )
+ tbb::empty_task;
+ unsigned int evens = task_info.evens;
+ unsigned int odds = task_info.odds;
+ root->set_ref_count(evens+1);
+ unsigned int n_blocked_workers = task_info.n_blocked_workers;
+ unsigned int n_workers = task_info.n_workers;
+ std::vector<internal::partition::PartitionWork<Worker,false>*>
+ worker(n_workers);
+ std::vector<internal::partition::PartitionWork<Worker,true>*>
+ blocked_worker(n_blocked_workers);
+ internal::MPIComCompress<OutVector> *worker_compr =
+ new(root->allocate_child())
+ internal::MPIComCompress<OutVector>(dst);
+ worker_compr->set_ref_count(1);
+ for(unsigned int j=0;j<evens;j++)
+ {
+ if(j>0)
+ {
+ worker[j] = new(root->allocate_child())
+ internal::partition::PartitionWork<Worker,false>
+ (func,2*j,task_info);
+ worker[j]->set_ref_count(2);
+ blocked_worker[j-1]->dummy = new(worker[j]->allocate_child())
+ tbb::empty_task;
+ if(j>1)
+ worker[j-1]->spawn(*blocked_worker[j-1]);
+ else
+ worker_compr->spawn(*blocked_worker[j-1]);
+ }
+ else
+ {
+ worker[j] = new(worker_compr->allocate_child())
+ internal::partition::PartitionWork<Worker,false>
+ (func,2*j,task_info);
+ worker[j]->set_ref_count(2);
+ internal::MPIComDistribute<InVector> *worker_dist =
+ new (worker[j]->allocate_child())
+ internal::MPIComDistribute<InVector>(src);
+ if(odds == 0)
+ break;
+ else
+ worker_dist->spawn(*worker_dist);
+ }
+ if(j<evens-1)
+ {
+ blocked_worker[j] = new(worker[j]->allocate_child())
+ internal::partition::PartitionWork<Worker,true>
+ (func,2*j+1,task_info);
+ }
+ else
+ {
+ if(odds==evens)
+ {
+ worker[evens] = new(worker[j]->allocate_child())
+ internal::partition::PartitionWork<Worker,false>
+ (func,2*j+1,task_info);
+ worker[j]->spawn(*worker[evens]);
+ }
+ else
+ {
+ tbb::empty_task *child = new(worker[j]->allocate_child())
+ tbb::empty_task();
+ worker[j]->spawn(*child);
+ }
+ }
+ }
+
+ root->wait_for_all();
+ root->destroy(*root);
+ internal::compress_finish(dst);
+ }
+ else // end of partition-partition, start of partition-color
+ {
+ internal::update_ghost_values_start(src);
+ unsigned int evens = task_info.evens;
+ unsigned int odds = task_info.odds;
+
+ // check whether there is only one
+ // partition. if not, build up the tree of
+ // partitions
+ if (odds > 0)
+ {
+ tbb::empty_task* root = new( tbb::task::allocate_root() ) tbb::empty_task;
+ root->set_ref_count(evens+1);
+ unsigned int n_blocked_workers = odds-(odds+evens+1)%2;
+ unsigned int n_workers = task_info.partition_color_blocks.data.size()-1-
+ n_blocked_workers;
+ std::vector<internal::color::PartitionWork<Worker,false>*> worker(n_workers);
+ std::vector<internal::color::PartitionWork<Worker,true>*> blocked_worker(n_blocked_workers);
+ unsigned int worker_index = 0, slice_index = 0;
+ unsigned int spawn_index = 0, spawn_index_new = 0;
+ int spawn_index_child = -2;
+ internal::MPIComCompress<OutVector> *worker_compr = new(root->allocate_child())
+ internal::MPIComCompress<OutVector>(dst);
+ worker_compr->set_ref_count(1);
+ for (unsigned int part=0;
+ part<task_info.partition_color_blocks.row_index.size()-1;part++)
+ {
+ spawn_index_new = worker_index;
+ if(part == 0)
+ worker[worker_index] = new(worker_compr->allocate_child())
+ internal::color::PartitionWork<Worker,false>(func,slice_index,task_info);
+ else
+ worker[worker_index] = new(root->allocate_child())
+ internal::color::PartitionWork<Worker,false>(func,slice_index,task_info);
+ slice_index++;
+ for(;slice_index<task_info.partition_color_blocks.row_index[part+1];
+ slice_index++)
+ {
+ worker[worker_index]->set_ref_count(1);
+ worker_index++;
+ worker[worker_index] = new (worker[worker_index-1]->allocate_child())
+ internal::color::PartitionWork<Worker,false>(func,slice_index,task_info);
+ }
+ worker[worker_index]->set_ref_count(2);
+ if(part>0)
+ {
+ blocked_worker[(part-1)/2]->dummy =
+ new (worker[worker_index]->allocate_child()) tbb::empty_task;
+ worker_index++;
+ if(spawn_index_child == -1)
+ worker[spawn_index]->spawn(*blocked_worker[(part-1)/2]);
+ else
+ worker[spawn_index]->spawn(*worker[spawn_index_child]);
+ spawn_index = spawn_index_new;
+ spawn_index_child = -2;
+ }
+ else
+ {
+ internal::MPIComDistribute<InVector> *worker_dist =
+ new (worker[worker_index]->allocate_child())
+ internal::MPIComDistribute<InVector>(src);
+ worker_dist->spawn(*worker_dist);
+ worker_index++;
+ }
+ part += 1;
+ if(part<task_info.partition_color_blocks.row_index.size()-1)
+ {
+ if(part<task_info.partition_color_blocks.row_index.size()-2)
+ {
+ blocked_worker[part/2] = new(worker[worker_index-1]->allocate_child())
+ internal::color::PartitionWork<Worker,true>(func,slice_index,task_info);
+ slice_index++;
+ if(slice_index<
+ task_info.partition_color_blocks.row_index[part+1])
+ {
+ blocked_worker[part/2]->set_ref_count(1);
+ worker[worker_index] = new(blocked_worker[part/2]->allocate_child())
+ internal::color::PartitionWork<Worker,false>(func,slice_index,task_info);
+ slice_index++;
+ }
+ else
+ {
+ spawn_index_child = -1;
+ continue;
+ }
+ }
+ for(;slice_index<task_info.partition_color_blocks.row_index[part+1];
+ slice_index++)
+ {
+ if(slice_index>
+ task_info.partition_color_blocks.row_index[part])
+ {
+ worker[worker_index]->set_ref_count(1);
+ worker_index++;
+ }
+ worker[worker_index] = new (worker[worker_index-1]->allocate_child())
+ internal::color::PartitionWork<Worker,false>(func,slice_index,task_info);
+ }
+ spawn_index_child = worker_index;
+ worker_index++;
+ }
+ else
+ {
+ tbb::empty_task *final = new (worker[worker_index-1]->allocate_child())
+ tbb::empty_task;
+ worker[spawn_index]->spawn(*final);
+ spawn_index_child = worker_index-1;
+ }
+ }
+ if(evens==odds)
+ worker[spawn_index]->spawn(*worker[spawn_index_child]);
+ root->wait_for_all();
+ root->destroy(*root);
+ }
+ // case when we only have one partition: this
+ // is the usual coloring scheme, and we just
+ // schedule a parallel for loop for each color
+ else
+ {
+ Assert(evens==1,ExcInternalError());
+ internal::update_ghost_values_finish(src);
+
+ for (unsigned int color=0;
+ color < task_info.partition_color_blocks.row_index[1];
+ ++color)
+ {
+ unsigned int lower = task_info.partition_color_blocks.data[color],
+ upper = task_info.partition_color_blocks.data[color+1];
+ parallel_for(tbb::blocked_range<unsigned int>(lower,upper,1),
+ internal::color::CellWork<Worker>
+ (func,task_info));
+ }
+
+ internal::compress_start(dst);
+ }
+ internal::compress_finish(dst);
+ }
+ }
+ else
+#endif
+ // serial loop
+ {
+ std::pair<unsigned int,unsigned int> cell_range;
+
+ internal::update_ghost_values_start (src);
+
+ // First operate on cells where no ghost data is needed (inner
+ // cells)
+ {
+ cell_range.first = 0;
+ cell_range.second = size_info.boundary_cells_start;
+ cell_operation (*this, dst, src, cell_range);
+ }
+
+ // before starting operations on cells that contain ghost nodes
+ // (outer cells), wait for the MPI commands to finish
+ internal::update_ghost_values_finish(src);
+
+ // For the outer cells, do the same procedure as for inner
+ // cells.
+ if (size_info.boundary_cells_end > size_info.boundary_cells_start)
+ {
+ cell_range.first = size_info.boundary_cells_start;
+ cell_range.second = size_info.boundary_cells_end;
+ cell_operation (*this, dst, src, cell_range);
+ }
+
+ internal::compress_start(dst);
+
+ // Finally operate on cells where no ghost data is needed (inner
+ // cells)
+ if (size_info.n_macro_cells > size_info.boundary_cells_end)
+ {
+ cell_range.first = size_info.boundary_cells_end;
+ cell_range.second = size_info.n_macro_cells;
+ cell_operation (*this, dst, src, cell_range);
+ }
+
+ internal::compress_finish(dst);
+ }
+}
+
+
+
+template <int dim, typename Number>
+template <typename CLASS, typename OutVector, typename InVector>
+inline
+void
+MatrixFree<dim,Number>::cell_loop
+(void (CLASS::*function_pointer)(const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)const,
+ const CLASS *owning_class,
+ OutVector &dst,
+ const InVector &src) const
+{
+ // here, use std_cxx1x::bind to hand a
+ // function handler with the appropriate
+ // argument to the other loop function
+ std_cxx1x::function<void (const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)>
+ function = std_cxx1x::bind<void>(function_pointer,
+ std_cxx1x::cref(*owning_class),
+ std_cxx1x::_1,
+ std_cxx1x::_2,
+ std_cxx1x::_3,
+ std_cxx1x::_4);
+ cell_loop (function, dst, src);
+}
+
+
+
+template <int dim, typename Number>
+template <typename CLASS, typename OutVector, typename InVector>
+inline
+void
+MatrixFree<dim,Number>::cell_loop
+(void(CLASS::*function_pointer)(const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &),
+ CLASS *owning_class,
+ OutVector &dst,
+ const InVector &src) const
+{
+ // here, use std_cxx1x::bind to hand a
+ // function handler with the appropriate
+ // argument to the other loop function
+ std_cxx1x::function<void (const MatrixFree<dim,Number> &,
+ OutVector &,
+ const InVector&,
+ const std::pair<unsigned int,
+ unsigned int> &)>
+ function = std_cxx1x::bind<void>(function_pointer,
+ std_cxx1x::ref(*owning_class),
+ std_cxx1x::_1,
+ std_cxx1x::_2,
+ std_cxx1x::_3,
+ std_cxx1x::_4);
+ cell_loop (function, dst, src);
+}
+
+
+#endif // ifndef DOXYGEN
+
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/memory_consumption.h>
+#include <deal.II/base/tensor_product_polynomials.h>
+#include <deal.II/base/mpi.h>
+#include <deal.II/dofs/dof_accessor.h>
+#include <deal.II/multigrid/mg_dof_accessor.h>
+#include <deal.II/fe/fe_poly.h>
+#include <deal.II/hp/q_collection.h>
+#include <deal.II/distributed/tria.h>
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation_data.templates.h>
+#include <deal.II/matrix_free/mapping_info.templates.h>
+#include <deal.II/matrix_free/dof_info.templates.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+
+// --------------------- MatrixFree -----------------------------------
+
+template <int dim, typename Number>
+MatrixFree<dim, Number>::MatrixFree()
+:
+ indices_are_initialized (false),
+ mapping_is_initialized (false)
+{}
+
+
+
+template <int dim, typename Number>
+MatrixFree<dim,Number>::~MatrixFree()
+{}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::
+copy_from (const MatrixFree<dim,Number> &v)
+{
+ clear ();
+ dof_handlers = v.dof_handlers;
+ dof_info = v.dof_info;
+ constraint_pool = v.constraint_pool;
+ mapping_info = v.mapping_info;
+ fe_evaluation_data = v.fe_evaluation_data;
+ cell_level_index = v.cell_level_index;
+ task_info = v.task_info;
+ size_info = v.size_info;
+ indices_are_initialized = v.indices_are_initialized;
+ mapping_is_initialized = v.mapping_is_initialized;
+}
+
+
+
+namespace internal
+{
+ template <int dim>
+ void assert_communicator_equality (const dealii::Triangulation<dim>&tria,
+ const MPI_Comm &comm_mf)
+ {
+#if DEAL_II_COMPILER_SUPPORTS_MPI
+ const parallel::distributed::Triangulation<dim>* dist_tria =
+ dynamic_cast<const parallel::distributed::Triangulation<dim>*>(&tria);
+ if (dist_tria != 0)
+ {
+ if (Utilities::System::job_supports_mpi())
+ {
+ int communicators_same = 0;
+ MPI_Comm_compare (dist_tria->get_communicator(), comm_mf,
+ &communicators_same);
+ Assert (communicators_same == MPI_IDENT ||
+ communicators_same == MPI_CONGRUENT,
+ ExcMessage ("MPI communicator in parallel::distributed::Triangulation "
+ "and matrix free class must be the same!"));
+ }
+ }
+#else
+ (void)tria;
+ (void)comm_mf;
+#endif
+ }
+}
+
+
+
+template <int dim, typename Number>
+template <typename DH>
+void MatrixFree<dim,Number>::
+internal_reinit(const Mapping<dim> &mapping,
+ const std::vector<const DH*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<hp::QCollection<1> > &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ if (additional_data.initialize_indices == true)
+ {
+ clear();
+ Assert (dof_handler.size() > 0, ExcMessage("No DoFHandler is given."));
+ AssertDimension (dof_handler.size(), constraint.size());
+ AssertDimension (dof_handler.size(), locally_owned_set.size());
+
+ // set variables that are independent of FE
+ internal::assert_communicator_equality (dof_handler[0]->get_tria(),
+ additional_data.mpi_communicator);
+ size_info.communicator = additional_data.mpi_communicator;
+ if (Utilities::System::job_supports_mpi() == true)
+ {
+ size_info.my_pid =
+ Utilities::MPI::this_mpi_process(size_info.communicator);
+ size_info.n_procs =
+ Utilities::MPI::n_mpi_processes(size_info.communicator);
+ }
+ else
+ {
+ size_info.my_pid = 0;
+ size_info.n_procs = 1;
+ }
+
+ initialize_dof_handlers (dof_handler, additional_data.level_mg_handler);
+ for (unsigned int no=0; no<dof_handler.size(); ++no)
+ dof_info[no].store_plain_indices = additional_data.store_plain_indices;
+
+ // initialize the basic multithreading
+ // information that needs to be passed to the
+ // DoFInfo structure
+#if DEAL_II_USE_MT == 1
+ if (additional_data.tasks_parallel_scheme != AdditionalData::none)
+ {
+ task_info.use_multithreading = true;
+ task_info.block_size = additional_data.tasks_block_size;
+ task_info.use_partition_partition =
+ (additional_data.tasks_parallel_scheme ==
+ AdditionalData::partition_partition ? true : false);
+ task_info.use_coloring_only =
+ (additional_data.tasks_parallel_scheme ==
+ AdditionalData::color ? true : false);
+ }
+ else
+#endif
+ task_info.use_multithreading = false;
+
+ // set dof_indices together with
+ // constraint_indicator and
+ // constraint_pool. It also reorders the way
+ // cells are gone through (to separate cells
+ // with overlap to other processors from
+ // others without).
+ initialize_indices (constraint, locally_owned_set);
+ }
+
+ // Reads out the FE information and stores the
+ // shape function values, gradients and
+ // Hessians for quadrature points.
+ const unsigned int n_fe = dof_handler.size();
+ const unsigned int n_quad = quad.size();
+ fe_evaluation_data.reinit (TableIndices<4>(n_fe, n_quad, 1, 1));
+ for (unsigned int no=0; no<n_fe; no++)
+ {
+ const FiniteElement<dim> &fe = dof_handler[no]->get_fe();
+ for(unsigned int nq =0;nq<n_quad;nq++)
+ {
+ AssertDimension (quad[nq].size(), 1);
+ fe_evaluation_data(no,nq,0,0).reinit(quad[nq][0], fe.base_element(0));
+ }
+ }
+
+ // Evaluates transformations from unit to real
+ // cell, Jacobian determinants, quadrature
+ // points in real space, based on the ordering
+ // of the cells determined in @p
+ // extract_local_to_global_indices.
+ if(additional_data.initialize_mapping == true)
+ {
+ mapping_info.initialize (dof_handler[0]->get_tria(), cell_level_index,
+ dof_info[0].cell_active_fe_index, mapping, quad,
+ additional_data.mapping_update_flags);
+
+ mapping_is_initialized = true;
+ }
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::
+internal_reinit(const Mapping<dim> &mapping,
+ const std::vector<const hp::DoFHandler<dim>*> &dof_handler,
+ const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set,
+ const std::vector<hp::QCollection<1> > &quad,
+ const MatrixFree<dim,Number>::AdditionalData additional_data)
+{
+ if (additional_data.initialize_indices == true)
+ {
+ clear();
+ Assert (dof_handler.size() > 0, ExcMessage("No DoFHandler is given."));
+ AssertDimension (dof_handler.size(), constraint.size());
+ AssertDimension (dof_handler.size(), locally_owned_set.size());
+
+ // set variables that are independent of FE
+ internal::assert_communicator_equality (dof_handler[0]->get_tria(),
+ additional_data.mpi_communicator);
+ size_info.communicator = additional_data.mpi_communicator;
+ if (Utilities::System::job_supports_mpi() == true)
+ {
+ size_info.my_pid =
+ Utilities::MPI::this_mpi_process(size_info.communicator);
+ size_info.n_procs =
+ Utilities::MPI::n_mpi_processes(size_info.communicator);
+ }
+ else
+ {
+ size_info.my_pid = 0;
+ size_info.n_procs = 1;
+ }
+
+ initialize_dof_handlers (dof_handler, additional_data.level_mg_handler);
+ for (unsigned int no=0; no<dof_handler.size(); ++no)
+ dof_info[no].store_plain_indices = additional_data.store_plain_indices;
+
+ // initialize the basic multithreading
+ // information that needs to be passed to the
+ // DoFInfo structure
+#if DEAL_II_USE_MT == 1
+ if (additional_data.tasks_parallel_scheme != AdditionalData::none)
+ {
+ task_info.use_multithreading = true;
+ task_info.block_size = additional_data.tasks_block_size;
+ task_info.use_partition_partition =
+ (additional_data.tasks_parallel_scheme ==
+ AdditionalData::partition_partition ? true : false);
+ task_info.use_coloring_only =
+ (additional_data.tasks_parallel_scheme ==
+ AdditionalData::color ? true : false);
+ }
+ else
+#endif
+ task_info.use_multithreading = false;
+
+ // set dof_indices together with
+ // constraint_indicator and
+ // constraint_pool. It also reorders the way
+ // cells are gone through (to separate cells
+ // with overlap to other processors from
+ // others without).
+ initialize_indices (constraint, locally_owned_set);
+ }
+
+ // Reads out the FE information and stores the
+ // shape function values, gradients and
+ // Hessians for quadrature points.
+ const unsigned int n_components = dof_handler.size();
+ const unsigned int n_quad = quad.size();
+ unsigned int n_fe_in_collection = 0;
+ for (unsigned int i=0; i<n_components; ++i)
+ n_fe_in_collection = std::max (n_fe_in_collection,
+ dof_handler[i]->get_fe().size());
+ unsigned int n_quad_in_collection = 0;
+ for (unsigned int q=0; q<n_quad; ++q)
+ n_quad_in_collection = std::max (n_quad_in_collection, quad[q].size());
+ fe_evaluation_data.reinit (TableIndices<4>(n_components, n_quad,
+ n_fe_in_collection,
+ n_quad_in_collection));
+ for (unsigned int no=0; no<n_components; no++)
+ for (unsigned int fe_no=0; fe_no<dof_handler[no]->get_fe().size(); ++fe_no)
+ {
+ const FiniteElement<dim> &fe = dof_handler[no]->get_fe()[fe_no];
+ for(unsigned int nq =0; nq<n_quad; nq++)
+ for (unsigned int q_no=0; q_no<quad[nq].size(); ++q_no)
+ fe_evaluation_data(no,nq,fe_no,q_no).reinit (quad[nq][q_no],
+ fe.base_element(0));
+ }
+
+ // Evaluates transformations from unit to real
+ // cell, Jacobian determinants, quadrature
+ // points in real space, based on the ordering
+ // of the cells determined in @p
+ // extract_local_to_global_indices.
+ if(additional_data.initialize_mapping == true)
+ {
+ mapping_info.initialize (dof_handler[0]->get_tria(), cell_level_index,
+ dof_info[0].cell_active_fe_index, mapping, quad,
+ additional_data.mapping_update_flags);
+
+ mapping_is_initialized = true;
+ }
+}
+
+
+
+namespace internal
+{
+
+ // steps through all children and adds the
+ // active cells recursively
+ template <typename InIterator>
+ void resolve_cell (const InIterator &cell,
+ std::vector<std::pair<unsigned int,unsigned int> >&cell_its,
+ const unsigned int subdomain_id)
+ {
+ if (cell->has_children())
+ for (unsigned int child=0; child<cell->n_children(); ++child)
+ resolve_cell (cell->child(child), cell_its,
+ subdomain_id);
+ else
+ if (cell->subdomain_id() == subdomain_id)
+ {
+ Assert (cell->active(), ExcInternalError());
+ cell_its.push_back (std::pair<unsigned int,unsigned int>
+ (cell->level(), cell->index()));
+ }
+ }
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::
+initialize_dof_handlers (const std::vector<const DoFHandler<dim>*> &dof_handler,
+ const unsigned int)
+{
+ dof_handlers.active_dof_handler = DoFHandlers::usual;
+ dof_handlers.n_dof_handlers = dof_handler.size();
+ dof_handlers.dof_handler.resize (dof_handlers.n_dof_handlers);
+ for (unsigned int no=0; no<dof_handlers.n_dof_handlers; ++no)
+ dof_handlers.dof_handler[no] = dof_handler[no];
+
+ dof_info.resize (dof_handlers.n_dof_handlers);
+
+ // go through cells on zeroth level and then
+ // successively step down into children. This
+ // gives a z-ordering of the cells, which is
+ // beneficial when setting up neighboring
+ // relations between cells for thread
+ // parallelization
+ const unsigned int n_mpi_procs = size_info.n_procs;
+ const unsigned int my_pid = size_info.my_pid;
+
+ const Triangulation<dim> &tria = dof_handlers.dof_handler[0]->get_tria();
+ {
+ if (n_mpi_procs == 1)
+ cell_level_index.reserve (tria.n_active_cells());
+ typename Triangulation<dim>::cell_iterator cell = tria.begin(0),
+ end_cell = tria.end(0);
+ for ( ; cell != end_cell; ++cell)
+ internal::resolve_cell (cell, cell_level_index, my_pid);
+ }
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::
+initialize_dof_handlers (const std::vector<const MGDoFHandler<dim>*> &dof_handler,
+ const unsigned int level)
+{
+ dof_handlers.active_dof_handler = DoFHandlers::multigrid;
+ dof_handlers.level = level;
+ dof_handlers.n_dof_handlers = dof_handler.size();
+ dof_handlers.mg_dof_handler.resize (dof_handlers.n_dof_handlers);
+ for (unsigned int no=0; no<dof_handlers.n_dof_handlers; ++no)
+ dof_handlers.mg_dof_handler[no] = dof_handler[no];
+
+ dof_info.resize (dof_handlers.n_dof_handlers);
+
+ // go through cells on zeroth level and then
+ // successively step down into children. This
+ // gives a z-ordering of the cells, which is
+ // beneficial when setting up neighboring
+ // relations between cells for thread
+ // parallelization
+ const unsigned int n_mpi_procs = size_info.n_procs;
+ const unsigned int my_pid = size_info.my_pid;
+
+ // if we have no level given, use the same as
+ // for the standard DoFHandler, otherwise we
+ // must loop through the respective level
+ const Triangulation<dim> &tria = dof_handlers.mg_dof_handler[0]->get_tria();
+
+ if (level == numbers::invalid_unsigned_int)
+ {
+ if (n_mpi_procs == 1)
+ cell_level_index.reserve (tria.n_active_cells());
+ typename Triangulation<dim>::cell_iterator cell = tria.begin(0),
+ end_cell = tria.end(0);
+ for ( ; cell != end_cell; ++cell)
+ internal::resolve_cell (cell, cell_level_index, my_pid);
+ }
+ else
+ {
+ AssertIndexRange (level, tria.n_levels());
+ cell_level_index.reserve (tria.n_cells(level));
+ typename Triangulation<dim>::cell_iterator cell = tria.begin(level),
+ end_cell = tria.end(level);
+ for ( ; cell != end_cell; ++cell)
+ cell_level_index.push_back (std::pair<unsigned int,unsigned int>
+ (cell->level(), cell->index()));
+ }
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::
+initialize_dof_handlers (const std::vector<const hp::DoFHandler<dim>*> &dof_handler,
+ const unsigned int)
+{
+ dof_handlers.active_dof_handler = DoFHandlers::hp;
+ dof_handlers.n_dof_handlers = dof_handler.size();
+ dof_handlers.hp_dof_handler.resize (dof_handlers.n_dof_handlers);
+ for (unsigned int no=0; no<dof_handlers.n_dof_handlers; ++no)
+ dof_handlers.hp_dof_handler[no] = dof_handler[no];
+
+ dof_info.resize (dof_handlers.n_dof_handlers);
+
+ // go through cells on zeroth level and then
+ // successively step down into children. This
+ // gives a z-ordering of the cells, which is
+ // beneficial when setting up neighboring
+ // relations between cells for thread
+ // parallelization
+ const unsigned int n_mpi_procs = size_info.n_procs;
+ const unsigned int my_pid = size_info.my_pid;
+
+ // if we have no level given, use the same as
+ // for the standard DoFHandler, otherwise we
+ // must loop through the respective level
+ const Triangulation<dim> &tria = dof_handler[0]->get_tria();
+
+ if (n_mpi_procs == 1)
+ {
+ cell_level_index.reserve (tria.n_active_cells());
+ }
+ typename hp::DoFHandler<dim>::cell_iterator cell = dof_handler[0]->begin(0),
+ end_cell = dof_handler[0]->end(0);
+ for ( ; cell != end_cell; ++cell)
+ internal::resolve_cell (cell, cell_level_index,
+ my_pid);
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::initialize_indices
+(const std::vector<const ConstraintMatrix*> &constraint,
+ const std::vector<IndexSet> &locally_owned_set)
+{
+ const unsigned int n_fe = dof_handlers.n_dof_handlers;
+ const unsigned int n_active_cells = cell_level_index.size();
+ const unsigned int n_mpi_procs = size_info.n_procs;
+
+ AssertDimension (n_active_cells, cell_level_index.size());
+ AssertDimension (n_fe, locally_owned_set.size());
+ AssertDimension (n_fe, constraint.size());
+
+ std::vector<unsigned int> local_dof_indices;
+ std::vector<std::vector<unsigned int> > ghost_dofs(n_fe);
+ std::vector<std::vector<std::vector<unsigned int> > > lexicographic_inv(n_fe);
+
+ internal::MatrixFreeFunctions::internal::ConstraintValues<double> constraint_values;
+ std::vector<unsigned int> constraint_indices;
+
+ for(unsigned int no=0; no<n_fe; ++no)
+ {
+ std::vector<const FiniteElement<dim>*> fes;
+ if (dof_handlers.active_dof_handler == DoFHandlers::hp)
+ {
+ const hp::DoFHandler<dim> *hpdof = dof_handlers.hp_dof_handler[no];
+ const hp::FECollection<dim> &fe = hpdof->get_fe();
+ for (unsigned int f=0; f<fe.size(); ++f)
+ fes.push_back (&fe[f]);
+
+ dof_info[no].cell_active_fe_index.resize(n_active_cells,
+ numbers::invalid_unsigned_int);
+ dof_info[no].max_fe_index = fe.size();
+ dof_info[no].fe_index_conversion.resize (fe.size());
+ for (unsigned int ind=0; ind<hpdof->get_fe().size(); ++ind)
+ dof_info[no].fe_index_conversion[ind] =
+ std::pair<unsigned int,unsigned int>(fe[ind].degree,
+ fe[ind].dofs_per_cell);
+ }
+ else
+ {
+ const DoFHandler<dim> * dofh =
+ dof_handlers.active_dof_handler == DoFHandlers::usual ?
+ &*dof_handlers.dof_handler[no] : &*dof_handlers.mg_dof_handler[no];
+ fes.push_back (&dofh->get_fe());
+ dof_info[no].max_fe_index = 1;
+ }
+
+ lexicographic_inv[no].resize (fes.size());
+ for (unsigned int fe_index = 0; fe_index<fes.size(); ++fe_index)
+ {
+ const FiniteElement<dim> &fe = *fes[fe_index];
+ Assert (fe.n_base_elements() == 1,
+ ExcMessage ("MatrixFree only works for DoFHandler with one base element"));
+ const unsigned int n_fe_components = fe.element_multiplicity (0);
+
+ // cache number of finite elements and
+ // dofs_per_cell
+ dof_info[no].dofs_per_cell.push_back (fe.dofs_per_cell);
+ dof_info[no].dofs_per_face.push_back (fe.dofs_per_face);
+ dof_info[no].n_components = n_fe_components;
+
+
+ // get permutation that gives lexicographic
+ // renumbering of the cell dofs
+ // renumber (this is necessary for FE_Q, for
+ // example, since there the vertex DoFs come
+ // first, which is incompatible with the
+ // lexicographic ordering necessary to apply
+ // tensor products efficiently)
+ const FE_Poly<TensorProductPolynomials<dim>,dim,dim> *cast_fe =
+ dynamic_cast<const FE_Poly<TensorProductPolynomials<dim>,dim,dim>*>
+ (&fe.base_element(0));
+ // This class currently only works for
+ // elements derived from
+ // FE_Poly<TensorProductPolynomials<dim>,dim,dim>.
+ // For any other element, the dynamic cast
+ // above will fail and give cast_fe == 0.
+ Assert (cast_fe != 0, ExcNotImplemented());
+
+ // create a derived finite element that gives
+ // us access to the inverse numbering (which
+ // we need in order to get a lexicographic
+ // ordering of local degrees of freedom)
+ const internal::MatrixFreeFunctions::internal::FE_PolyAccess<dim,dim>&fe_acc =
+ static_cast<const internal::MatrixFreeFunctions::internal::
+ FE_PolyAccess<dim,dim> &>(*cast_fe);
+ if (n_fe_components == 1)
+ {
+ lexicographic_inv[no][fe_index] = fe_acc.get_numbering_inverse();
+ AssertDimension (lexicographic_inv[no][fe_index].size(),
+ dof_info[no].dofs_per_cell[fe_index]);
+ }
+ else
+ {
+ // ok, we have more than one component
+ Assert (n_fe_components > 1, ExcInternalError());
+ std::vector<unsigned int> scalar_lex=fe_acc.get_numbering();
+ AssertDimension (scalar_lex.size() * n_fe_components,
+ dof_info[no].dofs_per_cell[fe_index]);
+ lexicographic_inv[no][fe_index].resize (dof_info[no].dofs_per_cell[fe_index]);
+ std::vector<unsigned int> lexicographic (dof_info[no].dofs_per_cell[fe_index]);
+ for (unsigned int comp=0; comp<n_fe_components; ++comp)
+ for (unsigned int i=0; i<scalar_lex.size(); ++i)
+ lexicographic[fe.component_to_system_index(comp,i)]
+ = scalar_lex.size () * comp + scalar_lex[i];
+
+ // invert numbering
+ for (unsigned int i=0; i<lexicographic.size(); ++i)
+ lexicographic_inv[no][fe_index][lexicographic[i]] = i;
+
+#ifdef DEBUG
+ // check that we got a useful permutation
+ lexicographic = lexicographic_inv[no][fe_index];
+ std::sort(lexicographic.begin(), lexicographic.end());
+ for (unsigned int i=0; i<lexicographic.size(); ++i)
+ AssertDimension (lexicographic[i], i);
+#endif
+ }
+ AssertDimension (lexicographic_inv[no][fe_index].size(),
+ dof_info[no].dofs_per_cell[fe_index]);
+ }
+
+ // set locally owned range for each component
+ Assert (locally_owned_set[no].is_contiguous(), ExcNotImplemented());
+ dof_info[no].vector_partitioner.reset
+ (new Utilities::MPI::Partitioner(locally_owned_set[no], size_info.communicator));
+
+ // initialize the arrays for indices
+ dof_info[no].row_starts.resize (n_active_cells+1);
+ dof_info[no].row_starts[0] = std_cxx1x::tuple<unsigned int,unsigned int,
+ unsigned int> (0,0,0);
+ dof_info[no].dof_indices.reserve
+ ((n_active_cells*dof_info[no].dofs_per_cell[0]*3)/2);
+
+ // cache the constrained indices for use in
+ // matrix-vector products
+ {
+ const unsigned int
+ start_index = dof_info[no].vector_partitioner->local_range().first,
+ end_index = dof_info[no].vector_partitioner->local_range().second;
+ for (unsigned int i=start_index; i<end_index; ++i)
+ if (constraint[no]->is_constrained(i)==true)
+ dof_info[no].constrained_dofs.push_back(i-start_index);
+ }
+
+ if (n_mpi_procs > 1)
+ ghost_dofs[no].reserve (locally_owned_set[no].n_elements()/10+1);
+ }
+
+ // extract all the global indices associated
+ // with the computation, and form the ghost
+ // indices
+ std::vector<unsigned int> boundary_cells;
+ for(unsigned int counter = 0 ; counter < n_active_cells ; ++counter)
+ {
+ bool cell_at_boundary = false;
+ for (unsigned int no=0; no<n_fe; ++no)
+ {
+ // OK, read indices from standard DoFHandler
+ // or active indices in MGDoFHandler. That is
+ // the usual stuff
+ if (dof_handlers.active_dof_handler == DoFHandlers::usual ||
+ (dof_handlers.active_dof_handler == DoFHandlers::multigrid &&
+ dof_handlers.level == numbers::invalid_unsigned_int))
+ {
+ const DoFHandler<dim> * dofh =
+ dof_handlers.active_dof_handler == DoFHandlers::usual ?
+ &*dof_handlers.dof_handler[no] : &*dof_handlers.mg_dof_handler[no];
+ typename DoFHandler<dim>::active_cell_iterator
+ cell_it (&dofh->get_tria(),
+ cell_level_index[counter].first,
+ cell_level_index[counter].second,
+ dofh);
+ local_dof_indices.resize (dof_info[no].dofs_per_cell[0]);
+ cell_it->get_dof_indices(local_dof_indices);
+ dof_info[no].read_dof_indices (local_dof_indices,
+ lexicographic_inv[no][0],
+ *constraint[no], counter,
+ constraint_values,
+ cell_at_boundary);
+ }
+ // ok, now we are requested to use a level in
+ // a MGDoFHandler
+ else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid &&
+ dof_handlers.level != numbers::invalid_unsigned_int)
+ {
+ const MGDoFHandler<dim> * dofh =
+ dof_handlers.mg_dof_handler[no];
+ AssertIndexRange (dof_handlers.level, dofh->get_tria().n_levels());
+ typename MGDoFHandler<dim>::cell_iterator
+ cell_it (&dofh->get_tria(),
+ cell_level_index[counter].first,
+ cell_level_index[counter].second,
+ dofh);
+ local_dof_indices.resize (dof_info[no].dofs_per_cell[0]);
+ cell_it->get_mg_dof_indices(local_dof_indices);
+ dof_info[no].read_dof_indices (local_dof_indices,
+ lexicographic_inv[no][0],
+ *constraint[no], counter,
+ constraint_values,
+ cell_at_boundary);
+ }
+ else if (dof_handlers.active_dof_handler == DoFHandlers::hp)
+ {
+ const hp::DoFHandler<dim> * dofh =
+ dof_handlers.hp_dof_handler[no];
+ typename hp::DoFHandler<dim>::active_cell_iterator
+ cell_it (&dofh->get_tria(),
+ cell_level_index[counter].first,
+ cell_level_index[counter].second,
+ dofh);
+ dof_info[no].cell_active_fe_index[counter] =
+ cell_it->active_fe_index();
+ local_dof_indices.resize (cell_it->get_fe().dofs_per_cell);
+ cell_it->get_dof_indices(local_dof_indices);
+ dof_info[no].read_dof_indices (local_dof_indices,
+ lexicographic_inv[no][cell_it->active_fe_index()],
+ *constraint[no], counter,
+ constraint_values,
+ cell_at_boundary);
+ }
+ else
+ {
+ Assert (false, ExcNotImplemented());
+ }
+ }
+
+ // if we found dofs on some FE component that
+ // belong to other processors, the cell is
+ // added to the boundary cells.
+ if (cell_at_boundary == true)
+ boundary_cells.push_back(counter);
+ }
+
+ // try to make the number of boundary cells
+ // divisible by the number of vectors in
+ // vectorization
+ const unsigned int n_vectors = VectorizedArray<Number>::n_array_elements;
+ {
+ unsigned int n_max_boundary_cells = boundary_cells.size();
+ unsigned int n_boundary_cells = n_max_boundary_cells;
+
+ /*
+ // try to balance the number of cells before
+ // and after the boundary part on each
+ // processor. probably not worth it!
+#if DEAL_II_COMPILER_SUPPORTS_MPI
+ MPI_Allreduce (&n_boundary_cells, &n_max_boundary_cells, 1, MPI_UNSIGNED,
+ MPI_MAX, size_info.communicator);
+#endif
+ if (n_max_boundary_cells > n_active_cells)
+ n_max_boundary_cells = n_active_cells;
+ */
+
+ unsigned int fillup_needed =
+ (n_vectors - n_boundary_cells%n_vectors)%n_vectors;
+ /*
+ if (task_info.use_multithreading == true)
+ fillup_needed =
+ (n_vectors - n_boundary_cells%n_vectors)%n_vectors;
+ else
+ fillup_needed = (n_max_boundary_cells +
+ (n_vectors - n_max_boundary_cells%n_vectors)%n_vectors -
+ n_boundary_cells);
+ */
+ if (fillup_needed > 0 && n_boundary_cells < n_active_cells)
+ {
+ // fill additional cells into the list of
+ // boundary cells to get a balanced number. Go
+ // through the indices successively until we
+ // found enough indices
+ std::vector<unsigned int> new_boundary_cells;
+ new_boundary_cells.reserve (n_max_boundary_cells);
+
+ unsigned int next_free_slot = 0, bound_index = 0;
+ while (fillup_needed > 0 && bound_index < boundary_cells.size())
+ {
+ if (next_free_slot < boundary_cells[bound_index])
+ {
+ // check if there are enough cells to fill
+ // with in the current slot
+ if (next_free_slot + fillup_needed <= boundary_cells[bound_index])
+ {
+ for (unsigned int j=boundary_cells[bound_index]-fillup_needed;
+ j < boundary_cells[bound_index]; ++j)
+ new_boundary_cells.push_back(j);
+ fillup_needed = 0;
+ }
+ // ok, not enough indices, so just take them
+ // all up to the next boundary cell
+ else
+ {
+ for (unsigned int j=next_free_slot;
+ j<boundary_cells[bound_index]; ++j)
+ new_boundary_cells.push_back(j);
+ fillup_needed -= boundary_cells[bound_index]-next_free_slot;
+ }
+ }
+ new_boundary_cells.push_back(boundary_cells[bound_index]);
+ next_free_slot = boundary_cells[bound_index]+1;
+ ++bound_index;
+ }
+ while (fillup_needed > 0 && (new_boundary_cells.size()==0 ||
+ new_boundary_cells.back()<n_active_cells-1))
+ new_boundary_cells.push_back(new_boundary_cells.back()+1);
+ while (bound_index<boundary_cells.size())
+ new_boundary_cells.push_back(boundary_cells[bound_index++]);
+
+ boundary_cells.swap(new_boundary_cells);
+ }
+ }
+
+ // set the number of cells
+ const unsigned int n_boundary_cells = boundary_cells.size();
+ std::sort (boundary_cells.begin(), boundary_cells.end());
+ std::vector<unsigned int> irregular_cells;
+ size_info.make_layout (n_active_cells, n_boundary_cells, n_vectors,
+ irregular_cells);
+
+ for (unsigned int no=0; no<n_fe; ++no)
+ dof_info[no].assign_ghosts (boundary_cells);
+
+ // reorganize the indices: we want to put the
+ // boundary cells at the beginning for
+ // multithreading. So just renumber the cell
+ // indices and put them at the beginning of
+ // the list that determines the order of the
+ // cells
+ std::vector<unsigned int> renumbering (n_active_cells,
+ numbers::invalid_unsigned_int);
+ {
+ std::vector<unsigned int> reverse_numbering (n_active_cells,
+ numbers::invalid_unsigned_int);
+ unsigned int counter;
+ if (task_info.use_multithreading == true)
+ {
+ for (unsigned int j=0; j<n_boundary_cells; ++j)
+ reverse_numbering[boundary_cells[j]] = j;
+ counter = n_boundary_cells;
+ for (unsigned int j=0; j<n_active_cells; ++j)
+ if (reverse_numbering[j] == numbers::invalid_unsigned_int)
+ reverse_numbering[j] = counter++;
+
+ size_info.boundary_cells_end = (size_info.boundary_cells_end -
+ size_info.boundary_cells_start);
+ size_info.boundary_cells_start = 0;
+ }
+ // Otherwise, we put the boundary cells to
+ // the middle.
+ else
+ {
+ for (unsigned int j=0; j<n_boundary_cells; ++j)
+ reverse_numbering[boundary_cells[j]] = j+n_vectors*size_info.boundary_cells_start;
+ counter = 0;
+ unsigned int j = 0;
+ while (counter < n_active_cells &&
+ counter < n_vectors * size_info.boundary_cells_start)
+ {
+ if (reverse_numbering[j] == numbers::invalid_unsigned_int)
+ reverse_numbering[j] = counter++;
+ j++;
+ }
+ counter = std::min (n_vectors*size_info.boundary_cells_start+n_boundary_cells,
+ n_active_cells);
+ if (counter < n_active_cells)
+ {
+ for ( ; j<n_active_cells; ++j)
+ if (reverse_numbering[j] == numbers::invalid_unsigned_int)
+ reverse_numbering[j] = counter++;
+ }
+ }
+ AssertDimension (counter, n_active_cells);
+ for (unsigned int j=0; j<n_active_cells; ++j)
+ {
+ AssertIndexRange (reverse_numbering[j], n_active_cells);
+ renumbering[reverse_numbering[j]] = j;
+ }
+ }
+
+ // reorder cells so that we can parallelize by
+ // threads
+ if (task_info.use_multithreading == true)
+ {
+ if(task_info.use_partition_partition == true)
+ dof_info[0].make_thread_graph_partition_partition
+ (size_info, task_info, renumbering, irregular_cells,
+ dof_handlers.active_dof_handler == DoFHandlers::hp);
+ else
+ dof_info[0].make_thread_graph_partition_color
+ (size_info, task_info, renumbering, irregular_cells,
+ dof_handlers.active_dof_handler == DoFHandlers::hp);
+ }
+ else
+ {
+ // In case, we have an hp-dofhandler, we have
+ // to reorder the cell according to the
+ // polynomial degree on the cell.
+ if (dof_handlers.active_dof_handler == DoFHandlers::hp)
+ {
+ const unsigned int max_fe_index =
+ dof_info[0].max_fe_index;
+ irregular_cells.resize (0);
+ irregular_cells.resize (size_info.n_macro_cells+3*max_fe_index);
+ const std::vector<unsigned int> &cell_active_fe_index =
+ dof_info[0].cell_active_fe_index;
+ std::vector<std::vector<unsigned int> > renumbering_fe_index;
+ renumbering_fe_index.resize(max_fe_index);
+ unsigned int counter,n_macro_cells_before = 0;
+ const unsigned int
+ start_bound = std::min (size_info.n_active_cells,
+ size_info.boundary_cells_start*n_vectors),
+ end_bound = std::min (size_info.n_active_cells,
+ size_info.boundary_cells_end*n_vectors);
+ for(counter=0; counter<start_bound; counter++)
+ {
+ renumbering_fe_index[cell_active_fe_index[renumbering[counter]]].
+ push_back(renumbering[counter]);
+ }
+ counter = 0;
+ for (unsigned int j=0;j<max_fe_index;j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].size();jj++)
+ renumbering[counter++] = renumbering_fe_index[j][jj];
+ irregular_cells[renumbering_fe_index[j].size()/n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].size()+n_vectors-1)/
+ n_vectors;
+ renumbering_fe_index[j].resize(0);
+ }
+ unsigned int new_boundary_start = n_macro_cells_before;
+ for(counter = start_bound; counter < end_bound; counter++)
+ {
+ renumbering_fe_index[cell_active_fe_index[renumbering[counter]]].
+ push_back(renumbering[counter]);
+ }
+ counter = start_bound;
+ for (unsigned int j=0;j<max_fe_index;j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].size();jj++)
+ renumbering[counter++] = renumbering_fe_index[j][jj];
+ irregular_cells[renumbering_fe_index[j].size()/n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].size()+n_vectors-1)/
+ n_vectors;
+ renumbering_fe_index[j].resize(0);
+ }
+ unsigned int new_boundary_end = n_macro_cells_before;
+ for(counter=end_bound; counter<n_active_cells; counter++)
+ {
+ renumbering_fe_index[cell_active_fe_index[renumbering[counter]]].
+ push_back(renumbering[counter]);
+ }
+ counter = end_bound;
+ for (unsigned int j=0;j<max_fe_index;j++)
+ {
+ for(unsigned int jj=0;jj<renumbering_fe_index[j].size();jj++)
+ renumbering[counter++] = renumbering_fe_index[j][jj];
+ irregular_cells[renumbering_fe_index[j].size()/n_vectors+
+ n_macro_cells_before] =
+ renumbering_fe_index[j].size()%n_vectors;
+ n_macro_cells_before += (renumbering_fe_index[j].size()+n_vectors-1)/
+ n_vectors;
+ }
+ AssertIndexRange (n_macro_cells_before,
+ size_info.n_macro_cells + 3*max_fe_index+1);
+ irregular_cells.resize (n_macro_cells_before);
+ size_info.n_macro_cells = n_macro_cells_before;
+ size_info.boundary_cells_start = new_boundary_start;
+ size_info.boundary_cells_end = new_boundary_end;
+ }
+ }
+ // Finally perform the renumbering. We also
+ // want to group several cells together to one
+ // "macro-cell" for vectorization (where the
+ // arithmetic operations will then be done
+ // simultaneously).
+#ifdef DEBUG
+ {
+ std::vector<unsigned int> sorted_renumbering (renumbering);
+ std::sort (sorted_renumbering.begin(), sorted_renumbering.end());
+ for (unsigned int i=0; i<sorted_renumbering.size(); ++i)
+ Assert (sorted_renumbering[i] == i, ExcInternalError());
+ }
+#endif
+ {
+ std::vector<std::pair<unsigned int,unsigned int> >
+ cell_level_index_old;
+ cell_level_index.swap (cell_level_index_old);
+ cell_level_index.reserve(size_info.n_macro_cells*n_vectors);
+ unsigned int position_cell=0;
+ for (unsigned int i=0; i<size_info.n_macro_cells; ++i)
+ {
+ unsigned int n_comp = (irregular_cells[i]>0)?
+ irregular_cells[i] : n_vectors;
+ for (unsigned int j=0; j<n_comp; ++j)
+ cell_level_index.push_back
+ (cell_level_index_old[renumbering[position_cell+j]]);
+
+ // generate a cell and level index
+ // also when we have not filled up
+ // n_vectors cells. This is needed for
+ // MappingInfo when the transformation
+ // data is initialized. We just set
+ // the value to the last valid cell in
+ // that case.
+ for (unsigned int j=n_comp; j<n_vectors; ++j)
+ cell_level_index.push_back
+ (cell_level_index_old[renumbering[position_cell+n_comp-1]]);
+ position_cell += n_comp;
+ }
+ AssertDimension (position_cell, size_info.n_active_cells);
+ AssertDimension (cell_level_index.size(),size_info.n_macro_cells*n_vectors);
+ }
+
+ // set constraint pool and reorder the indices
+ constraint_pool.row_index =
+ constraint_values.constraint_pool.row_index;
+ constraint_pool.data.resize (constraint_values.constraint_pool.data.size());
+ std::copy (constraint_values.constraint_pool.data.begin(),
+ constraint_values.constraint_pool.data.end(),
+ constraint_pool.data.begin());
+ for (unsigned int no=0; no<n_fe; ++no)
+ {
+ dof_info[no].reorder_cells(size_info, renumbering,
+ constraint_pool.row_index,
+ irregular_cells, n_vectors);
+ }
+
+ indices_are_initialized = true;
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::clear()
+{
+ dof_info.clear();
+ mapping_info.clear();
+ cell_level_index.clear();
+ size_info.clear();
+ task_info.clear();
+ indices_are_initialized = false;
+ mapping_is_initialized = false;
+}
+
+
+
+template <int dim, typename Number>
+std::size_t MatrixFree<dim,Number>::memory_consumption () const
+{
+ std::size_t memory = MemoryConsumption::memory_consumption (dof_info);
+ memory += MemoryConsumption::memory_consumption (cell_level_index);
+ memory += MemoryConsumption::memory_consumption (fe_evaluation_data);
+ memory += MemoryConsumption::memory_consumption (constraint_pool);
+ memory += MemoryConsumption::memory_consumption (task_info);
+ memory += sizeof(this);
+ memory += mapping_info.memory_consumption();
+ return memory;
+}
+
+
+template <int dim, typename Number>
+template <typename STREAM>
+void MatrixFree<dim,Number>::print_memory_consumption (STREAM &out) const
+{
+ out << " Memory cell FE operator total: --> ";
+ size_info.print_mem (out, memory_consumption());
+ out << " Memory cell index: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (cell_level_index));
+ for (unsigned int j=0; j<dof_info.size(); ++ j)
+ {
+ out << " Memory DoFInfo component "<< j << std::endl;
+ dof_info[j].print_memory_consumption(out, size_info);
+ }
+
+ out << " Memory mapping info" << std::endl;
+ mapping_info.print_memory_consumption(out, size_info);
+
+ out << " Memory unit cell shape data: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (fe_evaluation_data));
+ if (task_info.use_multithreading == true)
+ {
+ out << " Memory task partitioning info: ";
+ size_info.print_mem (out, MemoryConsumption::memory_consumption (task_info));
+ }
+}
+
+
+
+template <int dim, typename Number>
+void MatrixFree<dim,Number>::print (std::ostream &out) const
+{
+ // print indices local to global
+ for (unsigned int no=0; no<dof_info.size(); ++no)
+ {
+ out << "\n-- Index data for component " << no << " --" << std::endl;
+ dof_info[no].print (constraint_pool, out);
+ out << std::endl;
+ }
+}
+
+
+DEAL_II_NAMESPACE_CLOSE
MATRIX_FUNCTIONS(SparseMatrix<double>);
MATRIX_FUNCTIONS(SparseMatrix<float>);
+MATRIX_FUNCTIONS(FullMatrix<double>);
+MATRIX_FUNCTIONS(FullMatrix<float>);
MATRIX_VECTOR_FUNCTIONS(SparseMatrix<float>, Vector<float>);
BLOCK_MATRIX_FUNCTIONS(BlockSparseMatrix<double>);
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2011 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+#include <deal.II/matrix_free/matrix_free.templates.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/conditional_ostream.h>
+
+#include <iostream>
+
+DEAL_II_NAMESPACE_OPEN
+
+#include "matrix_free.inst"
+
+DEAL_II_NAMESPACE_CLOSE
--- /dev/null
+//---------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2010 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//---------------------------------------------------------------------------
+
+for (deal_II_dimension : DIMENSIONS)
+{
+ template class MatrixFree<deal_II_dimension,double>;
+ template class MatrixFree<deal_II_dimension,float>;
+
+ // reinit for DoFHandler
+ template void MatrixFree<deal_II_dimension,double>::reinit
+ (const Mapping<deal_II_dimension> &,
+ const std::vector<const DoFHandler<deal_II_dimension>*> &,
+ const std::vector<const ConstraintMatrix*> &,
+ const std::vector<IndexSet> &,
+ const std::vector<Quadrature<1> > &,
+ const MatrixFree<deal_II_dimension,double>::AdditionalData);
+ template void MatrixFree<deal_II_dimension,float>::reinit
+ (const Mapping<deal_II_dimension> &,
+ const std::vector<const DoFHandler<deal_II_dimension>*> &,
+ const std::vector<const ConstraintMatrix*> &,
+ const std::vector<IndexSet> &,
+ const std::vector<Quadrature<1> > &,
+ const MatrixFree<deal_II_dimension,float>::AdditionalData);
+
+
+ // reinit for MGDoFHandler
+ template void MatrixFree<deal_II_dimension,double>::reinit
+ (const Mapping<deal_II_dimension> &,
+ const std::vector<const MGDoFHandler<deal_II_dimension>*> &,
+ const std::vector<const ConstraintMatrix*> &,
+ const std::vector<IndexSet> &,
+ const std::vector<Quadrature<1> > &,
+ const MatrixFree<deal_II_dimension,double>::AdditionalData);
+ template void MatrixFree<deal_II_dimension,float>::reinit
+ (const Mapping<deal_II_dimension> &,
+ const std::vector<const MGDoFHandler<deal_II_dimension>*> &,
+ const std::vector<const ConstraintMatrix*> &,
+ const std::vector<IndexSet> &,
+ const std::vector<Quadrature<1> > &,
+ const MatrixFree<deal_II_dimension,float>::AdditionalData);
+
+ template void MatrixFree<deal_II_dimension,double>::
+ print_memory_consumption<std::ostream> (std::ostream &) const;
+ template void MatrixFree<deal_II_dimension,double>::
+ print_memory_consumption<ConditionalOStream> (ConditionalOStream &) const;
+
+ template void MatrixFree<deal_II_dimension,float>::
+ print_memory_consumption<std::ostream> (std::ostream &) const;
+ template void MatrixFree<deal_II_dimension,float>::
+ print_memory_consumption<ConditionalOStream> (ConditionalOStream &) const;
+}
+
multigrid \
bits \
hp \
+ matrix_free \
aniso \
codim_one \
serialization \
--- /dev/null
+//-----------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//-----------------------------------------------------------------------------
+
+// test for AlignedVector<unsigned int> which tests the basic stuff in the
+// aligned vector
+
+#include "../tests.h"
+#include <iomanip>
+#include <fstream>
+#include <cmath>
+
+#include <deal.II/base/vectorization.h>
+
+
+void test ()
+{
+ typedef AlignedVector<unsigned int> VEC;
+ VEC a(4);
+ deallog << "Constructor: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ deallog << a[i] << " ";
+ deallog << std::endl;
+
+ a[2] = 1;
+ a.push_back (5);
+ a.push_back (42);
+
+ VEC b (a);
+ b.push_back (27);
+ a.insert_back (b.begin(), b.end());
+
+ deallog << "Insertion: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ deallog << a[i] << " ";
+ deallog << std::endl;
+
+ a.resize(4);
+ deallog << "Shrinking: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ deallog << a[i] << " ";
+ deallog << std::endl;
+
+ a.reserve(100);
+ deallog << "Reserve: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ deallog << a[i] << " ";
+ deallog << std::endl;
+
+ a = b;
+ deallog << "Assignment: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ deallog << a[i] << " ";
+ deallog << std::endl;
+
+ // check setting elements for large vectors
+ a.resize (0);
+ a.resize (100000, 1);
+ deallog << "Check large initialization: ";
+ for (unsigned int i=0; i<100000; ++i)
+ AssertDimension (a[i], 1);
+ deallog << "OK" << std::endl;
+
+ // check resize for large vectors
+ deallog << "Check large resize: ";
+ a.resize (200000, 2);
+ a.resize (400000);
+ for (unsigned int i=0; i<100000; ++i)
+ AssertDimension (a[i], 1);
+ for (unsigned int i=100000; i<200000; ++i)
+ AssertDimension (a[i], 2);
+ for (unsigned int i=200000; i<400000; ++i)
+ AssertDimension (a[i], 0);
+ deallog << "OK" << std::endl;
+}
+
+
+
+
+int main()
+{
+ std::ofstream logfile("vectorization_01/output");
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ test ();
+}
--- /dev/null
+
+DEAL::Constructor: 0 0 0 0
+DEAL::Insertion: 0 0 1 0 5 42 0 0 1 0 5 42 27
+DEAL::Shrinking: 0 0 1 0
+DEAL::Reserve: 0 0 1 0
+DEAL::Assignment: 0 0 1 0 5 42 27
+DEAL::Check large initialization: OK
+DEAL::Check large resize: OK
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+//-----------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//-----------------------------------------------------------------------------
+
+// test for AlignedVector<VectorizedArray<double> >
+
+#include "../tests.h"
+#include <iomanip>
+#include <fstream>
+#include <cmath>
+
+#include <deal.II/base/vectorization.h>
+
+
+void test ()
+{
+ typedef VectorizedArray<double> vector_t;
+ const unsigned int n_vectors = VectorizedArray<double>::n_array_elements;
+ typedef AlignedVector<vector_t> VEC;
+ std::vector<double> a_ref(4), b_ref;
+ VEC a(4);
+ deallog << "Constructor: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == 0, ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ a[2] = 1;
+ a.push_back (make_vectorized_array(5.));
+ a.push_back (make_vectorized_array(42.));
+ a_ref[2] = 1;
+ a_ref.push_back(5);
+ a_ref.push_back(42);
+
+ VEC b (a);
+ b.push_back (make_vectorized_array(27.));
+ a.insert_back (b.begin(), b.end());
+
+ // keep reference in sync
+ b_ref = a_ref;
+ b_ref.push_back (27);
+ a_ref.insert (a_ref.end(), b_ref.begin(), b_ref.end());
+
+ deallog << "Insertion: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == a_ref[i], ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ a.resize(4);
+ deallog << "Shrinking: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == a_ref[i], ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ a.reserve(100);
+ deallog << "Reserve: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == a_ref[i], ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ a = b;
+ a_ref = b_ref;
+ deallog << "Assignment: ";
+ for (unsigned int i=0; i<a.size(); ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == a_ref[i], ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ // check setting elements for large vectors
+ a.resize (0);
+ a.resize (100000, make_vectorized_array(1.));
+ deallog << "Check large initialization: ";
+ for (unsigned int i=0; i<100000; ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == 1., ExcInternalError());
+ deallog << "OK" << std::endl;
+
+ // check resize for large vectors
+ deallog << "Check large resize: ";
+ a.resize (200000, make_vectorized_array(2.));
+ a.resize (400000);
+ for (unsigned int i=0; i<100000; ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == 1., ExcInternalError());
+ for (unsigned int i=100000; i<200000; ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == 2., ExcInternalError());
+ for (unsigned int i=200000; i<400000; ++i)
+ for (unsigned int d=0; d<n_vectors; ++d)
+ Assert (a[i][d] == 0., ExcInternalError());
+ deallog << "OK" << std::endl;
+}
+
+
+
+
+int main()
+{
+ std::ofstream logfile("vectorization_02/output");
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ test ();
+}
--- /dev/null
+
+DEAL::Constructor: OK
+DEAL::Insertion: OK
+DEAL::Shrinking: OK
+DEAL::Reserve: OK
+DEAL::Assignment: OK
+DEAL::Check large initialization: OK
+DEAL::Check large resize: OK
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+//-----------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//-----------------------------------------------------------------------------
+
+// test for AlignedVector<AlignedVector<unsigned int> >
+
+#include "../tests.h"
+#include <iomanip>
+#include <fstream>
+#include <cmath>
+
+#include <deal.II/base/vectorization.h>
+
+
+typedef AlignedVector<unsigned int> VEC;
+typedef AlignedVector<VEC> VECVEC;
+void print_vec (VECVEC &v)
+{
+ for (unsigned int i=0; i<v.size(); ++i)
+ {
+ deallog << "[";
+ for (unsigned int j=0; j<v[i].size(); ++j)
+ deallog << v[i][j] << " ";
+ deallog << "]";
+ }
+ deallog << std::endl;
+}
+
+void test ()
+{
+ typedef AlignedVector<unsigned int> VEC;
+ VEC a(4);
+ a[0] = 2;
+ a[1] = 1;
+ a[2] = 42;
+ VECVEC v (2);
+ deallog << "Constructor: ";
+ print_vec(v);
+
+ v[0] = a;
+ v[1] = a;
+
+ deallog << "Assignment: ";
+ print_vec(v);
+
+ VECVEC w (v);
+ deallog << "Assignment vector: ";
+ print_vec(w);
+ deallog << "Data consistency after assignment: ";
+ print_vec(v);
+
+ a[1] = 41;
+ a.push_back (100);
+ v.push_back(a);
+ deallog << "Insertion: ";
+ print_vec (v);
+
+ v.resize(1);
+ deallog << "Shrinking: ";
+ print_vec (v);
+
+ v.reserve(100);
+ deallog << "Reserve: ";
+ print_vec (v);
+
+ v.resize (10);
+ deallog << "Resize: ";
+ print_vec (v);
+
+ v.resize(0);
+ deallog << "Clear: ";
+ print_vec (v);
+}
+
+
+
+
+int main()
+{
+ std::ofstream logfile("vectorization_03/output");
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ test ();
+}
--- /dev/null
+
+DEAL::Constructor: [][]
+DEAL::Assignment: [2 1 42 0 ][2 1 42 0 ]
+DEAL::Assignment vector: [2 1 42 0 ][2 1 42 0 ]
+DEAL::Data consistency after assignment: [2 1 42 0 ][2 1 42 0 ]
+DEAL::Insertion: [2 1 42 0 ][2 1 42 0 ][2 41 42 0 100 ]
+DEAL::Shrinking: [2 1 42 0 ]
+DEAL::Reserve: [2 1 42 0 ]
+DEAL::Resize: [2 1 42 0 ][][][][][][][][][]
+DEAL::Clear:
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+
+DEAL::0042
+DEAL::42
+DEAL::3
+DEAL::413
+DEAL::1
+DEAL::-12
+DEAL::125
+DEAL::-177.002 -36.2427 43.4584
--- /dev/null
+//-----------------------------------------------------------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//-----------------------------------------------------------------------------
+
+// test for arithmetic operations on VectorizedArray
+
+#include "../tests.h"
+#include <iomanip>
+#include <limits>
+
+#include <deal.II/base/vectorization.h>
+
+
+template <typename Number>
+void test ()
+{
+ // since the number of array elements is system dependent, it is not a good
+ // idea to print them to an output file. Instead, check the values manually
+ VectorizedArray<Number> a, b, c;
+ const unsigned int n_vectors = VectorizedArray<Number>::n_array_elements;
+ a = Number(2.);
+ b = Number(-1.);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ c[i] = i;
+
+ deallog << "Addition: ";
+ VectorizedArray<Number> d = a + b;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == 1, ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Subtraction: ";
+ VectorizedArray<Number> e = d - b;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (e[i] == a[i], ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Multiplication: ";
+ d = a * c;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == a[i] * c[i], ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Division: ";
+ e = d / a;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (e[i] == c[i], ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Multiplication scalar: ";
+ a = 2. * a;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (a[i] == 4., ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Division scalar left: ";
+ d = 1. / a;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == 0.25, ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Division scalar right: ";
+ e = d / 0.25;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (e[i] == 1, ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Unary operator -: ";
+ d = -c;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == -(Number)i, ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Unary operator +: ";
+ d = c;
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == i, ExcInternalError());
+
+
+ deallog << "OK" << std::endl
+ << "Square root: ";
+ d = std::sqrt(c);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(d[i]-std::sqrt(Number(i)))<
+ std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+
+ deallog << "OK" << std::endl
+ << "Absolute value: ";
+ d = -c;
+ d = std::abs(d);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == Number(i), ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Maximum value: ";
+ d = std::max(a, c);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == std::max(a[i], c[i]), ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Minimum value: ";
+ d = std::min(0.5 * a + b, c);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (d[i] == std::min(Number(0.5 * a[i] + b[i]), c[i]), ExcInternalError());
+
+ deallog << "OK" << std::endl
+ << "Sine: ";
+ e = std::sin(b);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(e[i]-std::sin(b[i])) <
+ 10.*std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Cosine: ";
+ e = std::cos(c);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(e[i]-std::cos(c[i])) <
+ 10.*std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Tangent: ";
+ d = std::tan(e);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(d[i]-std::tan(e[i])) <
+ 10.*std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Exponential: ";
+ d = std::exp(c-a);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(d[i]-std::exp(c[i]-a[i])) <
+ 10.*std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+ deallog << "OK" << std::endl
+ << "Logarithm: ";
+ e = std::log(d);
+ for (unsigned int i=0; i<n_vectors; ++i)
+ Assert (std::fabs(e[i]-(c[i]-a[i])) <
+ 10.*std::numeric_limits<Number>::epsilon(),
+ ExcInternalError());
+ deallog << "OK" << std::endl;
+}
+
+
+
+
+int main()
+{
+ std::ofstream logfile("vectorization_04/output");
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ deallog.push("double");
+ test<double> ();
+ deallog.pop();
+ deallog.push("float");
+ test<float> ();
+ deallog.pop();
+
+ // test long double: in that case, the default
+ // path of VectorizedArray is taken no matter
+ // what was done for double or float
+ deallog.push("long double");
+ test<float> ();
+ deallog.pop();
+}
--- /dev/null
+
+DEAL:double::Addition: OK
+DEAL:double::Subtraction: OK
+DEAL:double::Multiplication: OK
+DEAL:double::Division: OK
+DEAL:double::Multiplication scalar: OK
+DEAL:double::Division scalar left: OK
+DEAL:double::Division scalar right: OK
+DEAL:double::Unary operator -: OK
+DEAL:double::Unary operator +: OK
+DEAL:double::Square root: OK
+DEAL:double::Absolute value: OK
+DEAL:double::Maximum value: OK
+DEAL:double::Minimum value: OK
+DEAL:double::Sine: OK
+DEAL:double::Cosine: OK
+DEAL:double::Tangent: OK
+DEAL:double::Exponential: OK
+DEAL:double::Logarithm: OK
+DEAL:float::Addition: OK
+DEAL:float::Subtraction: OK
+DEAL:float::Multiplication: OK
+DEAL:float::Division: OK
+DEAL:float::Multiplication scalar: OK
+DEAL:float::Division scalar left: OK
+DEAL:float::Division scalar right: OK
+DEAL:float::Unary operator -: OK
+DEAL:float::Unary operator +: OK
+DEAL:float::Square root: OK
+DEAL:float::Absolute value: OK
+DEAL:float::Maximum value: OK
+DEAL:float::Minimum value: OK
+DEAL:float::Sine: OK
+DEAL:float::Cosine: OK
+DEAL:float::Tangent: OK
+DEAL:float::Exponential: OK
+DEAL:float::Logarithm: OK
+DEAL:long double::Addition: OK
+DEAL:long double::Subtraction: OK
+DEAL:long double::Multiplication: OK
+DEAL:long double::Division: OK
+DEAL:long double::Multiplication scalar: OK
+DEAL:long double::Division scalar left: OK
+DEAL:long double::Division scalar right: OK
+DEAL:long double::Unary operator -: OK
+DEAL:long double::Unary operator +: OK
+DEAL:long double::Square root: OK
+DEAL:long double::Absolute value: OK
+DEAL:long double::Maximum value: OK
+DEAL:long double::Minimum value: OK
+DEAL:long double::Sine: OK
+DEAL:long double::Cosine: OK
+DEAL:long double::Tangent: OK
+DEAL:long double::Exponential: OK
+DEAL:long double::Logarithm: OK
--- /dev/null
+//------------------ constraints_local_to_global.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//------------------ constraints_local_to_global.cc ------------------------
+
+
+// this function tests the correctness of the implementation of
+// ConstraintMatrix::distribute_local_to_global for FullMatrix by comparing
+// the results with a sparse matrix. As a test case, we use a square mesh that
+// is refined once globally and then the first cell is refined adaptively.
+
+#include "../tests.h"
+
+#include <deal.II/base/function.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_accessor.h>
+#include <deal.II/grid/grid_refinement.h>
+#include <deal.II/dofs/dof_accessor.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/numerics/vectors.h>
+#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
+
+#include <fstream>
+#include <iostream>
+#include <complex>
+
+std::ofstream logfile("constraints_local_to_global/output");
+
+template <int dim>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.begin()->face(0)->set_boundary_indicator(1);
+ tria.refine_global(1);
+ tria.begin_active()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ FE_Q<dim> fe (1);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ SparsityPattern sparsity;
+ {
+ CompressedSimpleSparsityPattern csp (dof.n_dofs(), dof.n_dofs());
+ DoFTools::make_sparsity_pattern (dof, csp, constraints, false);
+ sparsity.copy_from (csp);
+ }
+ SparseMatrix<double> sparse (sparsity);
+ FullMatrix<double> full (dof.n_dofs(), dof.n_dofs());
+
+ FullMatrix<double> local_mat (fe.dofs_per_cell, fe.dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (fe.dofs_per_cell);
+
+ // loop over cells, fill local matrix with
+ // random values, insert both into sparse and
+ // full matrix. Make some random entries equal
+ // to zero
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(), endc = dof.end();
+ unsigned int counter = 0;
+ for ( ; cell != endc; ++cell)
+ {
+ for (unsigned int i=0; i<fe.dofs_per_cell; ++i)
+ for (unsigned int j=0; j<fe.dofs_per_cell; ++j, ++counter)
+ if (counter % 42 == 0)
+ local_mat(i,j) = 0;
+ else
+ local_mat (i,j) = (double)rand() / RAND_MAX;
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (local_mat, local_dof_indices,
+ sparse);
+ constraints.distribute_local_to_global (local_mat, local_dof_indices,
+ full);
+ }
+
+ // now check that the entries are indeed the
+ // same by copying the sparse matrix into a
+ // full matrix and checking the Frobenius norm
+ // of the difference matrix
+ FullMatrix<double> ref;
+ ref.copy_from (sparse);
+ full.add (-1., ref);
+ deallog << "Difference between full and sparse matrix: "
+ << full.frobenius_norm() << std::endl;
+}
+
+
+int main ()
+{
+ deallog << std::setprecision (2);
+ logfile << std::setprecision (2);
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-14);
+
+ test<2>();
+}
+
--- /dev/null
+
+DEAL::Difference between full and sparse matrix: 0
DEAL::27 23 0.156
DEAL::27 24 0.312
DEAL::27 25 -0.0625
-DEAL::27 26 0.313
+DEAL::27 26 0.312
DEAL::28 0 0
DEAL::28 1 0
DEAL::28 2 0
--- /dev/null
+############################################################
+# $Id$
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors
+############################################################
+
+include ../Makefile.paths
+include $D/common/Make.global_options
+include ../Makefile.rules
+include Makefile.depend
+include Makefile.tests
--- /dev/null
+//----------------------------- copy.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//----------------------------- copy.cc ------------------------
+
+
+// this function tests the correctness of the copy operation in the
+// matrix-free class on matrix-vector products similar to
+// thread_correctness.cc
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+
+std::ofstream logfile("copy/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree, typename number>
+void sub_test()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ deallog << "Testing " << fe.get_name() << std::endl;
+
+ // run test for several different meshes
+ for (unsigned int i=0; i<8-2*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (9-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData(MPI_COMM_SELF,MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf_ref (mf_data);
+
+ Vector<number> in_dist (dof.n_dofs());
+ Vector<number> out_ref (in_dist), out_copy (in_dist);
+ MatrixFree<dim,number> mf_copy;
+ mf_copy.copy_from (mf_data);
+ MatrixFreeTest<dim,fe_degree+1,number> copied (mf_copy);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ in_dist(i) = entry;
+ }
+
+ mf_ref.vmult (out_ref, in_dist);
+ copied.vmult (out_copy, in_dist);
+
+ out_copy -= out_ref;
+ double diff_norm = out_copy.linfty_norm();
+ deallog << "Error in copied MF: " << diff_norm
+ << std::endl;
+ }
+ deallog << std::endl;
+}
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ deallog << "Test doubles" << std::endl;
+ sub_test<dim,fe_degree,double>();
+ deallog.threshold_double(1.e-6);
+ deallog << "Test floats" << std::endl;
+ sub_test<dim,fe_degree,float>();
+}
--- /dev/null
+
+DEAL:2d::Test doubles
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::
+DEAL:2d::Test floats
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::
+DEAL:2d::Test doubles
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::
+DEAL:2d::Test floats
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::Error in copied MF: 0
+DEAL:2d::
+DEAL:3d::Test doubles
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::
+DEAL:3d::Test floats
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::
+DEAL:3d::Test doubles
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::
+DEAL:3d::Test floats
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::Error in copied MF: 0
+DEAL:3d::
--- /dev/null
+//----------------------- create_mesh.h -----------------------------
+// $Id$
+// Version: $Name$
+//
+//----------------------- create_mesh.h -----------------------------
+
+
+// this creates a mesh that contains cells of all different kinds detected in
+// the MatrixFree class: Use a mesh that consists of a square cell, then a
+// triangular cell, a parallelepiped cell and two trapezoidal cells, where one
+// is very close to being Cartesian (by 1e-8).
+
+#include <deal.II/base/point.h>
+#include <deal.II/grid/tria.h>
+
+#include <fstream>
+#include <iostream>
+
+
+void create_mesh (Triangulation<2> &tria,
+ const double scale_grid = 1.)
+{
+ const unsigned int dim = 2;
+ std::vector<Point<dim> > points (12);
+
+ // build the mesh layer by layer from points
+
+ // 1. cube cell
+ points[0] = Point<dim> (0, 0);
+ points[1] = Point<dim> (0, 1);
+ points[2] = Point<dim> (1 ,0);
+ points[3] = Point<dim> (1 ,1);
+
+ // 2. rectangular cell
+ points[4] = Point<dim> (3., 0);
+ points[5] = Point<dim> (3., 1);
+
+ // 3. parallelogram cell
+ points[6] = Point<dim> (5., 1.);
+ points[7] = Point<dim> (5., 2.);
+
+ // almost square cell (but trapezoidal by
+ // 1e-8)
+ points[8] = Point<dim> (6., 1.);
+ points[9] = Point<dim> (6., 2.+1e-8);
+
+ // apparently trapezoidal cell
+ points[10] = Point<dim> (7., 1.4);
+ points[11] = Point<dim> (7.5, numbers::PI);
+
+ if (scale_grid != 1.)
+ for (unsigned int i=0; i<points.size(); ++i)
+ points[i] *= scale_grid;
+
+
+ // connect the points to cells
+ std::vector<CellData<dim> > cells(5);
+ for (unsigned int i=0; i<5; ++i)
+ {
+ cells[i].vertices[0] = 0+2*i;
+ cells[i].vertices[1] = 2+2*i;
+ cells[i].vertices[2] = 1+2*i;
+ cells[i].vertices[3] = 3+2*i;
+ cells[i].material_id = 0;
+ }
+
+ tria.create_triangulation (points, cells, SubCellData());
+}
+
+
+
+void create_mesh (Triangulation<3> &tria,
+ const double scale_grid = 1.)
+{
+ const unsigned int dim = 3;
+ std::vector<Point<dim> > points (24);
+
+ // build the mesh layer by layer from points
+
+ // 1. cube cell
+ points[0] = Point<dim> (0,0,0);
+ points[1] = Point<dim> (0,1.,0);
+ points[2] = Point<dim> (0,0,1);
+ points[3] = Point<dim> (0,1.,1);
+ points[4] = Point<dim> (1.,0,0);
+ points[5] = Point<dim> (1.,1.,0);
+ points[6] = Point<dim> (1.,0,1);
+ points[7] = Point<dim> (1.,1.,1);
+
+ // 2. rectangular cell
+ points[8] = Point<dim> (3., 0, 0);
+ points[9] = Point<dim> (3., 1, 0);
+ points[10] = Point<dim> (3., 0,1);
+ points[11] = Point<dim> (3., 1,1);
+
+ // 3. parallelogram cell
+ points[12] = Point<dim> (5., 1., 1.);
+ points[13] = Point<dim> (5., 2., 1.);
+ points[14] = Point<dim> (5., 1., 2.);
+ points[15] = Point<dim> (5., 2., 2.);
+
+ // almost square cell (but trapezoidal by
+ // 1e-8 in y-direction)
+ points[16] = Point<dim> (6., 1., 1.);
+ points[17] = Point<dim> (6., 2.+1e-8, 1.);
+ points[18] = Point<dim> (6., 1., 2.);
+ points[19] = Point<dim> (6., 2., 2.);
+
+ // apparently trapezoidal cell
+ points[20] = Point<dim> (7., 1.4, 1.2231);
+ points[21] = Point<dim> (7.5, numbers::PI, 1.334);
+ points[22] = Point<dim> (7., 1.5, 7.1);
+ points[23] = Point<dim> (7.5, 3.8, 2.99);
+
+ if (scale_grid != 1.)
+ for (unsigned int i=0; i<points.size(); ++i)
+ points[i] *= scale_grid;
+
+ // connect the points to cells
+ std::vector<CellData<dim> > cells(5);
+ for (unsigned int i=0; i<5; ++i)
+ {
+ cells[i].vertices[0] = 0+4*i;
+ cells[i].vertices[1] = 4+4*i;
+ cells[i].vertices[2] = 1+4*i;
+ cells[i].vertices[3] = 5+4*i;
+ cells[i].vertices[4] = 2+4*i;
+ cells[i].vertices[5] = 6+4*i;
+ cells[i].vertices[6] = 3+4*i;
+ cells[i].vertices[7] = 7+4*i;
+ cells[i].material_id = 0;
+ }
+ tria.create_triangulation (points, cells, SubCellData());
+}
--- /dev/null
+//------------------ get_functions_cartesian.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_cartesian.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a cartesian mesh (hyper cube). This tests whether
+// cartesian meshes are treated correctly. The test case is without any
+// constraints
+
+#include "../tests.h"
+
+
+std::ofstream logfile("get_functions_cartesian/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ constraints.close();
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_circle.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_circle.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hyperball mesh. This tests whether general Q1
+// transformations work correctly. The test case includes hanging node
+// constraints, but no constraints from boundary conditions
+
+#include "../tests.h"
+
+std::ofstream logfile("get_functions_circle/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ // refine first and last cell
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global (4-dim);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ constraints.close();
+
+ do_test <dim, fe_degree, double> (dof, constraints);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_common.h ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_common.h ------------------------
+
+
+// this is a template for getting the function values and comparing them with
+// the output of FEValues on different kinds of meshes (Cartesian, general,
+// with and without hanging nodes). The tests does not include multithreading
+// because FEValues is not thread-safe
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+
+
+// forward declare this function. will be implemented in .cc files
+template <int dim, int fe_degree>
+void test ();
+
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d, typename Number=double>
+class MatrixFreeTest
+{
+ public:
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_vectors;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val (data.get_dof_handler().get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_hessians)
+ {};
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in,
+ const Mapping<dim> &mapping):
+ data (data_in),
+ fe_val (mapping, data.get_dof_handler().get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_hessians)
+ {};
+
+ virtual ~MatrixFreeTest ()
+ {}
+
+ // make function virtual to allow derived
+ // classes to define a different function
+ virtual void
+ operator () (const MatrixFree<dim,Number> &data,
+ Vector<Number> &,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ typedef VectorizedArray<Number> vector_t;
+ FEEvaluation<dim,n_dofs_1d,n_q_points_1d,1,Number> fe_eval (data);
+
+ std::vector<double> reference_values (fe_eval.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads (fe_eval.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess (fe_eval.n_q_points);
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+ fe_eval.read_dof_values(src);
+ fe_eval.evaluate (true,true,true);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ fe_val.reinit (data.get_cell_iterator(cell,j));
+ fe_val.get_function_values(src, reference_values);
+ fe_val.get_function_gradients(src, reference_grads);
+ fe_val.get_function_hessians(src, reference_hess);
+
+ for (int q=0; q<(int)fe_eval.n_q_points; q++)
+ {
+ errors[0] += std::fabs(fe_eval.get_value(q)[j]-
+ reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]-
+ reference_grads[q][d]);
+ errors[2] += std::fabs(fe_eval.get_laplacian(q)[j]-
+ trace(reference_hess[q]));
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ errors[3] += std::fabs(fe_eval.get_hessian_diagonal(q)[d][j]-
+ reference_hess[q][d][d]);
+ for (unsigned int e=0; e<dim; ++e)
+ errors[4] += std::fabs(fe_eval.get_hessian(q)[d][e][j]-
+ reference_hess[q][d][e]);
+ }
+
+ total[0] += std::fabs(reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[1] += std::fabs(reference_grads[q][d]);
+
+ // reference for second derivatives computed
+ // from fe_eval because FEValues is not
+ // accurate enough with finite differences
+ total[2] += std::fabs(fe_eval.get_laplacian(q)[j]);
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ total[3] += std::fabs(fe_eval.get_hessian_diagonal(q)[d][j]);
+ for (unsigned int e=0; e<dim; ++e)
+ total[4] += std::fabs(fe_eval.get_hessian(q)[d][e][j]);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ void test_functions (const Vector<Number> &src) const
+ {
+ for (unsigned int i=0; i<5; ++i)
+ {
+ errors[i] = 0;
+ total[i] = 0;
+ }
+ Vector<Number> dst_dummy;
+ data.cell_loop (&MatrixFreeTest::operator(), this, dst_dummy, src);
+
+ // for doubles, use a stricter condition than
+ // for floats for the relative error size
+ if (types_are_equal<Number,double>::value == true)
+ {
+ deallog.threshold_double (5e-14);
+ deallog << "Error function values: "
+ << errors[0]/total[0] << std::endl;
+ deallog << "Error function gradients: "
+ << errors[1]/total[1] << std::endl;
+
+ // need to set quite a loose tolerance because
+ // FEValues approximates Hessians with finite
+ // differences, which are not so
+ // accurate. moreover, Hessians are quite
+ // large since we chose random numbers. for
+ // some elements, it might also be zero
+ // (linear elements on quadrilaterals), so
+ // need to check for division by 0, too.
+ deallog.threshold_double (5e-7);
+ const double output2 = total[2] == 0 ? 0. : errors[2] / total[2];
+ deallog << "Error function Laplacians: " << output2 << std::endl;
+ const double output3 = total[3] == 0 ? 0. : errors[3] / total[3];
+ deallog << "Error function diagonal of Hessian: " << output3 << std::endl;
+ const double output4 = total[4] == 0 ? 0. : errors[4] / total[4];
+ deallog << "Error function Hessians: " << output4 << std::endl;
+ }
+ else if (types_are_equal<Number,float>::value == true)
+ {
+ deallog.threshold_double (1e-6);
+ deallog << "Error function values: "
+ << errors[0]/total[0] << std::endl;
+ deallog << "Error function gradients: "
+ << errors[1]/total[1] << std::endl;
+ const double output2 = total[2] == 0 ? 0. : errors[2] / total[2];
+ deallog.threshold_double (1e-5);
+ deallog << "Error function Laplacians: " << output2 << std::endl;
+ const double output3 = total[3] == 0 ? 0. : errors[3] / total[3];
+ deallog << "Error function diagonal of Hessian: " << output3 << std::endl;
+ const double output4 = total[4] == 0 ? 0. : errors[4] / total[4];
+ deallog << "Error function Hessians: " << output4 << std::endl;
+ }
+ deallog << std::endl;
+ };
+
+protected:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val;
+ mutable double errors[5], total[5];
+};
+
+
+
+// dummy with empty quadrature formula
+template <int dim, int n_dofs_1d,typename Number>
+class MatrixFreeTest<dim,n_dofs_1d,0,Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_vectors;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &)
+ {};
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &,
+ const Mapping<dim> &)
+ {};
+
+ void cell_integration (Vector<Number> &,
+ const Vector<Number> &,
+ const std::pair<unsigned int,unsigned int>) const {}
+
+ void test_functions (const Vector<Number> &) const
+ {}
+};
+
+
+
+
+template <int dim, int fe_degree, typename number>
+void do_test (const DoFHandler<dim> &dof,
+ const ConstraintMatrix&constraints)
+{
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ // use this for info on problem
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+
+ constraints.distribute(solution);
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(solution);
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+ {
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+//------------------ get_functions_constraints.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_constraints.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hypecube mesh with adaptive refinement.
+
+#include "../tests.h"
+
+std::ofstream logfile("get_functions_constraints/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_float.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_float.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a mesh with all different cell types (same as in
+// matrix_vector_06) with hanging nodes, boundary conditions for float values.
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include "create_mesh.h"
+
+std::ofstream logfile("get_functions_float/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria);
+ tria.refine_global(4-dim);
+
+ // refine a few cells
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test <dim, fe_degree, float> (dof, constraints);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_gl.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_gl.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hyperball mesh for Gauss-Lobatto elements
+// (identity values transformation). The test case includes hanging node
+// constraints, but no constraints from boundary conditions
+
+#include "../tests.h"
+
+
+std::ofstream logfile("get_functions_gl/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTestGL : public MatrixFreeTest<dim, n_dofs_1d, n_dofs_1d, Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTestGL(const MatrixFree<dim,Number> &data,
+ const Mapping<dim> &mapping):
+ MatrixFreeTest<dim, n_dofs_1d, n_dofs_1d, Number>(data, mapping)
+ {};
+
+ void operator() (const MatrixFree<dim,Number> &data,
+ Vector<Number> &,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ FEEvaluationGL<dim,n_dofs_1d,1,Number> fe_eval (this->data);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+ std::vector<double> reference_values (fe_eval.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads (fe_eval.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess (fe_eval.n_q_points);
+ fe_eval.read_dof_values(src);
+ fe_eval.evaluate (true,true,true);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ this->fe_val.reinit (data.get_cell_iterator(cell,j));
+ this->fe_val.get_function_values(src, reference_values);
+ this->fe_val.get_function_gradients(src, reference_grads);
+ this->fe_val.get_function_hessians(src, reference_hess);
+
+ for (int q=0; q<(int)fe_eval.n_q_points; q++)
+ {
+ this->errors[0] += std::fabs(fe_eval.get_value(q)[j]-
+ reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ this->errors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]-
+ reference_grads[q][d]);
+ this->errors[2] += std::fabs(fe_eval.get_laplacian(q)[j]-
+ trace(reference_hess[q]));
+ this->total[0] += std::fabs(reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ this->total[1] += std::fabs(reference_grads[q][d]);
+ this->total[2] += std::fabs(fe_eval.get_laplacian(q)[j]);
+ }
+ }
+ }
+ }
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ // refine first and last cell
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global (4-dim);
+
+ FE_Q<dim> fe (QGaussLobatto<1>(fe_degree+1));
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ constraints.close();
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+ constraints.distribute(solution);
+
+ MatrixFree<dim,number> mf_data;
+ deallog << "Test with fe_degree " << fe_degree
+ << std::endl;
+ const QGaussLobatto<1> quad (fe_degree+1);
+ MappingQ<dim> mapping (2);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (mapping, dof, constraints, quad, data);
+ MatrixFreeTestGL<dim,fe_degree+1,number> mf (mf_data, mapping);
+ mf.test_functions (solution);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Test with fe_degree 1
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Test with fe_degree 2
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(QGaussLobatto(4))
+DEAL:2d::Test with fe_degree 3
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(QGaussLobatto(5))
+DEAL:2d::Test with fe_degree 4
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Test with fe_degree 1
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Test with fe_degree 2
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_circle_mappingq.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_circle_mappingq.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hyperball mesh. This tests whether the general
+// MappingQ with higher order works correctly. The test case includes hanging
+// node constraints, but no constraints from boundary conditions
+
+#include "../tests.h"
+
+std::ofstream logfile("get_functions_mappingq/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ // refine first and last cell
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global (4-dim);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ constraints.close();
+
+ // in the other functions, use do_test in
+ // get_functions_common, but here we have to
+ // manually choose another mapping
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+
+ constraints.distribute(static_cast<Vector<number>&>(solution));
+ MappingQ<dim> mapping (4);
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (mapping, dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,fe_degree+1,number> mf (mf_data, mapping);
+ mf.test_functions(solution);
+ deallog << std::endl;
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_multife.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_multife.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hypecube mesh with adaptive refinement with
+// components in two different DoFHandlers.
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+
+
+std::ofstream logfile("get_functions_multife/output");
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d, typename Number=double>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef std::vector<Vector<Number> > VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val0 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_hessians),
+ fe_val1 (data.get_dof_handler(1).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_hessians)
+ {};
+
+ void
+ operator () (const MatrixFree<dim,Number> &data,
+ VectorType &,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval0 (data,0,0);
+ FEEvaluation<dim,n_dofs_1d+1,n_dofs_1d+1,1,Number> fe_eval1 (data,1,1);
+ std::vector<double> reference_values0 (fe_eval0.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads0 (fe_eval0.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess0 (fe_eval0.n_q_points);
+ std::vector<double> reference_values1 (fe_eval1.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads1 (fe_eval1.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess1 (fe_eval1.n_q_points);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval0.reinit (cell);
+ fe_eval0.read_dof_values(src[0]);
+ fe_eval0.evaluate (true,true,true);
+
+ fe_eval1.reinit (cell);
+ fe_eval1.read_dof_values(src[1]);
+ fe_eval1.evaluate (true,true,true);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ // FE 0
+ fe_val0.reinit (data.get_cell_iterator(cell,j,0));
+ fe_val0.get_function_values(src[0], reference_values0);
+ fe_val0.get_function_gradients(src[0], reference_grads0);
+ fe_val0.get_function_hessians(src[0], reference_hess0);
+
+ for (int q=0; q<(int)fe_eval0.n_q_points; q++)
+ {
+ errors[0] += std::fabs(fe_eval0.get_value(q)[j]-
+ reference_values0[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[1] += std::fabs(fe_eval0.get_gradient(q)[d][j]-
+ reference_grads0[q][d]);
+ errors[2] += std::fabs(fe_eval0.get_laplacian(q)[j]-
+ trace(reference_hess0[q]));
+ total[0] += std::fabs(reference_values0[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[1] += std::fabs(reference_grads0[q][d]);
+ total[2] += std::fabs(fe_eval0.get_laplacian(q)[j]);
+ }
+
+ // FE 1
+ fe_val1.reinit (data.get_cell_iterator(cell,j,1));
+ fe_val1.get_function_values(src[1], reference_values1);
+ fe_val1.get_function_gradients(src[1], reference_grads1);
+ fe_val1.get_function_hessians(src[1], reference_hess1);
+
+ for (int q=0; q<(int)fe_eval1.n_q_points; q++)
+ {
+ errors[3] += std::fabs(fe_eval1.get_value(q)[j]-
+ reference_values1[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[4] += std::fabs(fe_eval1.get_gradient(q)[d][j]-
+ reference_grads1[q][d]);
+ errors[5] += std::fabs(fe_eval1.get_laplacian(q)[j]-
+ trace(reference_hess1[q]));
+ total[3] += std::fabs(reference_values1[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[4] += std::fabs(reference_grads1[q][d]);
+ total[5] += std::fabs(fe_eval1.get_laplacian(q)[j]);
+ }
+ }
+ }
+ }
+
+ void test_functions (const VectorType &src) const
+ {
+ for (unsigned int i=0; i<3*2; ++i)
+ {
+ errors[i] = 0;
+ total[i] = 0;
+ }
+ VectorType dst_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,n_q_points_1d,Number>::operator(),
+ this, dst_dummy, src);
+
+ // for doubles, use a stricter condition then
+ // for floats for the relative error size
+ for (unsigned int i=0; i<2; ++i)
+ {
+ if (types_are_equal<Number,double>::value == true)
+ {
+ deallog.threshold_double (4e-14);
+ deallog << "Error function values FE " << i << ": "
+ << errors[i*3+0]/total[i*3+0] << std::endl;
+ deallog << "Error function gradients FE " << i << ": "
+ << errors[i*3+1]/total[i*3+1] << std::endl;
+
+ // need to set quite a loose tolerance because
+ // FEValues approximates Hessians with finite
+ // differences, which are not so
+ // accurate. moreover, Hessians are quite
+ // large since we chose random numbers. for
+ // some elements, it might also be zero
+ // (linear elements on quadrilaterals), so
+ // need to check for division by 0, too.
+ deallog.threshold_double (5e-7);
+ const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2];
+ deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl;
+ }
+ else if (types_are_equal<Number,float>::value == true)
+ {
+ deallog.threshold_double (1e-6);
+ deallog << "Error function values FE " << i << ": "
+ << errors[i*3+0]/total[i*3+0] << std::endl;
+ deallog << "Error function gradients FE " << i << ": "
+ << errors[i*3+1]/total[i*3+1] << std::endl;
+ const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2];
+ deallog.threshold_double (1e-6);
+ deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl;
+ }
+ }
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val0;
+ mutable FEValues<dim> fe_val1;
+ mutable double errors[6], total[6];
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.begin_active(tria.n_levels()-2)->set_refine_flag();
+ tria.begin_active(tria.n_levels()-3)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ FE_Q<dim> fe0(fe_degree);
+ FE_Q<dim> fe1(fe_degree+1);
+ DoFHandler<dim> dof0(tria);
+ dof0.distribute_dofs(fe0);
+ DoFHandler<dim> dof1(tria);
+ dof1.distribute_dofs(fe1);
+
+ std::vector<const DoFHandler<dim> *>dof(2);
+ dof[0] = &dof0;
+ dof[1] = &dof1;
+
+ deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+
+ std::vector<Vector<double> > src(dof.size());
+ for (unsigned int no=0; no<dof.size(); ++no)
+ src[no].reinit (dof[no]->n_dofs());
+
+ std::vector<const ConstraintMatrix *> constraints(2);
+ ConstraintMatrix constraint0;
+ DoFTools::make_hanging_node_constraints(*dof[0],constraint0);
+ constraint0.close();
+ constraints[0] = &constraint0;
+ ConstraintMatrix constraint1;
+ DoFTools::make_hanging_node_constraints(*dof[1],constraint1);
+ constraint1.close();
+ constraints[1] = &constraint1;
+
+ //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl;
+ //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl;
+
+ // create vector with random entries
+ for (unsigned int no=0; no<2; ++no)
+ for (unsigned int i=0; i<dof[no]->n_dofs(); ++i)
+ {
+ if(constraints[no]->is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ src[no](i) = entry;
+ }
+
+
+ constraints[0]->distribute(src[0]);
+ constraints[1]->distribute(src[1]);
+ MatrixFree<dim,number> mf_data;
+ {
+ std::vector<Quadrature<1> > quad;
+ for (unsigned int no=0; no<2; ++no)
+ quad.push_back(QGauss<1>(fe_degree+1+no));
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData
+ (MPI_COMM_SELF,
+ MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(src);
+ deallog << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ // need to set quite a loose tolerance because
+ // FEValues approximates Hessians with finite
+ // differences, which are not so accurate
+ deallog.threshold_double(2.e-5);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ test<3,3>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2) and FE_Q<2>(3)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3) and FE_Q<2>(4)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4) and FE_Q<2>(5)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2) and FE_Q<3>(3)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(3) and FE_Q<3>(4)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_multife2.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_multife2.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hypecube mesh with adaptive refinement with
+// components in three different DoFHandlers and two quadrature formulas, one
+// of which is a FE_DGQ(0)
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_dgq.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+
+
+std::ofstream logfile("get_functions_multife2/output");
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d, typename Number=double>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef std::vector<Vector<Number> > VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val0 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_hessians),
+ fe_val1 (data.get_dof_handler(1).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_hessians),
+ fe_val2 (data.get_dof_handler(2).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_hessians)
+ {};
+
+ void
+ operator () (const MatrixFree<dim,Number> &data,
+ VectorType &,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ FEEvaluation<dim,1,1,1,Number> fe_eval0 (data,0,0);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval1 (data,1,1);
+ FEEvaluation<dim,n_dofs_1d+1,n_dofs_1d,1,Number> fe_eval2 (data,2,1);
+ std::vector<double> reference_values0 (fe_eval0.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads0 (fe_eval0.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess0 (fe_eval0.n_q_points);
+ std::vector<double> reference_values1 (fe_eval1.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads1 (fe_eval1.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess1 (fe_eval1.n_q_points);
+ std::vector<double> reference_values2 (fe_eval2.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads2 (fe_eval2.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess2 (fe_eval2.n_q_points);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval0.reinit (cell);
+ fe_eval0.read_dof_values(src[0]);
+ fe_eval0.evaluate (true,true,true);
+
+ fe_eval1.reinit (cell);
+ fe_eval1.read_dof_values(src[1]);
+ fe_eval1.evaluate (true,true,true);
+
+ fe_eval2.reinit (cell);
+ fe_eval2.read_dof_values(src[2]);
+ fe_eval2.evaluate (true,true,true);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ // FE 0
+ fe_val0.reinit (data.get_cell_iterator(cell,j,0));
+ fe_val0.get_function_values(src[0], reference_values0);
+ fe_val0.get_function_gradients(src[0], reference_grads0);
+ fe_val0.get_function_hessians(src[0], reference_hess0);
+
+ for (int q=0; q<(int)fe_eval0.n_q_points; q++)
+ {
+ errors[0] += std::fabs(fe_eval0.get_value(q)[j]-
+ reference_values0[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[1] += std::fabs(fe_eval0.get_gradient(q)[d][j]-
+ reference_grads0[q][d]);
+ errors[2] += std::fabs(fe_eval0.get_laplacian(q)[j]-
+ trace(reference_hess0[q]));
+ total[0] += std::fabs(reference_values0[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[1] += std::fabs(reference_grads0[q][d]);
+ total[2] += std::fabs(fe_eval0.get_laplacian(q)[j]);
+ }
+
+ // FE 1
+ fe_val1.reinit (data.get_cell_iterator(cell,j,1));
+ fe_val1.get_function_values(src[1], reference_values1);
+ fe_val1.get_function_gradients(src[1], reference_grads1);
+ fe_val1.get_function_hessians(src[1], reference_hess1);
+
+ for (int q=0; q<(int)fe_eval1.n_q_points; q++)
+ {
+ errors[3] += std::fabs(fe_eval1.get_value(q)[j]-
+ reference_values1[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[4] += std::fabs(fe_eval1.get_gradient(q)[d][j]-
+ reference_grads1[q][d]);
+ errors[5] += std::fabs(fe_eval1.get_laplacian(q)[j]-
+ trace(reference_hess1[q]));
+ total[3] += std::fabs(reference_values1[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[4] += std::fabs(reference_grads1[q][d]);
+ total[5] += std::fabs(fe_eval1.get_laplacian(q)[j]);
+ }
+
+ // FE 2
+ fe_val2.reinit (data.get_cell_iterator(cell,j,2));
+ fe_val2.get_function_values(src[2], reference_values2);
+ fe_val2.get_function_gradients(src[2], reference_grads2);
+ fe_val2.get_function_hessians(src[2], reference_hess2);
+
+ for (int q=0; q<(int)fe_eval2.n_q_points; q++)
+ {
+ errors[6] += std::fabs(fe_eval2.get_value(q)[j]-
+ reference_values2[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ errors[7] += std::fabs(fe_eval2.get_gradient(q)[d][j]-
+ reference_grads2[q][d]);
+ errors[8] += std::fabs(fe_eval2.get_laplacian(q)[j]-
+ trace(reference_hess2[q]));
+ total[6] += std::fabs(reference_values2[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ total[7] += std::fabs(reference_grads2[q][d]);
+ total[8] += std::fabs(fe_eval2.get_laplacian(q)[j]);
+ }
+ }
+ }
+ }
+
+ void test_functions (const VectorType &src) const
+ {
+ for (unsigned int i=0; i<3*3; ++i)
+ {
+ errors[i] = 0;
+ total[i] = 0;
+ }
+ VectorType dst_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,n_q_points_1d,Number>::operator(),
+ this, dst_dummy, src);
+
+ // avoid dividing by zero
+ for (unsigned int i=0; i<9; ++i)
+ if (std::fabs(total[i]) < 1e-20)
+ total[i] = 1;
+
+ // for doubles, use a stricter condition then
+ // for floats for the relative error size
+ for (unsigned int i=0; i<3; ++i)
+ {
+ if (types_are_equal<Number,double>::value == true)
+ {
+ deallog.threshold_double (4e-14);
+ deallog << "Error function values FE " << i << ": "
+ << errors[i*3+0]/total[i*3+0] << std::endl;
+ deallog << "Error function gradients FE " << i << ": "
+ << errors[i*3+1]/total[i*3+1] << std::endl;
+
+ // need to set quite a loose tolerance because
+ // FEValues approximates Hessians with finite
+ // differences, which are not so
+ // accurate. moreover, Hessians are quite
+ // large since we chose random numbers. for
+ // some elements, it might also be zero
+ // (linear elements on quadrilaterals), so
+ // need to check for division by 0, too.
+ deallog.threshold_double (2e-6);
+ const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2];
+ deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl;
+ }
+ else if (types_are_equal<Number,float>::value == true)
+ {
+ deallog.threshold_double (1e-6);
+ deallog << "Error function values FE " << i << ": "
+ << errors[i*3+0]/total[i*3+0] << std::endl;
+ deallog << "Error function gradients FE " << i << ": "
+ << errors[i*3+1]/total[i*3+1] << std::endl;
+ const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2];
+ deallog.threshold_double (1e-6);
+ deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl;
+ }
+ }
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val0;
+ mutable FEValues<dim> fe_val1;
+ mutable FEValues<dim> fe_val2;
+ mutable double errors[9], total[9];
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.begin_active(tria.n_levels()-2)->set_refine_flag();
+ tria.begin_active(tria.n_levels()-3)->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ FE_DGQ<dim> fe0(0);
+ FE_Q<dim> fe1(fe_degree);
+ FE_Q<dim> fe2(fe_degree+1);
+ DoFHandler<dim> dof0(tria);
+ dof0.distribute_dofs(fe0);
+ DoFHandler<dim> dof1(tria);
+ dof1.distribute_dofs(fe1);
+ DoFHandler<dim> dof2(tria);
+ dof2.distribute_dofs(fe2);
+
+ std::vector<const DoFHandler<dim> *>dof(3);
+ dof[0] = &dof0;
+ dof[1] = &dof1;
+ dof[2] = &dof2;
+
+ deallog << "Testing " << fe0.get_name() << ", " << fe1.get_name()
+ << ", and " << fe1.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+
+ std::vector<Vector<double> > src (dof.size());
+ for (unsigned int i=0; i<dof.size(); ++i)
+ src[i].reinit (dof[i]->n_dofs());
+
+ std::vector<const ConstraintMatrix *> constraints(3);
+ ConstraintMatrix constraint0;
+ DoFTools::make_hanging_node_constraints(*dof[0],constraint0);
+ constraint0.close();
+ constraints[0] = &constraint0;
+ ConstraintMatrix constraint1;
+ DoFTools::make_hanging_node_constraints(*dof[1],constraint1);
+ constraint1.close();
+ constraints[1] = &constraint1;
+ ConstraintMatrix constraint2;
+ DoFTools::make_hanging_node_constraints(*dof[2],constraint2);
+ constraint2.close();
+ constraints[2] = &constraint2;
+
+ //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl;
+ //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl;
+ //std::cout << "Number of degrees of freedom FE 2: " << dof[2]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 2: " << constraints[2]->n_constraints() << std::endl;
+
+ // create vector with random entries
+ for (unsigned int no=0; no<3; ++no)
+ for (unsigned int i=0; i<dof[no]->n_dofs(); ++i)
+ {
+ if(constraints[no]->is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ src[no](i) = entry;
+ }
+
+
+ constraints[0]->distribute(src[0]);
+ constraints[1]->distribute(src[1]);
+ constraints[2]->distribute(src[2]);
+ MatrixFree<dim,number> mf_data;
+ {
+ std::vector<Quadrature<1> > quad;
+ quad.push_back (QGauss<1>(1));
+ quad.push_back (QGauss<1>(fe_degree+1));
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData
+ (MPI_COMM_SELF,
+ MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(src);
+ deallog << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ // need to set quite a loose tolerance because
+ // FEValues approximates Hessians with finite
+ // differences, which are not so accurate
+ deallog.threshold_double(2.e-5);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ test<3,3>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(1), and FE_Q<2>(1)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::Error function values FE 2: 0
+DEAL:2d::Error function gradients FE 2: 0
+DEAL:2d::Error function Laplacians FE 2: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(2), and FE_Q<2>(2)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::Error function values FE 2: 0
+DEAL:2d::Error function gradients FE 2: 0
+DEAL:2d::Error function Laplacians FE 2: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(3), and FE_Q<2>(3)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::Error function values FE 2: 0
+DEAL:2d::Error function gradients FE 2: 0
+DEAL:2d::Error function Laplacians FE 2: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(4), and FE_Q<2>(4)
+DEAL:2d::Error function values FE 0: 0
+DEAL:2d::Error function gradients FE 0: 0
+DEAL:2d::Error function Laplacians FE 0: 0
+DEAL:2d::Error function values FE 1: 0
+DEAL:2d::Error function gradients FE 1: 0
+DEAL:2d::Error function Laplacians FE 1: 0
+DEAL:2d::Error function values FE 2: 0
+DEAL:2d::Error function gradients FE 2: 0
+DEAL:2d::Error function Laplacians FE 2: 0
+DEAL:2d::
+DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(1), and FE_Q<3>(1)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::Error function values FE 2: 0
+DEAL:3d::Error function gradients FE 2: 0
+DEAL:3d::Error function Laplacians FE 2: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(2), and FE_Q<3>(2)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::Error function values FE 2: 0
+DEAL:3d::Error function gradients FE 2: 0
+DEAL:3d::Error function Laplacians FE 2: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(3), and FE_Q<3>(3)
+DEAL:3d::Error function values FE 0: 0
+DEAL:3d::Error function gradients FE 0: 0
+DEAL:3d::Error function Laplacians FE 0: 0
+DEAL:3d::Error function values FE 1: 0
+DEAL:3d::Error function gradients FE 1: 0
+DEAL:3d::Error function Laplacians FE 1: 0
+DEAL:3d::Error function values FE 2: 0
+DEAL:3d::Error function gradients FE 2: 0
+DEAL:3d::Error function Laplacians FE 2: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_q_hierarchical.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_q_hierarchical.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hyperball mesh for FE_Q_Hierarchical elements
+// (no symmetry in transformation). The test case includes hanging node
+// constraints, but no constraints from boundary conditions
+
+#include "../tests.h"
+#include <deal.II/fe/fe_q_hierarchical.h>
+#include <deal.II/numerics/vectors.h>
+#include <deal.II/base/function.h>
+
+std::ofstream logfile("get_functions_q_hierarchical/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTestGen : public MatrixFreeTest<dim, n_dofs_1d, n_dofs_1d, Number>
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTestGen(const MatrixFree<dim,Number> &data,
+ const Mapping<dim> &mapping):
+ MatrixFreeTest<dim, n_dofs_1d, n_dofs_1d, Number>(data, mapping)
+ {};
+
+ void operator () (const MatrixFree<dim,Number> &data,
+ Vector<Number> &,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ FEEvaluationGeneral<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+ std::vector<double> reference_values (fe_eval.n_q_points);
+ std::vector<Tensor<1,dim> > reference_grads (fe_eval.n_q_points);
+ std::vector<Tensor<2,dim> > reference_hess (fe_eval.n_q_points);
+ fe_eval.read_dof_values(src);
+ fe_eval.evaluate (true,true,true);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ this->fe_val.reinit (data.get_cell_iterator(cell,j));
+ this->fe_val.get_function_values(src, reference_values);
+ this->fe_val.get_function_gradients(src, reference_grads);
+ this->fe_val.get_function_hessians(src, reference_hess);
+
+ for (int q=0; q<(int)fe_eval.n_q_points; q++)
+ {
+ this->errors[0] += std::fabs(fe_eval.get_value(q)[j]-
+ reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ this->errors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]-
+ reference_grads[q][d]);
+ }
+ this->errors[2] += std::fabs(fe_eval.get_laplacian(q)[j]-
+ trace(reference_hess[q]));
+ this->total[0] += std::fabs(reference_values[q]);
+ for (unsigned int d=0; d<dim; ++d)
+ this->total[1] += std::fabs(reference_grads[q][d]);
+ this->total[2] += std::fabs(fe_eval.get_laplacian(q)[j]);
+ }
+ }
+ }
+ }
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ // refine first and last cell
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global (4-dim);
+
+ FE_Q_Hierarchical<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ constraints.close();
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+ constraints.distribute(solution);
+
+ MatrixFree<dim,number> mf_data;
+ deallog << "Test with fe_degree " << fe_degree
+ << std::endl;
+ const QGauss<1> quad (fe_degree+1);
+ MappingQ<dim> mapping (4);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (mapping, dof, constraints, quad, data);
+ MatrixFreeTestGen<dim,fe_degree+1,number> mf (mf_data, mapping);
+ mf.test_functions (solution);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q_Hierarchical<2>(1)
+DEAL:2d::Test with fe_degree 1
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q_Hierarchical<2>(2)
+DEAL:2d::Test with fe_degree 2
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q_Hierarchical<2>(3)
+DEAL:2d::Test with fe_degree 3
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q_Hierarchical<2>(4)
+DEAL:2d::Test with fe_degree 4
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q_Hierarchical<3>(1)
+DEAL:3d::Test with fe_degree 1
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q_Hierarchical<3>(2)
+DEAL:3d::Test with fe_degree 2
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_rect.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_rect.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a hyperball mesh with different sizes in the number
+// of degrees of freedom per cell and quadrature points per cell. The test
+// case includes hanging node constraints, but no constraints from boundary
+// conditions
+
+#include "../tests.h"
+
+std::ofstream logfile("get_functions_rect/output");
+
+#include "get_functions_common.h"
+
+
+template <int dim, int fe_degree, int n_q_points_1d, typename number>
+void sub_test (const DoFHandler<dim> &dof,
+ const ConstraintMatrix &constraints,
+ MatrixFree<dim,number> &mf_data,
+ Vector<number> &solution)
+{
+ deallog << "Test with fe_degree " << fe_degree
+ << ", n_q_points_1d: " << (n_q_points_1d) << std::endl;
+ const QGauss<1> quad (n_q_points_1d);
+ MappingQ<dim> mapping (2);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (mapping, dof, constraints, quad, data);
+ MatrixFreeTest<dim,fe_degree+1,n_q_points_1d,number> mf (mf_data, mapping);
+ mf.test_functions (solution);
+}
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ // refine first and last cell
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global (4-dim);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof, constraints);
+ constraints.close();
+
+
+ // in the other functions, use do_test in
+ // get_functions_common, but here we have to
+ // manually choose non-rectangular tests.
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+ constraints.distribute(solution);
+
+ MatrixFree<dim,number> mf_data;
+ if (fe_degree > 1)
+ sub_test <dim,fe_degree,fe_degree-1,number> (dof, constraints, mf_data,
+ solution);
+ sub_test <dim,fe_degree,fe_degree,number> (dof, constraints, mf_data,
+ solution);
+ sub_test <dim,fe_degree,fe_degree+2,number> (dof, constraints, mf_data,
+ solution);
+ if (dim == 2)
+ sub_test <dim,fe_degree,fe_degree+3,number> (dof, constraints, mf_data,
+ solution);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Test with fe_degree 1, n_q_points_1d: 1
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 1, n_q_points_1d: 3
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 1, n_q_points_1d: 4
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Test with fe_degree 2, n_q_points_1d: 1
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 2, n_q_points_1d: 2
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 2, n_q_points_1d: 4
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 2, n_q_points_1d: 5
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Test with fe_degree 3, n_q_points_1d: 2
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 3, n_q_points_1d: 3
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 3, n_q_points_1d: 5
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 3, n_q_points_1d: 6
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Test with fe_degree 4, n_q_points_1d: 3
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 4, n_q_points_1d: 4
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 4, n_q_points_1d: 6
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:2d::Test with fe_degree 4, n_q_points_1d: 7
+DEAL:2d::Error function values: 0
+DEAL:2d::Error function gradients: 0
+DEAL:2d::Error function Laplacians: 0
+DEAL:2d::Error function diagonal of Hessian: 0
+DEAL:2d::Error function Hessians: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Test with fe_degree 1, n_q_points_1d: 1
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Test with fe_degree 1, n_q_points_1d: 3
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Test with fe_degree 2, n_q_points_1d: 1
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Test with fe_degree 2, n_q_points_1d: 2
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
+DEAL:3d::Test with fe_degree 2, n_q_points_1d: 4
+DEAL:3d::Error function values: 0
+DEAL:3d::Error function gradients: 0
+DEAL:3d::Error function Laplacians: 0
+DEAL:3d::Error function diagonal of Hessian: 0
+DEAL:3d::Error function Hessians: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_functions_variants.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_functions_variants.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in getting the function values, the function gradients, and the
+// function Laplacians on a cartesian mesh (hyper cube) with the different
+// variants (functions only, gradients only, some combinations). The reference
+// is computed to the function that extracts all data at the same time (the
+// correctness of that must be tested elsewhere). No constraints.
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+
+#include <fstream>
+#include <iostream>
+
+std::ofstream logfile("get_functions_variants/output");
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef Vector<Number> VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void operator () (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+
+ void test_functions (const VectorType &src) const
+ {
+ for (unsigned int i=0; i<5; ++i)
+ errors[i] = 0;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,Number>::operator(), this,
+ const_cast<VectorType&>(src), src);
+
+ deallog << "Error val, function values alone: "
+ << errors[0] << std::endl;
+ deallog << "Error grad, function gradients alone: "
+ << errors[1] << std::endl;
+ deallog << "Error val, function values and gradients alone: "
+ << errors[2] << std::endl;
+ deallog << "Error grad, function values and gradients alone: "
+ << errors[3] << std::endl;
+ deallog << "Error Lapl, function Laplacians alone: "
+ << errors[4] << std::endl;
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable double errors [5];
+};
+
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void MatrixFreeTest<dim,n_dofs_1d,Number>::
+operator () (const MatrixFree<dim,Number> &data,
+ VectorType &,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval2 (data);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval3 (data);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval4 (data);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval5 (data);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+ fe_eval.read_dof_values(src);
+ fe_eval.evaluate (true,true,true);
+
+ // only for values (additional test)
+ fe_eval2.reinit (cell);
+ fe_eval2.read_dof_values(src);
+ fe_eval2.evaluate (true,false,false);
+
+ // only gradients
+ fe_eval3.reinit (cell);
+ fe_eval3.read_dof_values(src);
+ fe_eval3.evaluate (false,true,false);
+
+ // only values and gradients
+ fe_eval4.reinit (cell);
+ fe_eval4.read_dof_values(src);
+ fe_eval4.evaluate(true,true,false);
+
+ // only laplacians
+ fe_eval5.reinit (cell);
+ fe_eval5.read_dof_values(src);
+ fe_eval5.evaluate (false,false,true);
+
+
+ // compare values with the values that we get
+ // when expanding the full
+ // FEEvaluations. Those are tested in other
+ // functions and seen as reference here
+ for (unsigned int q=0; q<fe_eval.n_q_points; ++q)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ {
+ errors[0] += std::fabs(fe_eval.get_value(q)[j]-
+ fe_eval2.get_value(q)[j]);
+ errors[2] += std::fabs(fe_eval.get_value(q)[j]-
+ fe_eval4.get_value(q)[j]);
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ errors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]-
+ fe_eval3.get_gradient(q)[d][j]);
+ errors[3] += std::fabs(fe_eval.get_gradient(q)[d][j]-
+ fe_eval4.get_gradient(q)[d][j]);
+ }
+ errors[4] += std::fabs(fe_eval.get_laplacian(q)[j]-
+ fe_eval5.get_laplacian(q)[j]);
+ }
+ }
+}
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ deallog << "Testing " << fe.get_name() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+
+ Vector<double> solution_dist (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ const double entry = rand()/(double)RAND_MAX;
+ solution_dist(i) = entry;
+ }
+
+ ConstraintMatrix constraints;
+ MatrixFree<dim> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim>::AdditionalData::none;
+ data.mapping_update_flags = update_gradients | update_second_derivatives;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,double> mf (mf_data);
+ mf.test_functions(solution_dist);
+ deallog << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog.threshold_double(1.e-14);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ test<2,5>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ test<3,3>();
+ test<3,4>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error val, function values alone: 0
+DEAL:2d::Error grad, function gradients alone: 0
+DEAL:2d::Error val, function values and gradients alone: 0
+DEAL:2d::Error grad, function values and gradients alone: 0
+DEAL:2d::Error Lapl, function Laplacians alone: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error val, function values alone: 0
+DEAL:2d::Error grad, function gradients alone: 0
+DEAL:2d::Error val, function values and gradients alone: 0
+DEAL:2d::Error grad, function values and gradients alone: 0
+DEAL:2d::Error Lapl, function Laplacians alone: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error val, function values alone: 0
+DEAL:2d::Error grad, function gradients alone: 0
+DEAL:2d::Error val, function values and gradients alone: 0
+DEAL:2d::Error grad, function values and gradients alone: 0
+DEAL:2d::Error Lapl, function Laplacians alone: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error val, function values alone: 0
+DEAL:2d::Error grad, function gradients alone: 0
+DEAL:2d::Error val, function values and gradients alone: 0
+DEAL:2d::Error grad, function values and gradients alone: 0
+DEAL:2d::Error Lapl, function Laplacians alone: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(5)
+DEAL:2d::Error val, function values alone: 0
+DEAL:2d::Error grad, function gradients alone: 0
+DEAL:2d::Error val, function values and gradients alone: 0
+DEAL:2d::Error grad, function values and gradients alone: 0
+DEAL:2d::Error Lapl, function Laplacians alone: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error val, function values alone: 0
+DEAL:3d::Error grad, function gradients alone: 0
+DEAL:3d::Error val, function values and gradients alone: 0
+DEAL:3d::Error grad, function values and gradients alone: 0
+DEAL:3d::Error Lapl, function Laplacians alone: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error val, function values alone: 0
+DEAL:3d::Error grad, function gradients alone: 0
+DEAL:3d::Error val, function values and gradients alone: 0
+DEAL:3d::Error grad, function values and gradients alone: 0
+DEAL:3d::Error Lapl, function Laplacians alone: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(3)
+DEAL:3d::Error val, function values alone: 0
+DEAL:3d::Error grad, function gradients alone: 0
+DEAL:3d::Error val, function values and gradients alone: 0
+DEAL:3d::Error grad, function values and gradients alone: 0
+DEAL:3d::Error Lapl, function Laplacians alone: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(4)
+DEAL:3d::Error val, function values alone: 0
+DEAL:3d::Error grad, function gradients alone: 0
+DEAL:3d::Error val, function values and gradients alone: 0
+DEAL:3d::Error grad, function values and gradients alone: 0
+DEAL:3d::Error Lapl, function Laplacians alone: 0
+DEAL:3d::
--- /dev/null
+//------------------ get_values_plain.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ get_values_plain.cc ------------------------
+
+
+// this tests whether FEEvaluationBase::read_dof_values and
+// FEEvaluationBase::read_dof_values_plain get the same data on a vector where
+// constraints are distributed
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/function.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+
+std::ofstream logfile("get_values_plain/output");
+
+
+template <int dim, int n_dofs_1d, int n_q_points_1d=n_dofs_1d, typename Number=double>
+class MatrixFreeTest
+{
+ public:
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ // make function virtual to allow derived
+ // classes to define a different function
+ virtual void
+ operator () (const MatrixFree<dim,Number> &data,
+ Vector<Number> &,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ typedef VectorizedArray<Number> vector_t;
+ const unsigned int n_vectors = sizeof(vector_t)/sizeof(Number);
+ FEEvaluation<dim,n_dofs_1d,n_q_points_1d,1,Number> fe_eval (data);
+ FEEvaluation<dim,n_dofs_1d,n_q_points_1d,1,Number> fe_eval_plain (data);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+ fe_eval.read_dof_values(src);
+
+ fe_eval_plain.reinit (cell);
+ fe_eval_plain.read_dof_values_plain(src);
+
+ for (unsigned int i=0; i<fe_eval.dofs_per_cell; ++i)
+ for (unsigned int j=0; j<n_vectors; ++j)
+ {
+ error += std::fabs(fe_eval.get_dof_value(i)[j]-
+ fe_eval_plain.get_dof_value(i)[j]);
+ total += std::fabs(fe_eval.get_dof_value(i)[j]);
+ }
+ }
+ }
+
+
+
+ void test_functions (const Vector<Number> &src) const
+ {
+ error = 0;
+ total = 0;
+ Vector<Number> dst_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,n_q_points_1d,Number>::operator(),
+ this, dst_dummy, src);
+
+ deallog.threshold_double(1e-10);
+ deallog << "Error read_dof_values vs read_dof_values_plain: "
+ << error/total << std::endl << std::endl;
+ };
+
+protected:
+ const MatrixFree<dim,Number> &data;
+ mutable double error, total;
+};
+
+
+
+template <int dim, int fe_degree, typename number>
+void do_test (const DoFHandler<dim> &dof,
+ const ConstraintMatrix&constraints)
+{
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells()
+ // << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ Vector<number> solution (dof.n_dofs());
+
+ // create vector with random entries
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ solution(i) = entry;
+ }
+
+ constraints.distribute(solution);
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.store_plain_indices = true;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(solution);
+}
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_shell (tria, Point<dim>(), 1., 2., 96, true);
+ static const HyperShellBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.set_boundary (1, boundary);
+
+ // refine a few cells
+ for (unsigned int i=0; i<11-3*dim; ++i)
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+ {
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Error read_dof_values vs read_dof_values_plain: 0
+DEAL:3d::
--- /dev/null
+//------------------ integrate_functions.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ integrate_functions.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in integrating functions and gradients on a hypeball mesh with
+// adaptive refinement.
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+std::ofstream logfile("integrate_functions/output");
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef std::vector<Vector<Number>*> VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val (data.get_dof_handler().get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_JxW_values)
+ {};
+
+ void operator () (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+
+ void test_functions (Vector<Number> &dst,
+ Vector<Number> &dst_deal) const
+ {
+ dst = 0;
+ dst_deal = 0;
+ VectorType dst_data (2);
+ dst_data[0] = &dst;
+ dst_data[1] = &dst_deal;
+ VectorType src_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,Number>::operator(), this,
+ dst_data, src_dummy);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val;
+};
+
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void MatrixFreeTest<dim,n_dofs_1d,Number>::
+operator () (const MatrixFree<dim,Number> &data,
+ std::vector<Vector<Number>*> &dst,
+ const std::vector<Vector<Number>*> &,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+ const unsigned int dofs_per_cell = fe_eval.dofs_per_cell;
+ AlignedVector<vector_t> values (n_q_points);
+ AlignedVector<vector_t> gradients (dim*n_q_points);
+ std::vector<unsigned int> dof_indices (dofs_per_cell);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit(cell);
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ // generate random numbers at quadrature
+ // points and test them with basis functions
+ // and their gradients
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ values[q][j] = rand()/(double)RAND_MAX;
+ for (unsigned int d=0; d<dim; ++d)
+ gradients[q*dim+d][j] = -1. + 2. * (rand()/(double)RAND_MAX);
+ }
+ fe_val.reinit (data.get_cell_iterator(cell,j));
+ data.get_cell_iterator(cell,j)->get_dof_indices(dof_indices);
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ sum += values[q][j] * fe_val.shape_value(i,q) * fe_val.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients[q*dim+d][j] * fe_val.shape_grad(i,q)[d] *
+ fe_val.JxW(q));
+ }
+ (*dst[1])(dof_indices[i]) += sum;
+ }
+ }
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ fe_eval.submit_value (values[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients[q*dim+d];
+ fe_eval.submit_gradient (submit, q);
+ }
+ fe_eval.integrate (true,true);
+ fe_eval.distribute_local_to_global (*dst[0]);
+ }
+}
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<7-2*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ deallog << "Testing " << fe.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData(MPI_COMM_SELF,MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ Vector<number> solution (dof.n_dofs());
+ Vector<number> solution_dist (dof.n_dofs());
+
+ mf.test_functions(solution_dist, solution);
+
+ constraints.condense (solution);
+
+ Vector<number> compare (solution_dist);
+ compare -= solution;
+ const double diff_norm = compare.linfty_norm();
+
+ deallog << "Norm of difference: " << diff_norm << std::endl << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ test<3,3>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(3)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ integrate_functions_multife.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ integrate_functions_multife.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in integrating functions and gradients on a hypeball mesh with
+// adaptive refinement. It uses two different finite elements inside one
+// matrix_free structure.
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+std::ofstream logfile("integrate_functions_multife/output");
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef std::vector<Vector<Number> > VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val0 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_JxW_values),
+ fe_val01 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_JxW_values),
+ fe_val1 (data.get_dof_handler(1).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_JxW_values)
+ {};
+
+ void operator () (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+
+ void test_functions (VectorType &dst) const
+ {
+ for (unsigned int comp=0; comp<dst.size(); ++comp)
+ dst[comp] = 0;
+ VectorType src_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,Number>::operator(), this,
+ dst, src_dummy);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val0;
+ mutable FEValues<dim> fe_val01;
+ mutable FEValues<dim> fe_val1;
+};
+
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void MatrixFreeTest<dim,n_dofs_1d,Number>::
+operator () (const MatrixFree<dim,Number> &data,
+ std::vector<Vector<Number> > &dst,
+ const std::vector<Vector<Number> > &,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval0 (data, 0, 0);
+ FEEvaluation<dim,n_dofs_1d+1,n_dofs_1d+1,1,Number> fe_eval1 (data, 1, 1);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d+1,1,Number> fe_eval01 (data, 0, 1);
+ const unsigned int n_q_points0 = fe_eval0.n_q_points;
+ const unsigned int n_q_points1 = fe_eval1.n_q_points;
+ const unsigned int dofs_per_cell0 = fe_eval0.dofs_per_cell;
+ const unsigned int dofs_per_cell1 = fe_eval1.dofs_per_cell;
+ AlignedVector<vector_t> values0 (n_q_points0);
+ AlignedVector<vector_t> gradients0 (dim*n_q_points0);
+ AlignedVector<vector_t> values1 (n_q_points1);
+ AlignedVector<vector_t> gradients1 (dim*n_q_points1);
+ std::vector<unsigned int> dof_indices0 (dofs_per_cell0);
+ std::vector<unsigned int> dof_indices1 (dofs_per_cell1);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval0.reinit(cell);
+ fe_eval1.reinit(cell);
+ fe_eval01.reinit(cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ // FE 0, Quad 0
+ // generate random numbers at quadrature
+ // points and test them with basis functions
+ // and their gradients
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ values0[q][j] = rand()/(double)RAND_MAX;
+ for (unsigned int d=0; d<dim; ++d)
+ gradients0[q*dim+d][j] = -1. + 2. * (rand()/(double)RAND_MAX);
+ }
+ fe_val0.reinit (data.get_cell_iterator(cell,j,0));
+ data.get_cell_iterator(cell,j,0)->get_dof_indices(dof_indices0);
+
+ for (unsigned int i=0; i<dofs_per_cell0; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ sum += values0[q][j] * fe_val0.shape_value(i,q) * fe_val0.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients0[q*dim+d][j] * fe_val0.shape_grad(i,q)[d] *
+ fe_val0.JxW(q));
+ }
+ dst[0+1](dof_indices0[i]) += sum;
+ }
+
+ // FE 1, Quad 1
+ fe_val1.reinit (data.get_cell_iterator(cell,j,1));
+ data.get_cell_iterator(cell,j,1)->get_dof_indices(dof_indices1);
+
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ values1[q][j] = rand()/(double)RAND_MAX;
+ for (unsigned int d=0; d<dim; ++d)
+ gradients1[q*dim+d][j] = -1. + 2. * (rand()/(double)RAND_MAX);
+ }
+ for (unsigned int i=0; i<dofs_per_cell1; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ sum += values1[q][j] * fe_val1.shape_value(i,q) * fe_val1.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients1[q*dim+d][j] * fe_val1.shape_grad(i,q)[d] *
+ fe_val1.JxW(q));
+ }
+ dst[2+1](dof_indices1[i]) += sum;
+ }
+
+ // FE 0, Quad 1
+ fe_val01.reinit (data.get_cell_iterator(cell,j,0));
+ for (unsigned int i=0; i<dofs_per_cell0; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ sum += values1[q][j] * fe_val01.shape_value(i,q) * fe_val01.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients1[q*dim+d][j] * fe_val01.shape_grad(i,q)[d] *
+ fe_val01.JxW(q));
+ }
+ dst[4+1](dof_indices0[i]) += sum;
+ }
+ }
+
+ // FE 0, Quad 0
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ fe_eval0.submit_value (values0[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients0[q*dim+d];
+ fe_eval0.submit_gradient (submit, q);
+ }
+ fe_eval0.integrate (true,true);
+ fe_eval0.distribute_local_to_global (dst[0]);
+
+ // FE 1, Quad 1
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ fe_eval1.submit_value (values1[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients1[q*dim+d];
+ fe_eval1.submit_gradient (submit, q);
+ }
+ fe_eval1.integrate (true,true);
+ fe_eval1.distribute_local_to_global (dst[2]);
+
+ // FE 0, Quad 1
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ fe_eval01.submit_value (values1[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients1[q*dim+d];
+ fe_eval01.submit_gradient (submit, q);
+ }
+ fe_eval01.integrate (true,true);
+ fe_eval01.distribute_local_to_global (dst[4]);
+ }
+}
+
+
+
+template <int dim, int fe_degree, typename number>
+void test ()
+{
+ // create hyper ball geometry and refine some
+ // cells
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<7-2*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe0(fe_degree);
+ FE_Q<dim> fe1(fe_degree+1);
+ DoFHandler<dim> dof0(tria);
+ dof0.distribute_dofs(fe0);
+ DoFHandler<dim> dof1(tria);
+ dof1.distribute_dofs(fe1);
+
+ std::vector<const DoFHandler<dim> *>dof(2);
+ dof[0] = &dof0;
+ dof[1] = &dof1;
+
+ deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+
+ std::vector<Vector<number> > dst (6);
+ dst[0].reinit (dof[0]->n_dofs());
+ dst[1].reinit (dst[0]);
+ dst[2].reinit (dof[1]->n_dofs());
+ dst[3].reinit (dst[2]);
+ dst[4].reinit (dst[0]);
+ dst[5].reinit (dst[0]);
+
+ std::vector<const ConstraintMatrix *> constraints(2);
+ ConstraintMatrix constraint0;
+ DoFTools::make_hanging_node_constraints(*dof[0],constraint0);
+ constraint0.close();
+ constraints[0] = &constraint0;
+ ConstraintMatrix constraint1;
+ DoFTools::make_hanging_node_constraints(*dof[1],constraint1);
+ constraint1.close();
+ constraints[1] = &constraint1;
+
+ //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl;
+ //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ std::vector<Quadrature<1> > quad;
+ for (unsigned int no=0; no<2; ++no)
+ quad.push_back(QGauss<1>(fe_degree+1+no));
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData(MPI_COMM_SELF,MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(dst);
+
+ constraints[0]->condense(dst[1]);
+ constraints[1]->condense(dst[3]);
+ constraints[0]->condense(dst[5]);
+
+ dst[1] -= dst[0];
+ double diff_norm = dst[1].linfty_norm();
+ deallog << "FE 0, Quad 0; integration difference: " << diff_norm << std::endl;
+
+ dst[3] -= dst[2];
+ diff_norm = dst[3].linfty_norm();
+ deallog << "FE 1, Quad 1; integration difference: " << diff_norm << std::endl;
+
+ dst[5] -= dst[4];
+ diff_norm = dst[5].linfty_norm();
+ deallog << "FE 0, Quad 1; integration difference: " << diff_norm << std::endl << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1,double>();
+ test<2,2,double>();
+ test<2,3,double>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1,double>();
+ test<3,2,double>();
+ deallog.pop();
+ }
+
+ {
+ deallog << std::endl << "Test with floats" << std::endl << std::endl;
+ deallog.threshold_double(1.e-7);
+ deallog.push("2d");
+ test<2,1,float>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1,float>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2) and FE_Q<2>(3)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(3) and FE_Q<2>(4)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2) and FE_Q<3>(3)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
+DEAL::
+DEAL::Test with floats
+DEAL::
+DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ integrate_functions_multife2.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ integrate_functions_multife2.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// operations in integrating functions and gradients on a hypeball mesh with
+// adaptive refinement. Similar to integrate_functions_multife, but uses
+// FE_DGQ(0) as first component
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_dgq.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+std::ofstream logfile("integrate_functions_multife2/output");
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ typedef std::vector<Vector<Number> > VectorType;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in),
+ fe_val0 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(0)),
+ update_values | update_gradients | update_JxW_values),
+ fe_val01 (data.get_dof_handler(0).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_JxW_values),
+ fe_val1 (data.get_dof_handler(1).get_fe(),
+ Quadrature<dim>(data.get_quad(1)),
+ update_values | update_gradients | update_JxW_values)
+ {};
+
+ void operator () (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const;
+
+ void test_functions (VectorType &dst) const
+ {
+ for (unsigned int comp=0; comp<dst.size(); ++comp)
+ dst[comp] = 0;
+ VectorType src_dummy;
+ data.cell_loop (&MatrixFreeTest<dim,n_dofs_1d,Number>::operator(), this,
+ dst, src_dummy);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+ mutable FEValues<dim> fe_val0;
+ mutable FEValues<dim> fe_val01;
+ mutable FEValues<dim> fe_val1;
+};
+
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void MatrixFreeTest<dim,n_dofs_1d,Number>::
+operator () (const MatrixFree<dim,Number> &data,
+ std::vector<Vector<Number> > &dst,
+ const std::vector<Vector<Number> > &,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+{
+ FEEvaluation<dim,1,1,1,Number> fe_eval0 (data, 0, 0);
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval1 (data, 1, 1);
+ FEEvaluation<dim,1,n_dofs_1d,1,Number> fe_eval01 (data, 0, 1);
+ const unsigned int n_q_points0 = fe_eval0.n_q_points;
+ const unsigned int n_q_points1 = fe_eval1.n_q_points;
+ const unsigned int dofs_per_cell0 = fe_eval0.dofs_per_cell;
+ const unsigned int dofs_per_cell1 = fe_eval1.dofs_per_cell;
+ AlignedVector<vector_t> values0 (n_q_points0);
+ AlignedVector<vector_t> gradients0 (dim*n_q_points0);
+ AlignedVector<vector_t> values1 (n_q_points1);
+ AlignedVector<vector_t> gradients1 (dim*n_q_points1);
+ std::vector<unsigned int> dof_indices0 (dofs_per_cell0);
+ std::vector<unsigned int> dof_indices1 (dofs_per_cell1);
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval0.reinit(cell);
+ fe_eval1.reinit(cell);
+ fe_eval01.reinit(cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ for (unsigned int j=0; j<data.n_components_filled(cell); ++j)
+ {
+ // FE 0, Quad 0
+ // generate random numbers at quadrature
+ // points and test them with basis functions
+ // and their gradients
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ values0[q][j] = rand()/(double)RAND_MAX;
+ for (unsigned int d=0; d<dim; ++d)
+ gradients0[q*dim+d][j] = -1. + 2. * (rand()/(double)RAND_MAX);
+ }
+ fe_val0.reinit (data.get_cell_iterator(cell,j,0));
+ data.get_cell_iterator(cell,j,0)->get_dof_indices(dof_indices0);
+
+ for (unsigned int i=0; i<dofs_per_cell0; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ sum += values0[q][j] * fe_val0.shape_value(i,q) * fe_val0.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients0[q*dim+d][j] * fe_val0.shape_grad(i,q)[d] *
+ fe_val0.JxW(q));
+ }
+ dst[0+1](dof_indices0[i]) += sum;
+ }
+
+ // FE 1, Quad 1
+ fe_val1.reinit (data.get_cell_iterator(cell,j,1));
+ data.get_cell_iterator(cell,j,1)->get_dof_indices(dof_indices1);
+
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ values1[q][j] = rand()/(double)RAND_MAX;
+ for (unsigned int d=0; d<dim; ++d)
+ gradients1[q*dim+d][j] = -1. + 2. * (rand()/(double)RAND_MAX);
+ }
+ for (unsigned int i=0; i<dofs_per_cell1; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ sum += values1[q][j] * fe_val1.shape_value(i,q) * fe_val1.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients1[q*dim+d][j] * fe_val1.shape_grad(i,q)[d] *
+ fe_val1.JxW(q));
+ }
+ dst[2+1](dof_indices1[i]) += sum;
+ }
+
+ // FE 0, Quad 1
+ fe_val01.reinit (data.get_cell_iterator(cell,j,0));
+ for (unsigned int i=0; i<dofs_per_cell0; ++i)
+ {
+ double sum = 0.;
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ sum += values1[q][j] * fe_val01.shape_value(i,q) * fe_val01.JxW(q);
+ for (unsigned int d=0; d<dim; ++d)
+ sum += (gradients1[q*dim+d][j] * fe_val01.shape_grad(i,q)[d] *
+ fe_val01.JxW(q));
+ }
+ dst[4+1](dof_indices0[i]) += sum;
+ }
+ }
+
+ // FE 0, Quad 0
+ for (unsigned int q=0; q<n_q_points0; ++q)
+ {
+ fe_eval0.submit_value (values0[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients0[q*dim+d];
+ fe_eval0.submit_gradient (submit, q);
+ }
+ fe_eval0.integrate (true,true);
+ fe_eval0.distribute_local_to_global (dst[0]);
+
+ // FE 1, Quad 1
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ fe_eval1.submit_value (values1[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients1[q*dim+d];
+ fe_eval1.submit_gradient (submit, q);
+ }
+ fe_eval1.integrate (true,true);
+ fe_eval1.distribute_local_to_global (dst[2]);
+
+ // FE 0, Quad 1
+ for (unsigned int q=0; q<n_q_points1; ++q)
+ {
+ fe_eval01.submit_value (values1[q], q);
+ Tensor<1,dim,vector_t> submit (false);
+ for (unsigned int d=0; d<dim; ++d)
+ submit[d] = gradients1[q*dim+d];
+ fe_eval01.submit_gradient (submit, q);
+ }
+ fe_eval01.integrate (true,true);
+ fe_eval01.distribute_local_to_global (dst[4]);
+ }
+}
+
+
+
+template <int dim, int fe_degree, typename number>
+void test ()
+{
+ // create hyper ball geometry and refine some
+ // cells
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<7-2*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_DGQ<dim> fe0(0);
+ FE_Q<dim> fe1(fe_degree);
+ DoFHandler<dim> dof0(tria);
+ dof0.distribute_dofs(fe0);
+ DoFHandler<dim> dof1(tria);
+ dof1.distribute_dofs(fe1);
+
+ std::vector<const DoFHandler<dim> *>dof(2);
+ dof[0] = &dof0;
+ dof[1] = &dof1;
+
+ deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+
+ std::vector<Vector<number> > dst (6);
+ dst[0].reinit (dof[0]->n_dofs());
+ dst[1].reinit (dst[0]);
+ dst[2].reinit (dof[1]->n_dofs());
+ dst[3].reinit (dst[2]);
+ dst[4].reinit (dst[0]);
+ dst[5].reinit (dst[0]);
+
+ std::vector<const ConstraintMatrix *> constraints(2);
+ ConstraintMatrix constraint0;
+ constraint0.close();
+ constraints[0] = &constraint0;
+ ConstraintMatrix constraint1;
+ DoFTools::make_hanging_node_constraints(*dof[1],constraint1);
+ constraint1.close();
+ constraints[1] = &constraint1;
+
+ //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl;
+ //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl;
+ //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ std::vector<Quadrature<1> > quad;
+ quad.push_back (QGauss<1>(1));
+ quad.push_back (QGauss<1>(fe_degree+1));
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData(MPI_COMM_SELF,MatrixFree<dim,number>::AdditionalData::none));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ mf.test_functions(dst);
+
+ constraints[0]->condense(dst[1]);
+ constraints[1]->condense(dst[3]);
+ constraints[0]->condense(dst[5]);
+
+ dst[1] -= dst[0];
+ double diff_norm = dst[1].linfty_norm();
+ deallog << "FE 0, Quad 0; integration difference: " << diff_norm << std::endl;
+
+ dst[3] -= dst[2];
+ diff_norm = dst[3].linfty_norm();
+ deallog << "FE 1, Quad 1; integration difference: " << diff_norm << std::endl;
+
+ dst[5] -= dst[4];
+ diff_norm = dst[5].linfty_norm();
+ deallog << "FE 0, Quad 1; integration difference: " << diff_norm << std::endl << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1,double>();
+ test<2,2,double>();
+ test<2,3,double>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1,double>();
+ test<3,2,double>();
+ deallog.pop();
+ }
+
+ {
+ deallog << std::endl << "Test with floats" << std::endl << std::endl;
+ deallog.threshold_double(1.e-7);
+ deallog.push("2d");
+ test<2,1,float>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1,float>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(1)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(2)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(3)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(1)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(2)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
+DEAL::
+DEAL::Test with floats
+DEAL::
+DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(1)
+DEAL:2d::FE 0, Quad 0; integration difference: 0
+DEAL:2d::FE 1, Quad 1; integration difference: 0
+DEAL:2d::FE 0, Quad 1; integration difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(1)
+DEAL:3d::FE 0, Quad 0; integration difference: 0
+DEAL:3d::FE 1, Quad 1; integration difference: 0
+DEAL:3d::FE 0, Quad 1; integration difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_01.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_01.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypercube mesh with no hanging nodes and no other
+// constraints
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_01/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(5-dim);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_02.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_02.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypercube mesh with no hanging nodes, but with zero
+// Dirichlet conditions.
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+
+std::ofstream logfile("matrix_vector_02/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(5-dim);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_03.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_03.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypercube mesh with hanging nodes (created by
+// randomly refining some cells) and zero Dirichlet conditions.
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+
+std::ofstream logfile("matrix_vector_03/output");
+
+#include "matrix_vector_common.h"
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_cube (tria);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(2);
+ else
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_04.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_04.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypershell mesh without hanging nodes (only cell
+// type: 2)
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_04/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_shell (tria, Point<dim>(),
+ 0.5, 1., 96, true);
+ static const HyperShellBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.set_boundary (1, boundary);
+ if (dim == 2)
+ tria.refine_global (2);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_05.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_05.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a parallelogram mesh with hanging nodes (only cell
+// type: 1 = linear).
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_05/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ if (dim == 3)
+ return;
+ Triangulation<dim> tria;
+ Tensor<2,dim> points;
+ points[0][0] = 0.25;
+ points[0][1] = 0.123;
+ points[1][0] = 0.09983712334;
+ points[1][1] = 0.314159265358979;
+ GridGenerator::parallelogram (tria, points);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(2);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<5; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
--- /dev/null
+//------------------ matrix_vector_06.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_06.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a mesh consisting of several different cell types
+// according to the create_mesh helper function. Quite large mesh
+// mesh so that the thread parallelization is actually used
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include "create_mesh.h"
+
+std::ofstream logfile("matrix_vector_06/output");
+
+#include "matrix_vector_common.h"
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria);
+ tria.begin_active ()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ typename Triangulation<dim>::active_cell_iterator cell, endc;
+ cell = tria.begin_active ();
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.5)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global(1);
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_06.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_06.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hyperball mesh with hanging nodes generated by
+// randomly refining some cells. Same as matrix_vector_06, but uses FE_DGQ
+// which do not have any connections because DoFs do not overlap and we do not
+// evaluate flux terms (this stresses the partitioning in a different way
+// where non-connected DoFs are to be handled)
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include <deal.II/fe/fe_dgq.h>
+
+std::ofstream logfile("matrix_vector_07/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global(4-dim);
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_DGQ<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ // there should not be any hanging nodes or
+ // boundary conditions for FE_DGQ as there are
+ // only interior DoFs on the elements, but try
+ // anyway
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+
+ // test with coloring only as well
+ do_test<dim, fe_degree, double> (dof, constraints, 2);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_DGQ<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(1)
+DEAL:2d::Parallel option: 2
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_DGQ<2>(2)
+DEAL:2d::Parallel option: 2
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_DGQ<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(1)
+DEAL:3d::Parallel option: 2
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_DGQ<3>(2)
+DEAL:3d::Parallel option: 2
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_08.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_08.cc ------------------------
+
+
+// same test as matrix_vector_06 (quite large mesh, hanging nodes, different
+// cell types), but very large domain of size 1e20 to test correctness of
+// relative scaling in mapping info
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include "create_mesh.h"
+
+std::ofstream logfile("matrix_vector_08/output");
+
+#include "matrix_vector_common.h"
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria, 1e20);
+ tria.begin_active ()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ typename Triangulation<dim>::active_cell_iterator cell, endc;
+ cell = tria.begin_active ();
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.5*1e20)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global(1);
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_09.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_09.cc ------------------------
+
+
+// same test as matrix_vector_06 (quite large mesh, hanging nodes, different
+// cell types), but tiny domain of size 1e-20 to test correctness of
+// relative scaling in mapping info
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include "create_mesh.h"
+
+std::ofstream logfile("matrix_vector_09/output");
+
+#include "matrix_vector_common.h"
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria, 1e-20);
+ tria.begin_active ()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ typename Triangulation<dim>::active_cell_iterator cell, endc;
+ cell = tria.begin_active ();
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.5*1e-20)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ tria.refine_global(1);
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ do_test<dim, fe_degree, double> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_common.h ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_common.h ------------------------
+
+
+// this is a template for matrix-vector products with the Helmholtz equation
+// (zero and first derivatives) on different kinds of meshes (Cartesian,
+// general, with and without hanging nodes). It also tests the multithreading
+// in case it was enabled
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+
+// forward declare this function. will be implemented in .cc files
+template <int dim, int fe_degree>
+void test ();
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void
+helmholtz_operator (const MatrixFree<dim,Number> &data,
+ Vector<Number> &dst,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range)
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ fe_eval.read_dof_values (src);
+ fe_eval.template evaluate (true, true, false);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ fe_eval.submit_value (Number(10)*fe_eval.get_value(q),q);
+ fe_eval.submit_gradient (fe_eval.get_gradient(q),q);
+ }
+ fe_eval.template integrate (true,true);
+ fe_eval.distribute_local_to_global (dst);
+ }
+}
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_vectors;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void vmult (Vector<Number> &dst,
+ const Vector<Number> &src) const
+ {
+ dst = 0;
+ const std_cxx1x::function<void(const MatrixFree<dim,Number> &,
+ Vector<Number> &,
+ const Vector<Number> &,
+ const std::pair<unsigned int,unsigned int>&)>
+ wrap = helmholtz_operator<dim,n_dofs_1d,Number>;
+ data.cell_loop (wrap, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+
+
+template <int dim, int fe_degree, typename number>
+void do_test (const DoFHandler<dim> &dof,
+ const ConstraintMatrix&constraints,
+ const unsigned int parallel_option = 0)
+{
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ if (parallel_option > 0)
+ deallog << "Parallel option: " << parallel_option << std::endl;
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ if (parallel_option == 1)
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_color;
+ else if (parallel_option == 2)
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::color;
+ else
+ {
+ Assert (parallel_option == 0, ExcInternalError());
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_partition;
+ }
+ data.tasks_block_size = 7;
+
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ Vector<number> in (dof.n_dofs()), out (dof.n_dofs());
+ Vector<number> in_dist (dof.n_dofs());
+ Vector<number> out_dist (in_dist);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ in(i) = entry;
+ in_dist(i) = entry;
+ }
+
+ mf.vmult (out_dist, in_dist);
+
+
+ // assemble sparse matrix with (\nabla v,
+ // \nabla u) + (v, 10 * u)
+ SparsityPattern sparsity;
+ {
+ CompressedSimpleSparsityPattern csp(dof.n_dofs(), dof.n_dofs());
+ DoFTools::make_sparsity_pattern (dof, csp, constraints, true);
+ sparsity.copy_from(csp);
+ }
+ SparseMatrix<double> sparse_matrix (sparsity);
+ {
+ QGauss<dim> quadrature_formula(fe_degree+1);
+
+ FEValues<dim> fe_values (dof.get_fe(), quadrature_formula,
+ update_values | update_gradients |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_matrix = 0;
+ fe_values.reinit (cell);
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point)
+ +
+ 10. *
+ fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+
+ cell->get_dof_indices(local_dof_indices);
+ constraints.distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ sparse_matrix);
+ }
+ }
+
+ sparse_matrix.vmult (out, in);
+ out -= out_dist;
+ const double diff_norm = out.linfty_norm() / out_dist.linfty_norm();
+
+ deallog << "Norm of difference: " << diff_norm << std::endl << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+
+ {
+ deallog.threshold_double(5.e-11);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+//------------------ matrix_vector_curl.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_curl.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hyperball mesh with hanging nodes for a
+// vector-valued problem (curl-curl operator which does not really make a lot
+// of sense from a problem point of view, though).
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_curl/output");
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+#include <complex>
+
+const double global_coefficient = 0.1;
+
+
+template <int dim, int degree, typename VectorType>
+class MatrixFreeTest
+{
+ public:
+ typedef typename DoFHandler<dim>::active_cell_iterator CellIterator;
+ typedef double Number;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void
+ local_apply (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ typedef VectorizedArray<Number> vector_t;
+ FEEvaluation<dim,degree+1,degree+1,dim,Number> phi (data);
+ vector_t coeff = make_vectorized_array(global_coefficient);
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ phi.reinit (cell);
+ phi.read_dof_values (src);
+ phi.evaluate (false,true,false);
+
+ for (unsigned int q=0; q<phi.n_q_points; ++q)
+ phi.submit_curl (coeff * phi.get_curl(q), q);
+
+ phi.integrate (false,true);
+ phi.distribute_local_to_global (dst);
+ }
+ }
+
+ void vmult (VectorType &dst,
+ const VectorType &src) const
+ {
+ AssertDimension (dst.size(), dim);
+ for (unsigned int d=0; d<dim; ++d)
+ dst[d] = 0;
+ data.cell_loop (&MatrixFreeTest<dim,degree,VectorType>::local_apply,
+ this, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.refine_global(4-dim);
+
+ // refine a few cells
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+
+ FE_Q<dim> fe_sca (QGaussLobatto<1>(fe_degree+1));
+ FESystem<dim> fe (fe_sca, dim);
+ DoFHandler<dim> dof_handler_sca (tria);
+ DoFHandler<dim> dof_handler (tria);
+
+ MatrixFree<dim,double> mf_data;
+
+ ConstraintMatrix constraints;
+
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> system_matrix;
+
+ BlockVector<double> solution;
+ BlockVector<double> system_rhs;
+ std::vector<Vector<double> > vec1, vec2;
+
+ dof_handler.distribute_dofs (fe);
+ dof_handler_sca.distribute_dofs (fe_sca);
+ DoFRenumbering::component_wise (dof_handler);
+
+ DoFTools::make_hanging_node_constraints (dof_handler, constraints);
+ constraints.close ();
+
+ const unsigned int dofs_per_block = dof_handler_sca.n_dofs();
+ {
+ BlockCompressedSimpleSparsityPattern csp (dim,dim);
+ for (unsigned int d=0; d<dim; ++d)
+ for (unsigned int e=0; e<dim; ++e)
+ csp.block(d,e).reinit (dofs_per_block, dofs_per_block);
+
+ csp.collect_sizes();
+
+ DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
+ sparsity_pattern.copy_from (csp);
+ }
+
+ system_matrix.reinit (sparsity_pattern);
+
+ solution.reinit (dim);
+ for (unsigned int i=0; i<dim; ++i)
+ solution.block(i).reinit (dofs_per_block);
+ solution.collect_sizes ();
+
+ system_rhs.reinit (solution);
+
+ vec1.resize (dim);
+ vec2.resize (dim);
+ vec1[0].reinit (dofs_per_block);
+ vec2[0].reinit (vec1[0]);
+ for (unsigned int i=1; i<dim; ++i)
+ {
+ vec1[i].reinit (vec1[0]);
+ vec2[i].reinit (vec1[0]);
+ }
+
+ // assemble curl-curl operator
+ {
+ QGauss<dim> quadrature_formula(fe_degree+1);
+
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_values |
+ update_JxW_values |
+ update_gradients);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ const FEValuesExtractors::Vector sc (0);
+
+ const unsigned int curl_dim = dim==2 ? 1 : dim;
+ std::vector<Tensor<1,curl_dim> > phi_curl (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ local_matrix = 0;
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ const Tensor<2,dim> phi_grad = fe_values[sc].gradient(k,q);
+ if (dim == 2)
+ phi_curl[k][0] = phi_grad[1][0] - phi_grad[0][1];
+ else
+ {
+ phi_curl[k][0] = phi_grad[2][1] - phi_grad[1][2];
+ phi_curl[k][1] = phi_grad[0][2] - phi_grad[2][0];
+ phi_curl[k][2] = phi_grad[1][0] - phi_grad[0][1];
+ }
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<=i; ++j)
+ {
+ local_matrix(i,j) += (phi_curl[i] * phi_curl[j] *
+ global_coefficient)
+ * fe_values.JxW(q);
+ }
+ }
+ }
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=i+1; j<dofs_per_cell; ++j)
+ local_matrix(i,j) = local_matrix(j,i);
+
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ system_matrix);
+ }
+ }
+
+ // first system_rhs with random numbers
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int j=0; j<system_rhs.block(i).size(); ++j)
+ {
+ const double val = -1. + 2.*(double)rand()/double(RAND_MAX);
+ system_rhs.block(i)(j) = val;
+ }
+ constraints.condense(system_rhs);
+ for (unsigned int i=0; i<dim; ++i)
+ vec1[i] = system_rhs.block(i);
+
+ // setup matrix-free structure
+ {
+ QGauss<1> quad(fe_degree+1);
+ mf_data.reinit (dof_handler_sca, constraints, quad,
+ typename MatrixFree<dim>::AdditionalData
+ (MPI_COMM_WORLD,
+ MatrixFree<dim>::AdditionalData::none));
+ }
+
+ system_matrix.vmult (solution, system_rhs);
+
+ typedef std::vector<Vector<double> > VectorType;
+ MatrixFreeTest<dim,fe_degree,VectorType> mf (mf_data);
+ mf.vmult (vec2, vec1);
+
+ // Verification
+ double error = 0.;
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int j=0; j<system_rhs.block(i).size(); ++j)
+ error += std::fabs (solution.block(i)(j)-vec2[i](j));
+ double relative = solution.block(0).l1_norm();
+ deallog << " Verification fe degree " << fe_degree << ": "
+ << error/relative << std::endl << std::endl;
+}
+
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+
+ {
+ deallog << std::endl << "Test with doubles" << std::endl << std::endl;
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL::
+DEAL::Test with doubles
+DEAL::
+DEAL:2d:: Verification fe degree 1: 0
+DEAL:2d::
+DEAL:2d:: Verification fe degree 2: 0
+DEAL:2d::
+DEAL:3d:: Verification fe degree 1: 0
+DEAL:3d::
+DEAL:3d:: Verification fe degree 2: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_float.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_float.cc ------------------------
+
+
+// Same as matrix_vector_06 for testing correctness of matrix-vector products
+// compared with deal.II sparse matrices, but using floats instead of
+// doubles. this tests whether everything is alright for longer vector lengths
+// as well
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_float/output");
+
+#include "create_mesh.h"
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<1e-8)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ deallog.threshold_double (5e-6);
+ do_test<dim, fe_degree, float> (dof, constraints);
+}
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_hp.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_hp.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix for hp DoFHandler on a hyperball mesh with hanging nodes and finite
+// elements orders distributed randomly.
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_hp/output");
+
+#include "matrix_vector_common.h"
+#include <deal.II/hp/dof_handler.h>
+#include <deal.II/hp/fe_values.h>
+#include <deal.II/base/function.h>
+
+
+
+template <int dim, typename Number>
+class MatrixFreeTestHP
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTestHP(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void local_apply(const MatrixFree<dim,Number> &data,
+ Vector<Number> &dst,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ // ask MatrixFree for cell_range for different orders
+ std::pair<unsigned int,unsigned int> subrange_deg =
+ data.create_cell_subrange_hp (cell_range, 1);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,2,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 2);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,3,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 3);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,4,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 4);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,5,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 5);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,6,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 6);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,7,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 7);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,8,Number> (data, dst, src,
+ subrange_deg);
+ }
+
+ void vmult (Vector<Number> &dst,
+ const Vector<Number> &src) const
+ {
+ dst = 0;
+ data.cell_loop (&MatrixFreeTestHP<dim,Number>::local_apply, this, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ if (fe_degree > 1)
+ return;
+
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.refine_global(1);
+
+ // refine a few cells
+ for (unsigned int i=0; i<11-3*dim; ++i)
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ const unsigned int max_degree = 9-2*dim;
+
+ hp::FECollection<dim> fe_collection;
+ hp::QCollection<dim> quadrature_collection;
+ hp::QCollection<1> quadrature_collection_mf;
+
+ for (unsigned int deg=1; deg<=max_degree; ++deg)
+ {
+ fe_collection.push_back (FE_Q<dim>(QGaussLobatto<1>(deg+1)));
+ quadrature_collection.push_back (QGauss<dim>(deg+1));
+ quadrature_collection_mf.push_back (QGauss<1>(deg+1));
+ }
+
+ hp::DoFHandler<dim> dof(tria);
+ // set the active FE index in a random order
+ {
+ typename hp::DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ {
+ const unsigned int fe_index = rand() % max_degree;
+ cell->set_active_fe_index (fe_index);
+ }
+ }
+
+ // setup DoFs
+ dof.distribute_dofs(fe_collection);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof,
+ constraints);
+ VectorTools::interpolate_boundary_values (dof,
+ 0,
+ ZeroFunction<dim>(),
+ constraints);
+ constraints.close ();
+ CompressedSimpleSparsityPattern csp (dof.n_dofs(),
+ dof.n_dofs());
+ DoFTools::make_sparsity_pattern (dof, csp, constraints, false);
+ SparsityPattern sparsity;
+ sparsity.copy_from (csp);
+ SparseMatrix<double> system_matrix (sparsity);
+
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ // set up MatrixFree
+ MatrixFree<dim,number> mf_data;
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::none;
+ mf_data.reinit (dof, constraints, quadrature_collection_mf, data);
+ MatrixFreeTestHP<dim,number> mf (mf_data);
+
+ // assemble sparse matrix with (\nabla v,
+ // \nabla u) + (v, 10 * u)
+ {
+ hp::FEValues<dim> hp_fe_values (fe_collection,
+ quadrature_collection,
+ update_values | update_gradients |
+ update_JxW_values);
+ FullMatrix<double> cell_matrix;
+ std::vector<unsigned int> local_dof_indices;
+
+ typename hp::DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ {
+ const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell;
+
+ cell_matrix.reinit (dofs_per_cell, dofs_per_cell);
+ cell_matrix = 0;
+ hp_fe_values.reinit (cell);
+ const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values ();
+
+ for (unsigned int q_point=0;
+ q_point<fe_values.n_quadrature_points;
+ ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point) +
+ 10. * fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+ local_dof_indices.resize (dofs_per_cell);
+ cell->get_dof_indices (local_dof_indices);
+
+ constraints.distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ system_matrix);
+ }
+ }
+
+ // fill a right hand side vector with random
+ // numbers in unconstrained degrees of freedom
+ Vector<double> src (dof.n_dofs());
+ Vector<double> result_spmv(src), result_mf (src);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if (constraints.is_constrained(i) == false)
+ src(i) = (double)rand()/RAND_MAX;
+ }
+
+ // now perform matrix-vector product and check
+ // its correctness
+ system_matrix.vmult (result_spmv, src);
+ mf.vmult (result_mf, src);
+
+ result_mf -= result_spmv;
+ const double diff_norm = result_mf.linfty_norm();
+ deallog << "Norm of difference: " << diff_norm << std::endl << std::endl;
+}
+
--- /dev/null
+
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_mg.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_mg.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix for MG DoFHandler on a hyperball mesh with no hanging nodes but
+// homogeneous Dirichlet conditions
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_mg/output");
+
+#include "matrix_vector_common.h"
+#include <deal.II/multigrid/mg_dof_handler.h>
+#include <deal.II/multigrid/mg_tools.h>
+#include <deal.II/multigrid/mg_matrix.h>
+#include <deal.II/base/function.h>
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.refine_global(5-dim);
+
+ FE_Q<dim> fe (fe_degree);
+
+ // setup DoFs
+ MGDoFHandler<dim> dof(tria);
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ VectorTools::interpolate_boundary_values (dof,
+ 0,
+ ZeroFunction<dim>(),
+ constraints);
+ constraints.close ();
+
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+
+ // set up MatrixFree
+ QGauss<1> quad (fe_degree+1);
+ MatrixFree<dim> mf_data;
+ mf_data.reinit (dof, constraints, quad);
+ SparsityPattern sparsity;
+ SparseMatrix<double> system_matrix;
+ {
+ CompressedSimpleSparsityPattern csp (dof.n_dofs(), dof.n_dofs());
+ DoFTools::make_sparsity_pattern (static_cast<const DoFHandler<dim>&>(dof),
+ csp, constraints, false);
+ sparsity.copy_from (csp);
+ }
+ system_matrix.reinit (sparsity);
+
+ // setup MG levels
+ const unsigned int nlevels = tria.n_levels();
+ typedef MatrixFree<dim> MatrixFreeTestType;
+ MGLevelObject<MatrixFreeTestType> mg_matrices;
+ MGLevelObject<ConstraintMatrix> mg_constraints;
+ MGLevelObject<SparsityPattern> mg_sparsities;
+ MGLevelObject<SparseMatrix<double> > mg_ref_matrices;
+ mg_matrices.resize (0, nlevels-1);
+ mg_constraints.resize (0, nlevels-1);
+ mg_sparsities.resize (0, nlevels-1);
+ mg_ref_matrices.resize (0, nlevels-1);
+
+ typename FunctionMap<dim>::type dirichlet_boundary;
+ ZeroFunction<dim> homogeneous_dirichlet_bc (1);
+ dirichlet_boundary[0] = &homogeneous_dirichlet_bc;
+ std::vector<std::set<unsigned int> > boundary_indices(nlevels);
+ MGTools::make_boundary_list (dof, dirichlet_boundary, boundary_indices);
+ for (unsigned int level=0;level<nlevels;++level)
+ {
+ std::set<unsigned int>::iterator bc_it = boundary_indices[level].begin();
+ for ( ; bc_it != boundary_indices[level].end(); ++bc_it)
+ mg_constraints[level].add_line(*bc_it);
+ mg_constraints[level].close();
+ typename MatrixFree<dim>::AdditionalData data;
+ data.level_mg_handler = level;
+ mg_matrices[level].reinit(dof, mg_constraints[level], quad, data);
+
+ CompressedSimpleSparsityPattern csp;
+ csp.reinit (dof.n_dofs(level), dof.n_dofs(level));
+ MGTools::make_sparsity_pattern (dof, csp, level);
+ mg_sparsities[level].copy_from (csp);
+ mg_ref_matrices[level].reinit (mg_sparsities[level]);
+ }
+
+ // assemble sparse matrix with (\nabla v,
+ // \nabla u) + (v, 10 * u) on the actual
+ // discretization and on all levels
+ {
+ QGauss<dim> quad (fe_degree+1);
+ FEValues<dim> fe_values (fe, quad,
+ update_values | update_gradients |
+ update_JxW_values);
+ const unsigned int n_quadrature_points = quad.size();
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_matrix = 0;
+ fe_values.reinit (cell);
+
+ for (unsigned int q_point=0; q_point<n_quadrature_points; ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point) +
+ 10. * fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ system_matrix);
+ }
+
+ // now to the MG assembly
+ typename MGDoFHandler<dim>::cell_iterator
+ cellm = dof.begin(),
+ endcm = dof.end();
+ for (; cellm!=endcm; ++cellm)
+ {
+ cell_matrix = 0;
+ fe_values.reinit (cellm);
+
+ for (unsigned int q_point=0; q_point<n_quadrature_points; ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point) +
+ 10. * fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+ cellm->get_mg_dof_indices (local_dof_indices);
+ mg_constraints[cellm->level()]
+ .distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ mg_ref_matrices[cellm->level()]);
+ }
+ }
+
+ // fill a right hand side vector with random
+ // numbers in unconstrained degrees of freedom
+ Vector<double> src (dof.n_dofs());
+ Vector<double> result_spmv(src), result_mf (src);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if (constraints.is_constrained(i) == false)
+ src(i) = (double)rand()/RAND_MAX;
+ }
+
+ // now perform matrix-vector product and check
+ // its correctness
+ system_matrix.vmult (result_spmv, src);
+ MatrixFreeTest<dim,fe_degree+1,double> mf (mf_data);
+ mf.vmult (result_mf, src);
+
+ result_mf -= result_spmv;
+ const double diff_norm = result_mf.linfty_norm();
+ deallog << "Norm of difference active: " << diff_norm << std::endl;
+
+ for (unsigned int level=0; level<nlevels; ++level)
+ {
+ Vector<double> src (dof.n_dofs(level));
+ Vector<double> result_spmv(src), result_mf (src);
+
+ for (unsigned int i=0; i<dof.n_dofs(level); ++i)
+ {
+ if (mg_constraints[level].is_constrained(i) == false)
+ src(i) = (double)rand()/RAND_MAX;
+ }
+
+ // now perform matrix-vector product and check
+ // its correctness
+ mg_ref_matrices[level].vmult (result_spmv, src);
+ MatrixFreeTest<dim,fe_degree+1,double> mf_lev (mg_matrices[level]);
+ mf_lev.vmult (result_mf, src);
+
+ result_mf -= result_spmv;
+ const double diff_norm = result_mf.linfty_norm();
+ deallog << "Norm of difference MG level " << level
+ << ": " << diff_norm << std::endl;
+ }
+ deallog << std::endl;
+}
+
--- /dev/null
+
+DEAL:2d::Norm of difference active: 0
+DEAL:2d::Norm of difference MG level 0: 0
+DEAL:2d::Norm of difference MG level 1: 0
+DEAL:2d::Norm of difference MG level 2: 0
+DEAL:2d::Norm of difference MG level 3: 0
+DEAL:2d::
+DEAL:2d::Norm of difference active: 0
+DEAL:2d::Norm of difference MG level 0: 0
+DEAL:2d::Norm of difference MG level 1: 0
+DEAL:2d::Norm of difference MG level 2: 0
+DEAL:2d::Norm of difference MG level 3: 0
+DEAL:2d::
+DEAL:3d::Norm of difference active: 0
+DEAL:3d::Norm of difference MG level 0: 0
+DEAL:3d::Norm of difference MG level 1: 0
+DEAL:3d::Norm of difference MG level 2: 0
+DEAL:3d::
+DEAL:3d::Norm of difference active: 0
+DEAL:3d::Norm of difference MG level 0: 0
+DEAL:3d::Norm of difference MG level 1: 0
+DEAL:3d::Norm of difference MG level 2: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_stokes.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_stokes.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypercube mesh with no hanging nodes and no other
+// constraints for a vector-valued problem (stokes equations).
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_stokes/output");
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+#include <complex>
+
+
+
+template <int dim, int degree_p, typename VectorType>
+class MatrixFreeTest
+{
+ public:
+ typedef typename DoFHandler<dim>::active_cell_iterator CellIterator;
+ typedef double Number;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void
+ local_apply (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ typedef VectorizedArray<Number> vector_t;
+ FEEvaluation<dim,degree_p+2,degree_p+2,dim,Number> velocity (data, 0);
+ FEEvaluation<dim,degree_p+1,degree_p+2,1, Number> pressure (data, 1);
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ velocity.reinit (cell);
+ velocity.read_dof_values (src, 0);
+ velocity.evaluate (false,true,false);
+ pressure.reinit (cell);
+ pressure.read_dof_values (src, dim);
+ pressure.evaluate (true,false,false);
+
+ for (unsigned int q=0; q<velocity.n_q_points; ++q)
+ {
+ SymmetricTensor<2,dim,vector_t> sym_grad_u =
+ velocity.get_symmetric_gradient (q);
+ vector_t pres = pressure.get_value(q);
+ vector_t div = -velocity.get_divergence(q);
+ pressure.submit_value (div, q);
+
+ // subtract p * I
+ for (unsigned int d=0; d<dim; ++d)
+ sym_grad_u[d][d] -= pres;
+
+ velocity.submit_symmetric_gradient(sym_grad_u, q);
+ }
+
+ velocity.integrate (false,true);
+ velocity.distribute_local_to_global (dst, 0);
+ pressure.integrate (true,false);
+ pressure.distribute_local_to_global (dst, dim);
+ }
+ }
+
+
+ void vmult (VectorType &dst,
+ const VectorType &src) const
+ {
+ AssertDimension (dst.size(), dim+1);
+ for (unsigned int d=0; d<dim+1; ++d)
+ dst[d] = 0;
+ data.cell_loop (&MatrixFreeTest<dim,degree_p,VectorType>::local_apply,
+ this, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> triangulation;
+ {
+ std::vector<unsigned int> subdivisions (dim, 1);
+ subdivisions[0] = 4;
+
+ const Point<dim> bottom_left = (dim == 2 ?
+ Point<dim>(-2,-1) :
+ Point<dim>(-2,0,-1));
+ const Point<dim> top_right = (dim == 2 ?
+ Point<dim>(2,0) :
+ Point<dim>(2,1,0));
+
+ GridGenerator::subdivided_hyper_rectangle (triangulation,
+ subdivisions,
+ bottom_left,
+ top_right);
+ }
+ triangulation.refine_global (4-dim);
+
+ FE_Q<dim> fe_u (fe_degree+1);
+ FE_Q<dim> fe_p (fe_degree);
+ FESystem<dim> fe (fe_u, dim, fe_p, 1);
+ DoFHandler<dim> dof_handler_u (triangulation);
+ DoFHandler<dim> dof_handler_p (triangulation);
+ DoFHandler<dim> dof_handler (triangulation);
+
+ MatrixFree<dim,double> mf_data;
+
+ ConstraintMatrix constraints;
+
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> system_matrix;
+
+ BlockVector<double> solution;
+ BlockVector<double> system_rhs;
+ std::vector<Vector<double> > vec1, vec2;
+
+ dof_handler.distribute_dofs (fe);
+ dof_handler_u.distribute_dofs (fe_u);
+ dof_handler_p.distribute_dofs (fe_p);
+ DoFRenumbering::component_wise (dof_handler);
+
+ constraints.close ();
+
+ std::vector<unsigned int> dofs_per_block (dim+1);
+ DoFTools::count_dofs_per_component (dof_handler, dofs_per_block);
+
+ //std::cout << " Number of active cells: "
+ // << triangulation.n_active_cells()
+ // << std::endl
+ // << " Number of degrees of freedom: "
+ // << dof_handler.n_dofs()
+ // << " (" << n_u << '+' << n_p << ')'
+ // << std::endl;
+
+ {
+ BlockCompressedSimpleSparsityPattern csp (dim+1,dim+1);
+
+ for (unsigned int d=0; d<dim+1; ++d)
+ for (unsigned int e=0; e<dim+1; ++e)
+ csp.block(d,e).reinit (dofs_per_block[d], dofs_per_block[e]);
+
+ csp.collect_sizes();
+
+ DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
+ sparsity_pattern.copy_from (csp);
+ }
+
+ system_matrix.reinit (sparsity_pattern);
+
+ solution.reinit (dim+1);
+ for (unsigned int i=0; i<dim+1; ++i)
+ solution.block(i).reinit (dofs_per_block[i]);
+ solution.collect_sizes ();
+
+ system_rhs.reinit (solution);
+
+ vec1.resize (dim+1);
+ vec2.resize (dim+1);
+ vec1[0].reinit (dofs_per_block[0]);
+ vec2[0].reinit (vec1[0]);
+ for (unsigned int i=1; i<dim; ++i)
+ {
+ vec1[i].reinit (vec1[0]);
+ vec2[i].reinit (vec1[0]);
+ }
+ vec1[dim].reinit (dofs_per_block[dim]);
+ vec2[dim].reinit (vec1[dim]);
+
+ // this is from step-22
+ {
+ QGauss<dim> quadrature_formula(fe_degree+2);
+
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_values |
+ update_JxW_values |
+ update_gradients);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
+
+ std::vector<SymmetricTensor<2,dim> > phi_grads_u (dofs_per_cell);
+ std::vector<double> div_phi_u (dofs_per_cell);
+ std::vector<double> phi_p (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ local_matrix = 0;
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_grads_u[k] = fe_values[velocities].symmetric_gradient (k, q);
+ div_phi_u[k] = fe_values[velocities].divergence (k, q);
+ phi_p[k] = fe_values[pressure].value (k, q);
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<=i; ++j)
+ {
+ local_matrix(i,j) += (phi_grads_u[i] * phi_grads_u[j]
+ - div_phi_u[i] * phi_p[j]
+ - phi_p[i] * div_phi_u[j])
+ * fe_values.JxW(q);
+ }
+ }
+ }
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=i+1; j<dofs_per_cell; ++j)
+ local_matrix(i,j) = local_matrix(j,i);
+
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ system_matrix);
+ }
+ }
+
+ // first system_rhs with random numbers
+ for (unsigned int i=0; i<dim+1; ++i)
+ for (unsigned int j=0; j<system_rhs.block(i).size(); ++j)
+ {
+ const double val = -1. + 2.*(double)rand()/double(RAND_MAX);
+ system_rhs.block(i)(j) = val;
+ vec1[i](j) = val;
+ }
+
+ // setup matrix-free structure
+ {
+ std::vector<const DoFHandler<dim>*> dofs;
+ dofs.push_back(&dof_handler_u);
+ dofs.push_back(&dof_handler_p);
+ ConstraintMatrix dummy_constraints;
+ dummy_constraints.close();
+ std::vector<const ConstraintMatrix*> constraints;
+ constraints.push_back (&dummy_constraints);
+ constraints.push_back (&dummy_constraints);
+ QGauss<1> quad(fe_degree+2);
+ mf_data.reinit (dofs, constraints, quad,
+ typename MatrixFree<dim>::AdditionalData
+ (MPI_COMM_WORLD,
+ MatrixFree<dim>::AdditionalData::none));
+ }
+
+ system_matrix.vmult (solution, system_rhs);
+
+ typedef std::vector<Vector<double> > VectorType;
+ MatrixFreeTest<dim,fe_degree,VectorType> mf (mf_data);
+ mf.vmult (vec2, vec1);
+
+ // Verification
+ double error = 0.;
+ for (unsigned int i=0; i<dim+1; ++i)
+ for (unsigned int j=0; j<system_rhs.block(i).size(); ++j)
+ error += std::fabs (solution.block(i)(j)-vec2[i](j));
+ double relative = solution.block(0).l1_norm();
+ deallog << " Verification fe degree " << fe_degree << ": "
+ << error/relative << std::endl << std::endl;
+}
+
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+
+ {
+ deallog << std::endl << "Test with doubles" << std::endl << std::endl;
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL::
+DEAL::Test with doubles
+DEAL::
+DEAL:2d:: Verification fe degree 1: 0
+DEAL:2d::
+DEAL:2d:: Verification fe degree 2: 0
+DEAL:2d::
+DEAL:2d:: Verification fe degree 3: 0
+DEAL:2d::
+DEAL:2d:: Verification fe degree 4: 0
+DEAL:2d::
+DEAL:3d:: Verification fe degree 1: 0
+DEAL:3d::
+DEAL:3d:: Verification fe degree 2: 0
+DEAL:3d::
--- /dev/null
+//------------------ matrix_vector_stokes_noflux.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_vector_stokes_noflux.cc ------------------------
+
+
+// this function tests the correctness of the implementation of matrix free
+// matrix-vector products by comparing with the result of deal.II sparse
+// matrix. The mesh uses a hypershell mesh with hanging nodes and constraints
+// between the vector components in the form of no-normal flux constraints on
+// the Stokes equations.
+
+#include "../tests.h"
+
+std::ofstream logfile("matrix_vector_stokes_noflux/output");
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+#include <complex>
+
+
+
+template <int dim, int degree_p, typename VectorType>
+class MatrixFreeTest
+{
+ public:
+ typedef typename DoFHandler<dim>::active_cell_iterator CellIterator;
+ typedef double Number;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void
+ local_apply (const MatrixFree<dim,Number> &data,
+ VectorType &dst,
+ const VectorType &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ typedef VectorizedArray<Number> vector_t;
+ FEEvaluation<dim,degree_p+2,degree_p+2,dim,Number> velocity (data, 0);
+ FEEvaluation<dim,degree_p+1,degree_p+2,1, Number> pressure (data, 1);
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ velocity.reinit (cell);
+ velocity.read_dof_values (src[0]);
+ velocity.evaluate (false,true,false);
+ pressure.reinit (cell);
+ pressure.read_dof_values (src[1]);
+ pressure.evaluate (true,false,false);
+
+ for (unsigned int q=0; q<velocity.n_q_points; ++q)
+ {
+ SymmetricTensor<2,dim,vector_t> sym_grad_u =
+ velocity.get_symmetric_gradient (q);
+ vector_t pres = pressure.get_value(q);
+ vector_t div = -velocity.get_divergence(q);
+ pressure.submit_value (div, q);
+
+ // subtract p * I
+ for (unsigned int d=0; d<dim; ++d)
+ sym_grad_u[d][d] -= pres;
+
+ velocity.submit_symmetric_gradient(sym_grad_u, q);
+ }
+
+ velocity.integrate (false,true);
+ velocity.distribute_local_to_global (dst[0]);
+ pressure.integrate (true,false);
+ pressure.distribute_local_to_global (dst[1]);
+ }
+ }
+
+
+ void vmult (VectorType &dst,
+ const VectorType &src) const
+ {
+ AssertDimension (dst.size(), 2);
+ for (unsigned int d=0; d<2; ++d)
+ dst[d] = 0;
+ data.cell_loop (&MatrixFreeTest<dim,degree_p,VectorType>::local_apply,
+ this, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ Triangulation<dim> triangulation;
+ GridGenerator::hyper_shell (triangulation, Point<dim>(),
+ 0.5, 1., 96, true);
+ static HyperShellBoundary<dim> boundary;
+ triangulation.set_boundary (0, boundary);
+ triangulation.set_boundary (1, boundary);
+ triangulation.begin_active()->set_refine_flag();
+ triangulation.last()->set_refine_flag();
+ triangulation.execute_coarsening_and_refinement();
+ triangulation.refine_global (3-dim);
+ triangulation.last()->set_refine_flag();
+ triangulation.execute_coarsening_and_refinement();
+
+ MappingQ<dim> mapping (3);
+ FE_Q<dim> fe_u_scal (fe_degree+1);
+ FESystem<dim> fe_u (fe_u_scal,dim);
+ FE_Q<dim> fe_p (fe_degree);
+ FESystem<dim> fe (fe_u_scal, dim, fe_p, 1);
+ DoFHandler<dim> dof_handler_u (triangulation);
+ DoFHandler<dim> dof_handler_p (triangulation);
+ DoFHandler<dim> dof_handler (triangulation);
+
+ MatrixFree<dim,double> mf_data;
+
+ ConstraintMatrix constraints, constraints_u, constraints_p;
+
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> system_matrix;
+
+ BlockVector<double> solution;
+ BlockVector<double> system_rhs;
+ std::vector<Vector<double> > vec1, vec2;
+
+ dof_handler.distribute_dofs (fe);
+ dof_handler_u.distribute_dofs (fe_u);
+ dof_handler_p.distribute_dofs (fe_p);
+ std::vector<unsigned int> stokes_sub_blocks (dim+1,0);
+ stokes_sub_blocks[dim] = 1;
+ DoFRenumbering::component_wise (dof_handler, stokes_sub_blocks);
+
+ std::set<unsigned char> no_normal_flux_boundaries;
+ no_normal_flux_boundaries.insert (0);
+ no_normal_flux_boundaries.insert (1);
+ DoFTools::make_hanging_node_constraints (dof_handler,
+ constraints);
+ VectorTools::compute_no_normal_flux_constraints (dof_handler, 0,
+ no_normal_flux_boundaries,
+ constraints, mapping);
+ constraints.close ();
+ DoFTools::make_hanging_node_constraints (dof_handler_u,
+ constraints_u);
+ VectorTools::compute_no_normal_flux_constraints (dof_handler_u, 0,
+ no_normal_flux_boundaries,
+ constraints_u, mapping);
+ constraints_u.close ();
+ DoFTools::make_hanging_node_constraints (dof_handler_p,
+ constraints_p);
+ constraints_p.close ();
+
+ std::vector<unsigned int> dofs_per_block (2);
+ DoFTools::count_dofs_per_block (dof_handler, dofs_per_block,
+ stokes_sub_blocks);
+
+ //std::cout << "Number of active cells: "
+ // << triangulation.n_active_cells()
+ // << std::endl
+ // << "Number of degrees of freedom: "
+ // << dof_handler.n_dofs()
+ // << " (" << n_u << '+' << n_p << ')'
+ // << std::endl;
+
+ {
+ BlockCompressedSimpleSparsityPattern csp (2,2);
+
+ for (unsigned int d=0; d<2; ++d)
+ for (unsigned int e=0; e<2; ++e)
+ csp.block(d,e).reinit (dofs_per_block[d], dofs_per_block[e]);
+
+ csp.collect_sizes();
+
+ DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
+ sparsity_pattern.copy_from (csp);
+ }
+
+ system_matrix.reinit (sparsity_pattern);
+
+ // this is from step-22
+ {
+ QGauss<dim> quadrature_formula(fe_degree+2);
+
+ FEValues<dim> fe_values (mapping, fe, quadrature_formula,
+ update_values |
+ update_JxW_values |
+ update_gradients);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
+
+ std::vector<SymmetricTensor<2,dim> > phi_grads_u (dofs_per_cell);
+ std::vector<double> div_phi_u (dofs_per_cell);
+ std::vector<double> phi_p (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ local_matrix = 0;
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_grads_u[k] = fe_values[velocities].symmetric_gradient (k, q);
+ div_phi_u[k] = fe_values[velocities].divergence (k, q);
+ phi_p[k] = fe_values[pressure].value (k, q);
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<=i; ++j)
+ {
+ local_matrix(i,j) += (phi_grads_u[i] * phi_grads_u[j]
+ - div_phi_u[i] * phi_p[j]
+ - phi_p[i] * div_phi_u[j])
+ * fe_values.JxW(q);
+ }
+ }
+ }
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=i+1; j<dofs_per_cell; ++j)
+ local_matrix(i,j) = local_matrix(j,i);
+
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ system_matrix);
+ }
+ }
+
+
+ solution.reinit (2);
+ for (unsigned int d=0; d<2; ++d)
+ solution.block(d).reinit (dofs_per_block[d]);
+ solution.collect_sizes ();
+
+ system_rhs.reinit (solution);
+
+ vec1.resize (2);
+ vec2.resize (2);
+ for (unsigned int d=0; d<2; ++d)
+ {
+ vec1[d].reinit (dofs_per_block[d]);
+ vec2[d].reinit (vec1[d]);
+ }
+
+ // fill system_rhs with random numbers
+ for (unsigned int j=0; j<system_rhs.block(0).size(); ++j)
+ if (constraints_u.is_constrained(j) == false)
+ {
+ const double val = -1 + 2.*(double)rand()/double(RAND_MAX);
+ system_rhs.block(0)(j) = val;
+ vec1[0](j) = val;
+ }
+ for (unsigned int j=0; j<system_rhs.block(1).size(); ++j)
+ if (constraints_p.is_constrained(j) == false)
+ {
+ const double val = -1 + 2.*(double)rand()/double(RAND_MAX);
+ system_rhs.block(1)(j) = val;
+ vec1[1](j) = val;
+ }
+
+ // setup matrix-free structure
+ {
+ std::vector<const DoFHandler<dim>*> dofs;
+ dofs.push_back(&dof_handler_u);
+ dofs.push_back(&dof_handler_p);
+ std::vector<const ConstraintMatrix*> constraints;
+ constraints.push_back (&constraints_u);
+ constraints.push_back (&constraints_p);
+ QGauss<1> quad(fe_degree+2);
+ // no parallelism
+ mf_data.reinit (mapping, dofs, constraints, quad,
+ typename MatrixFree<dim>::AdditionalData
+ (MPI_COMM_WORLD,
+ MatrixFree<dim>::AdditionalData::none));
+ }
+
+ system_matrix.vmult (solution, system_rhs);
+
+ typedef std::vector<Vector<double> > VectorType;
+ MatrixFreeTest<dim,fe_degree,VectorType> mf (mf_data);
+ mf.vmult (vec2, vec1);
+
+ // Verification
+ double error = 0.;
+ for (unsigned int i=0; i<2; ++i)
+ for (unsigned int j=0; j<solution.block(i).size(); ++j)
+ error += std::fabs (solution.block(i)(j)-vec2[i](j));
+ double relative = solution.l1_norm();
+ deallog << "Verification fe degree " << fe_degree << ": "
+ << error/relative << std::endl << std::endl;
+}
+
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+
+ deallog << std::setprecision (3);
+
+ {
+ deallog << std::endl << "Test with doubles" << std::endl << std::endl;
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,3>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL::
+DEAL::Test with doubles
+DEAL::
+DEAL:2d::Verification fe degree 1: 0
+DEAL:2d::
+DEAL:2d::Verification fe degree 2: 0
+DEAL:2d::
+DEAL:2d::Verification fe degree 3: 0
+DEAL:2d::
+DEAL:2d::Verification fe degree 4: 0
+DEAL:2d::
+DEAL:3d::Verification fe degree 1: 0
+DEAL:3d::
+DEAL:3d::Verification fe degree 2: 0
+DEAL:3d::
--- /dev/null
+//------------------ quadrature_points.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ quadrature_points.cc ------------------------
+
+
+// this function tests the correctness of cached quadrature points
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/lac/vector.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <fstream>
+#include <iostream>
+
+std::ofstream logfile("quadrature_points/output");
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+ Triangulation<dim> tria;
+ GridGenerator::hyper_ball (tria);
+ static const HyperBallBoundary<dim> boundary;
+ tria.set_boundary (0, boundary);
+ tria.refine_global(5-dim);
+
+ MappingQ<dim> mapping (4);
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+ deallog << "Testing " << fe.get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ constraints.close();
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme = MatrixFree<dim,number>::AdditionalData::none;
+ data.mapping_update_flags = update_quadrature_points;
+ mf_data.reinit (mapping, dof, constraints, quad, data);
+ }
+
+ double error_points = 0, abs_points = 0;
+ const unsigned int n_cells = mf_data.get_size_info().n_macro_cells;
+ FEEvaluation<dim,fe_degree+1> fe_eval (mf_data);
+ FEValues<dim> fe_values (mapping, fe, mf_data.get_quad(),
+ update_quadrature_points);
+
+ typedef VectorizedArray<double> vector_t;
+ for (unsigned int cell=0; cell<n_cells; ++cell)
+ {
+ fe_eval.reinit(cell);
+ for (unsigned int j=0; j<mf_data.n_components_filled(cell); ++j)
+ {
+ fe_values.reinit (mf_data.get_cell_iterator(cell,j));
+ for (unsigned int q=0; q<fe_eval.n_q_points; ++q)
+ {
+ abs_points += fe_values.quadrature_point(q).norm();
+ for (unsigned int d=0; d<dim; ++d)
+ error_points += std::fabs(fe_values.quadrature_point(q)[d]-
+ fe_eval.quadrature_point(q)[d][j]);
+ }
+ }
+ }
+
+ deallog << "Norm of difference: " << error_points/abs_points
+ << std::endl << std::endl;
+}
+
+
+int main ()
+{
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog << std::setprecision (3);
+
+ {
+ deallog.threshold_double(1.e-12);
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ test<2,4>();
+ deallog.pop();
+ deallog.push("3d");
+ test<3,1>();
+ test<3,3>();
+ deallog.pop();
+ }
+}
+
--- /dev/null
+
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:2d::Testing FE_Q<2>(4)
+DEAL:2d::Norm of difference: 0
+DEAL:2d::
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
+DEAL:3d::Testing FE_Q<3>(3)
+DEAL:3d::Norm of difference: 0
+DEAL:3d::
--- /dev/null
+//------------------ thread_correctness.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ thread_correctness.cc ------------------------
+
+
+// this function tests the correctness of the thread parallelization of the
+// matrix-free class extensively
+
+#include "../tests.h"
+#include <deal.II/base/function.h>
+#include "create_mesh.h"
+
+std::ofstream logfile("thread_correctness/output");
+
+#include "matrix_vector_common.h"
+
+
+template <int dim, int fe_degree, typename number>
+void sub_test()
+{
+ Triangulation<dim> tria;
+ create_mesh (tria);
+ tria.begin_active ()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().norm()<0.5)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 || fe_degree < 2)
+ tria.refine_global(1);
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim==2 && fe_degree < 2)
+ tria.refine_global(2);
+ else
+ tria.refine_global(1);
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ deallog << "Testing " << fe.get_name() << std::endl;
+
+ // run test for several different meshes
+ for (unsigned int i=0; i<8-2*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ endc = tria.end();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (counter % (9-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+
+ dof.distribute_dofs(fe);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data, mf_data_color, mf_data_partition;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ mf_data.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData(MPI_COMM_SELF,MatrixFree<dim,number>::AdditionalData::none));
+
+ // choose block size of 3 which introduces
+ // some irregularity to the blocks (stress the
+ // non-overlapping computation harder)
+ mf_data_color.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData
+ (MPI_COMM_SELF,
+ MatrixFree<dim,number>::AdditionalData::partition_color,
+ 3));
+ mf_data_partition.reinit (dof, constraints, quad,
+ typename MatrixFree<dim,number>::AdditionalData
+ (MPI_COMM_SELF,
+ MatrixFree<dim,number>::AdditionalData::partition_partition,
+ 3));
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf_ref (mf_data);
+ MatrixFreeTest<dim,fe_degree+1,number> mf_color (mf_data_color);
+ MatrixFreeTest<dim,fe_degree+1,number> mf_partition (mf_data_partition);
+ Vector<number> in_dist (dof.n_dofs());
+ Vector<number> out_dist (in_dist), out_color (in_dist),
+ out_partition(in_dist);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if(constraints.is_constrained(i))
+ continue;
+ const double entry = rand()/(double)RAND_MAX;
+ in_dist(i) = entry;
+ }
+
+ mf_ref.vmult (out_dist, in_dist);
+
+ // make 10 sweeps in order to get in some
+ // variation to the threaded program
+ for (unsigned int sweep = 0; sweep < 10; ++sweep)
+ {
+ mf_color.vmult (out_color, in_dist);
+ mf_partition.vmult (out_partition, in_dist);
+
+ out_color -= out_dist;
+ double diff_norm = out_color.linfty_norm();
+ deallog << "Sweep " << sweep
+ << ", error in partition/color: " << diff_norm
+ << std::endl;
+ out_partition -= out_dist;
+ diff_norm = out_partition.linfty_norm();
+ deallog << "Sweep " << sweep
+ << ", error in partition/partition: " << diff_norm
+ << std::endl;
+ }
+ deallog << std::endl;
+ }
+ deallog << std::endl;
+
+}
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ deallog << "Test doubles" << std::endl;
+ sub_test<dim,fe_degree,double>();
+ deallog.threshold_double(2.e-6);
+ deallog << "Test floats" << std::endl;
+ sub_test<dim,fe_degree,float>();
+}
--- /dev/null
+
+DEAL:2d::Test doubles
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Test floats
+DEAL:2d::Testing FE_Q<2>(1)
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Test doubles
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:2d::Test floats
+DEAL:2d::Testing FE_Q<2>(2)
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::Sweep 0, error in partition/color: 0
+DEAL:2d::Sweep 0, error in partition/partition: 0
+DEAL:2d::Sweep 1, error in partition/color: 0
+DEAL:2d::Sweep 1, error in partition/partition: 0
+DEAL:2d::Sweep 2, error in partition/color: 0
+DEAL:2d::Sweep 2, error in partition/partition: 0
+DEAL:2d::Sweep 3, error in partition/color: 0
+DEAL:2d::Sweep 3, error in partition/partition: 0
+DEAL:2d::Sweep 4, error in partition/color: 0
+DEAL:2d::Sweep 4, error in partition/partition: 0
+DEAL:2d::Sweep 5, error in partition/color: 0
+DEAL:2d::Sweep 5, error in partition/partition: 0
+DEAL:2d::Sweep 6, error in partition/color: 0
+DEAL:2d::Sweep 6, error in partition/partition: 0
+DEAL:2d::Sweep 7, error in partition/color: 0
+DEAL:2d::Sweep 7, error in partition/partition: 0
+DEAL:2d::Sweep 8, error in partition/color: 0
+DEAL:2d::Sweep 8, error in partition/partition: 0
+DEAL:2d::Sweep 9, error in partition/color: 0
+DEAL:2d::Sweep 9, error in partition/partition: 0
+DEAL:2d::
+DEAL:2d::
+DEAL:3d::Test doubles
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::
+DEAL:3d::Test floats
+DEAL:3d::Testing FE_Q<3>(1)
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::
+DEAL:3d::Test doubles
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::
+DEAL:3d::Test floats
+DEAL:3d::Testing FE_Q<3>(2)
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::Sweep 0, error in partition/color: 0
+DEAL:3d::Sweep 0, error in partition/partition: 0
+DEAL:3d::Sweep 1, error in partition/color: 0
+DEAL:3d::Sweep 1, error in partition/partition: 0
+DEAL:3d::Sweep 2, error in partition/color: 0
+DEAL:3d::Sweep 2, error in partition/partition: 0
+DEAL:3d::Sweep 3, error in partition/color: 0
+DEAL:3d::Sweep 3, error in partition/partition: 0
+DEAL:3d::Sweep 4, error in partition/color: 0
+DEAL:3d::Sweep 4, error in partition/partition: 0
+DEAL:3d::Sweep 5, error in partition/color: 0
+DEAL:3d::Sweep 5, error in partition/partition: 0
+DEAL:3d::Sweep 6, error in partition/color: 0
+DEAL:3d::Sweep 6, error in partition/partition: 0
+DEAL:3d::Sweep 7, error in partition/color: 0
+DEAL:3d::Sweep 7, error in partition/partition: 0
+DEAL:3d::Sweep 8, error in partition/color: 0
+DEAL:3d::Sweep 8, error in partition/partition: 0
+DEAL:3d::Sweep 9, error in partition/color: 0
+DEAL:3d::Sweep 9, error in partition/partition: 0
+DEAL:3d::
+DEAL:3d::
--- /dev/null
+//------------------ thread_correctness_hp.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ thread_correctness_hp.cc ------------------------
+
+
+// this function tests the correctness of the implementation of parallel
+// matrix free matrix-vector products for hp elements by comparing to the
+// serial version
+
+#include "../tests.h"
+
+std::ofstream logfile("thread_correctness_hp/output");
+
+#include "create_mesh.h"
+#include "matrix_vector_common.h"
+#include <deal.II/hp/dof_handler.h>
+#include <deal.II/hp/fe_values.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/template_constraints.h>
+
+
+
+template <int dim, typename Number>
+class MatrixFreeTestHP
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTestHP(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void
+ local_apply (const MatrixFree<dim,Number> &data,
+ Vector<Number> &dst,
+ const Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range) const
+ {
+ // Ask MatrixFree for cell_range for different
+ // orders
+ std::pair<unsigned int,unsigned int> subrange_deg =
+ data.create_cell_subrange_hp (cell_range, 1);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,2,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 2);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,3,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 3);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,4,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 4);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,5,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 5);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,6,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 6);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,7,Number> (data, dst, src,
+ subrange_deg);
+ subrange_deg = data.create_cell_subrange_hp (cell_range, 7);
+ if (subrange_deg.second > subrange_deg.first)
+ helmholtz_operator<dim,8,Number> (data, dst, src,
+ subrange_deg);
+ }
+
+ void vmult (Vector<Number> &dst,
+ const Vector<Number> &src) const
+ {
+ dst = 0;
+ data.cell_loop (&MatrixFreeTestHP<dim,Number>::local_apply, this, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+template <int dim, typename number>
+void do_test (const unsigned int parallel_option)
+{
+ Triangulation<dim> tria;
+ create_mesh (tria);
+ tria.refine_global(2);
+
+ // refine a few cells
+ for (unsigned int i=0; i<11-3*dim; ++i)
+ {
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ for (; cell!=endc; ++cell)
+ if (rand() % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ const unsigned int max_degree = 9-2*dim;
+
+ hp::FECollection<dim> fe_collection;
+ hp::QCollection<1> quadrature_collection_mf;
+
+ for (unsigned int deg=1; deg<=max_degree; ++deg)
+ {
+ fe_collection.push_back (FE_Q<dim>(QGaussLobatto<1>(deg+1)));
+ quadrature_collection_mf.push_back (QGauss<1>(deg+1));
+ }
+
+ hp::DoFHandler<dim> dof(tria);
+ // set the active FE index in a random order
+ {
+ typename hp::DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ {
+ const unsigned int fe_index = rand() % max_degree;
+ cell->set_active_fe_index (fe_index);
+ }
+ }
+
+ // setup DoFs
+ dof.distribute_dofs(fe_collection);
+ ConstraintMatrix constraints;
+ DoFTools::make_hanging_node_constraints (dof,
+ constraints);
+ VectorTools::interpolate_boundary_values (dof,
+ 0,
+ ZeroFunction<dim>(),
+ constraints);
+ constraints.close ();
+
+ //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ // set up reference MatrixFree
+ MatrixFree<dim,number> mf_data;
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::none;
+ mf_data.reinit (dof, constraints, quadrature_collection_mf, data);
+ MatrixFreeTestHP<dim,number> mf (mf_data);
+
+ MatrixFree<dim,number> mf_data_par;
+ if (parallel_option == 0)
+ {
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_partition;
+ deallog << "Parallel option partition/partition" << std::endl;
+ }
+ else
+ {
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_color;
+ deallog << "Parallel option partition/color" << std::endl;
+ }
+ data.tasks_block_size = 1;
+ mf_data_par.reinit (dof, constraints, quadrature_collection_mf, data);
+ MatrixFreeTestHP<dim,number> mf_par(mf_data_par);
+
+ // fill a right hand side vector with random
+ // numbers in unconstrained degrees of freedom
+ Vector<number> src (dof.n_dofs());
+ Vector<number> result_ref(src), result_mf (src);
+
+ for (unsigned int i=0; i<dof.n_dofs(); ++i)
+ {
+ if (constraints.is_constrained(i) == false)
+ src(i) = (double)rand()/RAND_MAX;
+ }
+
+ // now perform 50 matrix-vector products in
+ // parallel and check their correctness (take
+ // many of them to make sure that we hit an
+ // error)
+ mf.vmult (result_ref, src);
+ deallog << "Norm of difference: ";
+ for (unsigned int i=0; i<50; ++i)
+ {
+ mf_par.vmult (result_mf, src);
+ result_mf -= result_ref;
+ double diff_norm = result_mf.linfty_norm()/result_ref.linfty_norm();
+ deallog << diff_norm << " ";
+ }
+ deallog << std::endl << std::endl;
+}
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ // 'misuse' fe_degree for setting the parallel
+ // option here
+ unsigned int parallel_option = 0;
+ if (fe_degree == 1)
+ parallel_option = 0;
+ else if (fe_degree == 2)
+ parallel_option = 1;
+ else
+ return;
+ deallog.push("double");
+ deallog.threshold_double(1.e-12);
+ do_test<dim,double>(parallel_option);
+ deallog.pop();
+ deallog.push("float");
+ deallog.threshold_double(1.e-6);
+ do_test<dim,float>(parallel_option);
+ deallog.pop();
+}
+
--- /dev/null
+
+DEAL:2d:double::Parallel option partition/partition
+DEAL:2d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:2d:double::
+DEAL:2d:float::Parallel option partition/partition
+DEAL:2d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:2d:float::
+DEAL:2d:double::Parallel option partition/color
+DEAL:2d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:2d:double::
+DEAL:2d:float::Parallel option partition/color
+DEAL:2d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:2d:float::
+DEAL:3d:double::Parallel option partition/partition
+DEAL:3d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:3d:double::
+DEAL:3d:float::Parallel option partition/partition
+DEAL:3d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:3d:float::
+DEAL:3d:double::Parallel option partition/color
+DEAL:3d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:3d:double::
+DEAL:3d:float::Parallel option partition/color
+DEAL:3d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DEAL:3d:float::
--- /dev/null
+//------------------ matrix_free_01.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_free_01.cc ------------------------
+
+
+// this tests the correctness of matrix free matrix-vector products by
+// comparing the result with a Trilinos sparse matrix assembled in the usual
+// way. The mesh is distributed among processors (hypercube) and has both
+// hanging nodes (by randomly refining some cells, so the mesh is going to be
+// different when run with different numbers of processors) and Dirichlet
+// boundary conditions
+//
+// this test does not use multithreading within the MPI nodes.
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/function.h>
+#include <deal.II/lac/parallel_vector.h>
+#include <deal.II/distributed/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/trilinos_sparse_matrix.h>
+#include <deal.II/lac/trilinos_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void
+helmholtz_operator (const MatrixFree<dim,Number> &data,
+ parallel::distributed::Vector<Number> &dst,
+ const parallel::distributed::Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range)
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ fe_eval.read_dof_values (src);
+ fe_eval.evaluate (true, true, false);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ fe_eval.submit_value (Number(10)*fe_eval.get_value(q),q);
+ fe_eval.submit_gradient (fe_eval.get_gradient(q),q);
+ }
+ fe_eval.integrate (true,true);
+ fe_eval.distribute_local_to_global (dst);
+ }
+}
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void vmult (parallel::distributed::Vector<Number> &dst,
+ const parallel::distributed::Vector<Number> &src) const
+ {
+ dst = 0;
+ const std_cxx1x::function<void(const MatrixFree<dim,Number> &,
+ parallel::distributed::Vector<Number>&,
+ const parallel::distributed::Vector<Number>&,
+ const std::pair<unsigned int,unsigned int>&)>
+ wrap = helmholtz_operator<dim,n_dofs_1d,Number>;
+ data.cell_loop (wrap, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+
+ parallel::distributed::Triangulation<dim> tria (MPI_COMM_WORLD);
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->is_locally_owned())
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 && fe_degree < 2)
+ tria.refine_global(2);
+ else
+ tria.refine_global(1);
+ if (tria.begin(tria.n_levels()-1)->is_locally_owned())
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ if (tria.last()->is_locally_owned())
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (cell->is_locally_owned())
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ IndexSet owned_set = dof.locally_owned_dofs();
+ IndexSet relevant_set;
+ DoFTools::extract_locally_relevant_dofs (dof, relevant_set);
+
+ ConstraintMatrix constraints (relevant_set);
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.mpi_communicator = MPI_COMM_WORLD;
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::none;
+ data.tasks_block_size = 7;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ parallel::distributed::Vector<number> in, out, ref;
+ mf_data.initialize_dof_vector (in);
+ out.reinit (in);
+ ref.reinit (in);
+
+ for (unsigned int i=0; i<in.local_size(); ++i)
+ {
+ const unsigned int glob_index =
+ owned_set.nth_index_in_set (i);
+ if(constraints.is_constrained(glob_index))
+ continue;
+ in.local_element(i) = (double)rand()/RAND_MAX;
+ }
+
+ mf.vmult (out, in);
+
+
+ // assemble trilinos sparse matrix with
+ // (\nabla v, \nabla u) + (v, 10 * u) for
+ // reference
+ TrilinosWrappers::SparseMatrix sparse_matrix;
+ {
+ TrilinosWrappers::SparsityPattern csp (owned_set, MPI_COMM_WORLD);
+ DoFTools::make_sparsity_pattern (dof, csp, constraints, true,
+ Utilities::MPI::this_mpi_process(MPI_COMM_WORLD));
+ csp.compress();
+ sparse_matrix.reinit (csp);
+ }
+ {
+ QGauss<dim> quadrature_formula(fe_degree+1);
+
+ FEValues<dim> fe_values (dof.get_fe(), quadrature_formula,
+ update_values | update_gradients |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ if (cell->is_locally_owned())
+ {
+ cell_matrix = 0;
+ fe_values.reinit (cell);
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point)
+ +
+ 10. *
+ fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+
+ cell->get_dof_indices(local_dof_indices);
+ constraints.distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ sparse_matrix);
+ }
+ }
+ sparse_matrix.compress();
+
+ sparse_matrix.vmult (ref, in);
+ out -= ref;
+ const double diff_norm = out.linfty_norm();
+
+ deallog << "Norm of difference: " << diff_norm << std::endl << std::endl;
+}
+
+
+int main (int argc, char ** argv)
+{
+ Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv);
+
+ unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD);
+ deallog.push(Utilities::int_to_string(myid));
+
+ if (myid == 0)
+ {
+ std::ofstream logfile(output_file_for_mpi("matrix_free_01").c_str());
+ deallog.attach(logfile);
+ deallog << std::setprecision(4);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ deallog.pop();
+
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+ else
+ {
+ deallog.depth_console(0);
+ test<2,1>();
+ test<2,2>();
+ test<3,1>();
+ test<3,2>();
+ }
+}
+
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference: 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference: 0
+DEAL:0:3d::
--- /dev/null
+//------------------ matrix_free_02.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_free_02.cc ------------------------
+
+
+// this tests the correctness of matrix free matrix-vector products when
+// threads are additionally used to MPI by comparing the result with a
+// non-parallel version. Otherwise same problem as matrix_free_01.cc
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/function.h>
+#include <deal.II/lac/parallel_vector.h>
+#include <deal.II/distributed/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void
+helmholtz_operator (const MatrixFree<dim,Number> &data,
+ parallel::distributed::Vector<Number> &dst,
+ const parallel::distributed::Vector<Number> &src,
+ const std::pair<unsigned int,unsigned int> &cell_range)
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,1,Number> fe_eval (data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ fe_eval.read_dof_values (src);
+ fe_eval.evaluate (true, true, false);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ fe_eval.submit_value (Number(10)*fe_eval.get_value(q),q);
+ fe_eval.submit_gradient (fe_eval.get_gradient(q),q);
+ }
+ fe_eval.integrate (true,true);
+ fe_eval.distribute_local_to_global (dst);
+ }
+}
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void vmult (parallel::distributed::Vector<Number> &dst,
+ const parallel::distributed::Vector<Number> &src) const
+ {
+ dst = 0;
+ const std_cxx1x::function<void(const MatrixFree<dim,Number> &,
+ parallel::distributed::Vector<Number>&,
+ const parallel::distributed::Vector<Number>&,
+ const std::pair<unsigned int,unsigned int>&)>
+ wrap = helmholtz_operator<dim,n_dofs_1d,Number>;
+ data.cell_loop (wrap, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+
+ parallel::distributed::Triangulation<dim> tria (MPI_COMM_WORLD);
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->is_locally_owned())
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (fe_degree < 2)
+ tria.refine_global(2);
+ else
+ tria.refine_global(1);
+ if (tria.begin(tria.n_levels()-1)->is_locally_owned())
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ if (tria.last()->is_locally_owned())
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<11-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (cell->is_locally_owned())
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ IndexSet owned_set = dof.locally_owned_dofs();
+ IndexSet relevant_set;
+ DoFTools::extract_locally_relevant_dofs (dof, relevant_set);
+
+ ConstraintMatrix constraints (relevant_set);
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints on 0: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.mpi_communicator = MPI_COMM_WORLD;
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::none;
+ data.tasks_block_size = 7;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ parallel::distributed::Vector<number> in, out, ref;
+ mf_data.initialize_dof_vector (in);
+ out.reinit (in);
+ ref.reinit (in);
+
+ for (unsigned int i=0; i<in.local_size(); ++i)
+ {
+ const unsigned int glob_index =
+ owned_set.nth_index_in_set (i);
+ if(constraints.is_constrained(glob_index))
+ continue;
+ in.local_element(i) = (double)rand()/RAND_MAX;
+ }
+
+ mf.vmult (ref, in);
+ MPI_Barrier (MPI_COMM_WORLD);
+
+ for (unsigned int parallel_option = 0; parallel_option < 3; ++parallel_option)
+ {
+ out -= ref;
+ const double diff_norm = out.linfty_norm();
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.mpi_communicator = MPI_COMM_WORLD;
+ if (parallel_option == 0)
+ {
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_partition;
+ deallog << "Parallel option: partition partition" << std::endl;
+ }
+ else if (parallel_option == 1)
+ {
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::partition_color;
+ deallog << "Parallel option: partition color" << std::endl;
+ }
+ else if (parallel_option == 2)
+ {
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::color;
+ deallog << "Parallel option: color" << std::endl;
+ }
+
+ data.tasks_block_size = 3;
+ mf_data.reinit (dof, constraints, quad, data);
+ MatrixFreeTest<dim, fe_degree+1, number> mf (mf_data);
+ MPI_Barrier(MPI_COMM_WORLD);
+ deallog << "Norm of difference:";
+
+ // run 10 times to make a possible error more
+ // likely to show up
+ for (unsigned int run=0; run<10; ++run)
+ {
+ mf.vmult (out, in);
+ out -= ref;
+ const double diff_norm = out.linfty_norm();
+ deallog << " " << diff_norm;
+ }
+ deallog << std::endl;
+ }
+ deallog << std::endl;
+}
+
+
+int main (int argc, char ** argv)
+{
+ Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv);
+
+ unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD);
+ deallog.push(Utilities::int_to_string(myid));
+
+ if (myid == 0)
+ {
+ std::ofstream logfile(output_file_for_mpi("matrix_free_02").c_str());
+ deallog.attach(logfile);
+ deallog << std::setprecision(4);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ deallog.pop();
+
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+ else
+ {
+ deallog.depth_console(0);
+ test<2,1>();
+ test<2,2>();
+ test<3,1>();
+ test<3,2>();
+ }
+}
+
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Parallel option: partition partition
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: partition color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Parallel option: partition partition
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: partition color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Parallel option: partition partition
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: partition color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Parallel option: partition partition
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: partition color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Parallel option: partition partition
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: partition color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Parallel option: partition partition
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: partition color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::Parallel option: color
+DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Parallel option: partition partition
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: partition color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Parallel option: partition partition
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: partition color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::Parallel option: color
+DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0
+DEAL:0:3d::
--- /dev/null
+//------------------ matrix_free_03.cc ------------------------
+// $Id$
+// Version: $Name$
+//
+//------------------ matrix_free_03.cc ------------------------
+
+
+// this tests the correctness of matrix free matrix-vector products for two
+// vectors on the same DoFHandler. Otherwise the same as matrix_free_01
+
+#include "../tests.h"
+
+#include <deal.II/matrix_free/matrix_free.h>
+#include <deal.II/matrix_free/fe_evaluation.h>
+
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/function.h>
+#include <deal.II/lac/parallel_vector.h>
+#include <deal.II/distributed/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/lac/constraint_matrix.h>
+#include <deal.II/lac/trilinos_sparse_matrix.h>
+#include <deal.II/lac/trilinos_sparsity_pattern.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/numerics/vectors.h>
+
+#include <iostream>
+
+
+template <int dim, int n_dofs_1d, typename Number>
+void
+helmholtz_operator (const MatrixFree<dim,Number> &data,
+ std::vector<parallel::distributed::Vector<Number> > &dst,
+ const std::vector<parallel::distributed::Vector<Number> > &src,
+ const std::pair<unsigned int,unsigned int> &cell_range)
+{
+ FEEvaluation<dim,n_dofs_1d,n_dofs_1d,2,Number> fe_eval (data);
+ const unsigned int n_q_points = fe_eval.n_q_points;
+
+ for(unsigned int cell=cell_range.first;cell<cell_range.second;++cell)
+ {
+ fe_eval.reinit (cell);
+
+ // compare values with the ones the FEValues
+ // gives us. Those are seen as reference
+ fe_eval.read_dof_values (src);
+ fe_eval.evaluate (true, true, false);
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ fe_eval.submit_value (make_vectorized_array(Number(10))*
+ fe_eval.get_value(q), q);
+ fe_eval.submit_gradient (fe_eval.get_gradient(q),q);
+ }
+ fe_eval.integrate (true,true);
+ fe_eval.distribute_local_to_global (dst);
+ }
+}
+
+
+
+template <int dim, int n_dofs_1d, typename Number>
+class MatrixFreeTest
+{
+ public:
+ typedef VectorizedArray<Number> vector_t;
+ static const std::size_t n_vectors = VectorizedArray<Number>::n_array_elements;
+
+ MatrixFreeTest(const MatrixFree<dim,Number> &data_in):
+ data (data_in)
+ {};
+
+ void vmult (std::vector<parallel::distributed::Vector<Number> > &dst,
+ const std::vector<parallel::distributed::Vector<Number> > &src) const
+ {
+ for (unsigned int i=0; i<dst.size(); ++i)
+ dst[i] = 0;
+ const std_cxx1x::function<void(const MatrixFree<dim,Number> &,
+ std::vector<parallel::distributed::Vector<Number> >&,
+ const std::vector<parallel::distributed::Vector<Number> >&,
+ const std::pair<unsigned int,unsigned int>&)>
+ wrap = helmholtz_operator<dim,n_dofs_1d,Number>;
+ data.cell_loop (wrap, dst, src);
+ };
+
+private:
+ const MatrixFree<dim,Number> &data;
+};
+
+
+
+
+
+template <int dim, int fe_degree>
+void test ()
+{
+ typedef double number;
+
+ parallel::distributed::Triangulation<dim> tria (MPI_COMM_WORLD);
+ GridGenerator::hyper_cube (tria);
+ tria.refine_global(1);
+ typename Triangulation<dim>::active_cell_iterator
+ cell = tria.begin_active (),
+ endc = tria.end();
+ cell = tria.begin_active ();
+ for (; cell!=endc; ++cell)
+ if (cell->is_locally_owned())
+ if (cell->center().norm()<0.2)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ if (dim < 3 && fe_degree < 2)
+ tria.refine_global(2);
+ else
+ tria.refine_global(1);
+ if (tria.begin(tria.n_levels()-1)->is_locally_owned())
+ tria.begin(tria.n_levels()-1)->set_refine_flag();
+ if (tria.last()->is_locally_owned())
+ tria.last()->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ cell = tria.begin_active ();
+ for (unsigned int i=0; i<10-3*dim; ++i)
+ {
+ cell = tria.begin_active ();
+ unsigned int counter = 0;
+ for (; cell!=endc; ++cell, ++counter)
+ if (cell->is_locally_owned())
+ if (counter % (7-i) == 0)
+ cell->set_refine_flag();
+ tria.execute_coarsening_and_refinement();
+ }
+
+ FE_Q<dim> fe (fe_degree);
+ DoFHandler<dim> dof (tria);
+ dof.distribute_dofs(fe);
+
+ IndexSet owned_set = dof.locally_owned_dofs();
+ IndexSet relevant_set;
+ DoFTools::extract_locally_relevant_dofs (dof, relevant_set);
+
+ ConstraintMatrix constraints (relevant_set);
+ DoFTools::make_hanging_node_constraints(dof, constraints);
+ VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction<dim>(),
+ constraints);
+ constraints.close();
+
+ deallog << "Testing " << dof.get_fe().get_name() << std::endl;
+ //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl;
+ //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl;
+ //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl;
+
+ MatrixFree<dim,number> mf_data;
+ {
+ const QGauss<1> quad (fe_degree+1);
+ typename MatrixFree<dim,number>::AdditionalData data;
+ data.mpi_communicator = MPI_COMM_WORLD;
+ data.tasks_parallel_scheme =
+ MatrixFree<dim,number>::AdditionalData::none;
+ data.tasks_block_size = 7;
+ mf_data.reinit (dof, constraints, quad, data);
+ }
+
+ MatrixFreeTest<dim,fe_degree+1,number> mf (mf_data);
+ parallel::distributed::Vector<number> ref;
+ std::vector<parallel::distributed::Vector<number> > in(2), out(2);
+ for (unsigned int i=0; i<2; ++i)
+ {
+ mf_data.initialize_dof_vector (in[i]);
+ mf_data.initialize_dof_vector (out[i]);
+ }
+ mf_data.initialize_dof_vector (ref);
+
+ for (unsigned int i=0; i<in[0].local_size(); ++i)
+ {
+ const unsigned int glob_index =
+ owned_set.nth_index_in_set (i);
+ if(constraints.is_constrained(glob_index))
+ continue;
+ in[0].local_element(i) = (double)rand()/RAND_MAX;
+ in[1].local_element(i) = (double)rand()/RAND_MAX;
+ }
+
+ mf.vmult (out, in);
+
+
+ // assemble trilinos sparse matrix with
+ // (\nabla v, \nabla u) + (v, 10 * u) for
+ // reference
+ TrilinosWrappers::SparseMatrix sparse_matrix;
+ {
+ TrilinosWrappers::SparsityPattern csp (owned_set, MPI_COMM_WORLD);
+ DoFTools::make_sparsity_pattern (dof, csp, constraints, true,
+ Utilities::MPI::this_mpi_process(MPI_COMM_WORLD));
+ csp.compress();
+ sparse_matrix.reinit (csp);
+ }
+ {
+ QGauss<dim> quadrature_formula(fe_degree+1);
+
+ FEValues<dim> fe_values (dof.get_fe(), quadrature_formula,
+ update_values | update_gradients |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof.begin_active(),
+ endc = dof.end();
+ for (; cell!=endc; ++cell)
+ if (cell->is_locally_owned())
+ {
+ cell_matrix = 0;
+ fe_values.reinit (cell);
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ cell_matrix(i,j) += ((fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point)
+ +
+ 10. *
+ fe_values.shape_value(i,q_point) *
+ fe_values.shape_value(j,q_point)) *
+ fe_values.JxW(q_point));
+ }
+
+ cell->get_dof_indices(local_dof_indices);
+ constraints.distribute_local_to_global (cell_matrix,
+ local_dof_indices,
+ sparse_matrix);
+ }
+ }
+ sparse_matrix.compress();
+
+ deallog << "Norm of difference (component 1/2): ";
+ for (unsigned int i=0; i<2; ++i)
+ {
+ sparse_matrix.vmult (ref, in[i]);
+ out[i] -= ref;
+ const double diff_norm = out[i].linfty_norm();
+ deallog << diff_norm << " ";
+ }
+ deallog << std::endl << std::endl;
+}
+
+
+int main (int argc, char ** argv)
+{
+ Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv);
+
+ unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD);
+ deallog.push(Utilities::int_to_string(myid));
+
+ if (myid == 0)
+ {
+ std::ofstream logfile(output_file_for_mpi("matrix_free_03").c_str());
+ deallog.attach(logfile);
+ deallog << std::setprecision(4);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ deallog.push("2d");
+ test<2,1>();
+ test<2,2>();
+ deallog.pop();
+
+ deallog.push("3d");
+ test<3,1>();
+ test<3,2>();
+ deallog.pop();
+ }
+ else
+ {
+ deallog.depth_console(0);
+ test<2,1>();
+ test<2,2>();
+ test<3,1>();
+ test<3,2>();
+ }
+}
+
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
--- /dev/null
+
+DEAL:0:2d::Testing FE_Q<2>(1)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:2d::Testing FE_Q<2>(2)
+DEAL:0:2d::Norm of difference (component 1/2): 0 0
+DEAL:0:2d::
+DEAL:0:3d::Testing FE_Q<3>(1)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
+DEAL:0:3d::Testing FE_Q<3>(2)
+DEAL:0:3d::Norm of difference (component 1/2): 0 0
+DEAL:0:3d::
if (myid==0) deallog << "numproc=" << numproc << std::endl;
- // each processor from processor 1 to 8
+ // each processor from processor 1 to 8
// owns 2 indices (the other processors do
// not own any dof), and all processors are
// ghosting element 1 (the second)
Assert(v(myid*2+1) == myid*4.0+2.0, ExcInternalError());
}
- // check l2 norm
+ // check l2 norm
{
const double l2_norm = v.l2_norm();
if (myid == 0)
deallog << "l2 norm: " << l2_norm << std::endl;
}
- // check l1 norm
+ // check l1 norm
{
const double l1_norm = v.l1_norm();
if (myid == 0)
deallog << "l1 norm: " << l1_norm << std::endl;
}
- // check linfty norm
+ // check linfty norm
{
const double linfty_norm = v.linfty_norm();
if (myid == 0)
deallog << "linfty norm: " << linfty_norm << std::endl;
}
- // check lp norm
+ // check lp norm
{
const double lp_norm = v.lp_norm(2.2);
if (myid == 0)
deallog << "l2.2 norm: " << lp_norm << std::endl;
Assert (std::fabs (v.l2_norm() - v.lp_norm(2.0)) < 1e-14,
- ExcInternalError());
+ ExcInternalError());
}
- // check mean value (should be equal to l1
- // norm here since we have no negative
- // entries)
+ // check mean value (should be equal to l1
+ // norm here since we have no negative
+ // entries)
{
const double mean = v.mean_value();
if (myid == 0)
deallog << "Mean value: " << mean << std::endl;
Assert (std::fabs (mean * v.size() - v.l1_norm()) < 1e-15,
- ExcInternalError());
+ ExcInternalError());
}
- // check inner product
+ // check inner product
{
const double norm_sqr = v.norm_sqr();
Assert (std::fabs(v * v - norm_sqr) < 1e-15,
- ExcInternalError());
+ ExcInternalError());
parallel::distributed::Vector<double> v2;
v2 = v;
Assert (std::fabs(v2 * v - norm_sqr) < 1e-15,
- ExcInternalError());
+ ExcInternalError());
if(myid<8)
v2.local_element(0) = -1;
deallog << "Inner product: " << inner_prod << std::endl;
}
- // check operator ==
+ // check operator ==
{
parallel::distributed::Vector<double> v2 (v);
bool equal = (v2 == v);
if (myid == 0)
deallog << " v!=v2 ? " << not_equal << std::endl;
- // change v2 on one proc only
+ // change v2 on one proc only
if (myid == 0)
v2.local_element(1) = 2.2212;
if (myid == 0)
deallog << " v!=v2 ? " << not_equal << std::endl;
- // reset
+ // reset
v2 = v;
equal = (v2 == v);
if (myid == 0)
if (myid == 0)
deallog << " v!=v2 ? " << not_equal << std::endl;
- // change some value on all procs
+ // change some value on all procs
if (myid < 8)
v2.local_element(0) = -1;
equal = (v2 == v);
deallog << " v!=v2 ? " << not_equal << std::endl;
}
- // check all_zero
+ // check all_zero
{
bool allzero = v.all_zero();
if (myid == 0)
if (myid == 0)
deallog << " v2==0 ? " << allzero << std::endl;
- // now change one element to nonzero
+ // now change one element to nonzero
if (myid == 0)
v2.local_element(1) = 1;
allzero = v2.all_zero();
}
- // check all_non_negative
+ // check all_non_negative
{
bool allnonneg = v.is_non_negative();
if (myid == 0)
deallog << " v>=0 ? " << allnonneg << std::endl;
parallel::distributed::Vector<double> v2, v3;
- // vector where all processors have
- // non-negative entries
+ // vector where all processors have
+ // non-negative entries
v2 = v;
if (myid < 8)
v2.local_element(0) = -1;
if (myid == 0)
deallog << " v2>=0 ? " << allnonneg << std::endl;
- // zero vector
+ // zero vector
v3.reinit (v2);
allnonneg = v3.is_non_negative();
if (myid == 0)
deallog << " v3>=0 ? " << allnonneg << std::endl;
- // only one processor has non-negative entry
+ // only one processor has non-negative entry
v3 = v;
if (myid == 1)
v3.local_element(0) = -1;
--- /dev/null
+//-------------------------- parallel_vector_12.cc -----------------------
+// $Id$
+// Version: $Name$
+//
+// Copyright (C) 2012 by the deal.II authors
+//
+// This file is subject to QPL and may not be distributed
+// without copyright and license information. Please refer
+// to the file deal.II/doc/license.html for the text and
+// further information on this license.
+//
+//-------------------------- parallel_vector_12.cc -----------------------
+
+// check parallel::distributed::Vector::swap
+
+#include "../tests.h"
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/index_set.h>
+#include <deal.II/lac/parallel_vector.h>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+DeclException2 (ExcNonEqual,
+ double, double,
+ << "Left compare: " << arg1 << ", right compare: " << arg2);
+
+void test ()
+{
+ unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD);
+ unsigned int numproc = Utilities::System::get_n_mpi_processes (MPI_COMM_WORLD);
+
+ if (myid==0) deallog << "numproc=" << numproc << std::endl;
+
+ // vector 0:
+ // global size: 20, local_size: 3 as long as
+ // less than 20
+ const unsigned int local_size0 = 3;
+ const unsigned int global_size0 = std::min(20U, local_size0 * numproc);
+ const unsigned int my_start0 = std::min (local_size0 * myid, global_size0);
+ const unsigned int my_end0 = std::min (local_size0 * (myid+1), global_size0);
+ const unsigned int actual_local_size0 = my_end0-my_start0;
+
+ IndexSet local_owned0 (global_size0);
+ if (my_end0 > my_start0)
+ local_owned0.add_range(static_cast<unsigned int>(my_start0),
+ static_cast<unsigned int>(my_end0));
+ IndexSet local_relevant0(global_size0);
+ local_relevant0 = local_owned0;
+ local_relevant0.add_index (2);
+ if (numproc > 2)
+ local_relevant0.add_index(8);
+
+ parallel::distributed::Vector<double> v0(local_owned0, local_relevant0,
+ MPI_COMM_WORLD);
+
+ // vector1: local size 4
+ const unsigned int local_size1 = 4;
+ const unsigned int global_size1 = local_size1 * numproc;
+ const int my_start1 = local_size1 * myid;
+ const int my_end1 = local_size1 * (myid+1);
+
+ IndexSet local_owned1 (global_size1);
+ local_owned1.add_range(static_cast<unsigned int>(my_start1),
+ static_cast<unsigned int>(my_end1));
+ IndexSet local_relevant1(global_size1);
+ local_relevant1 = local_owned1;
+ local_relevant1.add_index (0);
+ local_relevant1.add_index (2);
+ if (numproc > 2)
+ {
+ local_relevant1.add_index(8);
+ local_relevant1.add_index(10);
+ }
+
+ parallel::distributed::Vector<double> v1(local_owned1, local_relevant1,
+ MPI_COMM_WORLD);
+
+ v0 = 1;
+ v1 = 2;
+ // check assignment in initial state
+ for (unsigned int i=0; i<v0.local_size(); ++i)
+ Assert (v0.local_element(i) == 1., ExcNonEqual(v0.local_element(i),1.));
+ for (unsigned int i=0; i<v1.local_size(); ++i)
+ Assert (v1.local_element(i) == 2., ExcNonEqual(v1.local_element(i),2.));
+
+ // check ghost elements in initial state
+ v0.update_ghost_values();
+ v1.update_ghost_values();
+ Assert (v0(2) == 1., ExcNonEqual(v0(2),1.));
+ if (numproc > 2)
+ Assert (v0(8) == 1., ExcNonEqual(v0(8),2.));
+ Assert (v1(0) == 2., ExcNonEqual(v1(0),2.));
+ Assert (v1(2) == 2., ExcNonEqual(v1(2),2.));
+ if (numproc > 2)
+ {
+ Assert (v1(8) == 2., ExcNonEqual(v1(8),2.));
+ Assert (v1(10) == 2., ExcNonEqual(v1(10),2.));
+ }
+ if (myid==0) deallog << "Initial set and ghost update OK" << std::endl;
+
+ // now swap v1 and v0
+ v0.swap (v1);
+ AssertDimension (v0.local_size(), local_size1);
+ AssertDimension (v1.local_size(), actual_local_size0);
+ AssertDimension (v0.size(), global_size1);
+ AssertDimension (v1.size(), global_size0);
+ for (unsigned int i=0; i<local_size1; ++i)
+ Assert (v0.local_element(i) == 2., ExcNonEqual(v0.local_element(i),2.));
+ for (unsigned int i=0; i<actual_local_size0; ++i)
+ Assert (v1.local_element(i) == 1., ExcNonEqual(v1.local_element(i),1.));
+ if (myid==0) deallog << "First swap OK" << std::endl;
+ v0.update_ghost_values ();
+ v1.update_ghost_values ();
+ Assert (v1(2) == 1., ExcNonEqual(v1(2),1.));
+ if (numproc > 2)
+ Assert (v1(8) == 1., ExcNonEqual(v1(8),1.));
+ Assert (v0(0) == 2., ExcNonEqual(v0(0),2.));
+ Assert (v0(2) == 2., ExcNonEqual(v0(2),2.));
+ if (numproc > 2)
+ {
+ Assert (v0(8) == 2., ExcNonEqual(v0(8),2.));
+ Assert (v0(10) == 2., ExcNonEqual(v0(10),2.));
+ }
+ if (myid==0) deallog << "Ghost values after first swap OK" << std::endl;
+
+ // now set the vectors to some different
+ // values and check the ghost values again
+ v0 = 7.;
+ v1 = 42.;
+ v0.update_ghost_values();
+ v1.update_ghost_values();
+ Assert (v1(2) == 42., ExcNonEqual(v1(2),42.));
+ if (numproc > 2)
+ Assert (v1(8) == 42., ExcNonEqual(v1(8),42.));
+ Assert (v0(0) == 7., ExcNonEqual(v0(0),7.));
+ Assert (v0(2) == 7., ExcNonEqual(v0(2),7.));
+ if (numproc > 2)
+ {
+ Assert (v0(8) == 7., ExcNonEqual(v0(8),7.));
+ Assert (v0(10) == 7., ExcNonEqual(v0(10),7.));
+ }
+ if (myid==0) deallog << "Ghost values after re-set OK" << std::endl;
+
+ // swap with an empty vector
+ parallel::distributed::Vector<double> v2;
+ v2.swap (v0);
+ AssertDimension (v0.size(), 0);
+ AssertDimension (v2.size(), global_size1);
+ AssertDimension (v2.local_size(), local_size1);
+ for (int i=my_start1; i<my_end1; ++i)
+ Assert (v2(i) == 7., ExcNonEqual(v2(i),7.));
+ if (myid==0) deallog << "Second swap OK" << std::endl;
+ v2 = -1.;
+ v2.update_ghost_values();
+ Assert (v2(0) == -1., ExcNonEqual(v2(0), -1.));
+ Assert (v2(2) == -1., ExcNonEqual(v2(2),-1.));
+ if (numproc > 2)
+ {
+ Assert (v2(8) == -1., ExcNonEqual(v2(8),-1.));
+ Assert (v2(10) == -1., ExcNonEqual(v2(10),-1.));
+ }
+ if (myid==0) deallog << "Ghost values after second swap OK" << std::endl;
+}
+
+
+
+int main (int argc, char **argv)
+{
+ Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv);
+
+ unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD);
+ deallog.push(Utilities::int_to_string(myid));
+
+ if (myid == 0)
+ {
+ std::ofstream logfile(output_file_for_mpi("parallel_vector_12").c_str());
+ deallog.attach(logfile);
+ deallog << std::setprecision(4);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ test();
+ }
+ else
+ test();
+
+}
--- /dev/null
+
+DEAL:0::numproc=1
+DEAL:0::Initial set and ghost update OK
+DEAL:0::First swap OK
+DEAL:0::Ghost values after first swap OK
+DEAL:0::Ghost values after re-set OK
+DEAL:0::Second swap OK
+DEAL:0::Ghost values after second swap OK
--- /dev/null
+
+DEAL:0::numproc=10
+DEAL:0::Initial set and ghost update OK
+DEAL:0::First swap OK
+DEAL:0::Ghost values after first swap OK
+DEAL:0::Ghost values after re-set OK
+DEAL:0::Second swap OK
+DEAL:0::Ghost values after second swap OK
--- /dev/null
+
+DEAL:0::numproc=4
+DEAL:0::Initial set and ghost update OK
+DEAL:0::First swap OK
+DEAL:0::Ghost values after first swap OK
+DEAL:0::Ghost values after re-set OK
+DEAL:0::Second swap OK
+DEAL:0::Ghost values after second swap OK