From e86f026bef0f640d367e9600316b42deedd87146 Mon Sep 17 00:00:00 2001 From: heister Date: Sat, 23 Oct 2010 13:41:09 +0000 Subject: [PATCH] merge branch distributed_grids up to rev 22429. git-svn-id: https://svn.dealii.org/trunk@22430 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/Makefile | 1 + deal.II/aclocal.m4 | 68 +- deal.II/base/Makefile | 20 +- deal.II/base/include/base/config.h.in | 4 + deal.II/base/include/base/index_set.h | 19 + deal.II/base/include/base/timer.h | 57 +- deal.II/base/include/base/types.h | 9 +- deal.II/base/include/base/utilities.h | 55 + deal.II/base/source/index_set.cc | 34 + deal.II/base/source/timer.cc | 60 +- deal.II/base/source/utilities.cc | 123 +- deal.II/common/Make.global_options.in | 11 +- deal.II/common/scripts/normalize.pl | 6 +- deal.II/configure | 101 +- deal.II/configure.in | 30 +- deal.II/contrib/Makefile.in | 3 + deal.II/contrib/configure.in | 1 - deal.II/contrib/hsl/Makefile | 6 +- deal.II/contrib/umfpack/AMD/Source/Makefile | 6 +- .../contrib/umfpack/UMFPACK/Source/Makefile | 46 +- deal.II/deal.II/Makefile | 2 +- .../include/distributed/grid_refinement.h | 139 + .../include/distributed/solution_transfer.h | 187 + deal.II/deal.II/include/distributed/tria.h | 772 +++++ deal.II/deal.II/include/dofs/dof_accessor.h | 23 +- .../include/dofs/dof_accessor.templates.h | 167 + deal.II/deal.II/include/dofs/dof_handler.h | 320 +- .../deal.II/include/dofs/dof_handler_policy.h | 152 + .../deal.II/include/dofs/dof_renumbering.h | 5 +- deal.II/deal.II/include/dofs/dof_tools.h | 214 +- deal.II/deal.II/include/dofs/number_cache.h | 101 + .../deal.II/include/grid/filtered_iterator.h | 56 +- deal.II/deal.II/include/grid/grid_tools.h | 45 +- deal.II/deal.II/include/grid/tria.h | 266 +- deal.II/deal.II/include/grid/tria_accessor.h | 111 +- .../include/grid/tria_accessor.templates.h | 162 +- deal.II/deal.II/include/grid/tria_levels.h | 22 +- deal.II/deal.II/include/hp/dof_handler.h | 186 +- deal.II/deal.II/include/numerics/data_out.h | 119 +- .../include/numerics/error_estimator.h | 87 +- .../include/numerics/vectors.templates.h | 788 ++--- .../source/distributed/grid_refinement.cc | 775 +++++ .../distributed/grid_refinement.inst.in | 44 + .../source/distributed/solution_transfer.cc | 273 ++ deal.II/deal.II/source/distributed/tria.cc | 3016 +++++++++++++++++ deal.II/deal.II/source/dofs/dof_accessor.cc | 46 +- deal.II/deal.II/source/dofs/dof_handler.cc | 463 +-- .../deal.II/source/dofs/dof_handler_policy.cc | 1590 +++++++++ .../deal.II/source/dofs/dof_renumbering.cc | 141 +- deal.II/deal.II/source/dofs/dof_tools.cc | 2258 ++++++------ deal.II/deal.II/source/dofs/number_cache.cc | 45 + deal.II/deal.II/source/fe/fe_tools.cc | 4 +- .../deal.II/source/grid/grid_refinement.cc | 565 +-- .../source/grid/grid_refinement.inst.in | 105 + deal.II/deal.II/source/grid/grid_tools.cc | 18 +- deal.II/deal.II/source/grid/tria.cc | 159 +- deal.II/deal.II/source/grid/tria_accessor.cc | 18 +- deal.II/deal.II/source/hp/dof_handler.cc | 51 +- .../source/multigrid/mg_dof_handler.cc | 25 +- deal.II/deal.II/source/numerics/data_out.cc | 55 +- .../deal.II/source/numerics/data_out_faces.cc | 6 +- .../source/numerics/data_out_rotation.cc | 6 +- .../deal.II/source/numerics/data_out_stack.cc | 6 +- .../source/numerics/error_estimator.cc | 103 +- .../source/numerics/solution_transfer.cc | 41 +- deal.II/doc/development/Makefile | 3 +- deal.II/doc/development/makefiles.1.html | 14 +- deal.II/doc/doxygen/headers/distributed.h | 364 ++ deal.II/doc/doxygen/headers/glossary.h | 46 +- deal.II/doc/doxygen/headers/parallel.h | 9 +- .../doc/doxygen/images/distributed_mesh_0.png | Bin 0 -> 41469 bytes .../doc/doxygen/images/distributed_mesh_1.png | Bin 0 -> 52087 bytes .../doc/doxygen/images/distributed_mesh_2.png | Bin 0 -> 56663 bytes .../doc/doxygen/images/distributed_mesh_3.png | Bin 0 -> 37749 bytes deal.II/doc/doxygen/tutorial/Makefile | 6 +- deal.II/doc/doxygen/tutorial/steps.cmapx | 74 +- deal.II/doc/doxygen/tutorial/steps.pl | 16 +- deal.II/doc/doxygen/tutorial/steps.png | Bin 51820 -> 77138 bytes deal.II/doc/external-libs/p4est-setup.sh | 110 + deal.II/doc/external-libs/p4est.html | 62 + deal.II/doc/readme.html | 47 +- deal.II/examples/step-18/doc/builds-on | 2 +- deal.II/examples/step-32/Makefile | 2 +- deal.II/examples/step-32/doc/builds-on | 2 +- deal.II/examples/step-32/step-32.cc | 925 +++-- deal.II/examples/step-32/timeblock.h | 64 + deal.II/examples/step-40/Makefile | 191 ++ deal.II/examples/step-40/doc/builds-on | 1 + deal.II/examples/step-40/doc/intro.dox | 115 + deal.II/examples/step-40/doc/kind | 1 + deal.II/examples/step-40/doc/results.dox | 117 + deal.II/examples/step-40/doc/step-40.256.png | Bin 0 -> 11910 bytes deal.II/examples/step-40/doc/step-40.4096.png | Bin 0 -> 12158 bytes deal.II/examples/step-40/doc/step-40.mesh.png | Bin 0 -> 11346 bytes .../examples/step-40/doc/step-40.solution.png | Bin 0 -> 91586 bytes .../examples/step-40/doc/step-40.strong.png | Bin 0 -> 11211 bytes .../examples/step-40/doc/step-40.strong2.png | Bin 0 -> 12277 bytes deal.II/examples/step-40/doc/tooltip | 1 + deal.II/examples/step-40/step-40-commented.cc | 406 +++ deal.II/examples/step-40/step-40.cc | 715 ++++ deal.II/examples/step-40/timeblock.h | 54 + deal.II/lac/include/lac/block_matrix_base.h | 34 + .../lac/include/lac/block_sparsity_pattern.h | 59 +- deal.II/lac/include/lac/constraint_matrix.h | 12 +- .../lac/include/lac/petsc_parallel_vector.h | 6 +- deal.II/lac/include/lac/petsc_vector_base.h | 4 +- deal.II/lac/include/lac/sparsity_tools.h | 51 +- .../lac/include/lac/trilinos_sparse_matrix.h | 4 +- deal.II/lac/include/lac/trilinos_vector.h | 4 +- .../lac/include/lac/trilinos_vector_base.h | 38 +- deal.II/lac/source/block_sparsity_pattern.cc | 55 + .../compressed_simple_sparsity_pattern.cc | 2 + deal.II/lac/source/constraint_matrix.cc | 9 +- .../source/petsc_parallel_sparse_matrix.cc | 16 +- deal.II/lac/source/petsc_parallel_vector.cc | 8 +- deal.II/lac/source/petsc_vector_base.cc | 27 +- deal.II/lac/source/sparsity_tools.cc | 2 +- deal.II/lac/source/trilinos_vector_base.cc | 12 +- 118 files changed, 14705 insertions(+), 3382 deletions(-) create mode 100644 deal.II/deal.II/include/distributed/grid_refinement.h create mode 100644 deal.II/deal.II/include/distributed/solution_transfer.h create mode 100644 deal.II/deal.II/include/distributed/tria.h create mode 100644 deal.II/deal.II/include/dofs/dof_handler_policy.h create mode 100644 deal.II/deal.II/include/dofs/number_cache.h create mode 100644 deal.II/deal.II/source/distributed/grid_refinement.cc create mode 100644 deal.II/deal.II/source/distributed/grid_refinement.inst.in create mode 100644 deal.II/deal.II/source/distributed/solution_transfer.cc create mode 100644 deal.II/deal.II/source/distributed/tria.cc create mode 100644 deal.II/deal.II/source/dofs/dof_handler_policy.cc create mode 100644 deal.II/deal.II/source/dofs/number_cache.cc create mode 100644 deal.II/deal.II/source/grid/grid_refinement.inst.in create mode 100644 deal.II/doc/doxygen/headers/distributed.h create mode 100644 deal.II/doc/doxygen/images/distributed_mesh_0.png create mode 100644 deal.II/doc/doxygen/images/distributed_mesh_1.png create mode 100644 deal.II/doc/doxygen/images/distributed_mesh_2.png create mode 100644 deal.II/doc/doxygen/images/distributed_mesh_3.png create mode 100755 deal.II/doc/external-libs/p4est-setup.sh create mode 100644 deal.II/doc/external-libs/p4est.html create mode 100644 deal.II/examples/step-32/timeblock.h create mode 100644 deal.II/examples/step-40/Makefile create mode 100644 deal.II/examples/step-40/doc/builds-on create mode 100644 deal.II/examples/step-40/doc/intro.dox create mode 100644 deal.II/examples/step-40/doc/kind create mode 100644 deal.II/examples/step-40/doc/results.dox create mode 100644 deal.II/examples/step-40/doc/step-40.256.png create mode 100644 deal.II/examples/step-40/doc/step-40.4096.png create mode 100644 deal.II/examples/step-40/doc/step-40.mesh.png create mode 100644 deal.II/examples/step-40/doc/step-40.solution.png create mode 100644 deal.II/examples/step-40/doc/step-40.strong.png create mode 100644 deal.II/examples/step-40/doc/step-40.strong2.png create mode 100644 deal.II/examples/step-40/doc/tooltip create mode 100644 deal.II/examples/step-40/step-40-commented.cc create mode 100644 deal.II/examples/step-40/step-40.cc create mode 100644 deal.II/examples/step-40/timeblock.h diff --git a/deal.II/Makefile b/deal.II/Makefile index 7b33252f3b..b971d6cbd7 100644 --- a/deal.II/Makefile +++ b/deal.II/Makefile @@ -267,6 +267,7 @@ build-test-do: .PHONY: base baseg lac lacg 1d 2d 3d 1dg 2dg 3dg all \ online-doc doc printable-doc tex-doc contrib \ + contrib-functionparser \ clean clean-contrib clean-base clean-lac clean-dealII \ clean-doc clean-examples clean-lib clean-tests clean-common-scripts \ distclean \ diff --git a/deal.II/aclocal.m4 b/deal.II/aclocal.m4 index eb700d20a8..9256e092d2 100644 --- a/deal.II/aclocal.m4 +++ b/deal.II/aclocal.m4 @@ -319,7 +319,7 @@ AC_DEFUN(DEAL_II_DETERMINE_CXX_BRAND, dnl dnl ------------------------------------------------------------- -dnl See whether the compiler we have has MPI build in (e.g. if it +dnl See whether the compiler we have has MPI built in (e.g. if it dnl is actually mpiCC, etc) dnl dnl Usage: DEAL_II_DETERMINE_IF_SUPPORTS_MPI @@ -339,6 +339,12 @@ AC_DEFUN(DEAL_II_DETERMINE_IF_SUPPORTS_MPI, dnl AC_MSG_RESULT(yes) AC_DEFINE(DEAL_II_COMPILER_SUPPORTS_MPI, 1, [Defined if the compiler supports including ]) + + dnl Export this variable so that we can refer to it + dnl from contrib/configure.in when configuring p4est + DEAL_II_COMPILER_SUPPORTS_MPI=1 + export DEAL_II_COMPILER_SUPPORTS_MPI + DEAL_II_USE_MPI=yes ], [ @@ -1289,7 +1295,8 @@ dnl Set C compiler flags to their default values. They will be dnl modified according to other options in later steps of dnl configuration dnl -dnl CFLAGS : flags for optimized mode +dnl CFLAGS.o : flags for optimized mode +dnl CFLAGS.g : flags for debug mode dnl dnl Usage: DEAL_II_SET_CC_FLAGS dnl @@ -1298,7 +1305,8 @@ AC_DEFUN(DEAL_II_SET_CC_FLAGS, dnl [ dnl First the flags for gcc compilers if test "$GCC" = yes ; then - CFLAGS="$CFLAGS -O3 -funroll-loops -funroll-all-loops -fstrict-aliasing" + CFLAGSO="$CFLAGS -O3 -funroll-loops -funroll-all-loops -fstrict-aliasing" + CFLAGSG="$CFLAGS -g" dnl Set PIC flags. On some systems, -fpic/PIC is implied, so don't set dnl anything to avoid a warning. on AIX make sure we always pass -lpthread dnl because this seems to be somehow required to make things work. Likewise @@ -1340,18 +1348,18 @@ AC_DEFUN(DEAL_II_SET_CC_FLAGS, dnl case "$CC_VERSION" in ibm_xlc) - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGS -O2" CFLAGSPIC="-fPIC" SHLIBLD="$CXX" ;; MIPSpro*) - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGS -O2" CFLAGSPIC="-KPIC" ;; intel_icc*) - CFLAGS="$CFLAGS -O2 -unroll" + CFLAGSO="$CFLAGS -O2 -unroll" case "$CC_VERSION" in intel_icc5 | intel_icc6 | intel_icc7 | intel_icc8 | intel_icc9) CFLAGSPIC="-KPIC" @@ -1379,7 +1387,7 @@ AC_DEFUN(DEAL_II_SET_CC_FLAGS, dnl ;; esac - CFLAGS="$CFLAGS -ansi_alias -vec_report0" + CFLAGSO="$CFLAGSO -ansi_alias -vec_report0" dnl If we are on an x86 platform, add -tpp6 to optimization dnl flags @@ -1412,7 +1420,7 @@ AC_DEFUN(DEAL_II_SET_CC_FLAGS, dnl *) AC_MSG_RESULT(Unknown C compiler - using generic options) - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGSO -O2" ;; esac fi @@ -5492,6 +5500,7 @@ AC_DEFUN(DEAL_II_CONFIGURE_NETCDF, dnl ]) + dnl ------------------------------------------------------------ dnl Check whether PETSc is installed, and if so store the dnl respective links @@ -7021,3 +7030,46 @@ dnl fi fi ]) + + + +dnl ------------------------------------------------------------ +dnl Check whether P4EST is to be used to parallelize meshes +dnl +dnl Usage: DEAL_II_CONFIGURE_P4EST +dnl +dnl ------------------------------------------------------------ +AC_DEFUN(DEAL_II_CONFIGURE_P4EST, dnl +[ + AC_MSG_CHECKING(whether p4est shall be used) + + AC_ARG_WITH(p4est, + [ --with-p4est=/path/to/p4est makes deal.II use p4est to distribute meshes + on a cluster computer], + use_p4est=$withval, + use_p4est=no) + + if test "x$use_p4est" != "xno" ; then + AC_MSG_RESULT(yes) + + if test ! -d "${use_p4est}/DEBUG" -o ! -d "${use_p4est}/FAST" ; then + echo "${use_p4est}/DEBUG" + AC_MSG_ERROR([p4est directories $use_p4est/DEBUG or $use_p4est/FAST not found]) + fi + + AC_DEFINE(DEAL_II_USE_P4EST, 1, + [Defined if we are to use the p4est library to distribute + meshes on a cluster computer.]) + USE_CONTRIB_P4EST=yes + export USE_CONTRIB_P4EST + + DEAL_II_P4EST_DIR=${use_p4est} + export DEAL_II_P4EST_DIR + + CXXFLAGSG="$CXXFLAGSG -I$use_p4est/DEBUG/include" + CXXFLAGSO="$CXXFLAGSO -I$use_p4est/FAST/include" + else + AC_MSG_RESULT(no) + fi +]) + diff --git a/deal.II/base/Makefile b/deal.II/base/Makefile index 9f09356312..521d77063a 100644 --- a/deal.II/base/Makefile +++ b/deal.II/base/Makefile @@ -21,10 +21,11 @@ h-files = $(sort $(shell echo include/base/*.h)) # libbase. do similarly for the threading building block things if threading # is enabled ifeq ($(enable-parser),yes) - extra-o-files = $(LIBDIR)/contrib/functionparser/fparser.$(OBJEXT) + extra-o-files = $(LIBDIR)/contrib/functionparser/fparser.$(OBJEXT) extra-g.o-files = $(LIBDIR)/contrib/functionparser/fparser.$(OBJEXT) endif + # production rules $(LIBDIR)/base/%.g.$(OBJEXT) : @echo "=====base=============debug======$(MT)== $((-2); } diff --git a/deal.II/base/include/base/utilities.h b/deal.II/base/include/base/utilities.h index a045cc9cec..72ece1e8a7 100644 --- a/deal.II/base/include/base/utilities.h +++ b/deal.II/base/include/base/utilities.h @@ -216,6 +216,29 @@ namespace Utilities */ double get_cpu_load (); + /** + * Structure that hold information about + * memory usage in kB. Used by + * get_memory_stats(). See man 5 proc + * entry /status for details. + */ + struct MemoryStats + { + unsigned long int VmPeak; /** peak virtual memory size in kB */ + unsigned long int VmSize; /** current virtual memory size in kB */ + unsigned long int VmHWM; /** peak resident memory size in kB */ + unsigned long int VmRSS; /** current resident memory size in kB */ + }; + + + /** + * Fills the @param stats structure with + * information about the memory + * consumption of this process. This is + * only implemented on Linux. + */ + void get_memory_stats (MemoryStats & stats); + /** * Return the name of the host this @@ -331,6 +354,38 @@ namespace Utilities */ MPI_Comm duplicate_communicator (const MPI_Comm &mpi_communicator); + + /** + * Data structure to store the result of + * calculate_collective_mpi_min_max_avg. + */ + struct MinMaxAvg + { + double sum; + double min; + double max; + unsigned int min_index; + unsigned int max_index; + double avg; + }; + + /** + * Returns sum, average, minimum, + * maximum, processor id of minimum and + * maximum as a collective operation of + * on the given MPI communicator @param + * mpi_communicator . Each processor's + * value is given in @param my_value and + * the result will be returned in @param + * result . The result is available on all + * machines. + */ + void calculate_collective_mpi_min_max_avg(const MPI_Comm &mpi_communicator, + const double my_value, + MinMaxAvg & result); + + + /** * A class that is used to initialize the * MPI system at the beginning of a diff --git a/deal.II/base/source/index_set.cc b/deal.II/base/source/index_set.cc index 3d1d4f8464..2ecf4f2bc6 100644 --- a/deal.II/base/source/index_set.cc +++ b/deal.II/base/source/index_set.cc @@ -232,6 +232,40 @@ IndexSet::read(std::istream & in) } +void +IndexSet::block_write(std::ostream & out) const +{ + Assert (out, ExcIO()); + out.write(reinterpret_cast(&index_space_size), + sizeof(index_space_size)); + size_t n_ranges = ranges.size(); + out.write(reinterpret_cast(&n_ranges), + sizeof(n_ranges)); + if (ranges.size()) + out.write (reinterpret_cast(&*ranges.begin()), + reinterpret_cast(&*ranges.end()) + - reinterpret_cast(&*ranges.begin())); + Assert (out, ExcIO()); +} + +void +IndexSet::block_read(std::istream & in) +{ + unsigned int size; + size_t n_ranges; + in.read(reinterpret_cast(&size), sizeof(size)); + in.read(reinterpret_cast(&n_ranges), sizeof(n_ranges)); + // we have to clear ranges first + ranges.clear(); + set_size(size); + ranges.resize(n_ranges, Range(0,0)); + if (n_ranges) + in.read(reinterpret_cast(&*ranges.begin()), + reinterpret_cast(&*ranges.end()) + - reinterpret_cast(&*ranges.begin())); +} + + void IndexSet::subtract_set (const IndexSet & other) diff --git a/deal.II/base/source/timer.cc b/deal.II/base/source/timer.cc index a41ce678f6..a047f14f23 100644 --- a/deal.II/base/source/timer.cc +++ b/deal.II/base/source/timer.cc @@ -96,33 +96,6 @@ void Timer::start () } -#ifdef DEAL_II_COMPILER_SUPPORTS_MPI - -void Timer::TimeMinMaxAvg::max_reduce ( const void * in_lhs_, - void * inout_rhs_, - int * len, - MPI_Datatype * ) -{ - const Timer::TimeMinMaxAvg * in_lhs = static_cast(in_lhs_); - Timer::TimeMinMaxAvg * inout_rhs = static_cast(inout_rhs_); - - Assert(*len==1, ExcInternalError()); - - inout_rhs->sum += in_lhs->sum; - if (inout_rhs->min>in_lhs->min) - { - inout_rhs->min = in_lhs->min; - inout_rhs->min_index = in_lhs->min_index; - } - if (inout_rhs->maxmax) - { - inout_rhs->max = in_lhs->max; - inout_rhs->max_index = in_lhs->max_index; - } -} - - -#endif double Timer::stop () { @@ -148,36 +121,9 @@ double Timer::stop () #ifdef DEAL_II_COMPILER_SUPPORTS_MPI if (sync_wall_time) { - unsigned int my_id = dealii::Utilities::System::get_this_mpi_process(mpi_communicator); - - MPI_Op op; - int ierr = MPI_Op_create((MPI_User_function *)&Timer::TimeMinMaxAvg::max_reduce, - false, &op); - AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); - - TimeMinMaxAvg in; - in.set(time, my_id); - - MPI_Datatype type; - int lengths[]={3,2}; - MPI_Aint displacements[]={0,offsetof(TimeMinMaxAvg, min_index)}; - MPI_Datatype types[]={MPI_DOUBLE, MPI_INT}; - - ierr = MPI_Type_struct(2, lengths, displacements, types, &type); - AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); - - ierr = MPI_Type_commit(&type); - - ierr = MPI_Reduce ( &in, &this->mpi_data, 1, type, op, 0, mpi_communicator ); - AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); - - ierr = MPI_Type_free (&type); - AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); - - ierr = MPI_Op_free(&op); - AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); - - this->mpi_data.avg = this->mpi_data.sum / dealii::Utilities::System::get_n_mpi_processes(mpi_communicator); + Utilities::System + ::calculate_collective_mpi_min_max_avg(mpi_communicator, time, + this->mpi_data); cumulative_wall_time += this->mpi_data.max; } diff --git a/deal.II/base/source/utilities.cc b/deal.II/base/source/utilities.cc index ab8e94d807..614d399807 100644 --- a/deal.II/base/source/utilities.cc +++ b/deal.II/base/source/utilities.cc @@ -423,6 +423,40 @@ namespace Utilities #endif + + void get_memory_stats (MemoryStats & stats) + { + stats.VmPeak = stats.VmSize = stats.VmHWM = stats.VmRSS = 0; + + // parsing /proc/self/stat would be a + // lot easier, but it does not contain + // VmHWM, so we use /status instead. +#if defined(__linux__) + std::ifstream file("/proc/self/status"); + std::string line; + std::string name; + while (!file.eof()) + { + file >> name; + if (name == "VmPeak:") + file >> stats.VmPeak; + else if (name == "VmSize:") + file >> stats.VmSize; + else if (name == "VmHWM:") + file >> stats.VmHWM; + else if (name == "VmRSS:") + { + file >> stats.VmRSS; + break; //this is always the last entry + } + + getline(file, line); + } +#endif + } + + + std::string get_hostname () { const unsigned int N=1024; @@ -516,6 +550,82 @@ namespace Utilities } + namespace + { + // custom MIP_Op for + // calculate_collective_mpi_min_max_avg + void max_reduce ( const void * in_lhs_, + void * inout_rhs_, + int * len, + MPI_Datatype * ) + { + const MinMaxAvg * in_lhs = static_cast(in_lhs_); + MinMaxAvg * inout_rhs = static_cast(inout_rhs_); + + Assert(*len==1, ExcInternalError()); + + inout_rhs->sum += in_lhs->sum; + if (inout_rhs->min>in_lhs->min) + { + inout_rhs->min = in_lhs->min; + inout_rhs->min_index = in_lhs->min_index; + } + else if (inout_rhs->min == in_lhs->min) + { // choose lower cpu index when tied to make operator cumutative + if (inout_rhs->min_index > in_lhs->min_index) + inout_rhs->min_index = in_lhs->min_index; + } + + if (inout_rhs->max < in_lhs->max) + { + inout_rhs->max = in_lhs->max; + inout_rhs->max_index = in_lhs->max_index; + } + else if (inout_rhs->max == in_lhs->max) + { // choose lower cpu index when tied to make operator cumutative + if (inout_rhs->max_index > in_lhs->max_index) + inout_rhs->max_index = in_lhs->max_index; + } + } + } + + void calculate_collective_mpi_min_max_avg(const MPI_Comm &mpi_communicator, + double my_value, + MinMaxAvg & result) + { + unsigned int my_id = dealii::Utilities::System::get_this_mpi_process(mpi_communicator); + unsigned int numproc = dealii::Utilities::System::get_n_mpi_processes(mpi_communicator); + + MPI_Op op; + int ierr = MPI_Op_create((MPI_User_function *)&max_reduce, true, &op); + AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); + + MinMaxAvg in; + in.sum = in.min = in.max = my_value; + in.min_index = in.max_index = my_id; + + MPI_Datatype type; + int lengths[]={3,2}; + MPI_Aint displacements[]={0,offsetof(MinMaxAvg, min_index)}; + MPI_Datatype types[]={MPI_DOUBLE, MPI_INT}; + + ierr = MPI_Type_struct(2, lengths, displacements, types, &type); + AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); + + ierr = MPI_Type_commit(&type); + ierr = MPI_Allreduce ( &in, &result, 1, type, op, mpi_communicator ); + AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); + + ierr = MPI_Type_free (&type); + AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); + + ierr = MPI_Op_free(&op); + AssertThrow(ierr == MPI_SUCCESS, ExcInternalError()); + + result.avg = result.sum / numproc; + } + + std::vector compute_point_to_point_communication_pattern (const MPI_Comm & mpi_comm, const std::vector & destinations) @@ -605,7 +715,18 @@ namespace Utilities return 0; } - + void calculate_collective_mpi_min_max_avg(const MPI_Comm &mpi_communicator, + double my_value, + MinMaxAvg & result) + { + result.sum = my_value; + result.avg = my_value; + result.min = my_value; + result.max = my_value; + result.min_index = 0; + result.max_index = 0; + } + MPI_Comm duplicate_communicator (const MPI_Comm &mpi_communicator) { diff --git a/deal.II/common/Make.global_options.in b/deal.II/common/Make.global_options.in index 5f2bc63966..01bad74bfa 100644 --- a/deal.II/common/Make.global_options.in +++ b/deal.II/common/Make.global_options.in @@ -89,6 +89,9 @@ DEAL_II_METIS_LIBDIR = @DEAL_II_METIS_LIBDIR@ USE_CONTRIB_HSL = @USE_CONTRIB_HSL@ USE_CONTRIB_UMFPACK = @USE_CONTRIB_UMFPACK@ +USE_CONTRIB_P4EST = @USE_CONTRIB_P4EST@ +DEAL_II_P4EST_DIR = @DEAL_II_P4EST_DIR@ + TARGET = @target@ ###################################################### @@ -348,7 +351,8 @@ F77FLAGS.g = @DEFS@ @F77FLAGSG@ $(INCLUDE) F77FLAGS.o = @DEFS@ @F77FLAGSO@ $(INCLUDE) # compile flags for C compiler -CFLAGS = @CFLAGS@ +CFLAGS.g = @CFLAGSG@ +CFLAGS.o = @CFLAGSO@ # if in debug mode, add TBB assertions ifeq ($(enable-threads),yes) @@ -368,7 +372,12 @@ ifeq ($(USE_CONTRIB_PETSC),yes) include $(DEAL_II_PETSC_DIR)/bmake/common/variables endif else + # PETSC's $(PETSC_ARCH)/conf/petscvariables include file happens + # to have a variable $(CXX) of itself. we need to save our own + # variable and restore it later. this isn't pretty :-( + SAVE_CXX := $(CXX) include $(DEAL_II_PETSC_DIR)/conf/variables + CXX := $(SAVE_CXX) endif CXXFLAGS.g += $(GCXX_PETSCFLAGS) CXXFLAGS.o += $(OCXX_PETSCFLAGS) diff --git a/deal.II/common/scripts/normalize.pl b/deal.II/common/scripts/normalize.pl index 03037323fe..ec90f06c20 100644 --- a/deal.II/common/scripts/normalize.pl +++ b/deal.II/common/scripts/normalize.pl @@ -1,7 +1,7 @@ ###################################################################### # $Id$ # -# Copyright (C) 2001, 2003, 2005, the deal.II authors +# Copyright (C) 2001, 2003, 2005, 2010, the deal.II authors # # Remove insignificant volatile data from output files of tests # @@ -48,3 +48,7 @@ s/-0\.00/0.00/g; #s/value.*//; #s/with residual.*//; + + +# remove deal.II debug output +s/^DEAL.*::_.*\n//g; diff --git a/deal.II/configure b/deal.II/configure index 929b20921f..d1c4a64e29 100755 --- a/deal.II/configure +++ b/deal.II/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 22370 . +# From configure.in Revision: 22333 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.63 for deal.II 6.4.pre. # @@ -661,6 +661,9 @@ USE_CONTRIB_HSL HSL_INCLUDE_DIR NEEDS_F77LIBS DEAL_II_USE_BLAS +DEAL_II_P4EST_DIR +USE_CONTRIB_P4EST +DEAL_II_USE_P4EST DEAL_II_DEFINE_DEAL_II_USE_MUMPS DEAL_II_BLACS_ARCH DEAL_II_BLACS_DIR @@ -723,6 +726,8 @@ GREP CXXCPP enablethreads CFLAGSPIC +CFLAGSG +CFLAGSO CC_VERSION SHLIBFLAGS SHLIBLD @@ -799,6 +804,7 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking +enable_mpi enable_threads enable_shared enable_parser @@ -815,6 +821,7 @@ with_arpack with_mumps with_scalapack with_blacs +with_p4est with_blas with_zlib with_netcdf @@ -1467,6 +1474,8 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-mpi Select MPI-enabled compilers and MPI support in + deal.II. --enable-threads Use multiple threads inside deal.II --enable-shared Set compiler flags to generate shared libraries --enable-parser While switched on by default, this option allows to @@ -1532,6 +1541,8 @@ Optional Packages: Specify the path to the BLACS installation; use this if you want to override the BLACS_DIR environment variable. + --with-p4est=/path/to/p4est makes deal.II use p4est to distribute meshes + on a cluster computer --with-blas=blaslib Use the blas library blaslib. Make sure the path to the libary is searched by ld, since it is included by the argument -lblaslib. If no argument @@ -2251,6 +2262,24 @@ $as_echo "" >&6; } { $as_echo "$as_me:$LINENO: result: ---------------- configuring C/C++ compilers ----------------" >&5 $as_echo "---------------- configuring C/C++ compilers ----------------" >&6; } + +# Check whether --enable-mpi was given. +if test "${enable_mpi+set}" = set; then + enableval=$enable_mpi; + { $as_echo "$as_me:$LINENO: checking whether to explicitly use MPI" >&5 +$as_echo_n "checking whether to explicitly use MPI... " >&6; } + if test "x$enableval" = "xyes" ; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + if test "x$CXX" = "x" ; then CXX=mpiCC ; fi + if test "x$CC" = "x" ; then CC=mpicc ; fi + else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + fi +fi + + OLDCFLAGS="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3950,6 +3979,10 @@ cat >>confdefs.h <<\_ACEOF #define DEAL_II_COMPILER_SUPPORTS_MPI 1 _ACEOF + + DEAL_II_COMPILER_SUPPORTS_MPI=1 + export DEAL_II_COMPILER_SUPPORTS_MPI + DEAL_II_USE_MPI=yes else @@ -5424,7 +5457,8 @@ $as_echo "Unrecognized compiler -- still trying" >&6; } if test "$GCC" = yes ; then - CFLAGS="$CFLAGS -O3 -funroll-loops -funroll-all-loops -fstrict-aliasing" + CFLAGSO="$CFLAGS -O3 -funroll-loops -funroll-all-loops -fstrict-aliasing" + CFLAGSG="$CFLAGS -g" case "$target" in *aix* ) CFLAGSPIC= @@ -5454,18 +5488,18 @@ $as_echo "Unrecognized compiler -- still trying" >&6; } case "$CC_VERSION" in ibm_xlc) - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGS -O2" CFLAGSPIC="-fPIC" SHLIBLD="$CXX" ;; MIPSpro*) - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGS -O2" CFLAGSPIC="-KPIC" ;; intel_icc*) - CFLAGS="$CFLAGS -O2 -unroll" + CFLAGSO="$CFLAGS -O2 -unroll" case "$CC_VERSION" in intel_icc5 | intel_icc6 | intel_icc7 | intel_icc8 | intel_icc9) CFLAGSPIC="-KPIC" @@ -5487,7 +5521,7 @@ $as_echo "Unrecognized compiler -- still trying" >&6; } ;; esac - CFLAGS="$CFLAGS -ansi_alias -vec_report0" + CFLAGSO="$CFLAGSO -ansi_alias -vec_report0" case "$target" in *86*) @@ -5578,7 +5612,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext *) { $as_echo "$as_me:$LINENO: result: Unknown C compiler - using generic options" >&5 $as_echo "Unknown C compiler - using generic options" >&6; } - CFLAGS="$CFLAGS -O2" + CFLAGSO="$CFLAGSO -O2" ;; esac fi @@ -5590,6 +5624,7 @@ $as_echo "Unknown C compiler - using generic options" >&6; } + # Check whether --enable-threads was given. if test "${enable_threads+set}" = set; then enableval=$enable_threads; @@ -12135,7 +12170,8 @@ $as_echo "$as_me: WARNING: Shared libraries not supported on $target. Using stat esac if test "x$enableshared" = "xyes" ; then - CFLAGS="$CFLAGS $CFLAGSPIC" + CFLAGSO="$CFLAGSO $CFLAGSPIC" + CFLAGSG="$CFLAGSG $CFLAGSPIC" CXXFLAGSG="$CXXFLAGSG $CXXFLAGSPIC" CXXFLAGSO="$CXXFLAGSO $CXXFLAGSPIC" F77FLAGSG="$F77FLAGSG $F77FLAGSPIC" @@ -14483,6 +14519,55 @@ _ACEOF + + { $as_echo "$as_me:$LINENO: checking whether p4est shall be used" >&5 +$as_echo_n "checking whether p4est shall be used... " >&6; } + + +# Check whether --with-p4est was given. +if test "${with_p4est+set}" = set; then + withval=$with_p4est; use_p4est=$withval +else + use_p4est=no +fi + + + if test "x$use_p4est" != "xno" ; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + + if test ! -d "${use_p4est}/DEBUG" -o ! -d "${use_p4est}/FAST" ; then + echo "${use_p4est}/DEBUG" + { { $as_echo "$as_me:$LINENO: error: p4est directories $use_p4est/DEBUG or $use_p4est/FAST not found" >&5 +$as_echo "$as_me: error: p4est directories $use_p4est/DEBUG or $use_p4est/FAST not found" >&2;} + { (exit 1); exit 1; }; } + fi + + +cat >>confdefs.h <<\_ACEOF +#define DEAL_II_USE_P4EST 1 +_ACEOF + + USE_CONTRIB_P4EST=yes + export USE_CONTRIB_P4EST + + DEAL_II_P4EST_DIR=${use_p4est} + export DEAL_II_P4EST_DIR + + CXXFLAGSG="$CXXFLAGSG -I$use_p4est/DEBUG/include" + CXXFLAGSO="$CXXFLAGSO -I$use_p4est/FAST/include" + else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + fi + + + + + + + + if test "x$with_umfpack" != "x" -a "x$with_umfpack" != "xno" ; then if test "x$with_blas" = "x" -o "x$with_blas" = "xno"; then with_blas="yes" diff --git a/deal.II/configure.in b/deal.II/configure.in index 2aad166205..dae6764348 100644 --- a/deal.II/configure.in +++ b/deal.II/configure.in @@ -83,6 +83,22 @@ dnl ------------------------------------------------------------- AC_MSG_RESULT() AC_MSG_RESULT(---------------- configuring C/C++ compilers ----------------) + +dnl See if the user has specified --enable-mpi. If so, and if $CXX and $CC have +dnl not been set to a particular value, then override them with 'mpiCC' and 'mpicc'. +AC_ARG_ENABLE(mpi, + AS_HELP_STRING([--enable-mpi], + [Select MPI-enabled compilers and MPI support in deal.II.]), + [ + AC_MSG_CHECKING(whether to explicitly use MPI) + if test "x$enableval" = "xyes" ; then + AC_MSG_RESULT(yes) + if test "x$CXX" = "x" ; then CXX=mpiCC ; fi + if test "x$CC" = "x" ; then CC=mpicc ; fi + else + AC_MSG_RESULT(no) + fi]) + dnl Find a C compiler. This modifies the variable CC. dnl In order to get the absolute path of the compiler, use the dnl second line @@ -132,7 +148,8 @@ DEAL_II_DETERMINE_CC_BRAND DEAL_II_SET_CC_FLAGS AC_SUBST(CC_VERSION) -AC_SUBST(CFLAGS) +AC_SUBST(CFLAGSO) +AC_SUBST(CFLAGSG) AC_SUBST(CFLAGSPIC) dnl ------------------------------------------------------------- @@ -296,7 +313,8 @@ case "$target" in esac if test "x$enableshared" = "xyes" ; then - CFLAGS="$CFLAGS $CFLAGSPIC" + CFLAGSO="$CFLAGSO $CFLAGSPIC" + CFLAGSG="$CFLAGSG $CFLAGSPIC" CXXFLAGSG="$CXXFLAGSG $CXXFLAGSPIC" CXXFLAGSO="$CXXFLAGSO $CXXFLAGSPIC" F77FLAGSG="$F77FLAGSG $F77FLAGSPIC" @@ -447,6 +465,14 @@ AC_SUBST(DEAL_II_BLACS_DIR) dnl MUMPS dependency AC_SUBST(DEAL_II_BLACS_ARCH) AC_SUBST(DEAL_II_DEFINE_DEAL_II_USE_MUMPS) +DEAL_II_CONFIGURE_P4EST +AC_SUBST(DEAL_II_USE_P4EST) +AC_SUBST(USE_CONTRIB_P4EST) +AC_SUBST(DEAL_II_P4EST_DIR) + + + + dnl Make sure we configure for libraries used by other libraries. For dnl example, UMFPACK needs blas, and so does LAPACK. if test "x$with_umfpack" != "x" -a "x$with_umfpack" != "xno" ; then diff --git a/deal.II/contrib/Makefile.in b/deal.II/contrib/Makefile.in index 8772b62aab..613a5832a1 100644 --- a/deal.II/contrib/Makefile.in +++ b/deal.II/contrib/Makefile.in @@ -22,6 +22,7 @@ hsl-clean: endif + ifeq ($(USE_CONTRIB_UMFPACK),yes) umfpack: @cd umfpack ; $(MAKE) @@ -30,6 +31,7 @@ umfpack: endif + ifeq ($(enable-parser),yes) functionparser: @cd functionparser ; $(MAKE) @@ -54,3 +56,4 @@ clean: hsl-clean tbb-clean @-rm -f ../lib/contrib/*/*.o .PHONY: default hsl clean hsl-clean umfpack tbb tbb-clean functionparser + diff --git a/deal.II/contrib/configure.in b/deal.II/contrib/configure.in index 0549b475e2..2df5296e43 100644 --- a/deal.II/contrib/configure.in +++ b/deal.II/contrib/configure.in @@ -30,7 +30,6 @@ dnl ------------------------------------------------------------- AC_CONFIG_SUBDIRS(utilities) - dnl ------------------------------------------------------------- dnl Output results dnl ------------------------------------------------------------- diff --git a/deal.II/contrib/hsl/Makefile b/deal.II/contrib/hsl/Makefile index 7783f95244..fe35c28982 100644 --- a/deal.II/contrib/hsl/Makefile +++ b/deal.II/contrib/hsl/Makefile @@ -1,5 +1,5 @@ # $Id$ -# Copyright (C) 2001, 2002, 2003, 2004, 2006, 2008, 2009 by Wolfgang Bangerth +# Copyright (C) 2001, 2002, 2003, 2004, 2006, 2008, 2009, 2010 by Wolfgang Bangerth D = ../.. @@ -54,7 +54,9 @@ $(LIBDIR)/bin/detached_ma27$(EXEEXT): \ clean: - -rm -f *~ lib/lib* lib/o/*.$(OBJEXT) lib/Makefile.dep + -rm -f $(LIBDIR)/contrib/hsl/*.$(OBJEXT) $(LIBDIR)/libhsl$(lib-suffix) \ + $(LIBDIR)/bin/detached_ma27$(EXEEXT) \ + Makefile.dep diff --git a/deal.II/contrib/umfpack/AMD/Source/Makefile b/deal.II/contrib/umfpack/AMD/Source/Makefile index 63d45e000a..8aebbe4a64 100644 --- a/deal.II/contrib/umfpack/AMD/Source/Makefile +++ b/deal.II/contrib/umfpack/AMD/Source/Makefile @@ -26,15 +26,15 @@ AMDL = $(addprefix $D/lib/contrib/umfpack/, $(addsuffix .$(OBJEXT), $(subst amd_ $D/lib/contrib/umfpack/amd_i_%.$(OBJEXT): amd_%.c $(INC) @echo =====umfpack==========optimized====== $( +#include +#include + +#include +#include + +DEAL_II_NAMESPACE_OPEN + +namespace parallel +{ + namespace distributed + { + // forward declarations + template class Triangulation; + + +/** + * Collection of functions controlling refinement and coarsening of + * parallel::distributed::Triangulation objects. This namespace + * provides similar functionality to the dealii::GridRefinement + * namespace, except that it works for meshes that are parallel and + * distributed. + * + * @ingroup grid + * @author Wolfgang Bangerth, 2009 + */ + namespace GridRefinement + { + /** + * Like + * dealii::GridRefinement::refine_and_coarsen_fixed_number, + * but for parallel distributed + * triangulation. + * + * The vector of criteria needs + * to be a vector of refinement + * criteria for all cells + * active on the current + * triangulation, + * i.e. tria.dealii::Triangulation::n_active_cells(). However, + * the function will only look + * at the indicators that + * correspond to those cells + * that are actually locally + * owned, and ignore the + * indicators for all other + * cells. The function will + * then coordinate among all + * processors that store part + * of the triangulation so that + * at the end @p + * top_fraction_of_cells are + * refined, where the fraction + * is enforced as a fraction of + * Triangulation::n_global_active_cells, + * not + * Triangulation::n_locally_active_cells + * on each processor + * individually. In other + * words, it may be that on + * some processors, no cells + * are refined at all. + * + * The same is true for the fraction of + * cells that is coarsened. + */ + template + void + refine_and_coarsen_fixed_number ( + parallel::distributed::Triangulation &tria, + const Vector &criteria, + const double top_fraction_of_cells, + const double bottom_fraction_of_cells); + + /** + * Like + * dealii::GridRefinement::refine_and_coarsen_fixed_fraction, + * but for parallel distributed + * triangulation. + * + * The vector of criteria needs to be a + * vector of refinement criteria for + * all cells active on the current + * triangulation, + * i.e. tria.dealii::Triangulation::n_active_cells(). However, + * the function will only look at the + * indicators that correspond to those + * cells that are actually locally + * owned, and ignore the indicators for + * all other cells. The function will + * then coordinate among all processors + * that store part of the triangulation + * so that at the end the smallest + * fraction of + * Triangulation::n_global_active_cells + * (not + * Triangulation::n_locally_active_cells + * on each processor individually) is + * refined that together make up a + * total of @p top_fraction_of_error of + * the total error. In other words, it + * may be that on some processors, no + * cells are refined at all. + * + * The same is true for the fraction of + * cells that is coarsened. + */ + template + void + refine_and_coarsen_fixed_fraction ( + parallel::distributed::Triangulation &tria, + const Vector &criteria, + const double top_fraction_of_error, + const double bottom_fraction_of_error); + } + } +} + + +DEAL_II_NAMESPACE_CLOSE + +#endif //__deal2__distributed_grid_refinement_h diff --git a/deal.II/deal.II/include/distributed/solution_transfer.h b/deal.II/deal.II/include/distributed/solution_transfer.h new file mode 100644 index 0000000000..af3bee627c --- /dev/null +++ b/deal.II/deal.II/include/distributed/solution_transfer.h @@ -0,0 +1,187 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2009, 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. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__distributed_solution_transfer_h +#define __deal2__distributed_solution_transfer_h + +#include +#include +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + +namespace parallel +{ + + namespace distributed + { +/** + * Transfers a discrete FE function (like a solution vector) by + * interpolation while refining and/or + * coarsening a distributed grid and + * handles the necessary communication. + * + *

Usage

+ * @verbatim + * SolutionTransfer > soltrans(dof_handler); + * // flag some cells for refinement + * // and coarsening, e.g. + * GridRefinement::refine_and_coarsen_fixed_fraction( + * *tria, error_indicators, 0.3, 0.05); + * // prepare the triangulation, + * tria->prepare_coarsening_and_refinement(); + * // prepare the SolutionTransfer object + * // for coarsening and refinement and give + * // the solution vector that we intend to + * // interpolate later, + * soltrans.prepare_for_coarsening_and_refinement(solution); + * // actually execute the refinement, + * tria->execute_coarsening_and_refinement (); + * // redistribute dofs, + * dof_handler->distribute_dofs (fe); + * // and interpolate the solution + * Vector interpolate_solution(dof_handler->n_dofs()); + * soltrans.interpolate(solution, interpolated_solution); + * @endverbatim + * @ingroup distributed + * @author Timo Heister, 2009 + */ + template > + class SolutionTransfer + { + public: + /** + * Constructor, takes the current DoFHandler + * as argument. + */ + SolutionTransfer(const DH &dof); + /** + * Destructor. + */ + ~SolutionTransfer(); + + /** + * Prepares the @p SolutionTransfer for + * coarsening and refinement. It + * stores the dof indices of each cell and + * stores the dof values of the vectors in + * @p all_in in each cell that'll be coarsened. + * @p all_in includes all vectors + * that are to be interpolated + * onto the new (refined and/or + * coarsenend) grid. + */ + void prepare_for_coarsening_and_refinement (const std::vector &all_in); + + /** + * Same as previous function + * but for only one discrete function + * to be interpolated. + */ + void prepare_for_coarsening_and_refinement (const VECTOR &in); + + /** + * + */ + void interpolate (std::vector &all_out); + + /** + * Same as the previous function. + * It interpolates only one function. + * It assumes the vectors having the + * right sizes (i.e. in.size()==n_dofs_old, + * out.size()==n_dofs_refined) + * + * Multiple calling of this function is + * NOT allowed. Interpolating + * several functions can be performed + * in one step by using + * interpolate (all_in, all_out) + */ + void interpolate (VECTOR &out); + + + /** + * return the size in bytes that need + * to be stored per cell. + */ + unsigned int get_data_size() const; + + + + private: + /** + * Pointer to the degree of + * freedom handler to work + * with. + */ + SmartPointer > dof_handler; + + /** + * A vector that stores + * pointers to all the + * vectors we are supposed to + * copy over from the old to + * the new mesh. + */ + std::vector input_vectors; + + /** + * The offset that the + * Triangulation has assigned + * to this object starting at + * which we are allowed to + * write. + */ + unsigned int offset; + + /** + * A callback function used + * to pack the data on the + * current mesh into objects + * that can later be + * retrieved after + * refinement, coarsening and + * repartitioning. + */ + void pack_callback(const typename Triangulation::cell_iterator &cell, + const typename Triangulation::CellStatus status, + void* data); + + /** + * A callback function used + * to unpack the data on the + * current mesh that has been + * packed up previously on + * the mesh before + * refinement, coarsening and + * repartitioning. + */ + void unpack_callback(const typename Triangulation::cell_iterator &cell, + const typename Triangulation::CellStatus status, + const void* data, + std::vector &all_out); + + }; + + + } +} + + + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/deal.II/include/distributed/tria.h b/deal.II/deal.II/include/distributed/tria.h new file mode 100644 index 0000000000..c5f9deea39 --- /dev/null +++ b/deal.II/deal.II/include/distributed/tria.h @@ -0,0 +1,772 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2008, 2009, 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. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__distributed_tria_h +#define __deal2__distributed_tria_h + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef DEAL_II_USE_P4EST +#include +#include +#include + +#include +#include +#include +#endif + + +DEAL_II_NAMESPACE_OPEN + +template class Triangulation; + +#ifdef DEAL_II_USE_P4EST + +namespace internal +{ + namespace DoFHandler + { + namespace Policy + { + template class ParallelDistributed; + } + } +} + + +namespace internal +{ + namespace p4est + { + /** + * A structure whose explicit + * specializations contain + * typedefs to the relevant + * p4est_* and p8est_* + * types. Using this + * structure, for example by + * saying + * types@::connectivity + * we can write code in a + * dimension independent way, + * either referring to + * p4est_connectivity_t or + * p8est_connectivity_t, + * depending on template + * argument. + */ + template struct types; + + template <> + struct types<2> + { + typedef p4est_connectivity_t connectivity; + typedef p4est_t forest; + typedef p4est_tree_t tree; + typedef p4est_quadrant_t quadrant; + typedef p4est_topidx_t topidx; + typedef p4est_locidx_t locidx; + typedef p4est_balance_type_t balance_type; + typedef p4est_ghost_t ghost; + }; + + template <> + struct types<3> + { + typedef p8est_connectivity_t connectivity; + typedef p8est_t forest; + typedef p8est_tree_t tree; + typedef p8est_quadrant_t quadrant; + typedef p4est_topidx_t topidx; + typedef p4est_locidx_t locidx; + typedef p8est_balance_type_t balance_type; + typedef p8est_ghost_t ghost; + }; + + + /** + * Initialize the + * GeometryInfo::max_children_per_cell + * children of the cell + * p4est_cell. + */ + template + void + init_quadrant_children + (const typename types::quadrant & p4est_cell, + typename types::quadrant (&p4est_children)[GeometryInfo::max_children_per_cell]); + + + /** + * Initialize quadrant to represent a coarse cell. + */ + template + void + init_coarse_quadrant(typename types::quadrant & quad); + + + + /** + * Returns whether q1 and q2 are equal + */ + template + bool + quadrant_is_equal (const typename types::quadrant & q1, + const typename types::quadrant & q2); + + //TODO: remove these functions from + //public interface somehow? [TH] + + /** + * returns whether q1 is an ancestor of q2 + */ + template + bool + quadrant_is_ancestor (const typename types::quadrant & q1, + const typename types::quadrant & q2); + } +} + + + +namespace parallel +{ + namespace distributed + { + + +/** + * This class acts like the dealii::Triangulation class, but it + * distributes the mesh across a number of different processors when + * using MPI. The class's interface does not add a lot to the + * dealii::Triangulation class but there are a number of difficult + * algorithms under the hood that ensure we always have a + * load-balanced, fully distributed mesh. Use of this class is + * explained in step-40, step-32, the @ref distributed documentation + * module, as well as the @ref distributed_paper . See there for more + * information. + * + * @author Wolfgang Bangerth, Timo Heister 2008, 2009, 2010 + * @ingroup distributed + */ + template + class Triangulation : public dealii::Triangulation + { + public: + /** + * Import the various + * iterator typedefs from the + * base class. + */ + typedef typename dealii::Triangulation::active_cell_iterator active_cell_iterator; + typedef typename dealii::Triangulation::cell_iterator cell_iterator; + typedef typename dealii::Triangulation::raw_cell_iterator raw_cell_iterator; + + /** + * Constructor. + * + * @param mpi_communicator denotes + * the MPI communicator to be used for + * the triangulation. + * + * @param smooth_grid Degree + * and kind of mesh smoothing + * to be applied to the + * mesh. See the + * dealii::Triangulation + * class for a description of + * the kinds of smoothing + * operations that can be + * applied. + * + * @note This class does not + * currently support the + * check_for_distorted_cells + * argument provided by the + * base class. + */ + Triangulation (MPI_Comm mpi_communicator, + const typename dealii::Triangulation::MeshSmoothing + smooth_grid = (dealii::Triangulation::none)); + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Reset this triangulation into a + * virgin state by deleting all data. + * + * Note that this operation is only + * allowed if no subscriptions to this + * object exist any more, such as + * DoFHandler objects using it. + */ + virtual void clear (); + + /** + * Implementation of the same + * function as in the base + * class. + */ + virtual void copy_triangulation (const dealii::Triangulation &old_tria); + + /** + * Create a triangulation as + * documented in the base + * class. + * + * This function also sets up + * the various data + * structures necessary to + * distribute a mesh across a + * number of processors. This + * will be necessary once the + * mesh is being refined, + * though we will always keep + * the entire coarse mesh + * that is generated by this + * function on all + * processors. + */ + virtual void create_triangulation (const std::vector > &vertices, + const std::vector > &cells, + const SubCellData &subcelldata); + + /** + * Coarsen and refine the + * mesh according to + * refinement and coarsening + * flags set. + * + * Since the current + * processor only has control + * over those cells it owns + * (i.e. the ones for which + * cell-@>subdomain_id() + * == + * this-@>locally_owned_subdomain()), + * refinement and coarsening + * flags are only respected + * for those locally owned + * cells. Flags may be set on + * other cells as well (and + * may often, in fact, if you + * call + * Triangulation::prepare_coarsening_and_refinement) + * but will be largely + * ignored: the decision to + * refine the global mesh + * will only be affected by + * flags set on locally owned + * cells. + */ + virtual void execute_coarsening_and_refinement (); + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id_t locally_owned_subdomain () const; + + /** + * Return the number of + * active cells in the + * triangulation that are + * locally owned, i.e. that + * have a subdomain_id equal + * to + * locally_owned_subdomain(). Note + * that there may be more + * active cells in the + * triangulation stored on + * the present processor, + * such as for example ghost + * cells, or cells further + * away from the locally + * owned block of cells but + * that are needed to ensure + * that the triangulation + * that stores this + * processor's set of active + * cells still remains + * balanced with respect to + * the 2:1 size ratio of + * adjacent cells. + * + * As a consequence of the + * remark above, the result + * of this function is always + * smaller or equal to the + * result of + * ::Triangulation::n_active_cells() + * which includes the active + * ghost and artificial cells + * (see also @ref + * GlossArtificialCell and + * @ref GlossGhostCell). + */ + unsigned int n_locally_owned_active_cells () const; + + /** + * Return the sum over all + * processors of the number + * of active cells owned by + * each processor. This + * equals the overall number + * of active cells in the + * distributed triangulation. + */ + unsigned int n_global_active_cells () const; + + /** + * Return the number of + * active cells owned by each + * of the MPI processes that + * contribute to this + * triangulation. The element + * of this vector indexed by + * locally_owned_subdomain() + * equals the result of + * n_locally_owned_active_cells(). + */ + const std::vector & + n_locally_owned_active_cells_per_processor () const; + + /** + * Return the MPI + * communicator used by this + * triangulation. + */ + MPI_Comm get_communicator () const; + + /** + * Return the local memory + * consumption in bytes. + */ + virtual unsigned int memory_consumption () const; + + /** + * Return the local memory + * consumption contained in the p4est + * data structures alone. This is + * already contained in + * memory_consumption() but made + * available separately for debugging + * purposes. + */ + virtual unsigned int memory_consumption_p4est () const; + + /** + * A collective operation that produces + * a sequence of output files with the + * given file base name that contain + * the mesh in VTK format. + * + * More than anything else, this + * function is useful for debugging the + * interface between deal.II and p4est. + */ + void write_mesh_vtk (const char *file_basename) const; + + /** + * Produce a check sum of the + * triangulation. This is a + * collective operation and + * is mostly useful for + * debugging purposes. + */ + unsigned int get_checksum () const; + + /** + * Used to inform in the callbacks of + * register_data_attach() and + * notify_ready_to_unpack() how the + * cell with the given cell_iterator + * is going to change. Note that + * this may me different then the + * refine_flag() and coarsen_flag() + * in the cell_iterator because of + * refinement constraints that this + * machine does not see. + */ + enum CellStatus + { + CELL_PERSIST, CELL_REFINE, CELL_COARSEN, CELL_INVALID + }; + + /** + * Register a function with + * the current Triangulation + * object that will be used + * to attach data to active + * cells before + * execute_coarsening_and_refinement(). In + * execute_coarsening_and_refinement() + * the Triangulation will + * call the given function + * pointer and provide + * @p size bytes to store + * data. If necessary, this data will be + * transferred to the new + * owner of that cell during repartitioning + * the tree. See + * notify_ready_to_unpack() + * on how to retrieve the + * data. + * + * Callers need to store the + * return value. It + * specifies an offset of the + * position at which data can + * later be retrieved during + * a call to + * notify_ready_to_unpack(). + */ + unsigned int + register_data_attach (const size_t size, + const std_cxx1x::function & pack_callback); + + /** + * The given function is called for + * each new active cell and supplies + * a pointer to the data saved with + * register_data_attach(). + */ + void + notify_ready_to_unpack (const unsigned int offset, + const std_cxx1x::function & unpack_callback); + + private: + /** + * MPI communicator to be + * used for the + * triangulation. We create a + * unique communicator for + * this class, which is a + * duplicate of the one + * passed to the constructor. + */ + MPI_Comm mpi_communicator; + + /** + * The subdomain id to be + * used for the current + * processor. + */ + types::subdomain_id_t my_subdomain; + + /** + * A flag that indicates whether the + * triangulation has actual content. + */ + bool triangulation_has_content; + + /** + * A structure that contains + * some numbers about the + * distributed triangulation. + */ + struct NumberCache + { + std::vector n_locally_owned_active_cells; + unsigned int n_global_active_cells; + }; + + NumberCache number_cache; + + /** + * A data structure that holds the + * connectivity between trees. Since + * each tree is rooted in a coarse grid + * cell, this data structure holds the + * connectivity between the cells of + * the coarse grid. + */ + typename dealii::internal::p4est::types::connectivity *connectivity; + + /** + * A data structure that holds the + * local part of the global + * triangulation. + */ + typename dealii::internal::p4est::types::forest *parallel_forest; + + /** + * A flag that indicates + * whether refinement of a + * triangulation is currently + * in progress. We need this + * flag because the + * refinement_notification() + * function is called from + * Triangulation::execute_coarsening_and_refinement(), + * but + * refinement_notification() + * itself also calls + * Triangulation::execute_coarsening_and_refinement() + * and therefore gets called + * recursively again. While the + * first time we want to take + * over work to copy things + * from a refined p4est, the + * other times we don't want to + * get in the way as these + * latter calls to + * Triangulation::execute_coarsening_and_refinement() + * are simply there in order to + * re-create a triangulation + * that matches the p4est. + */ + bool refinement_in_progress; + + + /** + * + */ + unsigned int attached_data_size; + unsigned int n_attached_datas; + typedef std_cxx1x::function< + void(typename Triangulation::cell_iterator, CellStatus, void*) + > pack_callback_t; + + typedef std::pair callback_pair_t; + + typedef std::list callback_list_t; + callback_list_t attached_data_pack_callbacks; + + + + + + /** + * Two arrays that store which p4est + * tree corresponds to which coarse + * grid cell and vice versa. We need + * these arrays because p4est goes with + * the original order of coarse cells + * when it sets up its forest, and then + * applies the Morton ordering within + * each tree. But if coarse grid cells + * are badly ordered this may mean that + * individual parts of the forest + * stored on a local machine may be + * split across coarse grid cells that + * are not geometrically + * close. Consequently, we apply a + * Cuthill-McKee preordering to ensure + * that the part of the forest stored + * by p4est is located on geometrically + * close coarse grid cells. + */ + std::vector coarse_cell_to_p4est_tree_permutation; + std::vector p4est_tree_to_coarse_cell_permutation; + + /** + * Return a pointer to the p4est + * tree that belongs to the given + * dealii_coarse_cell_index() + */ + typename dealii::internal::p4est::types::tree * + init_tree(const int dealii_coarse_cell_index) const; + + /** + * The function that computes the + * permutation between the two data + * storage schemes. + */ + void setup_coarse_cell_to_p4est_tree_permutation (); + + /** + * Take the contents of a newly created + * triangulation we are attached to and + * copy it to p4est data structures. + * + * This function exists in 2d + * and 3d variants. + */ + void copy_new_triangulation_to_p4est (dealii::internal::int2type<2>); + void copy_new_triangulation_to_p4est (dealii::internal::int2type<3>); + + /** + * Copy the local part of the refined + * forest from p4est into the attached + * triangulation. + */ + void copy_local_forest_to_triangulation (); + + + /** + * Update the number_cache + * variable after mesh + * creation or refinement. + */ + void update_number_cache (); + + /** + * Internal function notifying all + * registered classes to attach their + * data before repartitioning + * occurs. Called from + * execute_coarsening_and_refinement(). + */ + void attach_mesh_data(); + + + template friend class dealii::internal::DoFHandler::Policy::ParallelDistributed; + }; + + + /** + * Specialization of the general template + * for the 1d case. There is currently no + * support for distributing 1d + * triangulations. Consequently, all this + * class does is throw an exception. + */ + template + class Triangulation<1,spacedim> : public dealii::Triangulation<1,spacedim> + { + public: + /** + * Constructor. The argument denotes + * the MPI communicator to be used for + * the triangulation. + */ + Triangulation (MPI_Comm mpi_communicator); + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Return the MPI + * communicator used by this + * triangulation. + */ + MPI_Comm get_communicator () const; + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id_t locally_owned_subdomain () const; + + /** + * Dummy arrays. This class + * isn't usable but the + * compiler wants to see + * these variables at a + * couple places anyway. + */ + std::vector coarse_cell_to_p4est_tree_permutation; + std::vector p4est_tree_to_coarse_cell_permutation; + }; + } +} + + +#else // DEAL_II_USE_P4EST + +namespace parallel +{ + namespace distributed + { + /** + * Dummy class the compiler chooses for + * parallel distributed triangulations if + * we didn't actually configure deal.II + * with the p4est library. The existence + * of this class allows us to refer to + * parallel::distributed::Triangulation + * objects throughout the library even if + * it is disabled. + * + * Since the constructor of this class is + * private, no such objects can actually + * be created if we don't have p4est + * available. + */ + template + class Triangulation : public dealii::Triangulation + { + private: + /** + * Constructor. + */ + Triangulation (); + + public: + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id_t locally_owned_subdomain () const; + }; + } +} + + +#endif + + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/deal.II/include/dofs/dof_accessor.h b/deal.II/deal.II/include/dofs/dof_accessor.h index f401d9bfba..1ab4b54a07 100644 --- a/deal.II/deal.II/include/dofs/dof_accessor.h +++ b/deal.II/deal.II/include/dofs/dof_accessor.h @@ -40,6 +40,10 @@ namespace internal namespace DoFHandler { struct Implementation; + namespace Policy + { + struct Implementation; + } } namespace hp @@ -51,8 +55,6 @@ namespace internal } } - - // note: the file dof_accessor.templates.h is included at the end of // this file. this includes a lot of templates and thus makes // compilation slower, but at the same time allows for more aggressive @@ -288,7 +290,7 @@ class DoFAccessor : public internal::DoFAccessor::Inheritance &da); - + /** * Return an iterator pointing to * the the parent. @@ -732,8 +734,10 @@ class DoFAccessor : public internal::DoFAccessor::Inheritance friend class DoFHandler; template friend class hp::DoFHandler; + friend class internal::DoFHandler::Policy::Implementation; friend class internal::DoFHandler::Implementation; friend class internal::hp::DoFHandler::Implementation; + friend class internal::DoFCellAccessor::Implementation; }; @@ -851,7 +855,7 @@ class DoFCellAccessor : public DoFAccessor */ typename internal::DoFHandler::Iterators::cell_iterator parent () const; - + /** * @name Accessing sub-objects and neighbors */ @@ -1382,7 +1386,15 @@ class DoFCellAccessor : public DoFAccessor * @} */ - private: + /** + * Set the DoF indices of this + * cell to the given values. This + * function bypasses the DoF + * cache, if one exists for the + * given DoF handler class. + */ + void set_dof_indices (const std::vector &dof_indices); + /** * Update the cache in which we * store the dof indices of this @@ -1397,7 +1409,6 @@ class DoFCellAccessor : public DoFAccessor * function */ template friend class DoFHandler; - friend class internal::DoFCellAccessor::Implementation; }; diff --git a/deal.II/deal.II/include/dofs/dof_accessor.templates.h b/deal.II/deal.II/include/dofs/dof_accessor.templates.h index f567fe5b2a..0768ac47f6 100644 --- a/deal.II/deal.II/include/dofs/dof_accessor.templates.h +++ b/deal.II/deal.II/include/dofs/dof_accessor.templates.h @@ -2049,6 +2049,163 @@ namespace internal Assert (false, ExcNotImplemented()); } + /** + * Implement setting dof + * indices on a + * cell. Currently not + * implemented for + * hp::DoFHandler objects. + */ + template + static + void + set_dof_indices (DoFCellAccessor > &accessor, + const std::vector &local_dof_indices) + { + Assert (accessor.has_children() == false, + ExcInternalError()); + + const unsigned int dofs_per_vertex = accessor.get_fe().dofs_per_vertex, + dofs_per_line = accessor.get_fe().dofs_per_line, + dofs_per_cell = accessor.get_fe().dofs_per_cell; + + Assert (local_dof_indices.size() == dofs_per_cell, + ExcInternalError()); + + unsigned int index = 0; + + for (unsigned int vertex=0; vertex<2; ++vertex) + for (unsigned int d=0; d + static + void + set_dof_indices (DoFCellAccessor > &accessor, + const std::vector &local_dof_indices) + { + Assert (accessor.has_children() == false, + ExcInternalError()); + + const unsigned int dofs_per_vertex = accessor.get_fe().dofs_per_vertex, + dofs_per_line = accessor.get_fe().dofs_per_line, + dofs_per_quad = accessor.get_fe().dofs_per_quad, + dofs_per_cell = accessor.get_fe().dofs_per_cell; + + Assert (local_dof_indices.size() == dofs_per_cell, + ExcInternalError()); + + unsigned int index = 0; + + for (unsigned int vertex=0; vertex<4; ++vertex) + for (unsigned int d=0; dset_dof_index(d, local_dof_indices[index]); + + for (unsigned int d=0; d + static + void + set_dof_indices (DoFCellAccessor > &accessor, + const std::vector &local_dof_indices) + { + Assert (accessor.has_children() == false, + ExcInternalError()); + + const unsigned int dofs_per_vertex = accessor.get_fe().dofs_per_vertex, + dofs_per_line = accessor.get_fe().dofs_per_line, + dofs_per_quad = accessor.get_fe().dofs_per_quad, + dofs_per_hex = accessor.get_fe().dofs_per_hex, + dofs_per_cell = accessor.get_fe().dofs_per_cell; + + Assert (local_dof_indices.size() == dofs_per_cell, + ExcInternalError()); + + unsigned int index = 0; + + for (unsigned int vertex=0; vertex<8; ++vertex) + for (unsigned int d=0; dset_dof_index(accessor.dof_handler->get_fe(). + adjust_line_dof_index_for_line_orientation(d, + accessor.line_orientation(line)), + local_dof_indices[index]); + // now copy dof numbers into the face. for + // faces with the wrong orientation, we + // have already made sure that we're ok by + // picking the correct lines and vertices + // (this happens automatically in the + // line() and vertex() functions). however, + // if the face is in wrong orientation, we + // look at it in flipped orientation and we + // will have to adjust the shape function + // indices that we see to correspond to the + // correct (cell-local) ordering. The same + // applies, if the face_rotation or + // face_orientation is non-standard + for (unsigned int quad=0; quad<6; ++quad) + for (unsigned int d=0; dset_dof_index(accessor.dof_handler->get_fe(). + adjust_quad_dof_index_for_face_orientation(d, + accessor.face_orientation(quad), + accessor.face_flip(quad), + accessor.face_rotation(quad)), + local_dof_indices[index]); + for (unsigned int d=0; d + static + void + set_dof_indices (const DoFCellAccessor > &, + const std::vector &) + { + Assert (false, ExcNotImplemented()); + } + /** * A function that collects the @@ -2832,6 +2989,8 @@ void DoFCellAccessor:: get_dof_indices (std::vector &dof_indices) const { + Assert (this->is_artificial() == false, + ExcMessage ("Can't ask for DoF indices on artificial cells.")); internal::DoFCellAccessor::Implementation::get_dof_indices (*this, dof_indices); } @@ -2843,6 +3002,8 @@ void DoFCellAccessor::get_dof_values (const InputVector &values, Vector &local_values) const { + Assert (this->is_artificial() == false, + ExcMessage ("Can't ask for DoF indices on artificial cells.")); internal::DoFCellAccessor::Implementation ::get_dof_values (*this, values, local_values.begin(), local_values.end()); } @@ -2856,6 +3017,8 @@ DoFCellAccessor::get_dof_values (const InputVector &values, ForwardIterator local_values_begin, ForwardIterator local_values_end) const { + Assert (this->is_artificial() == false, + ExcMessage ("Can't ask for DoF indices on artificial cells.")); internal::DoFCellAccessor::Implementation ::get_dof_values (*this, values, local_values_begin, local_values_end); } @@ -2870,6 +3033,8 @@ DoFCellAccessor::get_dof_values (const ConstraintMatrix &constraints, ForwardIterator local_values_begin, ForwardIterator local_values_end) const { + Assert (this->is_artificial() == false, + ExcMessage ("Can't ask for DoF indices on artificial cells.")); internal::DoFCellAccessor::Implementation ::get_dof_values (*this, constraints, values, local_values_begin, local_values_end); @@ -2883,6 +3048,8 @@ void DoFCellAccessor::set_dof_values (const Vector &local_values, OutputVector &values) const { + Assert (this->is_artificial() == false, + ExcMessage ("Can't ask for DoF indices on artificial cells.")); internal::DoFCellAccessor::Implementation ::set_dof_values (*this, local_values, values); } diff --git a/deal.II/deal.II/include/dofs/dof_handler.h b/deal.II/deal.II/include/dofs/dof_handler.h index 81f4992d79..52d9dd707a 100644 --- a/deal.II/deal.II/include/dofs/dof_handler.h +++ b/deal.II/deal.II/include/dofs/dof_handler.h @@ -18,9 +18,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include @@ -105,6 +108,19 @@ namespace internal * algorithms. * * + *

Interaction with distributed meshes

+ * + * Upon construction, this class takes a reference to a triangulation + * object. In most cases, this will be a reference to an object of + * type Triangulation, i.e. the class that represents triangulations + * that entirely reside on a single processor. However, it can also be + * of type parallel::distributed::Triangulation (see, for example, + * step-32, step-40 and in particular the @ref distributed module) in + * which case the DoFHandler object will proceed to only manage + * degrees of freedom on locally owned and ghost cells. This process + * is entirely transparent to the used. + * + * *

User defined renumbering schemes

* * The DoFRenumbering class offers a number of renumbering schemes like the @@ -131,7 +147,7 @@ class DoFHandler : public Subscriptor public: typedef typename IteratorSelector::CellAccessor cell_accessor; typedef typename IteratorSelector::FaceAccessor face_accessor; - + typedef typename IteratorSelector::raw_line_iterator raw_line_iterator; typedef typename IteratorSelector::line_iterator line_iterator; typedef typename IteratorSelector::active_line_iterator active_line_iterator; @@ -208,7 +224,7 @@ class DoFHandler : public Subscriptor * value. */ static const unsigned int default_fe_index = 0; - + /** * Standard constructor, not * initializing any data. After @@ -224,12 +240,12 @@ class DoFHandler : public Subscriptor * triangulation to work on. */ DoFHandler ( const Triangulation &tria); - + /** * Destructor. */ virtual ~DoFHandler (); - + /** * Assign a Triangulation and a * FiniteElement to the @@ -286,7 +302,7 @@ class DoFHandler : public Subscriptor * in the same object. */ void initialize_local_block_info(); - + /** * Clear all data of this object and * especially delete the lock this object @@ -300,31 +316,68 @@ class DoFHandler : public Subscriptor * a list of new dof numbers for all the * dofs. * - * @p new_numbers is an array of integers - * with size equal to the number of dofs - * on the present grid. It stores the new - * indices after renumbering in the - * order of the old indices. - * * This function is called by * the functions in * DoFRenumbering function * after computing the ordering * of the degrees of freedom. - * However, you can call this - * function yourself, which is - * necessary if a user wants to - * implement an ordering scheme - * herself, for example - * downwind numbering. + * This function is called, for + * example, by the functions in + * the DoFRenumbering + * namespace, but it can of + * course also be called from + * user code. * - * The @p new_number array must - * have a size equal to the + * @arg new_number This array + * must have a size equal to + * the number of degrees of + * freedom owned by the current + * processor, i.e. the size + * must be equal to what + * n_locally_owned_dofs() + * returns. If only one + * processor participates in + * storing the current mesh, + * then this equals the total * number of degrees of - * freedom. Each entry must - * state the new global DoF - * number of the degree of - * freedom referenced. + * freedom, i.e. the result of + * n_dofs(). The contents of + * this array are the new + * global indices for each + * freedom listed in the + * IndexSet returned by + * locally_owned_dofs(). In the + * case of a sequential mesh + * this means that the array is + * a list of new indices for + * each of the degrees of + * freedom on the current + * mesh. In the case that we + * have a + * parallel::distributed::Triangulation + * underlying this DoFHandler + * object, the array is a list + * of new indices for all the + * locally owned degrees of + * freedom, enumerated in the + * same order as the currently + * locally owned DoFs. In other + * words, assume that degree of + * freedom i is + * currently locally owned, + * then + * new_numbers[locally_owned_dofs().index_within_set(i)] + * returns the new global DoF + * index of + * i. Since the + * IndexSet of + * locally_owned_dofs() is + * complete in the sequential + * case, the latter convention + * for the content of the array + * reduces to the former in the + * case that only one processor + * participates in the mesh. */ void renumber_dofs (const std::vector &new_numbers); @@ -927,10 +980,35 @@ class DoFHandler : public Subscriptor /** - * Return number of degrees of freedom. - * Included in this number are those + * Return the global number of + * degrees of freedom. If the + * current object handles all + * degrees of freedom itself + * (even if you may intend to + * solve your linear system in + * parallel, such as in step-17 + * or step-18), then this number + * equals the number of locally + * owned degrees of freedom since + * this object doesn't know + * anything about what you want + * to do with it and believes + * that it owns every degree of + * freedom it knows about. + * + * On the other hand, if this + * object operates on a + * parallel::distributed::Triangulation + * object, then this function + * returns the global number of + * degrees of freedom, + * accumulated over all + * processors. + * + * In either case, included in + * the returned number are those * DoFs which are constrained by - * hanging nodes. + * hanging nodes, see @ref constraints. */ unsigned int n_dofs () const; @@ -994,7 +1072,99 @@ class DoFHandler : public Subscriptor * initialize_local_block_indices(). */ const BlockInfo& block_info() const; - + + + /** + * Return the number of + * degrees of freedom that + * belong to this + * process. + * + * If this is a sequential job, + * then the result equals that + * produced by n_dofs(). On the + * other hand, if we are + * operating on a + * parallel::distributed::Triangulation, + * then it includes only the + * degrees of freedom that the + * current processor owns. Note + * that in this case this does + * not include all degrees of + * freedom that have been + * distributed on the current + * processor's image of the mesh: + * in particular, some of the + * degrees of freedom on the + * interface between the cells + * owned by this processor and + * cells owned by other + * processors may be theirs, and + * degrees of freedom on ghost + * cells are also not necessarily + * included. + */ + unsigned int n_locally_owned_dofs() const; + + /** + * Return an IndexSet describing + * the set of locally owned DoFs + * as a subset of + * 0..n_dofs(). The number of + * elements of this set equals + * n_locally_owned_dofs(). + */ + const IndexSet & locally_owned_dofs() const; + + + /** + * Returns a vector that + * stores the locally owned + * DoFs of each processor. If + * you are only interested in + * the number of elements + * each processor owns then + * n_dofs_per_processor() is + * a better choice. + * + * If this is a sequential job, + * then the vector has a single + * element that equals the + * IndexSet representing the + * entire range [0,n_dofs()]. + */ + const std::vector & + locally_owned_dofs_per_processor () const; + + /** + * Return a vector that + * stores the number of + * degrees of freedom each + * processor that + * participates in this + * triangulation owns + * locally. The sum of all + * these numbers equals the + * number of degrees of + * freedom that exist + * globally, i.e. what + * n_dofs() returns. + * + * Each element of the vector + * returned by this function + * equals the number of + * elements of the + * corresponding sets + * returned by + * global_dof_indices(). + * + * If this is a sequential job, + * then the vector has a single + * element equal to n_dofs(). + */ + const std::vector & + n_locally_owned_dofs_per_processor () const; + /** * Return a constant reference to * the selected finite element @@ -1023,11 +1193,6 @@ class DoFHandler : public Subscriptor */ virtual unsigned int memory_consumption () const; - /** - * Exception - */ - DeclException0 (ExcInvalidTriangulation); - /** * @todo Replace by ExcInternalError. */ @@ -1072,22 +1237,30 @@ class DoFHandler : public Subscriptor int, << "You tried to do something on level " << arg1 << ", but this level is empty."); - - + + protected: /** * The object containing * information on the block structure. */ BlockInfo block_info_object; - - private: + + /** + * Array to store the indices for + * degrees of freedom located at + * vertices. + */ + std::vector vertex_dofs; + + /** * Address of the triangulation to * work on. */ - SmartPointer,DoFHandler > tria; + SmartPointer,DoFHandler > + tria; /** * Store a pointer to the finite element @@ -1101,7 +1274,30 @@ class DoFHandler : public Subscriptor * function (this clears all data of * this object as well, though). */ - SmartPointer,DoFHandler > selected_fe; + SmartPointer,DoFHandler > + selected_fe; + + /** + * An object that describes how degrees + * of freedom should be distributed and + * renumbered. + */ + std_cxx1x::shared_ptr > policy; + + /** + * A structure that contains all + * sorts of numbers that + * characterize the degrees of + * freedom this object works on. + * + * For most members of this + * structure, there is an + * accessor function in this + * class that returns its value. + */ + internal::DoFHandler::NumberCache number_cache; + + private: /** * Copy constructor. I can see no reason @@ -1149,19 +1345,6 @@ class DoFHandler : public Subscriptor */ internal::DoFHandler::DoFFaces *faces; - /** - * Store the number of dofs - * created last time. - */ - unsigned int used_dofs; - - /** - * Array to store the indices for - * degrees of freedom located at - * vertices. - */ - std::vector vertex_dofs; - /** * Make accessor objects friends. */ @@ -1171,6 +1354,7 @@ class DoFHandler : public Subscriptor friend class internal::DoFCellAccessor::Implementation; friend class internal::DoFHandler::Implementation; + friend class internal::DoFHandler::Policy::Implementation; }; @@ -1193,7 +1377,39 @@ inline unsigned int DoFHandler::n_dofs () const { - return used_dofs; + return number_cache.n_global_dofs; +} + + +template +unsigned int +DoFHandler::n_locally_owned_dofs() const +{ + return number_cache.n_locally_owned_dofs; +} + + +template +const IndexSet & +DoFHandler::locally_owned_dofs() const +{ + return number_cache.locally_owned_dofs; +} + + +template +const std::vector & +DoFHandler::n_locally_owned_dofs_per_processor() const +{ + return number_cache.n_locally_owned_dofs_per_processor; +} + + +template +const std::vector & +DoFHandler::locally_owned_dofs_per_processor () const +{ + return number_cache.locally_owned_dofs_per_processor; } diff --git a/deal.II/deal.II/include/dofs/dof_handler_policy.h b/deal.II/deal.II/include/dofs/dof_handler_policy.h new file mode 100644 index 0000000000..989ef73a07 --- /dev/null +++ b/deal.II/deal.II/include/dofs/dof_handler_policy.h @@ -0,0 +1,152 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 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. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__dof_handler_policy_h +#define __deal2__dof_handler_policy_h + + + +#include +#include +#include + +#include +#include +#include + +DEAL_II_NAMESPACE_OPEN + +template class FiniteElement; +template class DoFHandler; + + +namespace internal +{ + namespace DoFHandler + { + class NumberCache; + + /** + * A namespace in which we define + * classes that describe how to + * distribute and renumber + * degrees of freedom. + */ + namespace Policy + { + struct Implementation; + + /** + * A class that implements policies for + * how the DoFHandler::distribute_dofs + * and DoFHandler::renumber_dofs + * functions should work. + */ + template + class PolicyBase + { + public: + /** + * Destructor. + */ + virtual ~PolicyBase (); + + /** + * Distribute degrees of freedom on + * the object given as last argument. + */ + virtual + NumberCache + distribute_dofs (const unsigned int offset, + dealii::DoFHandler &dof_handler) const = 0; + + /** + * Renumber degrees of freedom as + * specified by the first argument. + */ + virtual + NumberCache + renumber_dofs (const std::vector &new_numbers, + dealii::DoFHandler &dof_handler) const = 0; + }; + + + /** + * This class implements the + * default policy for sequential + * operations, i.e. for the case where + * all cells get degrees of freedom. + */ + template + class Sequential : public PolicyBase + { + public: + /** + * Distribute degrees of freedom on + * the object given as last argument. + */ + virtual + NumberCache + distribute_dofs (const unsigned int offset, + dealii::DoFHandler &dof_handler) const; + + /** + * Renumber degrees of freedom as + * specified by the first argument. + */ + virtual + NumberCache + renumber_dofs (const std::vector &new_numbers, + dealii::DoFHandler &dof_handler) const; + }; + + + /** + * This class implements the + * policy for operations when + * we use a + * parallel::distributed::Triangulation + * object. + */ + template + class ParallelDistributed : public PolicyBase + { + public: + /** + * Distribute degrees of freedom on + * the object given as last argument. + */ + virtual + NumberCache + distribute_dofs (const unsigned int offset, + dealii::DoFHandler &dof_handler) const; + + /** + * Renumber degrees of freedom as + * specified by the first argument. + */ + virtual + NumberCache + renumber_dofs (const std::vector &new_numbers, + dealii::DoFHandler &dof_handler) const; + }; + } + } +} + + + +DEAL_II_NAMESPACE_CLOSE + +/*---------------------------- dof_handler_policy.h ---------------------------*/ +#endif +/*---------------------------- dof_handler_policy.h ---------------------------*/ diff --git a/deal.II/deal.II/include/dofs/dof_renumbering.h b/deal.II/deal.II/include/dofs/dof_renumbering.h index d991afc881..152c2c8e1b 100644 --- a/deal.II/deal.II/include/dofs/dof_renumbering.h +++ b/deal.II/deal.II/include/dofs/dof_renumbering.h @@ -743,7 +743,6 @@ namespace DoFRenumbering component_wise (hp::DoFHandler &dof_handler, const std::vector &target_component = std::vector ()); - /** * Sort the degrees of freedom by * component. It does the same @@ -786,9 +785,9 @@ namespace DoFRenumbering * the renumbering vector. */ template - static unsigned int + unsigned int compute_component_wise (std::vector& new_dof_indices, - ITERATOR& start, + const ITERATOR& start, const ENDITERATOR& end, const std::vector &target_component); diff --git a/deal.II/deal.II/include/dofs/dof_tools.h b/deal.II/deal.II/include/dofs/dof_tools.h index 60fd39120d..bfc15afb6c 100644 --- a/deal.II/deal.II/include/dofs/dof_tools.h +++ b/deal.II/deal.II/include/dofs/dof_tools.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -457,7 +459,7 @@ class DoFTools SparsityPattern &sparsity_pattern, const ConstraintMatrix &constraints = ConstraintMatrix(), const bool keep_constrained_dofs = true, - const unsigned int subdomain_id = numbers::invalid_unsigned_int); + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id); /** * Locate non-zero entries for @@ -618,7 +620,7 @@ class DoFTools SparsityPattern &sparsity_pattern, const ConstraintMatrix &constraints = ConstraintMatrix(), const bool keep_constrained_dofs = true, - const unsigned int subdomain_id = numbers::invalid_unsigned_int); + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id); /** * @deprecated This is the old @@ -763,10 +765,10 @@ class DoFTools /** * This function does the same as * the other with the same name, - * but it gets a ConstraintMatrix - * additionally. - * This is for the case where you - * have fluxes but constraints as + * but it gets a ConstraintMatrix + * additionally. + * This is for the case where you + * have fluxes but constraints as * well. * Not implemented for * hp::DoFHandler. @@ -1169,9 +1171,81 @@ class DoFTools template static void extract_subdomain_dofs (const DH &dof_handler, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, std::vector &selected_dofs); + + /** + * Extract the set of global DoF + * indices that are owned by the + * current processor. For regular + * DoFHandler objects, this set + * is the complete set with all + * DoF indices. In either case, + * it equals what + * DoFHandler::locally_owned_dofs() + * returns. + */ + template + static void + extract_locally_owned_dofs (const DH & dof_handler, + IndexSet & dof_set); + + + /** + * Extract the set of global DoF + * indices that are active on the + * current DoFHandler. For + * regular DoFHandlers, these are + * all DoF indices, but for + * DoFHandler objects built on + * parallel::distributed::Triangulation + * this set is a superset of + * DoFHandler::locally_owned_dofs() + * and contains all DoF indices + * that live on all locally owned + * cells (including on the + * interface to ghost + * cells). However, it does not + * contain the DoF indices that + * are exclusively defined on + * ghost or artificial cells (see + * @ref GlossArtificialCell "the + * glossary"). + * + * The degrees of freedom identified by + * this function equal those obtained + * from the + * dof_indices_with_subdomain_association() + * function when called with the locally + * owned subdomain id. + */ + template + static void + extract_locally_active_dofs (const DH & dof_handler, + IndexSet & dof_set); + + /** + * Extract the set of global DoF + * indices that are active on the + * current DoFHandler. For + * regular DoFHandlers, these are + * all DoF indices, but for + * DoFHandler objects built on + * parallel::distributed::Triangulation + * this set is the union of + * DoFHandler::locally_owned_dofs() + * and the DoF indices on all + * ghost cells. In essence, it is + * the DoF indices on all cells + * that are not artificial (see + * @ref GlossArtificialCell "the glossary"). + */ + template + static void + extract_locally_relevant_dofs (const DH & dof_handler, + IndexSet & dof_set); + /** * Extract a vector that represents the * constant modes of the DoFHandler for @@ -1196,13 +1270,13 @@ class DoFTools * consist of as many vectors as there * are true arguments in * component_select, each of - * which will be one in one component and + * which will be one in one vector component and * zero in all others. We store this * object in a vector of vectors, where * the outer vector is of the size of the * number of selected components, and * each inner vector has as many - * components as there are degrees of + * components as there are (locally owned) degrees of * freedom in the selected * components. Note that any matrix * associated with this null space must @@ -1279,11 +1353,27 @@ class DoFTools * function, or use the * GridTools::get_subdomain_association * function. + * + * Note that this function is of + * questionable use for DoFHandler objects built on + * parallel::distributed::Triangulation + * since in that case ownership of + * individual degrees of freedom by MPI + * processes is controlled by the DoF + * handler object, not based on some + * geometric algorithm in conjunction + * with subdomain id. In particular, the + * degrees of freedom identified by the + * functions in this namespace as + * associated with a subdomain are not + * the same the + * DoFHandler class + * identifies as those it owns. */ template static void get_subdomain_association (const DH &dof_handler, - std::vector &subdomain); + std::vector &subdomain); /** * Count how many degrees of freedom are @@ -1307,25 +1397,27 @@ class DoFTools * @em cells with this subdomain, use the * GridTools::count_cells_with_subdomain_association * function. + * + * Note that this function is of + * questionable use for DoFHandler objects built on + * parallel::distributed::Triangulation + * since in that case ownership of + * individual degrees of freedom by MPI + * processes is controlled by the DoF + * handler object, not based on some + * geometric algorithm in conjunction + * with subdomain id. In particular, the + * degrees of freedom identified by the + * functions in this namespace as + * associated with a subdomain are not + * the same the + * DoFHandler class + * identifies as those it owns. */ template static unsigned int count_dofs_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain); - - /** - * Similar to the previous - * function, but do not just - * return the number of degrees - * of freedom that are owned by a - * given subdomain, but return a - * set of their indices. - */ - template - static - IndexSet - dof_indices_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain); + const types::subdomain_id_t subdomain); /** * Count how many degrees of freedom are @@ -1342,13 +1434,71 @@ class DoFTools * therefore store how many degrees of * freedom of each vector component are * associated with the given subdomain. + * + * Note that this function is of + * questionable use for DoFHandler objects built on + * parallel::distributed::Triangulation + * since in that case ownership of + * individual degrees of freedom by MPI + * processes is controlled by the DoF + * handler object, not based on some + * geometric algorithm in conjunction + * with subdomain id. In particular, the + * degrees of freedom identified by the + * functions in this namespace as + * associated with a subdomain are not + * the same the + * DoFHandler class + * identifies as those it owns. */ template static void count_dofs_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain, + const types::subdomain_id_t subdomain, std::vector &n_dofs_on_subdomain); + /** + * Return a set of indices that denotes + * the degrees of freedom that live on + * the given subdomain, i.e. that are on + * cells owned by the current + * processor. Note that this includes the + * ones that this subdomain "owns" + * (i.e. the ones for which + * get_subdomain_association() returns a + * value equal to the subdomain given + * here and that are selected by the + * extract_locally_owned() function) but + * also all of those that sit on the + * boundary between the given subdomain + * and other subdomain. In essence, + * degrees of freedom that sit on + * boundaries between subdomain will be + * in the index sets returned by this + * function for more than one subdomain. + * + * Note that this function is of + * questionable use for DoFHandler objects built on + * parallel::distributed::Triangulation + * since in that case ownership of + * individual degrees of freedom by MPI + * processes is controlled by the DoF + * handler object, not based on some + * geometric algorithm in conjunction + * with subdomain id. In particular, the + * degrees of freedom identified by the + * functions in this namespace as + * associated with a subdomain are not + * the same the + * DoFHandler class + * identifies as those it owns. + */ + template + static + IndexSet + dof_indices_with_subdomain_association (const DH &dof_handler, + const types::subdomain_id_t subdomain); + /** * Count how many degrees of * freedom out of the total @@ -1454,12 +1604,22 @@ class DoFTools * step-31, and * step-32 tutorial * programs. + * + * @pre The dofs_per_block + * variable has as many + * components as the finite + * element used by the + * dof_handler argument has + * blocks, or alternatively as + * many blocks as are enumerated + * in the target_blocks argument + * if given. */ template static void count_dofs_per_block (const DoFHandler& dof_handler, std::vector& dofs_per_block, - std::vector target_block + std::vector target_blocks = std::vector()); /** diff --git a/deal.II/deal.II/include/dofs/number_cache.h b/deal.II/deal.II/include/dofs/number_cache.h new file mode 100644 index 0000000000..e6bacba8f8 --- /dev/null +++ b/deal.II/deal.II/include/dofs/number_cache.h @@ -0,0 +1,101 @@ +//---------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2006, 2007, 2008, 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. +// +//---------------------------------------------------------------------- +#ifndef __deal2__number_cache_h +#define __deal2__number_cache_h + +#include +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + +namespace internal +{ + namespace DoFHandler + { + /** + * A structure used by the + * DoFHandler classes to store + * information about the degrees + * of freedom they deals with. + */ + struct NumberCache + { + NumberCache (); + + /** + * Determine an estimate for the + * memory consumption (in bytes) of + * this object. + */ + unsigned int memory_consumption () const; + + /** + * Total number of dofs, + * accumulated over all + * processors that may + * participate on this mesh. + */ + unsigned int n_global_dofs; + + /** + * Number of dofs owned by + * this MPI process. If this + * is a sequential + * computation, then this + * equals n_global_dofs. + */ + unsigned int n_locally_owned_dofs; + + /** + * An index set denoting the + * set of locally owned + * dofs. If this is a + * sequential computation, + * then it contains the + * entire range + * [0,n_global_dofs). + */ + IndexSet locally_owned_dofs; + + /** + * The number of dofs owned + * by each of the various MPI + * processes. If this is a + * sequential job, then the + * vector contains a single + * element equal to + * n_global_dofs. + */ + std::vector n_locally_owned_dofs_per_processor; + + /** + * The dofs owned by each of + * the various MPI + * processes. If this is a + * sequential job, then the + * vector has a single + * element equal to + * locally_owned_dofs. + */ + std::vector locally_owned_dofs_per_processor; + }; + } +} + + +DEAL_II_NAMESPACE_CLOSE + +#endif // __deal2__dof_iterator_selector_h diff --git a/deal.II/deal.II/include/grid/filtered_iterator.h b/deal.II/deal.II/include/grid/filtered_iterator.h index 0a7fb90297..c56fc0bc79 100644 --- a/deal.II/deal.II/include/grid/filtered_iterator.h +++ b/deal.II/deal.II/include/grid/filtered_iterator.h @@ -48,7 +48,7 @@ namespace IteratorFilters * * @ingroup Iterators */ - class Active + class Active { public: /** @@ -59,7 +59,7 @@ namespace IteratorFilters template bool operator () (const Iterator &i) const; }; - + /** * Filter that evaluates to true if * either the iterator points to an @@ -81,7 +81,7 @@ namespace IteratorFilters template bool operator () (const Iterator &i) const; }; - + /** * Filter that evaluates to true if @@ -106,7 +106,7 @@ namespace IteratorFilters bool operator () (const Iterator &i) const; }; - + /** * Filter for iterators that * evaluates to true if either the @@ -117,7 +117,7 @@ namespace IteratorFilters * * @ingroup Iterators */ - class LevelEqualTo + class LevelEqualTo { public: /** @@ -161,7 +161,7 @@ namespace IteratorFilters * * @ingroup Iterators */ - class SubdomainEqualTo + class SubdomainEqualTo { public: /** @@ -170,7 +170,7 @@ namespace IteratorFilters * shall have to be evaluated * to true. */ - SubdomainEqualTo (const unsigned int subdomain_id); + SubdomainEqualTo (const types::subdomain_id_t subdomain_id); /** * Evaluation operator. Returns @@ -188,7 +188,7 @@ namespace IteratorFilters * Stored value to compare the * subdomain with. */ - const unsigned int subdomain_id; + const types::subdomain_id_t subdomain_id; }; } @@ -245,7 +245,7 @@ namespace IteratorFilters * { * return (static_cast(c->level()) == level); * }; - * @endcode + * @endcode * then * @code * std::bind2nd (std::ptr_fun(&level_equal_to), 3) @@ -257,7 +257,7 @@ namespace IteratorFilters * * Finally, classes can be predicates. The following class is one: * @code - * class Active + * class Active * { * public: * template @@ -272,7 +272,7 @@ namespace IteratorFilters * class SubdomainEqualTo * { * public: - * SubdomainEqualTo (const unsigned int subdomain_id) + * SubdomainEqualTo (const types::subdomain_id_t subdomain_id) * : subdomain_id (subdomain_id) {}; * * template @@ -281,7 +281,7 @@ namespace IteratorFilters * } * * private: - * const unsigned int subdomain_id; + * const types::subdomain_id_t subdomain_id; * }; * @endcode * Objects like SubdomainEqualTo(3) can then be used as predicates. @@ -432,7 +432,7 @@ class FilteredIterator : public BaseIterator * will automatically be advanced * to the first cell that has. */ - template + template FilteredIterator (Predicate p, const BaseIterator &bi); @@ -447,7 +447,7 @@ class FilteredIterator : public BaseIterator * Destructor. */ ~FilteredIterator (); - + /** * Assignment operator. Copy the * iterator value of the @@ -490,7 +490,7 @@ class FilteredIterator : public BaseIterator */ FilteredIterator & set_to_next_positive (const BaseIterator &bi); - + /** * As above, but search for the * previous iterator from @p bi @@ -507,7 +507,7 @@ class FilteredIterator : public BaseIterator */ FilteredIterator & set_to_previous_positive (const BaseIterator &bi); - + /** * Compare for equality of the * underlying iterator values of @@ -559,7 +559,7 @@ class FilteredIterator : public BaseIterator * * We do not compare the * predicates. - */ + */ bool operator < (const FilteredIterator &fi) const; /** @@ -618,7 +618,7 @@ class FilteredIterator : public BaseIterator << "The element " << arg1 << " with which you want to compare or which you want to" << " assign from is invalid since it does not satisfy the predicate."); - + private: /** @@ -705,7 +705,7 @@ class FilteredIterator : public BaseIterator * type of this pointer. */ virtual PredicateBase * clone () const; - + private: /** * Copy of the predicate. @@ -830,7 +830,7 @@ FilteredIterator:: operator = (const BaseIterator &bi) { Assert ((bi.state() != IteratorState::valid) || (*predicate)(bi), - ExcInvalidElement(bi)); + ExcInvalidElement(bi)); BaseIterator::operator = (bi); return *this; } @@ -847,7 +847,7 @@ set_to_next_positive (const BaseIterator &bi) while ((this->state() == IteratorState::valid) && ( ! (*predicate)(*this))) BaseIterator::operator++ (); - + return *this; } @@ -863,7 +863,7 @@ set_to_previous_positive (const BaseIterator &bi) while ((this->state() == IteratorState::valid) && ( ! (*predicate)(*this))) BaseIterator::operator-- (); - + return *this; } @@ -964,7 +964,7 @@ FilteredIterator:: operator ++ (int) { const FilteredIterator old_state = *this; - + if (this->state() == IteratorState::valid) do BaseIterator::operator++ (); @@ -999,7 +999,7 @@ FilteredIterator:: operator -- (int) { const FilteredIterator old_state = *this; - + if (this->state() == IteratorState::valid) do BaseIterator::operator-- (); @@ -1043,7 +1043,7 @@ clone () const -namespace IteratorFilters +namespace IteratorFilters { // ---------------- IteratorFilters::Active --------- @@ -1079,7 +1079,7 @@ namespace IteratorFilters } -// ---------------- IteratorFilters::LevelEqualTo --------- +// ---------------- IteratorFilters::LevelEqualTo --------- inline LevelEqualTo::LevelEqualTo (const unsigned int level) : @@ -1100,7 +1100,7 @@ namespace IteratorFilters // ---------------- IteratorFilters::SubdomainEqualTo --------- inline - SubdomainEqualTo::SubdomainEqualTo (const unsigned int subdomain_id) + SubdomainEqualTo::SubdomainEqualTo (const types::subdomain_id_t subdomain_id) : subdomain_id (subdomain_id) {} @@ -1112,7 +1112,7 @@ namespace IteratorFilters bool SubdomainEqualTo::operator () (const Iterator &i) const { - return (static_cast(i->subdomain_id()) == subdomain_id); + return (i->subdomain_id() == subdomain_id); } } diff --git a/deal.II/deal.II/include/grid/grid_tools.h b/deal.II/deal.II/include/grid/grid_tools.h index c4ef0cc767..606709fc68 100644 --- a/deal.II/deal.II/include/grid/grid_tools.h +++ b/deal.II/deal.II/include/grid/grid_tools.h @@ -68,7 +68,7 @@ class GridTools * Same function, but for 1d. */ static - double diameter (const Triangulation<1> &tria); + double diameter (const Triangulation<1> &tria); /** * Same function, but for 1d, 2sd. */ @@ -95,7 +95,7 @@ class GridTools static double cell_measure (const std::vector > &all_vertices, const unsigned int (&vertex_indices)[GeometryInfo::vertices_per_cell]); - + /** * Remove vertices that are not * referenced by any of the @@ -124,7 +124,7 @@ class GridTools * by that class, so we have to * eliminate unused vertices * beforehand. - * + * * Not implemented for the * codimension one case. */ @@ -133,7 +133,7 @@ class GridTools void delete_unused_vertices (std::vector > &vertices, std::vector > &cells, SubCellData &subcelldata); - + /** * Remove vertices that are duplicated, * due to the input of a structured grid, @@ -164,7 +164,7 @@ class GridTools SubCellData &subcelldata, std::vector &considered_vertices, const double tol=1e-12); - + /** * Transform the vertices of the * given triangulation by @@ -362,7 +362,7 @@ class GridTools * the unit cell. In this case, * even points at a very small * distance outside the unit cell - * are allowed. + * are allowed. * * If a point lies on the * boundary of two or more cells, @@ -379,7 +379,7 @@ class GridTools * outside an actual unit cell, * due to numerical roundoff. * Therefore, the point returned - * by this function should + * by this function should * be projected onto the unit cell, * using GeometryInfo::project_to_unit_cell. * This is not automatically performed @@ -445,7 +445,7 @@ class GridTools template static void get_active_neighbors (const typename Container::active_cell_iterator &cell, - std::vector &active_neighbors); + std::vector &active_neighbors); /** * Produce a sparsity pattern in which @@ -462,7 +462,7 @@ class GridTools static void get_face_connectivity_of_cells (const Triangulation &triangulation, SparsityPattern &connectivity); - + /** * Use the METIS partitioner to generate * a partitioning of the active cells @@ -571,7 +571,7 @@ class GridTools partition_triangulation (const unsigned int n_partitions, const SparsityPattern &cell_connection_graph, Triangulation &triangulation); - + /** * For each active cell, return in the * output array to which subdomain (as @@ -590,16 +590,21 @@ class GridTools template static void get_subdomain_association (const Triangulation &triangulation, - std::vector &subdomain); + std::vector &subdomain); /** * Count how many cells are uniquely * associated with the given @p subdomain * index. * - * This function will generate an - * exception if there are no cells with - * the given @p subdomain index. + * This function may return zero + * if there are no cells with the + * given @p subdomain index. This + * can happen, for example, if + * you try to partition a coarse + * mesh into more partitions (one + * for each processor) than there + * are cells in the mesh. * * This function returns the number of * cells associated with one @@ -612,7 +617,7 @@ class GridTools template static unsigned int count_cells_with_subdomain_association (const Triangulation &triangulation, - const unsigned int subdomain); + const types::subdomain_id_t subdomain); /** * Given two mesh containers @@ -789,7 +794,7 @@ class GridTools typename Triangulation::DistortedCellList fix_up_distorted_child_cells (const typename Triangulation::DistortedCellList &distorted_cells, Triangulation &triangulation); - + /** * Exception */ @@ -849,7 +854,7 @@ void GridTools::transform (const Predicate &predicate, { Assert (triangulation.n_levels() == 1, ExcTriangulationHasBeenRefined()); - + std::vector treated_vertices (triangulation.n_vertices(), false); @@ -880,7 +885,7 @@ std::vector GridTools::get_active_child_cells (const typename DH::cell_iterator& cell) { std::vector child_cells; - + if (cell->has_children()) { for (unsigned int child=0; @@ -900,7 +905,7 @@ GridTools::get_active_child_cells (const typename DH::cell_iterator& cell) } - + #if deal_II_dimension == 1 template @@ -923,7 +928,7 @@ GridTools::get_active_neighbors(const typename Container::active_cell_iterator { while (neighbor_child->has_children()) neighbor_child = neighbor_child->child (n==0 ? 1 : 0); - + Assert (neighbor_child->neighbor(n==0 ? 1 : 0)==cell, ExcInternalError()); } diff --git a/deal.II/deal.II/include/grid/tria.h b/deal.II/deal.II/include/grid/tria.h index 893b6f63ed..0c634137c4 100644 --- a/deal.II/deal.II/include/grid/tria.h +++ b/deal.II/deal.II/include/grid/tria.h @@ -62,6 +62,7 @@ namespace hp } template class MGDoFHandler; + /*------------------------------------------------------------------------*/ /** @@ -1521,22 +1522,22 @@ class Triangulation : public Subscriptor * Reset this triangulation into a * virgin state by deleting all data. * - * Note that this operation is only allowed - * if no subscriptions to this object exist - * any more, such as DoFHandler objects - * using it. + * Note that this operation is only + * allowed if no subscriptions to this + * object exist any more, such as + * DoFHandler objects using it. */ - void clear (); + virtual void clear (); /** * Sets the mesh smoothing to @p * mesh_smoothing. This overrides * the MeshSmoothing given to the - * constructor. It is allow to + * constructor. It is allowed to * call this function only if - * triangulation is empty. + * the triangulation is empty. */ - void set_mesh_smoothing(const MeshSmoothing mesh_smoothing); + virtual void set_mesh_smoothing (const MeshSmoothing mesh_smoothing); /** * If @p dim==spacedim, assign a boundary @@ -1945,13 +1946,13 @@ class Triangulation : public Subscriptor * RefinementListener. Adding * listeners to the * Triangulation allows other - * classes to be informed, when + * classes to be informed when * the Triangulation is refined. */ void add_refinement_listener (RefinementListener &listener) const; /** - * Removes a + * Remove a * RefinementListener. When some * class needs no longer to be * informed about refinements, @@ -2295,8 +2296,9 @@ class Triangulation : public Subscriptor * Iterator to the first active * cell on level @p level. * - * This function calls @p begin_active_line - * in 1D and @p begin_active_quad in 2D. + * This function calls @p + * begin_active_line in 1D and @p + * begin_active_quad in 2D. */ active_cell_iterator begin_active(const unsigned int level = 0) const; @@ -2346,8 +2348,9 @@ class Triangulation : public Subscriptor raw_cell_iterator last_raw () const; /** - * Return an iterator pointing to the last - * cell of the level @p level, used or not. + * Return an iterator pointing to the + * last cell of the level @p level, used + * or not. * * This function calls @p last_raw_line * in 1D and @p last_raw_quad in 2D. @@ -2355,8 +2358,8 @@ class Triangulation : public Subscriptor raw_cell_iterator last_raw (const unsigned int level) const; /** - * Return an iterator pointing to the last - * used cell. + * Return an iterator pointing to the + * last used cell. * * This function calls @p last_line * in 1D and @p last_quad in 2D. @@ -2364,8 +2367,8 @@ class Triangulation : public Subscriptor cell_iterator last () const; /** - * Return an iterator pointing to the last - * used cell on level @p level. + * Return an iterator pointing to the + * last used cell on level @p level. * * This function calls @p last_line * in 1D and @p last_quad in 2D. @@ -2373,17 +2376,18 @@ class Triangulation : public Subscriptor cell_iterator last (const unsigned int level) const; /** - * Return an iterator pointing to the last - * active cell. + * Return an iterator pointing to the + * last active cell. * - * This function calls @p last_active_line - * in 1D and @p last_active_quad in 2D. + * This function calls @p + * last_active_line in 1D and @p + * last_active_quad in 2D. */ active_cell_iterator last_active () const; /** - * Return an iterator pointing to the last - * active cell on level @p level. + * Return an iterator pointing to the + * last active cell on level @p level. * * This function calls @p last_active_line * in 1D and @p last_active_quad in 2D. @@ -2420,8 +2424,9 @@ class Triangulation : public Subscriptor * Iterator to the first active * face. * - * This function calls @p begin_active_line - * in 2D and @p begin_active_quad in 3D. + * This function calls @p + * begin_active_line in 2D and @p + * begin_active_quad in 3D. */ active_face_iterator begin_active_face() const; @@ -2437,16 +2442,18 @@ class Triangulation : public Subscriptor raw_face_iterator end_face () const; /** - * Return a raw iterator which is past the end. - * This is the same as end() and is - * only for combatibility with older versions. + * Return a raw iterator which is past + * the end. This is the same as + * end() and is only for + * combatibility with older versions. */ raw_face_iterator end_raw_face () const; /** - * Return an active iterator which is past the end. - * This is the same as end() and is - * only for combatibility with older versions. + * Return an active iterator which is + * past the end. This is the same as + * end() and is only for + * combatibility with older versions. */ active_face_iterator end_active_face () const; @@ -2460,20 +2467,21 @@ class Triangulation : public Subscriptor raw_face_iterator last_raw_face () const; /** - * Return an iterator pointing to the last - * used face. + * Return an iterator pointing to the + * last used face. * - * This function calls @p last_line - * in 2D and @p last_quad in 3D. + * This function calls @p last_line in + * 2D and @p last_quad in 3D. */ face_iterator last_face () const; /** - * Return an iterator pointing to the last - * active face. + * Return an iterator pointing to the + * last active face. * - * This function calls @p last_active_line - * in 2D and @p last_active_quad in 3D. + * This function calls @p + * last_active_line in 2D and @p + * last_active_quad in 3D. */ active_face_iterator last_active_face () const; @@ -2487,8 +2495,8 @@ class Triangulation : public Subscriptor */ /*@{*/ /** - * Iterator to the first line, used - * or not, on level @p level. If a level + * Iterator to the first line, used or + * not, on level @p level. If a level * has no lines, a past-the-end iterator * is returned. * If lines are no cells, i.e. for @p dim>1 @@ -2531,18 +2539,18 @@ class Triangulation : public Subscriptor line_iterator end_line (const unsigned int level) const; /** - * Return a raw iterator which is the first - * iterator not on level. If @p level is - * the last level, then this returns - * end(). + * Return a raw iterator which is the + * first iterator not on level. If @p + * level is the last level, then this + * returns end(). */ raw_line_iterator end_raw_line (const unsigned int level) const; /** * Return an active iterator which is the - * first iterator not on level. If @p level - * is the last level, then this returns - * end(). + * first iterator not on level. If @p + * level is the last level, then this + * returns end(). */ active_line_iterator end_active_line (const unsigned int level) const; @@ -2554,36 +2562,37 @@ class Triangulation : public Subscriptor last_raw_line () const; /** - * Return an iterator pointing to the last - * line of the level @p level, used or not. + * Return an iterator pointing to the + * last line of the level @p level, used + * or not. */ raw_line_iterator last_raw_line (const unsigned int level) const; /** - * Return an iterator pointing to the last - * used line. + * Return an iterator pointing to the + * last used line. */ line_iterator last_line () const; /** - * Return an iterator pointing to the last - * used line on level @p level. + * Return an iterator pointing to the + * last used line on level @p level. */ line_iterator last_line (const unsigned int level) const; /** - * Return an iterator pointing to the last - * active line. + * Return an iterator pointing to the + * last active line. */ active_line_iterator last_active_line () const; /** - * Return an iterator pointing to the last - * active line on level @p level. + * Return an iterator pointing to the + * last active line on level @p level. */ active_line_iterator last_active_line (const unsigned int level) const; @@ -2597,12 +2606,12 @@ class Triangulation : public Subscriptor /*@{ */ /** - * Iterator to the first quad, used - * or not, on the given level. If a level + * Iterator to the first quad, used or + * not, on the given level. If a level * has no quads, a past-the-end iterator - * is returned. - * If quads are no cells, i.e. for $dim>2$ - * no level argument must be given. + * is returned. If quads are no cells, + * i.e. for $dim>2$ no level argument + * must be given. */ raw_quad_iterator @@ -2639,18 +2648,18 @@ class Triangulation : public Subscriptor quad_iterator end_quad (const unsigned int level) const; /** - * Return a raw iterator which is the first - * iterator not on level. If @p level is - * the last level, then this returns - * end(). + * Return a raw iterator which is the + * first iterator not on level. If @p + * level is the last level, then this + * returns end(). */ raw_quad_iterator end_raw_quad (const unsigned int level) const; /** * Return an active iterator which is the - * first iterator not on level. If @p level - * is the last level, then this returns - * end(). + * first iterator not on level. If @p + * level is the last level, then this + * returns end(). */ active_quad_iterator end_active_quad (const unsigned int level) const; @@ -2662,37 +2671,38 @@ class Triangulation : public Subscriptor last_raw_quad () const; /** - * Return an iterator pointing to the last - * quad of the level @p level, used or not. + * Return an iterator pointing to the + * last quad of the level @p level, used + * or not. */ raw_quad_iterator last_raw_quad (const unsigned int level) const; /** - * Return an iterator pointing to the last - * used quad. + * Return an iterator pointing to the + * last used quad. */ quad_iterator last_quad () const; /** - * Return an iterator pointing to the last - * used quad on level @p level. + * Return an iterator pointing to the + * last used quad on level @p level. */ quad_iterator last_quad (const unsigned int level) const; /** - * Return an iterator pointing to the last - * active quad. + * Return an iterator pointing to the + * last active quad. */ active_quad_iterator last_active_quad () const; /** - * Return an iterator pointing to the last - * active quad on level @p level. + * Return an iterator pointing to the + * last active quad on level @p level. */ active_quad_iterator last_active_quad (const unsigned int level) const; @@ -2755,9 +2765,9 @@ class Triangulation : public Subscriptor /** * Return an active iterator which is the - * first iterator not on level. If @p level - * is the last level, then this returns - * end(). + * first iterator not on level. If @p + * level is the last level, then this + * returns end(). */ active_hex_iterator end_active_hex (const unsigned int level) const; @@ -2769,37 +2779,38 @@ class Triangulation : public Subscriptor last_raw_hex () const; /** - * Return an iterator pointing to the last - * hex of the level @p level, used or not. + * Return an iterator pointing to the + * last hex of the level @p level, used + * or not. */ raw_hex_iterator last_raw_hex (const unsigned int level) const; /** - * Return an iterator pointing to the last - * used hex. + * Return an iterator pointing to the + * last used hex. */ hex_iterator last_hex () const; /** - * Return an iterator pointing to the last - * used hex on level @p level. + * Return an iterator pointing to the + * last used hex on level @p level. */ hex_iterator last_hex (const unsigned int level) const; /** - * Return an iterator pointing to the last - * active hex. + * Return an iterator pointing to the + * last active hex. */ active_hex_iterator last_active_hex () const; /** - * Return an iterator pointing to the last - * active hex on level @p level. + * Return an iterator pointing to the + * last active hex on level @p level. */ active_hex_iterator last_active_hex (const unsigned int level) const; @@ -2913,26 +2924,28 @@ class Triangulation : public Subscriptor unsigned int n_raw_hexs (const unsigned int level) const; /** - * Return total number of used hexahedra, - * active or not. + * Return total number of used + * hexahedra, active or not. */ unsigned int n_hexs() const; /** - * Return total number of used hexahedra, - * active or not on level @p level. + * Return total number of used + * hexahedra, active or not on level @p + * level. */ unsigned int n_hexs(const unsigned int level) const; /** - * Return total number of active hexahedra, - * active or not. + * Return total number of active + * hexahedra, active or not. */ unsigned int n_active_hexs() const; /** - * Return total number of active hexahedra, - * active or not on level @p level. + * Return total number of active + * hexahedra, active or not on level @p + * level. */ unsigned int n_active_hexs(const unsigned int level) const; @@ -2944,8 +2957,8 @@ class Triangulation : public Subscriptor /** * Return total number of used cells, - * active or not. - * Maps to n_lines() in one space + * active or not. Maps to + * n_lines() in one space * dimension and so on. */ unsigned int n_cells () const; @@ -2953,22 +2966,22 @@ class Triangulation : public Subscriptor /** * Return total number of used cells, * active or not, on level @p level. - * Maps to n_lines(level) in one space - * dimension and so on. + * Maps to n_lines(level) in + * one space dimension and so on. */ unsigned int n_cells (const unsigned int level) const; /** * Return total number of active cells. - * Maps to n_active_lines() in one space - * dimension and so on. + * Maps to n_active_lines() in + * one space dimension and so on. */ unsigned int n_active_cells () const; /** - * Return total number of active cells - * on level @p level. - * Maps to n_active_lines(level) in one + * Return total number of active cells on + * level @p level. Maps to + * n_active_lines(level) in one * space dimension and so on. */ unsigned int n_active_cells (const unsigned int level) const; @@ -3159,6 +3172,13 @@ class Triangulation : public Subscriptor << ", but this level is empty."); /*@}*/ protected: + /** + * Do some smoothing in the process + * of refining the triangulation. See + * the general doc of this class for + * more information about this. + */ + MeshSmoothing smooth_grid; /** * Write a bool vector to the given stream, @@ -3202,16 +3222,13 @@ class Triangulation : public Subscriptor std::istream &in); private: - /** - * The (public) clear() will only - * work when the triangulation is - * not subscriped to by other - * users. The - * clear_despite_subscriptions() - * function now allows the - * triangulation being cleared - * even when there are + * The (public) function clear() will + * only work when the triangulation is + * not subscribed to by other users. The + * clear_despite_subscriptions() function + * now allows the triangulation being + * cleared even when there are * subscriptions. * * Make sure, you know what you @@ -3327,14 +3344,7 @@ class Triangulation : public Subscriptor */ bool anisotropic_refinement; - /** - * Do some smoothing in the process - * of refining the triangulation. See - * the general doc of this class for - * more information about this. - */ - MeshSmoothing smooth_grid; - + /** * A flag that determines whether * we are to check for distorted diff --git a/deal.II/deal.II/include/grid/tria_accessor.h b/deal.II/deal.II/include/grid/tria_accessor.h index aa91f89254..9a60c48f80 100644 --- a/deal.II/deal.II/include/grid/tria_accessor.h +++ b/deal.II/deal.II/include/grid/tria_accessor.h @@ -70,7 +70,7 @@ namespace internal */ type () {} - + /** * Dummy * constructor. Only @@ -298,6 +298,7 @@ class TriaAccessorBase */ static const unsigned int structure_dimension = structdim; + protected: /** * Declare the data type that * this accessor class expects to @@ -708,7 +709,7 @@ class TriaAccessor : public TriaAccessorBase * returned. */ int parent_index () const; - + /** * @name Accessing sub-objects @@ -1317,7 +1318,7 @@ class TriaAccessor : public TriaAccessorBase */ double diameter () const; - /** + /** * Length of an object in the direction * of the given axis, specified in the * local coordinate system. See the @@ -1513,7 +1514,7 @@ class TriaAccessor : public TriaAccessorBase * in the library. */ void clear_refinement_case () const; - + /** * Set the parent of a cell. */ @@ -2187,22 +2188,54 @@ class CellAccessor : public TriaAccessor /** * Return the subdomain id of * this cell. + * + * See the @ref GlossSubdomainId + * "glossary" for more + * information. This function + * should not be called if you + * use a + * parallel::distributed::Triangulation + * object. */ - unsigned int subdomain_id () const; + types::subdomain_id_t subdomain_id () const; /** * Set the subdomain id of this * cell. + * + * See the @ref GlossSubdomainId + * "glossary" for more + * information. This function + * should not be called if you + * use a + * parallel::distributed::Triangulation + * object. + */ + void set_subdomain_id (const types::subdomain_id_t new_subdomain_id) const; + + /** + * Set the subdomain id of this + * cell and all its children (and + * grand-children, and so on) to + * the given value. + * + * See the @ref GlossSubdomainId + * "glossary" for more + * information. This function + * should not be called if you + * use a + * parallel::distributed::Triangulation + * object. */ - void set_subdomain_id (const unsigned int new_subdomain_id) const; - + void recursively_set_subdomain_id (const types::subdomain_id_t new_subdomain_id) const; + /** * Return an iterator to the * parent. */ TriaIterator > parent () const; - + /** * @} */ @@ -2218,9 +2251,71 @@ class CellAccessor : public TriaAccessor * Test whether the cell has children * (this is the criterion for activity * of a cell). + * + * See the @ref GlossActive "glossary" + * for more information. */ bool active () const; + /** + * Return whether this cell + * exists in the global mesh but + * (i) is owned by another + * processor, i.e. has a + * subdomain_id different from + * the one the current processor + * owns and (ii) is adjacent to a + * cell owned by the current + * processor. + * + * This function only makes sense + * if the triangulation used is + * of kind + * parallel::distributed::Triangulation. In + * all other cases, the returned + * value is always false. + * + * See the @ref GlossGhostCell + * "glossary" and the @ref + * distributed module for more + * information. + */ + bool is_ghost () const; + + /** + * Return whether this cell is + * artificial, i.e. it isn't one + * of the cells owned by the + * current processor, and it also + * doesn't border on one. As a + * consequence, it exists in the + * mesh to ensure that each + * processor has all coarse mesh + * cells and that the 2:1 ratio + * of neighboring cells is + * maintained, but it is not one + * of the cells we should work on + * on the current processor. In + * particular, there is no + * guarantee that this cell + * isn't, in fact, further + * refined on one of the other + * processors. + * + * This function only makes sense + * if the triangulation used is + * of kind + * parallel::distributed::Triangulation. In + * all other cases, the returned + * value is always false. + * + * See the @ref + * GlossArtificialCell "glossary" + * and the @ref distributed + * module for more information. + */ + bool is_artificial () const; + /** * Test whether the point @p p * is inside this cell. Points on diff --git a/deal.II/deal.II/include/grid/tria_accessor.templates.h b/deal.II/deal.II/include/grid/tria_accessor.templates.h index 69e83a4bc2..3665478044 100644 --- a/deal.II/deal.II/include/grid/tria_accessor.templates.h +++ b/deal.II/deal.II/include/grid/tria_accessor.templates.h @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -22,12 +23,13 @@ #include #include #include -#include +#include #include DEAL_II_NAMESPACE_OPEN + /*------------------------ Functions: TriaAccessorBase ---------------------------*/ template @@ -42,7 +44,7 @@ TriaAccessorBase::TriaAccessorBase ( present_index (index), tria (tria) { - + // non-cells have no level, so a 0 // should have been passed, or a -1 // for an end-iterator, or -2 for @@ -74,7 +76,7 @@ TriaAccessorBase::copy_from (const TriaAccessorBase::operator= (const TriaAccessorBase::operator -- () if (structdim != dim) { - if (this->present_index < 0) + if (this->present_index < 0) this->present_index = -1; } else { - while (this->present_index < 0) + while (this->present_index < 0) { // no -> go one level down --this->present_level; // lowest level reached? - if (this->present_level == -1) + if (this->present_level == -1) { // return with past the end pointer this->present_level = this->present_index = -1; @@ -278,7 +280,7 @@ namespace internal { return &faces->quads; } - + inline internal::Triangulation::TriaObjects >* get_objects (internal::Triangulation::TriaFaces<1>*, @@ -287,7 +289,7 @@ namespace internal Assert (false, ExcInternalError()); return 0; } - + inline internal::Triangulation::TriaObjects >* get_objects (internal::Triangulation::TriaFaces<2>*, @@ -296,7 +298,7 @@ namespace internal Assert (false, ExcInternalError()); return 0; } - + inline internal::Triangulation::TriaObjects >* get_objects (internal::Triangulation::TriaFaces<3>*, @@ -305,7 +307,7 @@ namespace internal Assert (false, ExcInternalError()); return 0; } - + /** * This function should never be * used, but we need it for the @@ -501,7 +503,7 @@ namespace internal // enclosing namespace // internal::TriaAccessor using dealii::TriaAccessor; - + /** * A class with the same purpose as the similarly named class of the * Triangulation class. See there for more information. @@ -536,7 +538,7 @@ namespace internal return accessor.objects().cells[accessor.present_index].face(i); } - + template static unsigned int @@ -572,7 +574,7 @@ namespace internal const unsigned int quad_index=lookup_table[i][0]; const unsigned int std_line_index=lookup_table[i][1]; - + const unsigned int line_index=GeometryInfo::standard_to_real_face_line( std_line_index, accessor.face_orientation(quad_index), @@ -602,7 +604,7 @@ namespace internal return numbers::invalid_unsigned_int; } - + template static unsigned int @@ -630,7 +632,7 @@ namespace internal { return true; } - + template static @@ -657,7 +659,7 @@ namespace internal { return true; } - + template static @@ -671,7 +673,7 @@ namespace internal < accessor.tria->levels[accessor.present_level] ->cells.face_flips.size(), ExcInternalError()); - + return (accessor.tria->levels[accessor.present_level] ->cells.face_flips[accessor.present_index * GeometryInfo<3>::faces_per_cell @@ -693,7 +695,7 @@ namespace internal { return true; } - + template static @@ -707,7 +709,7 @@ namespace internal < accessor.tria->levels[accessor.present_level] ->cells.face_rotations.size(), ExcInternalError()); - + return (accessor.tria->levels[accessor.present_level] ->cells.face_rotations[accessor.present_index * GeometryInfo<3>::faces_per_cell @@ -727,7 +729,7 @@ namespace internal { return true; } - + template static @@ -753,7 +755,7 @@ namespace internal //TODO: why is this face_orientation, not line_orientation as in the setter function? return accessor.tria->faces->quads.face_orientation(accessor.present_index, line); } - + template static @@ -791,13 +793,13 @@ namespace internal const unsigned int quad_index=lookup_table[line][0]; const unsigned int std_line_index=lookup_table[line][1]; - + const unsigned int line_index=GeometryInfo::standard_to_real_face_line( std_line_index, accessor.face_orientation(quad_index), accessor.face_flip(quad_index), accessor.face_rotation(quad_index)); - + // now we got to the correct line and ask // the quad for its line_orientation. however, if // the face is rotated, it might be possible, @@ -831,8 +833,8 @@ namespace internal { false, false }}, { { true, false }, { false, true }}}}; - - + + return (accessor.quad(quad_index) ->line_orientation(line_index) == bool_table[std_line_index/2] @@ -857,7 +859,7 @@ namespace internal { Assert (false, ExcInternalError()); } - + template static @@ -896,7 +898,7 @@ namespace internal { Assert (false, ExcInternalError()); } - + template static @@ -911,7 +913,7 @@ namespace internal < accessor.tria->levels[accessor.present_level] ->cells.face_flips.size(), ExcInternalError()); - + accessor.tria->levels[accessor.present_level] ->cells.face_flips[accessor.present_index * GeometryInfo<3>::faces_per_cell @@ -934,7 +936,7 @@ namespace internal { Assert (false, ExcInternalError()); } - + template static @@ -949,7 +951,7 @@ namespace internal < accessor.tria->levels[accessor.present_level] ->cells.face_rotations.size(), ExcInternalError()); - + accessor.tria->levels[accessor.present_level] ->cells.face_rotations[accessor.present_index * GeometryInfo<3>::faces_per_cell @@ -970,7 +972,7 @@ namespace internal { Assert (false, ExcInternalError()); } - + template static @@ -1006,7 +1008,7 @@ namespace internal + line] = value; } - + template static @@ -1049,9 +1051,9 @@ namespace internal // // second index: vertex index to be switched // (or not) - + static const unsigned int switch_table[2][2]={{1,0},{0,1}}; - + return accessor.line(corner%2) ->vertex_index(switch_table[accessor.line_orientation(corner%2)][corner/2]); } @@ -1104,7 +1106,7 @@ TriaAccessor (const Triangulation *parent, template inline bool -TriaAccessor::used () const +TriaAccessor::used () const { Assert (this->state() == IteratorState::valid, TriaAccessorExceptions::ExcDereferenceInvalidObject()); @@ -1120,7 +1122,7 @@ vertex_index (const unsigned int corner) const { Assert (corner::vertices_per_cell, ExcIndexRange(corner,0,GeometryInfo::vertices_per_cell)); - + return internal::TriaAccessor::Implementation::vertex_index (*this, corner); } @@ -1189,7 +1191,7 @@ bool TriaAccessor::face_orientation (const unsigned int face) const { Assert (used(), TriaAccessorExceptions::ExcCellNotUsed()); - + return internal::TriaAccessor::Implementation::face_orientation (*this, face); } @@ -1201,7 +1203,7 @@ bool TriaAccessor::face_flip (const unsigned int face) const { Assert (used(), TriaAccessorExceptions::ExcCellNotUsed()); - + return internal::TriaAccessor::Implementation::face_flip (*this, face); } @@ -1239,7 +1241,7 @@ TriaAccessor::set_face_orientation (const unsigned int f const bool value) const { Assert (used(), TriaAccessorExceptions::ExcCellNotUsed()); - + internal::TriaAccessor::Implementation::set_face_orientation (*this, face, value); } @@ -1252,7 +1254,7 @@ TriaAccessor::set_face_flip (const unsigned int face, const bool value) const { Assert (used(), TriaAccessorExceptions::ExcCellNotUsed()); - + internal::TriaAccessor::Implementation::set_face_flip (*this, face, value); } @@ -1327,7 +1329,7 @@ child_index (const unsigned int i) const Assert (has_children(), TriaAccessorExceptions::ExcCellHasNoChildren()); Assert (i::no_refinement, TriaAccessorExceptions::ExcCellHasNoChildren()); - + if (this_refinement_case == RefinementCase<2>::cut_xy) return child_index(i); else if ((this_refinement_case == RefinementCase<2>::cut_x) @@ -1462,7 +1464,7 @@ isotropic_child (const unsigned int i) const Assert (this_refinement_case != RefinementCase<2>::no_refinement, TriaAccessorExceptions::ExcCellHasNoChildren()); - + if (this_refinement_case == RefinementCase<2>::cut_xy) return child(i); else if ((this_refinement_case == RefinementCase<2>::cut_x) @@ -1475,7 +1477,7 @@ isotropic_child (const unsigned int i) const return child(i/2)->child(i%2); else Assert(false, - ExcMessage("This cell has no grandchildren equivalent to isotropic refinement")); + ExcMessage("This cell has no grandchildren equivalent to isotropic refinement")); } default: @@ -1564,14 +1566,14 @@ TriaAccessor::set_children (const unsigned int i, // each set of two children are stored // consecutively, so we only have to find // the location of the set of children - const unsigned int n_sets_of_two = GeometryInfo::max_children_per_cell/2; + const unsigned int n_sets_of_two = GeometryInfo::max_children_per_cell/2; Assert ((index==-1) || (i==0 && !this->has_children() && (index>=0)) || (i>0 && this->has_children() && (index>=0) && this->objects().children[n_sets_of_two*this->present_index+i/2] == -1), TriaAccessorExceptions::ExcCantSetChildren(index)); - + this->objects().children[n_sets_of_two*this->present_index+i/2] = index; } @@ -1618,7 +1620,7 @@ TriaAccessor::user_flag_set () const template inline void -TriaAccessor::set_user_flag () const +TriaAccessor::set_user_flag () const { Assert (this->used(), TriaAccessorExceptions::ExcCellNotUsed()); this->objects().user_flags[this->present_index] = true; @@ -1634,7 +1636,7 @@ TriaAccessor::clear_user_flag () const Assert (this->used(), TriaAccessorExceptions::ExcCellNotUsed()); this->objects().user_flags[this->present_index] = false; } - + template @@ -1988,7 +1990,7 @@ CellAccessor (const CellAccessor &cell_accessor) template inline TriaIterator > -CellAccessor::face (const unsigned int i) const +CellAccessor::face (const unsigned int i) const { switch (dim) { @@ -2014,7 +2016,7 @@ CellAccessor::face (const unsigned int i) const template inline unsigned int -CellAccessor::face_index (const unsigned int i) const +CellAccessor::face_index (const unsigned int i) const { switch (dim) { @@ -2026,10 +2028,10 @@ CellAccessor::face_index (const unsigned int i) const case 2: return this->line_index(i); - + case 3: return this->quad_index(i); - + default: return numbers::invalid_unsigned_int; } @@ -2040,7 +2042,7 @@ CellAccessor::face_index (const unsigned int i) const template inline int -CellAccessor::neighbor_index (const unsigned int i) const +CellAccessor::neighbor_index (const unsigned int i) const { AssertIndexRange (i,GeometryInfo::faces_per_cell); return this->tria->levels[this->present_level]-> @@ -2087,7 +2089,7 @@ CellAccessor::set_refine_flag (const RefinementCase refinemen Assert (this->used() && this->active(), ExcRefineCellNotActive()); Assert (!coarsen_flag_set(), ExcCellFlaggedForCoarsening()); - + this->tria->levels[this->present_level]->refine_flags[this->present_index] = refinement_case; } @@ -2116,13 +2118,13 @@ CellAccessor::flag_for_face_refinement (const unsigned int face_no ExcIndexRange(face_no,0,GeometryInfo::faces_per_cell)); Assert (face_refinement_case < RefinementCase::isotropic_refinement+1, ExcIndexRange(face_refinement_case,0,RefinementCase::isotropic_refinement+1)); - + // the new refinement case is a combination // of the minimum required one for the given // face refinement and the already existing // flagged refinement case RefinementCase old_ref_case = refine_flag_set(); - RefinementCase + RefinementCase new_ref_case = (old_ref_case | GeometryInfo::min_cell_refinement_case_for_face_refinement(face_refinement_case, face_no, @@ -2225,7 +2227,7 @@ CellAccessor<3>::subface_case(const unsigned int face_no) const { Assert(face(face_no)->child(1)->refinement_case()==RefinementCase<2>::cut_y, ExcInternalError()); - return internal::SubfaceCase<3>::case_x1y2y; + return internal::SubfaceCase<3>::case_x1y2y; } else return internal::SubfaceCase<3>::case_x1y; @@ -2251,7 +2253,7 @@ CellAccessor<3>::subface_case(const unsigned int face_no) const { Assert(face(face_no)->child(1)->refinement_case()==RefinementCase<2>::cut_x, ExcInternalError()); - return internal::SubfaceCase<3>::case_y1x2x; + return internal::SubfaceCase<3>::case_y1x2x; } else return internal::SubfaceCase<3>::case_y1x; @@ -2305,7 +2307,7 @@ CellAccessor::set_coarsen_flag () const { Assert (this->used() && this->active(), ExcRefineCellNotActive()); Assert (!refine_flag_set(), ExcCellFlaggedForRefinement()); - + this->tria->levels[this->present_level]->coarsen_flags[this->present_index] = true; } @@ -2364,6 +2366,44 @@ CellAccessor::active () const +template +inline +bool +CellAccessor::is_ghost () const +{ +#ifndef DEAL_II_USE_P4EST + return false; +#else + const types::subdomain_id_t subdomain = this->subdomain_id(); + if (subdomain == types::artificial_subdomain_id) + return false; + + const parallel::distributed::Triangulation *pdt + = dynamic_cast *>(this->tria); + + if (pdt == 0) + return false; + else + return (subdomain != pdt->locally_owned_subdomain()); +#endif +} + + + +template +inline +bool +CellAccessor::is_artificial () const +{ +#ifndef DEAL_II_USE_P4EST + return false; +#else + return (this->subdomain_id() == types::artificial_subdomain_id); +#endif +} + + + template inline unsigned int @@ -2378,7 +2418,7 @@ CellAccessor::neighbor_face_no (const unsigned int neighbor) const // return this value as the // neighbor is not coarser return n2; - else + else // the neighbor is coarser return neighbor_of_coarser_neighbor(neighbor).first; } diff --git a/deal.II/deal.II/include/grid/tria_levels.h b/deal.II/deal.II/include/grid/tria_levels.h index 9974135839..6f2f0dbd06 100644 --- a/deal.II/deal.II/include/grid/tria_levels.h +++ b/deal.II/deal.II/include/grid/tria_levels.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2010 by the deal.II authors +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -75,7 +75,7 @@ namespace internal * coarsened. */ std::vector coarsen_flags; - + /** * Levels and indices of the neighbors * of the cells. Convention is, that the @@ -137,15 +137,15 @@ namespace internal * cells with a given subdomain * number. */ - std::vector subdomain_ids; - + std::vector subdomain_ids; + /** * One integer for every consecutive * pair of cells to store which * index their parent has. */ std::vector parents; - + /** * Reserve enough space to accomodate * @p total_cells cells on this level. @@ -180,7 +180,7 @@ namespace internal * of this object. */ unsigned int memory_consumption () const; - + /** * The object containing the data on lines and * related functions @@ -204,9 +204,9 @@ namespace internal << "The containers have sizes " << arg1 << " and " << arg2 << ", which is not as expected."); }; - + //TODO: Replace TriaObjectsHex to avoid this specialization - + /** * Specialization of TriaLevels for 3D. Since we need TriaObjectsHex * instead of TriaObjects. Refer to the documentation of the template @@ -220,7 +220,7 @@ namespace internal std::vector coarsen_flags; std::vector > neighbors; std::vector parents; - std::vector subdomain_ids; + std::vector subdomain_ids; void reserve_space (const unsigned int total_cells, const unsigned int dimension); void monitor_memory (const unsigned int true_dimension) const; @@ -244,8 +244,8 @@ namespace internal << "The containers have sizes " << arg1 << " and " << arg2 << ", which is not as expected."); }; - - + + } } diff --git a/deal.II/deal.II/include/hp/dof_handler.h b/deal.II/deal.II/include/hp/dof_handler.h index b45ac89836..b4264ae56e 100644 --- a/deal.II/deal.II/include/hp/dof_handler.h +++ b/deal.II/deal.II/include/hp/dof_handler.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -800,12 +801,37 @@ namespace hp /*---------------------------------------*/ - /** - * Return number of degrees of freedom. - * Included in this number are those - * DoFs which are constrained by - * hanging nodes. - */ + /** + * Return the global number of + * degrees of freedom. If the + * current object handles all + * degrees of freedom itself + * (even if you may intend to + * solve your linear system in + * parallel, such as in step-17 + * or step-18), then this number + * equals the number of locally + * owned degrees of freedom since + * this object doesn't know + * anything about what you want + * to do with it and believes + * that it owns every degree of + * freedom it knows about. + * + * On the other hand, if this + * object operates on a + * parallel::distributed::Triangulation + * object, then this function + * returns the global number of + * degrees of freedom, + * accumulated over all + * processors. + * + * In either case, included in + * the returned number are those + * DoFs which are constrained by + * hanging nodes, see @ref constraints. + */ unsigned int n_dofs () const; /** @@ -841,6 +867,97 @@ namespace hp unsigned int n_boundary_dofs (const std::set &boundary_indicators) const; + /** + * Return the number of + * degrees of freedom that + * belong to this + * process. + * + * If this is a sequential job, + * then the result equals that + * produced by n_dofs(). On the + * other hand, if we are + * operating on a + * parallel::distributed::Triangulation, + * then it includes only the + * degrees of freedom that the + * current processor owns. Note + * that in this case this does + * not include all degrees of + * freedom that have been + * distributed on the current + * processor's image of the mesh: + * in particular, some of the + * degrees of freedom on the + * interface between the cells + * owned by this processor and + * cells owned by other + * processors may be theirs, and + * degrees of freedom on ghost + * cells are also not necessarily + * included. + */ + unsigned int n_locally_owned_dofs() const; + + /** + * Return an IndexSet describing + * the set of locally owned DoFs + * as a subset of + * 0..n_dofs(). The number of + * elements of this set equals + * n_locally_owned_dofs(). + */ + const IndexSet & locally_owned_dofs() const; + + + /** + * Returns a vector that + * stores the locally owned + * DoFs of each processor. If + * you are only interested in + * the number of elements + * each processor owns then + * n_dofs_per_processor() is + * a better choice. + * + * If this is a sequential job, + * then the vector has a single + * element that equals the + * IndexSet representing the + * entire range [0,n_dofs()]. + */ + const std::vector & + locally_owned_dofs_per_processor () const; + + /** + * Return a vector that + * stores the number of + * degrees of freedom each + * processor that + * participates in this + * triangulation owns + * locally. The sum of all + * these numbers equals the + * number of degrees of + * freedom that exist + * globally, i.e. what + * n_dofs() returns. + * + * Each element of the vector + * returned by this function + * equals the number of + * elements of the + * corresponding sets + * returned by + * global_dof_indices(). + * + * If this is a sequential job, + * then the vector has a single + * element equal to n_dofs(). + */ + const std::vector & + n_locally_owned_dofs_per_processor () const; + /** * Return a constant reference to * the set of finite element @@ -1100,12 +1217,18 @@ namespace hp */ internal::hp::DoFFaces * faces; - - /** - * Store the number of dofs - * created last time. - */ - unsigned int used_dofs; + /** + * A structure that contains all + * sorts of numbers that + * characterize the degrees of + * freedom this object works on. + * + * For most members of this + * structure, there is an + * accessor function in this + * class that returns its value. + */ + internal::DoFHandler::NumberCache number_cache; /** * Array to store the indices @@ -1200,15 +1323,44 @@ namespace hp /* ----------------------- Inline functions ---------------------------------- */ - template + template inline unsigned int DoFHandler::n_dofs () const { - Assert (finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - return used_dofs; + return number_cache.n_global_dofs; + } + + + template + unsigned int + DoFHandler::n_locally_owned_dofs() const + { + return number_cache.n_locally_owned_dofs; + } + + + template + const IndexSet & + DoFHandler::locally_owned_dofs() const + { + return number_cache.locally_owned_dofs; + } + + + template + const std::vector & + DoFHandler::n_locally_owned_dofs_per_processor() const + { + return number_cache.n_locally_owned_dofs_per_processor; + } + + + template + const std::vector & + DoFHandler::locally_owned_dofs_per_processor () const + { + return number_cache.locally_owned_dofs_per_processor; } diff --git a/deal.II/deal.II/include/numerics/data_out.h b/deal.II/deal.II/include/numerics/data_out.h index dffd98ea68..c0e627abbd 100644 --- a/deal.II/deal.II/include/numerics/data_out.h +++ b/deal.II/deal.II/include/numerics/data_out.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -79,7 +79,7 @@ namespace internal const unsigned int n_q_points, const std::vector &n_postprocessor_outputs, const FE &finite_elements); - + const unsigned int n_components; const unsigned int n_datasets; const unsigned int n_subdivisions; @@ -92,7 +92,7 @@ namespace internal std::vector > > patch_hessians_system; std::vector > > postprocessed_values; - const dealii::hp::FECollection fe_collection; + const dealii::hp::FECollection fe_collection; }; @@ -223,7 +223,7 @@ namespace internal * if () ... else ... clauses yourself), and also functions * and classes offering ways to control the appearance of the output * by setting flags for each output format. - * + * * *

Information for derived classes

* @@ -316,19 +316,19 @@ class DataOut_DoFData : public DataOutInterface * associated to degrees of freedom */ type_dof_data, - + /** * Data vector entries are one per * grid cell */ type_cell_data, - + /** * Find out automatically */ type_automatic }; - + /** * Constructor */ @@ -620,7 +620,7 @@ class DataOut_DoFData : public DataOutInterface template void merge_patches (const DataOut_DoFData &source, const Point &shift = Point()); - + /** * Release the pointers to the * data vectors and the DoF @@ -706,7 +706,7 @@ class DataOut_DoFData : public DataOutInterface << "components. However, the vector component at\n" << "position " << arg1 << " with name <" << arg2 << "> does not satisfy these conditions."); - + protected: /** * For each vector that has been added @@ -753,12 +753,12 @@ class DataOut_DoFData : public DataOutInterface * be aquired from the postprocessor. */ DataEntryBase (const DataPostprocessor *data_postprocessor); - + /** * Destructor made virtual. */ virtual ~DataEntryBase (); - + /** * Assuming that the stored vector is * a cell vector, extract the given @@ -778,7 +778,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_values (const FEValuesBase &fe_patch_values, std::vector &patch_values) const = 0; - + /** * Given a FEValuesBase object, * extract the values on the present @@ -802,7 +802,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_gradients (const FEValuesBase &fe_patch_values, std::vector > &patch_gradients) const = 0; - + /** * Given a FEValuesBase object, * extract the gradients on the present @@ -826,7 +826,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_hessians (const FEValuesBase &fe_patch_values, std::vector > &patch_hessians) const = 0; - + /** * Given a FEValuesBase object, extract * the second derivatives on the @@ -845,7 +845,7 @@ class DataOut_DoFData : public DataOutInterface * vectors. */ virtual void clear () = 0; - + /** * Determine an estimate for * the memory consumption (in @@ -869,7 +869,7 @@ class DataOut_DoFData : public DataOutInterface */ const std::vector data_component_interpretation; - + /** * Pointer to a DataPostprocessing * object which shall be applied to @@ -945,7 +945,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_values (const FEValuesBase &fe_patch_values, std::vector &patch_values) const; - + /** * Given a FEValuesBase object, * extract the values on the present @@ -969,7 +969,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_gradients (const FEValuesBase &fe_patch_values, std::vector > &patch_gradients) const; - + /** * Given a FEValuesBase object, * extract the gradients on the present @@ -993,7 +993,7 @@ class DataOut_DoFData : public DataOutInterface void get_function_hessians (const FEValuesBase &fe_patch_values, std::vector > &patch_hessians) const; - + /** * Given a FEValuesBase object, extract * the second derivatives on the @@ -1012,7 +1012,7 @@ class DataOut_DoFData : public DataOutInterface * vectors. */ virtual void clear (); - + /** * Determine an estimate for * the memory consumption (in @@ -1063,7 +1063,7 @@ class DataOut_DoFData : public DataOutInterface * classes. */ std::vector patches; - + /** * Function by which the base * class's functions get to know @@ -1091,7 +1091,7 @@ class DataOut_DoFData : public DataOutInterface virtual std::vector > get_vector_data_ranges () const; - + /** * Make all template siblings * friends. Needed for the @@ -1139,20 +1139,21 @@ class DataOut_DoFData : public DataOutInterface * * The only thing this class offers is the function build_patches() which * loops over all cells of the triangulation stored by the - * attach_dof_handler() function of the base class and convert the data on - * these to actual patches which are the objects that are later output by the - * functions of the base classes. You can give a parameter to the function - * which determines how many subdivisions in each coordinate direction are to - * be performed, i.e. of how many subcells each patch shall consist. Default - * is one, but you may want to choose a higher number for higher order - * elements, so as two for quadratic elements, three for cubic elements three, - * and so on. The purpose of this parameter is because most graphics programs - * do not allow to specify higher order shape functions in the file formats: - * only data at vertices can be plotted and is then shown as a bilinear - * interpolation within the interior of cells. This may be insufficient if you - * have higher order finite elements, and the only way to achieve better - * output is to subdivide each cell of the mesh into several cells for - * graphical output. + * attach_dof_handler() function of the base class (with the exception of + * cells of parallel::distributed::Triangulation objects that are not owned by + * the current processor) and converts the data on these to actual patches + * which are the objects that are later output by the functions of the base + * classes. You can give a parameter to the function which determines how many + * subdivisions in each coordinate direction are to be performed, i.e. of how + * many subcells each patch shall consist. Default is one, but you may want to + * choose a higher number for higher order elements, so as two for quadratic + * elements, three for cubic elements three, and so on. The purpose of this + * parameter is because most graphics programs do not allow to specify higher + * order shape functions in the file formats: only data at vertices can be + * plotted and is then shown as a bilinear interpolation within the interior + * of cells. This may be insufficient if you have higher order finite + * elements, and the only way to achieve better output is to subdivide each + * cell of the mesh into several cells for graphical output. * * Note that after having called build_patches() once, you can call one or * more of the write() functions of DataOutInterface. You can therefore @@ -1200,12 +1201,12 @@ class DataOut_DoFData : public DataOutInterface * cannot be interpolated to a coarser cell. If you do have cell data and use * this pair of functions and they return a non-active cell, then an exception * will be thrown. - * + * * @ingroup output * @author Wolfgang Bangerth, 1999 */ template > -class DataOut : public DataOut_DoFData +class DataOut : public DataOut_DoFData { public: /** @@ -1227,7 +1228,7 @@ class DataOut : public DataOut_DoFData curved_boundary, curved_inner_cells }; - + /** * This is the central function * of this class since it builds @@ -1300,7 +1301,7 @@ class DataOut : public DataOut_DoFData virtual void build_patches (const Mapping &mapping, const unsigned int n_subdivisions = 0, const CurvedCellRegion curved_region = curved_boundary); - + /** * Return the first cell which we * want output for. The default @@ -1310,7 +1311,7 @@ class DataOut : public DataOut_DoFData * cells in a derived class. */ virtual cell_iterator first_cell (); - + /** * Return the next cell after * @p cell which we want output @@ -1341,12 +1342,40 @@ class DataOut : public DataOut_DoFData int, << "The number of subdivisions per patch, " << arg1 << ", is not valid."); - + private: + + /** + * Return the first cell produced + * by the + * first_cell()/next_cell() + * function pair that is locally + * owned. If this object operates + * on a non-distributed + * triangulation, the result + * equals what first_cell() + * returns. + */ + cell_iterator first_locally_owned_cell (); + + /** + * Return the next cell produced + * by the next_cell() function + * that is locally owned. If this + * object operates on a + * non-distributed triangulation, + * the result equals what + * first_cell() returns. + */ + cell_iterator next_locally_owned_cell (const cell_iterator &cell); + /** * Build one patch. This function * is called in a WorkStream * context. + * + * The result is written into the patch + * variable. */ void build_one_patch (const std::pair *cell_and_index, internal::DataOut::ParallelData &data, @@ -1448,7 +1477,7 @@ merge_patches (const DataOut_DoFData &source, ExcMessage ("Both sources need to declare the same components " "as vectors.")); } - + // merge patches. store old number // of elements, since we need to // adjust patch numbers, etc @@ -1463,11 +1492,11 @@ merge_patches (const DataOut_DoFData &source, for (unsigned int i=old_n_patches; i::vertices_per_cell; ++v) patches[i].vertices[v] += shift; - + // adjust patch numbers for (unsigned int i=old_n_patches; i::faces_per_cell; ++n) diff --git a/deal.II/deal.II/include/numerics/error_estimator.h b/deal.II/deal.II/include/numerics/error_estimator.h index 46ae47d84f..9a8bd60395 100644 --- a/deal.II/deal.II/include/numerics/error_estimator.h +++ b/deal.II/deal.II/include/numerics/error_estimator.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008 by the deal.II authors +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -61,7 +61,7 @@ namespace hp * since accuracy is not so important here, and since this can save rather * a lot of memory, when using many cells. * - * + * *

Implementation

* * In principle, the implementation of the error estimation is simple: let @@ -102,7 +102,7 @@ namespace hp * $h$ is taken to be the greatest length of the diagonals of the cell. For * more or less uniform cells without deformed angles, this coincides with * the diameter of the cell. - * + * * *

Vector-valued functions

* @@ -127,7 +127,7 @@ namespace hp * * *

%Boundary values

- * + * * If the face is at the boundary, i.e. there is no neighboring cell to which * the jump in the gradiend could be computed, there are two possibilities: *
    @@ -163,9 +163,9 @@ namespace hp * Thanks go to Franz-Theo Suttmeier for clarifications about boundary * conditions. * - * + * *

    Handling of hanging nodes

    - * + * * The integration along faces with hanging nodes is quite tricky, since one * of the elements has to be shifted one level up or down. See the * documentation for the FESubFaceValues class for more information about @@ -272,7 +272,7 @@ class KellyErrorEstimator * afterwards. This is in particular true * for the case where meshes are very * large and computing indicators for @em - * every cells is too expensive, while + * every cell is too expensive, while * computing indicators for only local * cells is acceptable. Note that if you * only ask for the indicators of a @@ -301,6 +301,29 @@ class KellyErrorEstimator * parameter is retained for * compatibility with old versions of the * library. + * + * @note If the DoFHandler object + * given as an argument to this + * function builds on a + * parallel::distributed::Triangulation, + * this function skips + * computations on all cells that + * are not locally owned. In that + * case, the only valid value for + * the subdomain_id argument + * (besides the invalid value) is + * the subdomain id that is + * associated with the currently + * processor, as reported by + * parallel::distributed::Triangulation::locally_owned_subdomain(). Even + * though nothing is computed on + * cells that we don't locally + * own, the error indicator + * vector must still have a + * length equal to the number of + * active cell in the mesh as + * reported by + * parallel::distributed::Triangulation::n_locally_owned_active_cells(). */ template static void estimate (const Mapping &mapping, @@ -311,15 +334,15 @@ class KellyErrorEstimator Vector &error, const std::vector &component_mask = std::vector(), const Function *coefficients = 0, - const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const unsigned int n_threads = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); - + /** * Calls the @p estimate * function, see above, with * mapping=MappingQ1@(). - */ + */ template static void estimate (const DH &dof, const Quadrature &quadrature, @@ -329,9 +352,9 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); - + /** * Same function as above, but * accepts more than one solution @@ -369,7 +392,7 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); /** @@ -386,7 +409,7 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -406,7 +429,7 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -425,7 +448,7 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -445,7 +468,7 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -464,10 +487,10 @@ class KellyErrorEstimator const std::vector &component_mask = std::vector(), const Function *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); - - + + /** * Exception */ @@ -575,14 +598,14 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); /** * Calls the @p estimate * function, see above, with * mapping=MappingQ1<1>(). - */ + */ template static void estimate (const DH &dof, const Quadrature<0> &quadrature, @@ -592,9 +615,9 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); - + /** * Same function as above, but * accepts more than one solution @@ -632,7 +655,7 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); /** @@ -649,7 +672,7 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -669,7 +692,7 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -688,7 +711,7 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -708,7 +731,7 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); @@ -727,9 +750,9 @@ class KellyErrorEstimator<1,spacedim> const std::vector &component_mask = std::vector(), const Function<1> *coefficients = 0, const unsigned int n_threads = multithread_info.n_default_threads, - const unsigned int subdomain_id = numbers::invalid_unsigned_int, + const types::subdomain_id_t subdomain_id = types::invalid_subdomain_id, const unsigned int material_id = numbers::invalid_unsigned_int); - + /** * Exception */ diff --git a/deal.II/deal.II/include/numerics/vectors.templates.h b/deal.II/deal.II/include/numerics/vectors.templates.h index 842b437a44..e0494237be 100644 --- a/deal.II/deal.II/include/numerics/vectors.templates.h +++ b/deal.II/deal.II/include/numerics/vectors.templates.h @@ -186,6 +186,7 @@ void VectorTools::interpolate (const Mapping fe, support_quadrature, update_quadrature_points); for (; cell!=endc; ++cell) + if (!cell->is_artificial() && !cell->is_ghost()) { const unsigned int fe_index = cell->active_fe_index(); @@ -1237,9 +1238,9 @@ VectorTools::interpolate_boundary_values (const Mapping &component_mask_) { //ConstraintMatrix boundary_constraints(); - // interpolate_boundary_values (dof, boundary_component, boundary_function, + // interpolate_boundary_values (dof, boundary_component, boundary_function, // boundary_constraints, component_mask); - + const unsigned int dim=DH::dimension; const unsigned int spacedim=DH::space_dimension; @@ -1489,197 +1490,198 @@ interpolate_boundary_values (const Mapping typename DH::active_cell_iterator cell = dof.begin_active(), endc = dof.end(); for (; cell!=endc; ++cell) - for (unsigned int face_no = 0; face_no < GeometryInfo::faces_per_cell; - ++face_no) - { - const FiniteElement &fe = cell->get_fe(); - - // we can presently deal only with - // primitive elements for boundary - // values. this does not preclude - // us using non-primitive elements - // in components that we aren't - // interested in, however. make - // sure that all shape functions - // that are non-zero for the - // components we are interested in, - // are in fact primitive - for (unsigned int i=0; iget_fe().dofs_per_cell; ++i) - { - const std::vector &nonzero_component_array - = cell->get_fe().get_nonzero_components (i); - for (unsigned int c=0; cget_fe().is_primitive (i), - ExcMessage ("This function can only deal with requested boundary " - "values that correspond to primitive (scalar) base " - "elements")); - } + if (!cell->is_artificial()) + for (unsigned int face_no = 0; face_no < GeometryInfo::faces_per_cell; + ++face_no) + { + const FiniteElement &fe = cell->get_fe(); + + // we can presently deal only with + // primitive elements for boundary + // values. this does not preclude + // us using non-primitive elements + // in components that we aren't + // interested in, however. make + // sure that all shape functions + // that are non-zero for the + // components we are interested in, + // are in fact primitive + for (unsigned int i=0; iget_fe().dofs_per_cell; ++i) + { + const std::vector &nonzero_component_array + = cell->get_fe().get_nonzero_components (i); + for (unsigned int c=0; cget_fe().is_primitive (i), + ExcMessage ("This function can only deal with requested boundary " + "values that correspond to primitive (scalar) base " + "elements")); + } - typename DH::face_iterator face = cell->face(face_no); - const unsigned char boundary_component = face->boundary_indicator(); - if (function_map.find(boundary_component) != function_map.end()) - { - // face is of the right component - x_fe_values.reinit(cell, face_no); - const FEFaceValues &fe_values = x_fe_values.get_present_fe_values(); - - // get indices, physical location and - // boundary values of dofs on this - // face - face_dofs.resize (fe.dofs_per_face); - face->get_dof_indices (face_dofs, cell->active_fe_index()); - const std::vector > &dof_locations - = fe_values.get_quadrature_points (); + typename DH::face_iterator face = cell->face(face_no); + const unsigned char boundary_component = face->boundary_indicator(); + if (function_map.find(boundary_component) != function_map.end()) + { + // face is of the right component + x_fe_values.reinit(cell, face_no); + const FEFaceValues &fe_values = x_fe_values.get_present_fe_values(); - if (fe_is_system) - { - // resize - // array. avoid - // construction of a - // memory allocating - // temporary if - // possible - if (dof_values_system.size() < fe.dofs_per_face) - dof_values_system.resize (fe.dofs_per_face, - Vector(fe.n_components())); - else - dof_values_system.resize (fe.dofs_per_face); - - function_map.find(boundary_component)->second - ->vector_value_list (dof_locations, dof_values_system); - - // enter those dofs - // into the list that - // match the - // component - // signature. avoid - // the usual - // complication that - // we can't just use - // *_system_to_component_index - // for non-primitive - // FEs - for (unsigned int i=0; iget_dof_indices (face_dofs, cell->active_fe_index()); + const std::vector > &dof_locations + = fe_values.get_quadrature_points (); - if (component_mask[component] == true) - boundary_values[face_dofs[i]] = dof_values_system[i](component); - } - } - else - // fe has only one component, - // so save some computations - { - // get only the one component that - // this function has - dof_values_scalar.resize (fe.dofs_per_face); - function_map.find(boundary_component)->second - ->value_list (dof_locations, dof_values_scalar, 0); + if (fe_is_system) + { + // resize + // array. avoid + // construction of a + // memory allocating + // temporary if + // possible + if (dof_values_system.size() < fe.dofs_per_face) + dof_values_system.resize (fe.dofs_per_face, + Vector(fe.n_components())); + else + dof_values_system.resize (fe.dofs_per_face); + + function_map.find(boundary_component)->second + ->vector_value_list (dof_locations, dof_values_system); + + // enter those dofs + // into the list that + // match the + // component + // signature. avoid + // the usual + // complication that + // we can't just use + // *_system_to_component_index + // for non-primitive + // FEs + for (unsigned int i=0; isecond + ->value_list (dof_locations, dof_values_scalar, 0); - // enter into list + // enter into list - for (unsigned int i=0; i boundary_values; interpolate_boundary_values (mapping, dof, function_map, boundary_values, component_mask_); - std::map::const_iterator boundary_value = + std::map::const_iterator boundary_value = boundary_values.begin(); for ( ; boundary_value !=boundary_values.end(); ++boundary_value) { - if (!constraints.is_constrained(boundary_value->first)) + if (constraints.can_store_line (boundary_value->first) + && + !constraints.is_constrained(boundary_value->first)) { constraints.add_line (boundary_value->first); constraints.set_inhomogeneity (boundary_value->first, @@ -2038,7 +2042,7 @@ VectorTools::project_boundary_values (const Mapping &mappin std::map boundary_values; project_boundary_values (mapping, dof, boundary_functions, q, boundary_values, component_mapping); - std::map::const_iterator boundary_value = + std::map::const_iterator boundary_value = boundary_values.begin(); for ( ; boundary_value !=boundary_values.end(); ++boundary_value) { @@ -2140,7 +2144,7 @@ namespace internal const Tensor<1,dim> &constraining_vector, ConstraintMatrix &constraints) { - + // choose the DoF that has the // largest component in the // constraining_vector as the @@ -2164,7 +2168,9 @@ namespace internal { if (std::fabs(constraining_vector[0]) > std::fabs(constraining_vector[1])) { - if (!constraints.is_constrained(dof_indices.dof_indices[0])) + if (!constraints.is_constrained(dof_indices.dof_indices[0]) + && + constraints.can_store_line(dof_indices.dof_indices[0])) { constraints.add_line (dof_indices.dof_indices[0]); @@ -2177,7 +2183,9 @@ namespace internal } else { - if (!constraints.is_constrained(dof_indices.dof_indices[1])) + if (!constraints.is_constrained(dof_indices.dof_indices[1]) + && + constraints.can_store_line(dof_indices.dof_indices[1])) { constraints.add_line (dof_indices.dof_indices[1]); @@ -2197,7 +2205,9 @@ namespace internal && (std::fabs(constraining_vector[0]) >= std::fabs(constraining_vector[2]))) { - if (!constraints.is_constrained(dof_indices.dof_indices[0])) + if (!constraints.is_constrained(dof_indices.dof_indices[0]) + && + constraints.can_store_line(dof_indices.dof_indices[0])) { constraints.add_line (dof_indices.dof_indices[0]); @@ -2219,7 +2229,9 @@ namespace internal && (std::fabs(constraining_vector[1]) >= std::fabs(constraining_vector[2]))) { - if (!constraints.is_constrained(dof_indices.dof_indices[1])) + if (!constraints.is_constrained(dof_indices.dof_indices[1]) + && + constraints.can_store_line(dof_indices.dof_indices[1])) { constraints.add_line (dof_indices.dof_indices[1]); @@ -2238,7 +2250,9 @@ namespace internal } else { - if (!constraints.is_constrained(dof_indices.dof_indices[2])) + if (!constraints.is_constrained(dof_indices.dof_indices[2]) + && + constraints.can_store_line(dof_indices.dof_indices[2])) { constraints.add_line (dof_indices.dof_indices[2]); @@ -2373,7 +2387,7 @@ namespace internal namespace internals { namespace VectorTools { - + // This function computes the // projection of the boundary // function on edges for 3D. @@ -2388,12 +2402,12 @@ namespace internals { std::vector& dof_values) { const unsigned int dim = 3; - + hp_fe_values.reinit (cell, (cell->active_fe_index () * GeometryInfo::faces_per_cell + face) * GeometryInfo::lines_per_face + line); - + // Initialize the required // objects. const FEValues& @@ -2402,20 +2416,20 @@ namespace internals { jacobians = fe_values.get_jacobians (); const std::vector >& quadrature_points = fe_values.get_quadrature_points (); - + std::vector > tangentials (fe_values.n_quadrature_points); std::vector > values (fe_values.n_quadrature_points, Vector (dim)); - + // Get boundary function values // at quadrature points. boundary_function.vector_value_list (quadrature_points, values); - + const std::vector >& reference_quadrature_points = fe_values.get_quadrature ().get_points (); const unsigned int superdegree = cell->get_fe ().degree; const unsigned int degree = superdegree - 1; - + // coordinate directions of // the edges of the face. const unsigned int @@ -2428,13 +2442,13 @@ namespace internals { { 0, 0, 2, 2 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 } }; - + // The interpolation for the // lowest order edge shape // functions is just the mean - // value of the tangential + // value of the tangential // components of the boundary - // function on the edge. + // function on the edge. for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points; ++q_point) { @@ -2443,7 +2457,7 @@ namespace internals { // the quadrature point. Point shifted_reference_point_1 = reference_quadrature_points[q_point]; Point shifted_reference_point_2 = reference_quadrature_points[q_point]; - + shifted_reference_point_1 (edge_coordinate_direction[face][line]) += 1e-13; shifted_reference_point_2 (edge_coordinate_direction[face][line]) -= 1e-13; tangentials[q_point] @@ -2457,7 +2471,7 @@ namespace internals { shifted_reference_point_2))); tangentials[q_point] /= std::sqrt (tangentials[q_point].square ()); - + // Compute the mean value. dof_values[line * superdegree] += (fe_values.JxW (q_point) @@ -2471,7 +2485,7 @@ namespace internals { + jacobians[q_point][2][edge_coordinate_direction[face][line]] * jacobians[q_point][2][edge_coordinate_direction[face][line]])); } - + // If there are also higher // order shape functions we // have still some work left. @@ -2480,7 +2494,7 @@ namespace internals { const FEValuesExtractors::Vector vec (first_vector_component); FullMatrix assembling_matrix (degree, fe_values.n_quadrature_points); Vector assembling_vector (fe_values.n_quadrature_points); - + // We set up a linear system // of equations to get the // values for the remaining @@ -2509,7 +2523,7 @@ namespace internals { jacobians[q_point][2][edge_coordinate_direction[face][line]] * jacobians[q_point][2][edge_coordinate_direction[face][line]])) * tangentials[q_point]; - + const Tensor<1, dim> shape_value = fe_values[vec].value (cell->get_fe () .face_to_cell_index (line * superdegree, face), @@ -2531,7 +2545,7 @@ namespace internals { (values[q_point] (2) - dof_values[line * superdegree] * shape_value[2]) * tmp[2]); - + for (unsigned int i = 0; i < degree; ++i) assembling_matrix (i, q_point) = fe_values[vec].value (cell->get_fe () @@ -2539,37 +2553,37 @@ namespace internals { face), q_point) * tmp; } - + FullMatrix cell_matrix (degree, degree); - + // Create the system matrix // by multiplying the // assembling matrix with its // transposed. assembling_matrix.mTmult (cell_matrix, assembling_matrix); - + FullMatrix cell_matrix_inv (degree, degree); // Compute its inverse. cell_matrix_inv.invert (cell_matrix); - + Vector cell_rhs (degree); - + // Create the system right // hand side vector by // multiplying the assembling // matrix with the assembling // vector. assembling_matrix.vmult (cell_rhs, assembling_vector); - + Vector solution (degree); - + cell_matrix_inv.vmult (solution, cell_rhs); // Store the computed values. for (unsigned int i = 0; i < degree; ++i) - dof_values[i + line * superdegree + 1] = solution (i); + dof_values[i + line * superdegree + 1] = solution (i); } } - + // dummy implementation of above // function for all other // dimensions @@ -2585,7 +2599,7 @@ namespace internals { { Assert (false, ExcInternalError ()); } - + // This function computes the // projection of the boundary // function on the interior of @@ -2607,10 +2621,10 @@ namespace internals { fe_values = hp_fe_values.get_present_fe_values (); const std::vector >& jacobians = fe_values.get_jacobians (); - + std::vector > values (fe_values.n_quadrature_points, Vector (dim)); - + switch (dim) { case 2: @@ -2619,29 +2633,29 @@ namespace internals { quadrature_points = fe_values.get_quadrature_points (); std::vector > tangentials (fe_values.n_quadrature_points); - + // Get boundary function // values at quadrature // points. boundary_function.vector_value_list (quadrature_points, values); - + const std::vector >& reference_quadrature_points = fe_values.get_quadrature ().get_points (); const unsigned int degree = cell->get_fe ().degree - 1; - + // coordinate directions // of the face. const unsigned int face_coordinate_direction[GeometryInfo::faces_per_cell] = { 1, 1, 0, 0 }; - + // The interpolation for // the lowest order face // shape functions is just // the mean value of the // tangential components // of the boundary function - // on the edge. + // on the edge. for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points; ++q_point) { @@ -2653,7 +2667,7 @@ namespace internals { = reference_quadrature_points[q_point]; Point shifted_reference_point_2 = reference_quadrature_points[q_point]; - + shifted_reference_point_1 (face_coordinate_direction[face]) += 1e-13; shifted_reference_point_2 (face_coordinate_direction[face]) @@ -2681,7 +2695,7 @@ namespace internals { + jacobians[q_point][1][face_coordinate_direction[face]] * jacobians[q_point][1][face_coordinate_direction[face]]); } - + // If there are also // higher order shape // functions we have @@ -2692,7 +2706,7 @@ namespace internals { FullMatrix assembling_matrix (degree, fe_values.n_quadrature_points); Vector assembling_vector (fe_values.n_quadrature_points); - + // We set up a // linear system // of equations to @@ -2723,12 +2737,12 @@ namespace internals { + jacobians[q_point][1][face_coordinate_direction[face]] * jacobians[q_point][1][face_coordinate_direction[face]])) * tangentials[q_point]; - + const Tensor<1, dim> shape_value = fe_values[vec].value (cell->get_fe () .face_to_cell_index (0, face), q_point); - + assembling_vector (q_point) = (values[q_point] (0) - dof_values[0] * shape_value[0]) * tmp[0] @@ -2736,7 +2750,7 @@ namespace internals { (values[q_point] (1) - dof_values[1] * shape_value[1]) * tmp[1]; - + // In the weak // form the // right hand @@ -2751,22 +2765,22 @@ namespace internals { .face_to_cell_index (i + 1, face), q_point) * tmp; } - + FullMatrix cell_matrix (degree, degree); - + // Create the system // matrix by multiplying // the assembling // matrix with its // transposed. assembling_matrix.mTmult (cell_matrix, assembling_matrix); - + FullMatrix cell_matrix_inv (degree, degree); // Compute its inverse. cell_matrix_inv.invert (cell_matrix); - + Vector cell_rhs (degree); - + // Create the system // right hand side // vector by @@ -2775,30 +2789,30 @@ namespace internals { // with the assembling // vector. assembling_matrix.vmult (cell_rhs, assembling_vector); - + Vector solution (degree); - + cell_matrix_inv.vmult (solution, cell_rhs); - + // Store the computed // values. for (unsigned int i = 0; i < degree; ++i) dof_values[i + 1] = solution (i); } - + break; } - + case 3: { const std::vector >& quadrature_points = fe_values.get_quadrature_points (); - + // Get boundary function // values at quadrature // points. boundary_function.vector_value_list (quadrature_points, values); - + const FEValuesExtractors::Vector vec (first_vector_component); const unsigned int superdegree = cell->get_fe ().degree; const unsigned int degree = superdegree - 1; @@ -2812,7 +2826,7 @@ namespace internals { FullMatrix cell_matrix_inv (assembling_matrix.m (), assembling_matrix.m ()); Vector solution (cell_matrix.m ()); - + // Get coordinate directions // of the face. const unsigned int @@ -2831,7 +2845,7 @@ namespace internals { { 0, 1 }, { 1, 0 }, { 1, 0 } }; - + // The projection is // divided into two steps. // In the first step we @@ -2864,17 +2878,17 @@ namespace internals { // interpolated part // on the edges. Tensor<1, dim> tmp; - + for (unsigned int d = 0; d < dim; ++d) tmp[d] = values[q_point] (d); - + for (unsigned int i = 0; i < 2; ++i) for (unsigned int j = 0; j <= degree; ++j) tmp -= dof_values[(i + 2 * local_face_coordinate_directions[face][0]) * superdegree + j] * fe_values[vec].value (cell->get_fe ().face_to_cell_index ((i + 2 * local_face_coordinate_directions[face][0]) * superdegree + j, face), q_point); - + const double JxW = std::sqrt (fe_values.JxW (q_point) / ((jacobians[q_point][0][global_face_coordinate_directions[face][0]] @@ -2894,7 +2908,7 @@ namespace internals { + jacobians[q_point][2][global_face_coordinate_directions[face][1]] * jacobians[q_point][2][global_face_coordinate_directions[face][1]]))); - + // In the weak form // the right hand // side function @@ -2906,7 +2920,7 @@ namespace internals { // the face. for (unsigned int d = 0; d < dim; ++d) assembling_vector (dim * q_point + d) = JxW * tmp[d]; - + for (unsigned int i = 0; i <= degree; ++i) for (unsigned int j = 0; j < degree; ++j) { @@ -2920,14 +2934,14 @@ namespace internals { + GeometryInfo::lines_per_face, face), q_point)); - + for (unsigned int d = 0; d < dim; ++d) assembling_matrix (i * degree + j, dim * q_point + d) = shape_value[d]; } } - + // Create the system // matrix by // multiplying the @@ -2945,7 +2959,7 @@ namespace internals { cell_matrix_inv.invert (cell_matrix); assembling_matrix.vmult (cell_rhs, assembling_vector); cell_matrix_inv.vmult (solution, cell_rhs); - + // Store the computed // values. for (unsigned int i = 0; i <= degree; ++i) @@ -2953,7 +2967,7 @@ namespace internals { dof_values[(i + GeometryInfo::lines_per_face) * degree + j + GeometryInfo::lines_per_face] = solution (i * degree + j); - + // Now we do the // same as above // with the vertical @@ -2964,10 +2978,10 @@ namespace internals { q_point < fe_values.n_quadrature_points; ++q_point) { Tensor<1, dim> tmp; - + for (unsigned int d = 0; d < dim; ++d) tmp[d] = values[q_point] (d); - + for (unsigned int i = 0; i < 2; ++i) for (unsigned int j = 0; j <= degree; ++j) tmp @@ -2975,7 +2989,7 @@ namespace internals { * fe_values[vec].value (cell->get_fe ().face_to_cell_index ((i + 2 * local_face_coordinate_directions[face][1]) * superdegree + j, face), q_point); - + const double JxW = std::sqrt (fe_values.JxW (q_point) / ((jacobians[q_point][0][global_face_coordinate_directions[face][0]] @@ -2995,10 +3009,10 @@ namespace internals { + jacobians[q_point][2][global_face_coordinate_directions[face][1]] * jacobians[q_point][2][global_face_coordinate_directions[face][1]]))); - + for (unsigned int d = 0; d < dim; ++d) assembling_vector (dim * q_point + d) = JxW * tmp[d]; - + for (unsigned int i = 0; i < degree; ++i) for (unsigned int j = 0; j <= degree; ++j) { @@ -3007,34 +3021,34 @@ namespace internals { * fe_values[vec].value (cell->get_fe ().face_to_cell_index ((i + degree + GeometryInfo::lines_per_face) * superdegree + j, face), q_point)); - + for (unsigned int d = 0; d < dim; ++d) assembling_matrix (i * superdegree + j, dim * q_point + d) = shape_value[d]; } } - + assembling_matrix.mTmult (cell_matrix, assembling_matrix); cell_matrix_inv.invert (cell_matrix); assembling_matrix.vmult (cell_rhs, assembling_vector); cell_matrix_inv.vmult (solution, cell_rhs); - + for (unsigned int i = 0; i < degree; ++i) for (unsigned int j = 0; j <= degree; ++j) dof_values[(i + degree + GeometryInfo::lines_per_face) * superdegree + j] = solution (i * superdegree + j); - + break; } - + default: Assert (false, ExcNotImplemented ()); } } } } - - + + template @@ -3093,22 +3107,22 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, hp::FECollection fe_collection (dof_handler.get_fe ()); hp::MappingCollection mapping_collection (mapping); hp::QCollection face_quadrature_collection; - + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) face_quadrature_collection.push_back (QProjector::project_to_face (reference_face_quadrature, face)); - + hp::FEValues fe_face_values (mapping_collection, fe_collection, face_quadrature_collection, update_jacobians | update_JxW_values | update_quadrature_points | update_values); - + std::vector dof_values (dofs_per_face); std::vector face_dof_indices (dofs_per_face); typename DoFHandler::active_cell_iterator cell = dof_handler.begin_active (); - + switch (dim) { case 2: @@ -3125,10 +3139,10 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, typedef FiniteElement FEL; AssertThrow (dynamic_cast*> (&cell->get_fe ()) != 0, typename FEL::ExcInterpolationNotImplemented ()); - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) dof_values[dof] = 0.0; - + // Compute the // projection of the // boundary function on @@ -3136,10 +3150,10 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, internals::VectorTools ::compute_face_projection (cell, face, fe_face_values, boundary_function, - first_vector_component, dof_values); + first_vector_component, dof_values); cell->face (face)->get_dof_indices (face_dof_indices, cell->active_fe_index ()); - + // Add the computed // constraints to the // constraint matrix, @@ -3150,29 +3164,29 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, if (!(constraints.is_constrained (face_dof_indices[dof]))) { constraints.add_line (face_dof_indices[dof]); - + if (std::abs (dof_values[dof]) > 1e-14) constraints.set_inhomogeneity (face_dof_indices[dof], dof_values[dof]); } } - + break; } - + case 3: { const QGauss reference_edge_quadrature (2 * superdegree); const unsigned int degree = superdegree - 1; const unsigned int n_dofs = dof_handler.n_dofs (); hp::QCollection edge_quadrature_collection; - + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) for (unsigned int line = 0; line < GeometryInfo::lines_per_face; ++line) edge_quadrature_collection.push_back (QProjector::project_to_face (QProjector::project_to_face (reference_edge_quadrature, line), face)); - + hp::FEValues fe_edge_values (mapping_collection, fe_collection, edge_quadrature_collection, update_jacobians | @@ -3181,10 +3195,10 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, update_values); std::vector computed_constraints (n_dofs); std::vector projected_dofs (n_dofs); - + for (unsigned int dof = 0; dof < n_dofs; ++dof) projected_dofs[dof] = -1; - + for (; cell != dof_handler.end (); ++cell) if (cell->at_boundary ()) for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) @@ -3197,13 +3211,13 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, typedef FiniteElement FEL; AssertThrow (dynamic_cast*> (&cell->get_fe ()) != 0, typename FEL::ExcInterpolationNotImplemented ()); - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) dof_values[dof] = 0.0; - + cell->face (face)->get_dof_indices (face_dof_indices, cell->active_fe_index ()); - + // First we compute the // projection on the // edges. @@ -3242,7 +3256,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, dof < (line + 1) * superdegree; ++dof) projected_dofs[face_dof_indices[dof]] = degree; } - + // If we have // computed the // values in a @@ -3255,7 +3269,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, dof < (line + 1) * superdegree; ++dof) dof_values[dof] = computed_constraints[face_dof_indices[dof]]; } - + // If there are higher // order shape // functions, there is @@ -3273,7 +3287,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, boundary_function, first_vector_component, dof_values); - + // Mark the projected // degrees of // freedom. @@ -3281,7 +3295,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, dof < dofs_per_face; ++dof) projected_dofs[face_dof_indices[dof]] = degree; } - + // Store the computed // values in the global // vector. @@ -3289,7 +3303,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, if (std::abs (dof_values[dof]) > 1e-14) computed_constraints[face_dof_indices[dof]] = dof_values[dof]; } - + // Add the computed constraints // to the constraint matrix, if // the degree of freedom is not @@ -3301,7 +3315,7 @@ project_boundary_values_curl_conforming (const DoFHandler& dof_handler, constraints.set_inhomogeneity (dof, computed_constraints[dof]); } } - + default: Assert (false, ExcNotImplemented ()); } @@ -3321,17 +3335,17 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, { hp::FECollection fe_collection (dof_handler.get_fe ()); hp::QCollection face_quadrature_collection; - + for (unsigned int i = 0; i < fe_collection.size (); ++i) { const QGauss reference_face_quadrature (2 * fe_collection[i].degree); - + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) face_quadrature_collection.push_back (QProjector::project_to_face (reference_face_quadrature, face)); } - + hp::FEValues fe_face_values (mapping_collection, fe_collection, face_quadrature_collection, update_jacobians | @@ -3341,7 +3355,7 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, std::vector dof_values; std::vector face_dof_indices; typename hp::DoFHandler::active_cell_iterator cell = dof_handler.begin_active (); - + switch (dim) { case 2: @@ -3358,14 +3372,14 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, typedef FiniteElement FEL; AssertThrow (dynamic_cast *> (&cell->get_fe ()) != 0, typename FEL::ExcInterpolationNotImplemented ()); - + const unsigned int dofs_per_face = cell->get_fe ().dofs_per_face; - + dof_values.resize (dofs_per_face); - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) dof_values[dof] = 0.0; - + internals::VectorTools ::compute_face_projection (cell, face, fe_face_values, boundary_function, @@ -3374,30 +3388,30 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, face_dof_indices.resize (dofs_per_face); cell->face (face)->get_dof_indices (face_dof_indices, cell->active_fe_index ()); - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) if (!(constraints.is_constrained (face_dof_indices[dof]))) { constraints.add_line (face_dof_indices[dof]); - + if (std::abs (dof_values[dof]) > 1e-14) constraints.set_inhomogeneity (face_dof_indices[dof], dof_values[dof]); } } - + break; } - + case 3: { const unsigned int n_dofs = dof_handler.n_dofs (); hp::QCollection edge_quadrature_collection; - + for (unsigned int i = 0; i < fe_collection.size (); ++i) { const QGauss reference_edge_quadrature (2 * fe_collection[i].degree); - + for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) for (unsigned int line = 0; line < GeometryInfo::lines_per_face; ++line) edge_quadrature_collection.push_back @@ -3405,7 +3419,7 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, (QProjector::project_to_face (reference_edge_quadrature, line), face)); } - + hp::FEValues fe_edge_values (mapping_collection, fe_collection, edge_quadrature_collection, update_jacobians | @@ -3414,10 +3428,10 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, update_values); std::vector computed_constraints (n_dofs); std::vector projected_dofs (n_dofs); - + for (unsigned int dof = 0; dof < n_dofs; ++dof) projected_dofs[dof] = -1; - + for (; cell != dof_handler.end (); ++cell) if (cell->at_boundary ()) for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) @@ -3430,20 +3444,20 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, typedef FiniteElement FEL; AssertThrow (dynamic_cast *> (&cell->get_fe ()) != 0, typename FEL::ExcInterpolationNotImplemented ()); - + const unsigned int superdegree = cell->get_fe ().degree; const unsigned int degree = superdegree - 1; const unsigned int dofs_per_face = cell->get_fe ().dofs_per_face; - + dof_values.resize (dofs_per_face); - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) dof_values[dof] = 0.0; - + face_dof_indices.resize (dofs_per_face); cell->face (face)->get_dof_indices (face_dof_indices, cell->active_fe_index ()); - + for (unsigned int line = 0; line < GeometryInfo::lines_per_face; ++line) { @@ -3457,18 +3471,18 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, boundary_function, first_vector_component, dof_values); - + for (unsigned int dof = line * superdegree; dof < (line + 1) * superdegree; ++dof) projected_dofs[face_dof_indices[dof]] = degree; } - + else for (unsigned int dof = line * superdegree; dof < (line + 1) * superdegree; ++dof) dof_values[dof] = computed_constraints[face_dof_indices[dof]]; } - + if (degree > 0) { internals::VectorTools @@ -3476,17 +3490,17 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, boundary_function, first_vector_component, dof_values); - + for (unsigned int dof = GeometryInfo::lines_per_face * superdegree; dof < dofs_per_face; ++dof) projected_dofs[face_dof_indices[dof]] = degree; } - + for (unsigned int dof = 0; dof < dofs_per_face; ++dof) if (std::abs (dof_values[dof]) > 1e-14) computed_constraints[face_dof_indices[dof]] = dof_values[dof]; } - + for (unsigned int dof = 0; dof < n_dofs; ++dof) if ((projected_dofs[dof] != -1) && !(constraints.is_constrained (dof))) { @@ -3494,7 +3508,7 @@ project_boundary_values_curl_conforming (const hp::DoFHandler& dof_handler, constraints.set_inhomogeneity (dof, computed_constraints[dof]); } } - + default: Assert (false, ExcNotImplemented ()); } @@ -3558,67 +3572,68 @@ compute_no_normal_flux_constraints (const DH &dof_handler, cell = dof_handler.begin_active(), endc = dof_handler.end(); for (; cell!=endc; ++cell) - for (unsigned int face_no=0; face_no < GeometryInfo::faces_per_cell; - ++face_no) - if (boundary_ids.find(cell->face(face_no)->boundary_indicator()) - != boundary_ids.end()) - { - typename DH::face_iterator face = cell->face(face_no); - - // get the indices of the - // dofs on this cell... - face->get_dof_indices (face_dofs, cell->active_fe_index()); - - // ...and the normal - // vectors at the locations - // where they are defined: - const std::vector > & - unit_support_points = fe.get_unit_face_support_points(); - Quadrature aux_quad (unit_support_points); - FEFaceValues fe_values (mapping, fe, aux_quad, - update_normal_vectors); - fe_values.reinit(cell, face_no); - - // then identify which of - // them correspond to the - // selected set of vector - // components - for (unsigned int i=0; i vector_dofs; - vector_dofs.dof_indices[0] = face_dofs[i]; + if (!cell->is_artificial()) + for (unsigned int face_no=0; face_no < GeometryInfo::faces_per_cell; + ++face_no) + if (boundary_ids.find(cell->face(face_no)->boundary_indicator()) + != boundary_ids.end()) + { + typename DH::face_iterator face = cell->face(face_no); - for (unsigned int k=0; k= - first_vector_component) - && - (fe.face_system_to_component_index(k).first < - first_vector_component + dim)) - vector_dofs.dof_indices[fe.face_system_to_component_index(k).first - - first_vector_component] - = face_dofs[k]; - - for (unsigned int d=0; dget_dof_indices (face_dofs, cell->active_fe_index()); + + // ...and the normal + // vectors at the locations + // where they are defined: + const std::vector > & + unit_support_points = fe.get_unit_face_support_points(); + Quadrature aux_quad (unit_support_points); + FEFaceValues fe_values (mapping, fe, aux_quad, + update_normal_vectors); + fe_values.reinit(cell, face_no); + + // then identify which of + // them correspond to the + // selected set of vector + // components + for (unsigned int i=0; i vector_dofs; + vector_dofs.dof_indices[0] = face_dofs[i]; + + for (unsigned int k=0; k= + first_vector_component) + && + (fe.face_system_to_component_index(k).first < + first_vector_component + dim)) + vector_dofs.dof_indices[fe.face_system_to_component_index(k).first - + first_vector_component] + = face_dofs[k]; + +/* for (unsigned int d=0; d &dof_handler, // constrained for (unsigned int i=0; ifirst.dof_indices[i])) + ->first.dof_indices[i]) + && + constraints.can_store_line( + same_dof_range[0]->first.dof_indices[i])) { constraints.add_line (same_dof_range[0]->first.dof_indices[i]); // no add_entries here diff --git a/deal.II/deal.II/source/distributed/grid_refinement.cc b/deal.II/deal.II/source/distributed/grid_refinement.cc new file mode 100644 index 0000000000..fbb0e3cc4b --- /dev/null +++ b/deal.II/deal.II/source/distributed/grid_refinement.cc @@ -0,0 +1,775 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 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. +// +//--------------------------------------------------------------------------- + + + +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEAL_II_USE_P4EST + +#include +#include +#include +#include + +#include + +#include +#include + + +DEAL_II_NAMESPACE_OPEN + + +namespace +{ + template + inline + number + max_element (const Vector &criteria) + { + return *std::max_element(criteria.begin(), criteria.end()); + } + + + + template + inline + number + min_element (const Vector &criteria) + { + return *std::min_element(criteria.begin(), criteria.end()); + } + + + /** + * Compute the global max and min + * of the criteria vector. These + * are returned only on the + * processor with rank zero, all + * others get a pair of zeros. + */ + template + std::pair + compute_global_min_and_max_at_root (const Vector &criteria, + MPI_Comm mpi_communicator) + { + // we'd like to compute the + // global max and min from the + // local ones in one MPI + // communication. we can do that + // by taking the elementwise + // minimum of the local min and + // the negative maximum over all + // processors + + const double local_min = min_element (criteria), + local_max = max_element (criteria); + double comp[2] = { local_min, -local_max }; + double result[2] = { 0, 0 }; + + // compute the minimum on + // processor zero + MPI_Reduce (&comp, &result, 2, MPI_DOUBLE, + MPI_MIN, 0, mpi_communicator); + + // make sure only processor zero + // got something + if (Utilities::System::get_this_mpi_process (mpi_communicator) != 0) + Assert ((result[0] == 0) && (result[1] == 0), + ExcInternalError()); + + return std::make_pair (result[0], -result[1]); + } + + + + /** + * Compute the global sum over the elements + * of the vectors passed to this function + * on all processors. This number is + * returned only on the processor with rank + * zero, all others get zero. + */ + template + double + compute_global_sum (const Vector &criteria, + MPI_Comm mpi_communicator) + { + double my_sum = std::accumulate (criteria.begin(), + criteria.end(), + /* do accumulation in the correct data type: */ + number()); + + double result = 0; + // compute the minimum on + // processor zero + MPI_Reduce (&my_sum, &result, 1, MPI_DOUBLE, + MPI_SUM, 0, mpi_communicator); + + // make sure only processor zero + // got something + if (Utilities::System::get_this_mpi_process (mpi_communicator) != 0) + Assert (result == 0, ExcInternalError()); + + return result; + } + + + + /** + * Given a vector of refinement criteria + * for all cells of a mesh (locally owned + * or not), extract those that pertain to + * locally owned cells. + */ + template + void + get_locally_owned_indicators (const parallel::distributed::Triangulation &tria, + const Vector &criteria, + dealii::Vector &locally_owned_indicators) + { + Assert (locally_owned_indicators.size() == tria.n_locally_owned_active_cells(), + ExcInternalError()); + + unsigned int active_index = 0; + unsigned int owned_index = 0; + for (typename Triangulation::active_cell_iterator + cell = tria.begin_active(); + cell != tria.end(); ++cell, ++active_index) + if (cell->subdomain_id() == tria.locally_owned_subdomain()) + { + locally_owned_indicators(owned_index) + = criteria(active_index); + ++owned_index; + } + Assert (owned_index == tria.n_locally_owned_active_cells(), + ExcInternalError()); + Assert ((active_index == tria.dealii::Triangulation::n_active_cells()), + ExcInternalError()); + } + + + // we compute refinement + // thresholds by bisection of the + // interval spanned by the + // smallest and largest error + // indicator. this leads to a + // small problem: if, for + // example, we want to coarsen + // zero per cent of the cells, + // then we need to pick a + // threshold equal to the + // smallest indicator, but of + // course the bisection algorithm + // can never find a threshold + // equal to one of the end points + // of the interval. So we + // slightly increase the interval + // before we even start + void adjust_interesting_range (double (&interesting_range)[2]) + { + Assert (interesting_range[0] <= interesting_range[1], + ExcInternalError()); + + if (interesting_range[0] > 0) + interesting_range[0] *= 0.99; + else + interesting_range[0] + -= 0.01 * (interesting_range[1] - interesting_range[0]); + + if (interesting_range[1] > 0) + interesting_range[1] *= 1.01; + else + interesting_range[1] + += 0.01 * (interesting_range[1] - interesting_range[0]); + } + + + + /** + * Given a vector of criteria and bottom + * and top thresholds for coarsening and + * refinement, mark all those cells that we + * locally own as appropriate for + * coarsening or refinement. + */ + template + void + mark_cells (parallel::distributed::Triangulation &tria, + const Vector &criteria, + const double top_threshold, + const double bottom_threshold) + { + dealii::GridRefinement::refine (tria, criteria, top_threshold); + dealii::GridRefinement::coarsen (tria, criteria, bottom_threshold); + + // as a final good measure, + // delete all flags again + // from cells that we don't + // locally own + for (typename Triangulation::active_cell_iterator + cell = tria.begin_active(); + cell != tria.end(); ++cell) + if (cell->subdomain_id() != tria.locally_owned_subdomain()) + { + cell->clear_refine_flag (); + cell->clear_coarsen_flag (); + } + } + + + + + namespace RefineAndCoarsenFixedNumber + { + /** + * Compute a threshold value so + * that exactly n_target_cells have + * a value that is larger. + */ + template + number + master_compute_threshold (const Vector &criteria, + const std::pair global_min_and_max, + const unsigned int n_target_cells, + MPI_Comm mpi_communicator) + { + double interesting_range[2] = { global_min_and_max.first, + global_min_and_max.second }; + adjust_interesting_range (interesting_range); + + unsigned int iteration = 0; + + do + { + MPI_Bcast (&interesting_range[0], 2, MPI_DOUBLE, + 0, mpi_communicator); + + if (interesting_range[0] == interesting_range[1]) + return interesting_range[0]; + + const double test_threshold + = (interesting_range[0] > 0 + ? + std::sqrt(interesting_range[0] * + interesting_range[1]) + : + (interesting_range[0] + interesting_range[1]) / 2); + + // count how many of our own + // elements would be above + // this threshold and then + // add to it the number for + // all the others + unsigned int + my_count = std::count_if (criteria.begin(), + criteria.end(), + std::bind2nd (std::greater(), + test_threshold)); + + unsigned int total_count; + MPI_Reduce (&my_count, &total_count, 1, MPI_UNSIGNED, + MPI_SUM, 0, mpi_communicator); + + // now adjust the range. if + // we have to many cells, we + // take the upper half of the + // previous range, otherwise + // the lower half. if we have + // hit the right number, then + // set the range to the exact + // value + if (total_count > n_target_cells) + interesting_range[0] = test_threshold; + else if (total_count < n_target_cells) + interesting_range[1] = test_threshold; + else + interesting_range[0] = interesting_range[1] = test_threshold; + + // terminate the iteration + // after 10 go-arounds. this + // is necessary because + // oftentimes error + // indicators on cells have + // exactly the same value, + // and so there may not be a + // particular value that cuts + // the indicators in such a + // way that we can achieve + // the desired number of + // cells. using a max of 10 + // iterations means that we + // terminate the iteration + // after 10 steps if the + // indicators were perfectly + // badly distributed, and we + // make at most a mistake of + // 1/2^10 in the number of + // cells flagged if + // indicators are perfectly + // equidistributed + ++iteration; + if (iteration == 25) + interesting_range[0] = interesting_range[1] = test_threshold; + } + while (true); + + Assert (false, ExcInternalError()); + return -1; + } + + + /** + * The corresponding function to + * the one above, to be run on the + * slaves. + */ + template + number + slave_compute_threshold (const Vector &criteria, + MPI_Comm mpi_communicator) + { + do + { + double interesting_range[2] = { -1, -1 }; + MPI_Bcast (&interesting_range[0], 2, MPI_DOUBLE, + 0, mpi_communicator); + + if (interesting_range[0] == interesting_range[1]) + return interesting_range[0]; + + // count how many elements + // there are that are bigger + // than the following trial + // threshold + const double test_threshold + = (interesting_range[0] > 0 + ? + std::exp((std::log(interesting_range[0]) + + std::log(interesting_range[1])) / 2) + : + (interesting_range[0] + interesting_range[1]) / 2); + unsigned int + my_count = std::count_if (criteria.begin(), + criteria.end(), + std::bind2nd (std::greater(), + test_threshold)); + + MPI_Reduce (&my_count, 0, 1, MPI_UNSIGNED, + MPI_SUM, 0, mpi_communicator); + } + while (true); + + Assert (false, ExcInternalError()); + return -1; + } + } + + + + namespace RefineAndCoarsenFixedFraction + { + /** + * Compute a threshold value so + * that exactly n_target_cells have + * a value that is larger. + */ + template + number + master_compute_threshold (const Vector &criteria, + const std::pair global_min_and_max, + const double target_error, + MPI_Comm mpi_communicator) + { + double interesting_range[2] = { global_min_and_max.first, + global_min_and_max.second }; + adjust_interesting_range (interesting_range); + + unsigned int iteration = 0; + + do + { + MPI_Bcast (&interesting_range[0], 2, MPI_DOUBLE, + 0, mpi_communicator); + + if (interesting_range[0] == interesting_range[1]) + return interesting_range[0]; + + const double test_threshold + = (interesting_range[0] > 0 + ? + std::exp((std::log(interesting_range[0]) + + std::log(interesting_range[1])) / 2) + : + (interesting_range[0] + interesting_range[1]) / 2); + + // accumulate the error of those + // our own elements above this + // threshold and then add to it the + // number for all the others + double my_error = 0; + for (unsigned int i=0; i test_threshold) + my_error += criteria(i); + + double total_error; + MPI_Reduce (&my_error, &total_error, 1, MPI_DOUBLE, + MPI_SUM, 0, mpi_communicator); + + // now adjust the range. if + // we have to many cells, we + // take the upper half of the + // previous range, otherwise + // the lower half. if we have + // hit the right number, then + // set the range to the exact + // value + if (total_error > target_error) + interesting_range[0] = test_threshold; + else if (total_error < target_error) + interesting_range[1] = test_threshold; + else + interesting_range[0] = interesting_range[1] = test_threshold; + + // terminate the iteration + // after 10 go-arounds. this + // is necessary because + // oftentimes error + // indicators on cells have + // exactly the same value, + // and so there may not be a + // particular value that cuts + // the indicators in such a + // way that we can achieve + // the desired number of + // cells. using a max of 10 + // iterations means that we + // terminate the iteration + // after 10 steps if the + // indicators were perfectly + // badly distributed, and we + // make at most a mistake of + // 1/2^10 in the number of + // cells flagged if + // indicators are perfectly + // equidistributed + ++iteration; + if (iteration == 25) + interesting_range[0] = interesting_range[1] = test_threshold; + } + while (true); + + Assert (false, ExcInternalError()); + return -1; + } + + + /** + * The corresponding function to + * the one above, to be run on the + * slaves. + */ + template + number + slave_compute_threshold (const Vector &criteria, + MPI_Comm mpi_communicator) + { + do + { + double interesting_range[2] = { -1, -1 }; + MPI_Bcast (&interesting_range[0], 2, MPI_DOUBLE, + 0, mpi_communicator); + + if (interesting_range[0] == interesting_range[1]) + return interesting_range[0]; + + // count how many elements + // there are that are bigger + // than the following trial + // threshold + const double test_threshold + = (interesting_range[0] > 0 + ? + std::exp((std::log(interesting_range[0]) + + std::log(interesting_range[1])) / 2) + : + (interesting_range[0] + interesting_range[1]) / 2); + + double my_error = 0; + for (unsigned int i=0; i test_threshold) + my_error += criteria(i); + + MPI_Reduce (&my_error, 0, 1, MPI_DOUBLE, + MPI_SUM, 0, mpi_communicator); + } + while (true); + + Assert (false, ExcInternalError()); + return -1; + } + } +} + + + +namespace parallel +{ + namespace distributed + { + namespace GridRefinement + { + template + void + refine_and_coarsen_fixed_number ( + parallel::distributed::Triangulation &tria, + const Vector &criteria, + const double top_fraction_of_cells, + const double bottom_fraction_of_cells) + { + Assert ((top_fraction_of_cells>=0) && (top_fraction_of_cells<=1), + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert ((bottom_fraction_of_cells>=0) && (bottom_fraction_of_cells<=1), + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert (top_fraction_of_cells+bottom_fraction_of_cells <= 1, + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert (criteria.is_non_negative (), + dealii::GridRefinement::ExcNegativeCriteria()); + + // first extract from the + // vector of indicators the + // ones that correspond to + // cells that we locally own + dealii::Vector + locally_owned_indicators (tria.n_locally_owned_active_cells()); + get_locally_owned_indicators (tria, + criteria, + locally_owned_indicators); + + MPI_Comm mpi_communicator = tria.get_communicator (); + + // figure out the global + // max and min of the + // indicators. we don't + // need it here, but it's a + // collective communication + // call + const std::pair global_min_and_max + = compute_global_min_and_max_at_root (locally_owned_indicators, + mpi_communicator); + + // from here on designate a + // master and slaves + double top_threshold, bottom_threshold; + if (Utilities::System::get_this_mpi_process (mpi_communicator) == 0) + { + // this is the master + // processor + top_threshold + = + RefineAndCoarsenFixedNumber:: + master_compute_threshold (locally_owned_indicators, + global_min_and_max, + static_cast + (top_fraction_of_cells * + tria.n_global_active_cells()), + mpi_communicator); + + // compute bottom + // threshold only if + // necessary. otherwise + // use a threshold lower + // than the smallest + // value we have locally + if (bottom_fraction_of_cells > 0) + bottom_threshold + = + RefineAndCoarsenFixedNumber:: + master_compute_threshold (locally_owned_indicators, + global_min_and_max, + static_cast + ((1-bottom_fraction_of_cells) * + tria.n_global_active_cells()), + mpi_communicator); + else + { + bottom_threshold = *std::min_element (criteria.begin(), + criteria.end()); + bottom_threshold -= std::fabs(bottom_threshold); + } + } + else + { + // this is a slave + // processor + top_threshold + = + RefineAndCoarsenFixedNumber:: + slave_compute_threshold (locally_owned_indicators, + mpi_communicator); + // compute bottom + // threshold only if + // necessary + if (bottom_fraction_of_cells > 0) + bottom_threshold + = + RefineAndCoarsenFixedNumber:: + slave_compute_threshold (locally_owned_indicators, + mpi_communicator); + else + { + bottom_threshold = *std::min_element (criteria.begin(), + criteria.end()); + bottom_threshold -= std::fabs(bottom_threshold); + } + } + + // now refine the mesh + mark_cells (tria, criteria, top_threshold, bottom_threshold); + } + + + template + void + refine_and_coarsen_fixed_fraction ( + parallel::distributed::Triangulation &tria, + const Vector &criteria, + const double top_fraction_of_error, + const double bottom_fraction_of_error) + { + Assert ((top_fraction_of_error>=0) && (top_fraction_of_error<=1), + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert ((bottom_fraction_of_error>=0) && (bottom_fraction_of_error<=1), + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert (top_fraction_of_error+bottom_fraction_of_error <= 1, + dealii::GridRefinement::ExcInvalidParameterValue()); + Assert (criteria.is_non_negative (), + dealii::GridRefinement::ExcNegativeCriteria()); + + // first extract from the + // vector of indicators the + // ones that correspond to + // cells that we locally own + dealii::Vector + locally_owned_indicators (tria.n_locally_owned_active_cells()); + get_locally_owned_indicators (tria, + criteria, + locally_owned_indicators); + + MPI_Comm mpi_communicator = tria.get_communicator (); + + // figure out the global + // max and min of the + // indicators. we don't + // need it here, but it's a + // collective communication + // call + const std::pair global_min_and_max + = compute_global_min_and_max_at_root (locally_owned_indicators, + mpi_communicator); + + const double total_error + = compute_global_sum (locally_owned_indicators, + mpi_communicator); + + // from here on designate a + // master and slaves + double top_threshold, bottom_threshold; + if (Utilities::System::get_this_mpi_process (mpi_communicator) == 0) + { + // this is the master + // processor + top_threshold + = + RefineAndCoarsenFixedFraction:: + master_compute_threshold (locally_owned_indicators, + global_min_and_max, + static_cast + (top_fraction_of_error * + total_error), + mpi_communicator); + + // compute bottom + // threshold only if + // necessary. otherwise + // use a threshold lower + // than the smallest + // value we have locally + if (bottom_fraction_of_error > 0) + bottom_threshold + = + RefineAndCoarsenFixedFraction:: + master_compute_threshold (locally_owned_indicators, + global_min_and_max, + static_cast + ((1-bottom_fraction_of_error) * + total_error), + mpi_communicator); + else + { + bottom_threshold = *std::min_element (criteria.begin(), + criteria.end()); + bottom_threshold -= std::fabs(bottom_threshold); + } + } + else + { + // this is a slave + // processor + top_threshold + = + RefineAndCoarsenFixedFraction:: + slave_compute_threshold (locally_owned_indicators, + mpi_communicator); + + // compute bottom + // threshold only if + // necessary. otherwise + // use a threshold lower + // than the smallest + // value we have locally + if (bottom_fraction_of_error > 0) + bottom_threshold + = + RefineAndCoarsenFixedFraction:: + slave_compute_threshold (locally_owned_indicators, + mpi_communicator); + else + { + bottom_threshold = *std::min_element (criteria.begin(), + criteria.end()); + bottom_threshold -= std::fabs(bottom_threshold); + } + } + + // now refine the mesh + mark_cells (tria, criteria, top_threshold, bottom_threshold); + } + } + } +} + + +// explicit instantiations +#include "grid_refinement.inst" + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/deal.II/source/distributed/grid_refinement.inst.in b/deal.II/deal.II/source/distributed/grid_refinement.inst.in new file mode 100644 index 0000000000..a7454dbc75 --- /dev/null +++ b/deal.II/deal.II/source/distributed/grid_refinement.inst.in @@ -0,0 +1,44 @@ +//--------------------------------------------------------------------------- +// $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 (S : REAL_SCALARS) +{ +#if deal_II_dimension != 1 +namespace parallel +\{ + namespace distributed + \{ + namespace GridRefinement + \{ + template + void + refine_and_coarsen_fixed_number,deal_II_dimension> + (parallel::distributed::Triangulation &, + const dealii::Vector &, + const double, + const double); + + template + void + refine_and_coarsen_fixed_fraction,deal_II_dimension> + (parallel::distributed::Triangulation &, + const dealii::Vector &, + const double, + const double); + \} + \} +\} +#endif +} diff --git a/deal.II/deal.II/source/distributed/solution_transfer.cc b/deal.II/deal.II/source/distributed/solution_transfer.cc new file mode 100644 index 0000000000..e4e19d65e7 --- /dev/null +++ b/deal.II/deal.II/source/distributed/solution_transfer.cc @@ -0,0 +1,273 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2009, 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. +// +//--------------------------------------------------------------------------- + +#include + +#ifdef DEAL_II_USE_P4EST + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +DEAL_II_NAMESPACE_OPEN + +namespace parallel +{ + namespace distributed + { + + namespace + { + template + void compress_vector_insert(VECTOR & vec) + { + vec.compress(); + } + +#ifdef DEAL_II_USE_TRILINOS + void compress_vector_insert(TrilinosWrappers::Vector & vec) + { + vec.compress(Insert); + } + + void compress_vector_insert(TrilinosWrappers::BlockVector & vec) + { + for (unsigned int i=0;i + SolutionTransfer::SolutionTransfer(const DH &dof) + : + dof_handler(&dof, typeid(*this).name()) + {} + + + + template + SolutionTransfer::~SolutionTransfer() + {} + + + + template + void + SolutionTransfer:: + prepare_for_coarsening_and_refinement (const std::vector &all_in) + { + Assert(all_in.size() > 0, ExcMessage("Please transfer atleast one vector!")); + input_vectors = all_in; + SolutionTransfer *ptr = this; + +//TODO: casting away constness is bad + parallel::distributed::Triangulation * tria + = (dynamic_cast*> + (const_cast*> + (&dof_handler->get_tria()))); + Assert (tria != 0, ExcInternalError()); + + offset + = tria->register_data_attach(static_cast + (get_data_size() * input_vectors.size()), + std_cxx1x::bind(&SolutionTransfer::pack_callback, + ptr, + _1, + _2, + _3)); + } + + + + template + void + SolutionTransfer:: + prepare_for_coarsening_and_refinement (const VECTOR &in) + { + std::vector all_in(1, &in); + prepare_for_coarsening_and_refinement(all_in); + } + + + + template + void + SolutionTransfer:: + interpolate (std::vector &all_out) + { + Assert(input_vectors.size()==all_out.size(), + ExcDimensionMismatch(input_vectors.size(), all_out.size()) ); + +//TODO: casting away constness is bad + parallel::distributed::Triangulation * tria + = (dynamic_cast*> + (const_cast*> + (&dof_handler->get_tria()))); + Assert (tria != 0, ExcInternalError()); + + tria->notify_ready_to_unpack(offset, + std_cxx1x::bind(&SolutionTransfer::unpack_callback, + this, + _1, + _2, + _3, + std_cxx1x::ref(all_out))); + + for (typename std::vector::iterator it=all_out.begin(); + it !=all_out.end(); + ++it) + compress_vector_insert(*(*it)); + + input_vectors.clear(); + } + + + + template + void + SolutionTransfer:: + interpolate (VECTOR &out) + { + std::vector all_out(1, &out); + interpolate(all_out); + } + + + + template + unsigned int + SolutionTransfer:: + get_data_size() const + { + return sizeof(double)* DoFTools::max_dofs_per_cell(*dof_handler); + } + + + template + void + SolutionTransfer:: + pack_callback(const typename Triangulation::cell_iterator & cell_, + const typename Triangulation::CellStatus /*status*/, + void* data) + { + double *data_store = reinterpret_cast(data); + + typename DH::cell_iterator cell(&dof_handler->get_tria(), cell_->level(), cell_->index(), dof_handler); + + const unsigned int dofs_per_cell=cell->get_fe().dofs_per_cell; + Vector dofvalues(dofs_per_cell); + for (typename std::vector::iterator it=input_vectors.begin(); + it !=input_vectors.end(); + ++it) + { + cell->get_interpolated_dof_values(*(*it), dofvalues); + std::memcpy(data_store, &dofvalues(0), sizeof(double)*dofs_per_cell); + data_store += dofs_per_cell; + } + } + + + template + void + SolutionTransfer:: + unpack_callback(const typename Triangulation::cell_iterator & cell_, + const typename Triangulation::CellStatus /*status*/, + const void* data, + std::vector &all_out) + { + typename DH::cell_iterator + cell(&dof_handler->get_tria(), cell_->level(), cell_->index(), dof_handler); + + const unsigned int dofs_per_cell=cell->get_fe().dofs_per_cell; + Vector dofvalues(dofs_per_cell); + const double *data_store = reinterpret_cast(data); + + for (typename std::vector::iterator it = all_out.begin(); + it != all_out.end(); + ++it) + { + std::memcpy(&dofvalues(0), data_store, sizeof(double)*dofs_per_cell); + cell->set_dof_values_by_interpolation(dofvalues, *(*it)); + data_store += dofs_per_cell; + } + } + + + } +} + + +// explicit instantiations + +namespace parallel +{ + namespace distributed + { +#if deal_II_dimension > 1 + template class SolutionTransfer, DoFHandler >; + + +#ifdef DEAL_II_USE_PETSC + template class SolutionTransfer >; + + template class SolutionTransfer >; + + template class SolutionTransfer >; + + template class SolutionTransfer >; + +#endif + +#ifdef DEAL_II_USE_TRILINOS + template class SolutionTransfer >; + + template class SolutionTransfer >; + + template class SolutionTransfer >; + + template class SolutionTransfer >; +#endif +#endif + } +} + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/deal.II/source/distributed/tria.cc b/deal.II/deal.II/source/distributed/tria.cc new file mode 100644 index 0000000000..b99e8dfd63 --- /dev/null +++ b/deal.II/deal.II/source/distributed/tria.cc @@ -0,0 +1,3016 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2008, 2009, 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. +// +//--------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEAL_II_USE_P4EST +# include +# include +# include +# include + +# include +# include +# include +# include +#endif + +#include +#include + +#include + +# include + + +DEAL_II_NAMESPACE_OPEN + + +#ifdef DEAL_II_USE_P4EST + +namespace internal +{ + namespace p4est + { + /** + * A structure whose explicit + * specializations contain + * pointers to the relevant + * p4est_* and p8est_* + * functions. Using this + * structure, for example by + * saying + * functions::quadrant_compare, + * we can write code in a + * dimension independent way, + * either calling + * p4est_quadrant_compare or + * p8est_quadrant_compare, + * depending on template + * argument. + */ + template struct functions; + + template <> struct functions<2> + { + static + int (&quadrant_compare) (const void *v1, const void *v2); + + static + void (&quadrant_childrenv) (const types<2>::quadrant * q, + types<2>::quadrant c[]); + + static + int (&quadrant_overlaps_tree) (types<2>::tree * tree, + const types<2>::quadrant * q); + + static + void (&quadrant_set_morton) (types<2>::quadrant * quadrant, + int level, + uint64_t id); + + static + int (&quadrant_is_equal) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2); + + static + int (&quadrant_is_sibling) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2); + + static + int (&quadrant_is_ancestor) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2); + + static + types<2>::connectivity * (&connectivity_new) (types<2>::topidx num_vertices, + types<2>::topidx num_trees, + types<2>::topidx num_corners, + types<2>::topidx num_vtt); + + static + void (&connectivity_destroy) (p4est_connectivity_t *connectivity); + + static + types<2>::forest * (&new_forest) (MPI_Comm mpicomm, + types<2>::connectivity * connectivity, + types<2>::locidx min_quadrants, + int min_level, + int fill_uniform, + size_t data_size, + p4est_init_t init_fn, + void *user_pointer); + + static + void (&destroy) (types<2>::forest * p4est); + + static + void (&refine) (types<2>::forest * p4est, + int refine_recursive, + p4est_refine_t refine_fn, + p4est_init_t init_fn); + + static + void (&coarsen) (types<2>::forest * p4est, + int coarsen_recursive, + p4est_coarsen_t coarsen_fn, + p4est_init_t init_fn); + + static + void (&balance) (types<2>::forest * p4est, + types<2>::balance_type btype, + p4est_init_t init_fn); + + static + void (&partition) (types<2>::forest * p4est, + int partition_for_coarsening, + p4est_weight_t weight_fn); + + static + void (&save) (const char *filename, + types<2>::forest * p4est, + int save_data); + + static + types<2>::forest * (&load) (const char *filename, + MPI_Comm mpicomm, + std::size_t data_size, + int load_data, + void *user_pointer, + types<2>::connectivity ** p4est); + + static + void (&connectivity_save) (const char *filename, + types<2>::connectivity * connectivity); + + static + types<2>::connectivity * (&connectivity_load) (const char *filename, + long *length); + + static + unsigned (&checksum) (types<2>::forest * p4est); + + static + void (&vtk_write_file) (types<2>::forest * p4est, + p4est_geometry_t*, + const char *baseName); + + static + types<2>::ghost* (&ghost_new) (types<2>::forest * p4est, + types<2>::balance_type btype); + + static + void (&ghost_destroy) (types<2>::ghost * ghost); + + static + void (&reset_data) (types<2>::forest * p4est, + size_t data_size, + p4est_init_t init_fn, + void *user_pointer); + + static + size_t (&forest_memory_used) (types<2>::forest * p4est); + + static + size_t (&connectivity_memory_used) (types<2>::connectivity * p4est); + }; + + int (&functions<2>::quadrant_compare) (const void *v1, const void *v2) + = p4est_quadrant_compare; + + void (&functions<2>::quadrant_childrenv) (const types<2>::quadrant * q, + types<2>::quadrant c[]) + = p4est_quadrant_childrenv; + + int (&functions<2>::quadrant_overlaps_tree) (types<2>::tree * tree, + const types<2>::quadrant * q) + = p4est_quadrant_overlaps_tree; + + void (&functions<2>::quadrant_set_morton) (types<2>::quadrant * quadrant, + int level, + uint64_t id) + = p4est_quadrant_set_morton; + + int (&functions<2>::quadrant_is_equal) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2) + = p4est_quadrant_is_equal; + + int (&functions<2>::quadrant_is_sibling) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2) + = p4est_quadrant_is_sibling; + + int (&functions<2>::quadrant_is_ancestor) (const types<2>::quadrant * q1, + const types<2>::quadrant * q2) + = p4est_quadrant_is_ancestor; + + types<2>::connectivity * (&functions<2>::connectivity_new) (types<2>::topidx num_vertices, + types<2>::topidx num_trees, + types<2>::topidx num_corners, + types<2>::topidx num_vtt) + = p4est_connectivity_new; + + void (&functions<2>::connectivity_destroy) (p4est_connectivity_t *connectivity) + = p4est_connectivity_destroy; + + types<2>::forest * (&functions<2>::new_forest) (MPI_Comm mpicomm, + types<2>::connectivity * connectivity, + types<2>::locidx min_quadrants, + int min_level, + int fill_uniform, + size_t data_size, + p4est_init_t init_fn, + void *user_pointer) + = p4est_new_ext; + + void (&functions<2>::destroy) (types<2>::forest * p4est) + = p4est_destroy; + + void (&functions<2>::refine) (types<2>::forest * p4est, + int refine_recursive, + p4est_refine_t refine_fn, + p4est_init_t init_fn) + = p4est_refine; + + void (&functions<2>::coarsen) (types<2>::forest * p4est, + int coarsen_recursive, + p4est_coarsen_t coarsen_fn, + p4est_init_t init_fn) + = p4est_coarsen; + + void (&functions<2>::balance) (types<2>::forest * p4est, + types<2>::balance_type btype, + p4est_init_t init_fn) + = p4est_balance; + + void (&functions<2>::partition) (types<2>::forest * p4est, + int partition_for_coarsening, + p4est_weight_t weight_fn) + = p4est_partition_ext; + + void (&functions<2>::save) (const char *filename, + types<2>::forest * p4est, + int save_data) + = p4est_save; + + types<2>::forest * + (&functions<2>::load) (const char *filename, + MPI_Comm mpicomm, + std::size_t data_size, + int load_data, + void *user_pointer, + types<2>::connectivity ** p4est) + = p4est_load; + + void (&functions<2>::connectivity_save) (const char *filename, + types<2>::connectivity *connectivity) + = p4est_connectivity_save; + + types<2>::connectivity * + (&functions<2>::connectivity_load) (const char *filename, + long *length) + = p4est_connectivity_load; + + unsigned (&functions<2>::checksum) (types<2>::forest * p4est) + = p4est_checksum; + + void (&functions<2>::vtk_write_file) (types<2>::forest * p4est, + p4est_geometry_t*, + const char *baseName) + = p4est_vtk_write_file; + + types<2>::ghost* (&functions<2>::ghost_new) (types<2>::forest * p4est, + types<2>::balance_type btype) + = p4est_ghost_new; + + void (&functions<2>::ghost_destroy) (types<2>::ghost * ghost) + = p4est_ghost_destroy; + + void (&functions<2>::reset_data) (types<2>::forest * p4est, + size_t data_size, + p4est_init_t init_fn, + void *user_pointer) + = p4est_reset_data; + + size_t (&functions<2>::forest_memory_used) (types<2>::forest * p4est) + = p4est_memory_used; + + size_t (&functions<2>::connectivity_memory_used) (types<2>::connectivity * p4est) + = p4est_connectivity_memory_used; + + template <> struct functions<3> + { + static + int (&quadrant_compare) (const void *v1, const void *v2); + + static + void (&quadrant_childrenv) (const types<3>::quadrant * q, + types<3>::quadrant c[]); + + static + int (&quadrant_overlaps_tree) (types<3>::tree * tree, + const types<3>::quadrant * q); + + static + void (&quadrant_set_morton) (types<3>::quadrant * quadrant, + int level, + uint64_t id); + + static + int (&quadrant_is_equal) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2); + + static + int (&quadrant_is_sibling) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2); + + static + int (&quadrant_is_ancestor) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2); + + static + types<3>::connectivity * (&connectivity_new) (types<3>::topidx num_vertices, + types<3>::topidx num_trees, + types<3>::topidx num_edges, + types<3>::topidx num_ett, + types<3>::topidx num_corners, + types<3>::topidx num_ctt); + + static + void (&connectivity_destroy) (p8est_connectivity_t *connectivity); + + static + types<3>::forest * (&new_forest) (MPI_Comm mpicomm, + types<3>::connectivity * connectivity, + types<3>::locidx min_quadrants, + int min_level, + int fill_uniform, + size_t data_size, + p8est_init_t init_fn, + void *user_pointer); + + static + void (&destroy) (types<3>::forest * p8est); + + static + void (&refine) (types<3>::forest * p8est, + int refine_recursive, + p8est_refine_t refine_fn, + p8est_init_t init_fn); + + static + void (&coarsen) (types<3>::forest * p8est, + int coarsen_recursive, + p8est_coarsen_t coarsen_fn, + p8est_init_t init_fn); + + static + void (&balance) (types<3>::forest * p8est, + types<3>::balance_type btype, + p8est_init_t init_fn); + + static + void (&partition) (types<3>::forest * p8est, + int partition_for_coarsening, + p8est_weight_t weight_fn); + + static + void (&save) (const char *filename, + types<3>::forest * p4est, + int save_data); + + static + types<3>::forest * (&load) (const char *filename, + MPI_Comm mpicomm, + std::size_t data_size, + int load_data, + void *user_pointer, + types<3>::connectivity ** p4est); + + static + void (&connectivity_save) (const char *filename, + types<3>::connectivity * connectivity); + + static + types<3>::connectivity * (&connectivity_load) (const char *filename, + long *length); + + static + unsigned (&checksum) (types<3>::forest * p8est); + + static + void (&vtk_write_file) (types<3>::forest * p8est, + p8est_geometry_t*, + const char *baseName); + + static + types<3>::ghost* (&ghost_new) (types<3>::forest * p4est, + types<3>::balance_type btype); + + static + void (&ghost_destroy) (types<3>::ghost * ghost); + + static + void (&reset_data) (types<3>::forest * p4est, + size_t data_size, + p8est_init_t init_fn, + void *user_pointer); + + static + size_t (&forest_memory_used) (types<3>::forest * p4est); + + static + size_t (&connectivity_memory_used) (types<3>::connectivity * p4est); + }; + + int (&functions<3>::quadrant_compare) (const void *v1, const void *v2) + = p8est_quadrant_compare; + + void (&functions<3>::quadrant_childrenv) (const types<3>::quadrant * q, + types<3>::quadrant c[]) + = p8est_quadrant_childrenv; + + int (&functions<3>::quadrant_overlaps_tree) (types<3>::tree * tree, + const types<3>::quadrant * q) + = p8est_quadrant_overlaps_tree; + + void (&functions<3>::quadrant_set_morton) (types<3>::quadrant * quadrant, + int level, + uint64_t id) + = p8est_quadrant_set_morton; + + int (&functions<3>::quadrant_is_equal) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2) + = p8est_quadrant_is_equal; + + int (&functions<3>::quadrant_is_sibling) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2) + = p8est_quadrant_is_sibling; + + int (&functions<3>::quadrant_is_ancestor) (const types<3>::quadrant * q1, + const types<3>::quadrant * q2) + = p8est_quadrant_is_ancestor; + + types<3>::connectivity * (&functions<3>::connectivity_new) (types<3>::topidx num_vertices, + types<3>::topidx num_trees, + types<3>::topidx num_edges, + types<3>::topidx num_ett, + types<3>::topidx num_corners, + types<3>::topidx num_ctt) + = p8est_connectivity_new; + + void (&functions<3>::connectivity_destroy) (p8est_connectivity_t *connectivity) + = p8est_connectivity_destroy; + + types<3>::forest * (&functions<3>::new_forest) (MPI_Comm mpicomm, + types<3>::connectivity * connectivity, + types<3>::locidx min_quadrants, + int min_level, + int fill_uniform, + size_t data_size, + p8est_init_t init_fn, + void *user_pointer) + = p8est_new_ext; + + void (&functions<3>::destroy) (types<3>::forest * p8est) + = p8est_destroy; + + void (&functions<3>::refine) (types<3>::forest * p8est, + int refine_recursive, + p8est_refine_t refine_fn, + p8est_init_t init_fn) + = p8est_refine; + + void (&functions<3>::coarsen) (types<3>::forest * p8est, + int coarsen_recursive, + p8est_coarsen_t coarsen_fn, + p8est_init_t init_fn) + = p8est_coarsen; + + void (&functions<3>::balance) (types<3>::forest * p8est, + types<3>::balance_type btype, + p8est_init_t init_fn) + = p8est_balance; + + void (&functions<3>::partition) (types<3>::forest * p8est, + int partition_for_coarsening, + p8est_weight_t weight_fn) + = p8est_partition_ext; + + void (&functions<3>::save) (const char *filename, + types<3>::forest * p4est, + int save_data) + = p8est_save; + + types<3>::forest * + (&functions<3>::load) (const char *filename, + MPI_Comm mpicomm, + std::size_t data_size, + int load_data, + void *user_pointer, + types<3>::connectivity ** p4est) + = p8est_load; + + void (&functions<3>::connectivity_save) (const char *filename, + types<3>::connectivity *connectivity) + = p8est_connectivity_save; + + types<3>::connectivity * + (&functions<3>::connectivity_load) (const char *filename, + long *length) + = p8est_connectivity_load; + + unsigned (&functions<3>::checksum) (types<3>::forest * p8est) + = p8est_checksum; + + void (&functions<3>::vtk_write_file) (types<3>::forest * p8est, + p8est_geometry_t*, + const char *baseName) + = p8est_vtk_write_file; + + types<3>::ghost* (&functions<3>::ghost_new) (types<3>::forest * p4est, + types<3>::balance_type btype) + = p8est_ghost_new; + + void (&functions<3>::ghost_destroy) (types<3>::ghost * ghost) + = p8est_ghost_destroy; + + void (&functions<3>::reset_data) (types<3>::forest * p4est, + size_t data_size, + p8est_init_t init_fn, + void *user_pointer) + = p8est_reset_data; + + size_t (&functions<3>::forest_memory_used) (types<3>::forest * p4est) + = p8est_memory_used; + + size_t (&functions<3>::connectivity_memory_used) (types<3>::connectivity * p4est) + = p8est_connectivity_memory_used; + + + + + template + void + init_quadrant_children + (const typename types::quadrant & p4est_cell, + typename types::quadrant (&p4est_children)[GeometryInfo::max_children_per_cell]) + { + + for (unsigned int c=0; + c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_children[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_children[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + + functions::quadrant_childrenv (&p4est_cell, + p4est_children); + + } + + + template + void + init_coarse_quadrant(typename types::quadrant & quad) + { + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&quad); + break; + case 3: + P8EST_QUADRANT_INIT(&quad); + break; + default: + Assert (false, ExcNotImplemented()); + } + functions::quadrant_set_morton (&quad, + /*level=*/0, + /*index=*/0); + } + + + template + bool + quadrant_is_equal (const typename types::quadrant & q1, + const typename types::quadrant & q2) + { + return functions::quadrant_is_equal(&q1, &q2); + } + + + + template + bool + quadrant_is_ancestor (const typename types::quadrant & q1, + const typename types::quadrant & q2) + { + return functions::quadrant_is_ancestor(&q1, &q2); + } + } +} + + +namespace +{ + template + void + get_vertex_to_cell_mappings (const Triangulation &triangulation, + std::vector &vertex_touch_count, + std::vector::active_cell_iterator,unsigned int> > > + & vertex_to_cell) + { + vertex_touch_count.resize (triangulation.n_vertices()); + vertex_to_cell.resize (triangulation.n_vertices()); + + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) + for (unsigned int v=0; v::vertices_per_cell; ++v) + { + ++vertex_touch_count[cell->vertex_index(v)]; + vertex_to_cell[cell->vertex_index(v)] + .push_back (std::make_pair (cell, v)); + } + } + + + + template + void + get_edge_to_cell_mappings (const Triangulation &triangulation, + std::vector &edge_touch_count, + std::vector::active_cell_iterator,unsigned int> > > + & edge_to_cell) + { + Assert (triangulation.n_levels() == 1, ExcInternalError()); + + edge_touch_count.resize (triangulation.n_active_lines()); + edge_to_cell.resize (triangulation.n_active_lines()); + + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) + for (unsigned int l=0; l::lines_per_cell; ++l) + { + ++edge_touch_count[cell->line(l)->index()]; + edge_to_cell[cell->line(l)->index()] + .push_back (std::make_pair (cell, l)); + } + } + + + + /** + * Set all vertex and cell + * related information in the + * p4est connectivity structure. + */ + template + void + set_vertex_and_cell_info (const Triangulation &triangulation, + const std::vector &vertex_touch_count, + const std::vector::active_cell_iterator,unsigned int> > > + & vertex_to_cell, + const std::vector &coarse_cell_to_p4est_tree_permutation, + const bool set_vertex_info, + typename internal::p4est::types::connectivity *connectivity) + { + // copy the vertices into the + // connectivity structure. the + // triangulation exports the array of + // vertices, but some of the entries are + // sometimes unused; this shouldn't be + // the case for a newly created + // triangulation, but make sure + // + // note that p4est stores coordinates as + // a triplet of values even in 2d + Assert (triangulation.get_used_vertices().size() == + triangulation.get_vertices().size(), + ExcInternalError()); + Assert (std::find (triangulation.get_used_vertices().begin(), + triangulation.get_used_vertices().end(), + false) + == triangulation.get_used_vertices().end(), + ExcInternalError()); + if (set_vertex_info == true) + for (unsigned int v=0; vvertices[3*v ] = triangulation.get_vertices()[v][0]; + connectivity->vertices[3*v+1] = triangulation.get_vertices()[v][1]; + connectivity->vertices[3*v+2] = (spacedim == 2 ? + 0 + : + triangulation.get_vertices()[v][2]); + } + + // next store the tree_to_vertex indices + // (each tree is here only a single cell + // in the coarse mesh). p4est requires + // vertex numbering in clockwise + // orientation + // + // while we're at it, also copy the + // neighborship information between cells + typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(), + endc = triangulation.end(); + for (; cell != endc; ++cell) + { + const unsigned int + index = coarse_cell_to_p4est_tree_permutation[cell->index()]; + + for (unsigned int v=0; v::vertices_per_cell; ++v) + { + if (set_vertex_info == true) + connectivity->tree_to_vertex[index*GeometryInfo::vertices_per_cell+v] = cell->vertex_index(v); + connectivity->tree_to_corner[index*GeometryInfo::vertices_per_cell+v] = cell->vertex_index(v); + } + + // neighborship information. if a + // cell is at a boundary, then enter + // the index of the cell itself here + for (unsigned int f=0; f::faces_per_cell; ++f) + if (cell->face(f)->at_boundary() == false) + connectivity->tree_to_tree[index*GeometryInfo::faces_per_cell + f] + = coarse_cell_to_p4est_tree_permutation[cell->neighbor(f)->index()]; + else + connectivity->tree_to_tree[index*GeometryInfo::faces_per_cell + f] + = coarse_cell_to_p4est_tree_permutation[cell->index()]; + + // fill tree_to_face, which is + // essentially neighbor_to_neighbor; + // however, we have to remap the + // resulting face number as well + for (unsigned int f=0; f::faces_per_cell; ++f) + if (cell->face(f)->at_boundary() == false) + { + switch (dim) + { + case 2: + { + connectivity->tree_to_face[index*GeometryInfo::faces_per_cell + f] + = cell->neighbor_of_neighbor (f); + break; + } + + case 3: + { + /* + * The values for + * tree_to_face are in + * 0..23 where ttf % 6 + * gives the face + * number and ttf / 6 + * the face orientation + * code. The + * orientation is + * determined as + * follows. Let + * my_face and + * other_face be the + * two face numbers of + * the connecting trees + * in 0..5. Then the + * first face vertex of + * the lower of my_face + * and other_face + * connects to a face + * vertex numbered 0..3 + * in the higher of + * my_face and + * other_face. The + * face orientation is + * defined as this + * number. If my_face + * == other_face, + * treating either of + * both faces as the + * lower one leads to + * the same result. + */ + + connectivity->tree_to_face[index*6 + f] + = cell->neighbor_of_neighbor (f); + + unsigned int face_idx_list[2] = + {f, cell->neighbor_of_neighbor (f)}; + typename Triangulation::active_cell_iterator + cell_list[2] = {cell, cell->neighbor(f)}; + unsigned int smaller_idx = 0; + + if (f>cell->neighbor_of_neighbor (f)) + smaller_idx = 1; + + unsigned larger_idx = (smaller_idx+1) % 2; + //smaller = *_list[smaller_idx] + //larger = *_list[larger_idx] + + unsigned int v = 0; + + // global vertex index of + // vertex 0 on face of + // cell with smaller + // local face index + unsigned int g_idx = + cell_list[smaller_idx]->vertex_index( + GeometryInfo::face_to_cell_vertices( + face_idx_list[smaller_idx], + 0, + cell_list[smaller_idx]->face_orientation(face_idx_list[smaller_idx]), + cell_list[smaller_idx]->face_flip(face_idx_list[smaller_idx]), + cell_list[smaller_idx]->face_rotation(face_idx_list[smaller_idx])) + ); + + // loop over vertices on + // face from other cell + // and compare global + // vertex numbers + for (unsigned int i=0; i::vertices_per_face; ++i) + { + unsigned int idx + = + cell_list[larger_idx]->vertex_index( + GeometryInfo::face_to_cell_vertices( + face_idx_list[larger_idx], + i) + ); + + if (idx==g_idx) + { + v = i; + break; + } + } + + connectivity->tree_to_face[index*6 + f] += 6*v; + break; + } + + default: + Assert (false, ExcNotImplemented()); + } + } + else + connectivity->tree_to_face[index*GeometryInfo::faces_per_cell + f] = f; + } + + // now fill the vertex information + connectivity->ctt_offset[0] = 0; + std::partial_sum (vertex_touch_count.begin(), + vertex_touch_count.end(), + &connectivity->ctt_offset[1]); + + const typename internal::p4est::types::locidx + num_vtt = std::accumulate (vertex_touch_count.begin(), + vertex_touch_count.end(), + 0); + Assert (connectivity->ctt_offset[triangulation.n_vertices()] == + num_vtt, + ExcInternalError()); + + for (unsigned int v=0; v::active_cell_iterator, + unsigned int> >::const_iterator + p = vertex_to_cell[v].begin(); + for (unsigned int c=0; ccorner_to_tree[connectivity->ctt_offset[v]+c] + = coarse_cell_to_p4est_tree_permutation[p->first->index()]; + connectivity->corner_to_corner[connectivity->ctt_offset[v]+c] + = p->second; + } + } + } + + + + template + bool + tree_exists_locally (const typename internal::p4est::types::forest *parallel_forest, + const typename internal::p4est::types::topidx coarse_grid_cell) + { + Assert (coarse_grid_cell < parallel_forest->connectivity->num_trees, + ExcInternalError()); + return ((coarse_grid_cell >= parallel_forest->first_local_tree) + && + (coarse_grid_cell <= parallel_forest->last_local_tree)); + } + + + template + void + delete_all_children_and_self (const typename Triangulation::cell_iterator &cell) + { + if (cell->has_children()) + for (unsigned int c=0; cn_children(); ++c) + delete_all_children_and_self (cell->child(c)); + else + cell->set_coarsen_flag (); + } + + + + template + void + delete_all_children (const typename Triangulation::cell_iterator &cell) + { + if (cell->has_children()) + for (unsigned int c=0; cn_children(); ++c) + delete_all_children_and_self (cell->child(c)); + } + + + + template + void + match_tree_recursively (const typename internal::p4est::types::tree &tree, + const typename Triangulation::cell_iterator &dealii_cell, + const typename internal::p4est::types::quadrant &p4est_cell, + const typename internal::p4est::types::forest &forest, + const types::subdomain_id_t my_subdomain) + { + // check if this cell exists in + // the local p4est cell + if (sc_array_bsearch(const_cast(&tree.quadrants), + &p4est_cell, + internal::p4est::functions::quadrant_compare) + != -1) + { + // yes, cell found in local part of p4est + delete_all_children (dealii_cell); + dealii_cell->set_subdomain_id(my_subdomain); + } + else + { + // no, cell not found in + // local part of + // p4est. this means that + // the local part is more + // refined than the current + // cell. if this cell has + // no children of its own, + // we need to refine it, + // and if it does already + // have children then loop + // over all children and + // see if they are locally + // available as well + if (dealii_cell->has_children () == false) + dealii_cell->set_refine_flag (); + else + { + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; + c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + + internal::p4est::functions:: + quadrant_childrenv (&p4est_cell, + p4est_child); + + for (unsigned int c=0; + c::max_children_per_cell; ++c) + if (internal::p4est::functions:: + quadrant_overlaps_tree (const_cast::tree*>(&tree), + &p4est_child[c]) + == false) + { + // no, this child + // is locally not + // available in + // the p4est. + // delete all + // its children + // but, because this + // may not be successfull, + // make sure to mark all + // children recursively as + // not local. + delete_all_children (dealii_cell->child(c)); + dealii_cell->child(c) + ->recursively_set_subdomain_id(types::artificial_subdomain_id); + } + else + { + // at least some + // part of the + // tree rooted in + // this child is + // locally + // available + match_tree_recursively (tree, + dealii_cell->child(c), + p4est_child[c], + forest, + my_subdomain); + } + } + } + } + + + template + void + match_quadrant_recursively (const typename internal::p4est::types::quadrant &this_quadrant, + const typename Triangulation::cell_iterator &dealii_cell, + const typename internal::p4est::types::quadrant &ghost_quadrant, + const typename internal::p4est::types::forest &forest, + unsigned int ghost_owner) + { + if (internal::p4est::functions::quadrant_is_equal(&this_quadrant, &ghost_quadrant)) + { + // this is the ghostcell + dealii_cell->set_subdomain_id(ghost_owner); + + if (dealii_cell->has_children()) + delete_all_children (dealii_cell); + else + dealii_cell->clear_coarsen_flag(); + return; + } + + if (! internal::p4est::functions::quadrant_is_ancestor ( &this_quadrant, &ghost_quadrant)) + { + return; + } + + if (dealii_cell->has_children () == false) + { + dealii_cell->clear_coarsen_flag(); + dealii_cell->set_refine_flag (); + return; + } + + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; + c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + + internal::p4est::functions:: + quadrant_childrenv (&this_quadrant, p4est_child); + + for (unsigned int c=0; + c::max_children_per_cell; ++c) + + { + match_quadrant_recursively (p4est_child[c], dealii_cell->child(c), ghost_quadrant, forest, ghost_owner); + } + + + } + + + + template + void + attach_mesh_data_recursively (const typename internal::p4est::types::tree &tree, + const typename Triangulation::cell_iterator &dealii_cell, + const typename internal::p4est::types::quadrant &p4est_cell, + const typename std::list::cell_iterator, + typename parallel::distributed::Triangulation::CellStatus, + void*) + > > > &attached_data_pack_callbacks) + { + typedef std::list::cell_iterator, + typename parallel::distributed::Triangulation::CellStatus, + void*) + > > > callback_list_t; + + int idx = sc_array_bsearch(const_cast(&tree.quadrants), + &p4est_cell, + internal::p4est::functions::quadrant_compare); + + if (idx == -1 && (internal::p4est::functions:: + quadrant_overlaps_tree (const_cast::tree*>(&tree), + &p4est_cell) + == false)) + return; //this quadrant and none of it's childs belongs to us. + + bool p4est_has_children = (idx == -1); + + if (p4est_has_children && dealii_cell->has_children()) + { + //recurse further + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + internal::p4est::functions:: + quadrant_childrenv (&p4est_cell, p4est_child); + + for (unsigned int c=0; + c::max_children_per_cell; ++c) + { + attach_mesh_data_recursively (tree, + dealii_cell->child(c), + p4est_child[c], + attached_data_pack_callbacks); + } + } + else if (!p4est_has_children && !dealii_cell->has_children()) + { + //this active cell didn't change + typename internal::p4est::types::quadrant *q; + q = static_cast::quadrant*> ( + sc_array_index (const_cast(&tree.quadrants), idx) + ); + *static_cast::CellStatus*>(q->p.user_data) = parallel::distributed::Triangulation::CELL_PERSIST; + + for(typename callback_list_t::const_iterator it = attached_data_pack_callbacks.begin(); + it != attached_data_pack_callbacks.end(); + ++it) + { + void * ptr = static_cast(q->p.user_data) + (*it).first; //add offset + ((*it).second)(dealii_cell, + parallel::distributed::Triangulation::CELL_PERSIST, + ptr); + } + } + else if (p4est_has_children) + { + //this cell got refined + + //attach to the first child, because + // we can only attach to active + // quadrants + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + internal::p4est::functions:: + quadrant_childrenv (&p4est_cell, p4est_child); + int child0_idx = sc_array_bsearch(const_cast(&tree.quadrants), + &p4est_child[0], + internal::p4est::functions::quadrant_compare); + Assert(child0_idx != -1, ExcMessage("the first child should exist as an active quadrant!")); + + typename internal::p4est::types::quadrant *q; + q = static_cast::quadrant*> ( + sc_array_index (const_cast(&tree.quadrants), child0_idx) + ); + *static_cast::CellStatus*>(q->p.user_data) = parallel::distributed::Triangulation::CELL_REFINE; + + for(typename callback_list_t::const_iterator it = attached_data_pack_callbacks.begin(); + it != attached_data_pack_callbacks.end(); + ++it) + { + void * ptr = static_cast(q->p.user_data) + (*it).first; //add offset + + ((*it).second)(dealii_cell, + parallel::distributed::Triangulation::CELL_REFINE, + ptr); + } + + //mark other childs as invalid, so that unpack only happens once + for (unsigned int i=1;i::max_children_per_cell; ++i) + { + int child_idx = sc_array_bsearch(const_cast(&tree.quadrants), + &p4est_child[i], + internal::p4est::functions::quadrant_compare); + q = static_cast::quadrant*> ( + sc_array_index (const_cast(&tree.quadrants), child_idx) + ); + *static_cast::CellStatus*>(q->p.user_data) = parallel::distributed::Triangulation::CELL_INVALID; + } + + + } + else + { + //it's children got coarsened into + //this cell + typename internal::p4est::types::quadrant *q; + q = static_cast::quadrant*> ( + sc_array_index (const_cast(&tree.quadrants), idx) + ); + *static_cast::CellStatus*>(q->p.user_data) = parallel::distributed::Triangulation::CELL_COARSEN; + + for(typename callback_list_t::const_iterator it = attached_data_pack_callbacks.begin(); + it != attached_data_pack_callbacks.end(); + ++it) + { + void * ptr = static_cast(q->p.user_data) + (*it).first; //add offset + ((*it).second)(dealii_cell, + parallel::distributed::Triangulation::CELL_COARSEN, + ptr); + } + } + } + + + template + void + post_mesh_data_recursively (const typename internal::p4est::types::tree &tree, + const typename Triangulation::cell_iterator &dealii_cell, + const typename Triangulation::cell_iterator &parent_cell, + const typename internal::p4est::types::quadrant &p4est_cell, + const unsigned int offset, + const typename std_cxx1x::function< + void(typename parallel::distributed::Triangulation::cell_iterator, typename parallel::distributed::Triangulation::CellStatus, void*) + > &unpack_callback) + { + int idx = sc_array_bsearch(const_cast(&tree.quadrants), + &p4est_cell, + internal::p4est::functions::quadrant_compare); + if (idx == -1 && (internal::p4est::functions:: + quadrant_overlaps_tree (const_cast::tree*>(&tree), + &p4est_cell) + == false)) + // this quadrant and none of + // it's children belong to us. + return; + + + const bool p4est_has_children = (idx == -1); + if (p4est_has_children) + { + Assert(dealii_cell->has_children(), ExcInternalError()); + + //recurse further + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + + internal::p4est::functions:: + quadrant_childrenv (&p4est_cell, p4est_child); + + for (unsigned int c=0; + c::max_children_per_cell; ++c) + { + post_mesh_data_recursively (tree, + dealii_cell->child(c), + dealii_cell, + p4est_child[c], + offset, + unpack_callback); + } + } + else + { + Assert(! dealii_cell->has_children(), ExcInternalError()); + + typename internal::p4est::types::quadrant *q; + q = static_cast::quadrant*> ( + sc_array_index (const_cast(&tree.quadrants), idx) + ); + + void * ptr = static_cast(q->p.user_data) + offset; + typename parallel::distributed::Triangulation::CellStatus + status = * static_cast< + typename parallel::distributed::Triangulation::CellStatus* + >(q->p.user_data); + switch (status) + { + case parallel::distributed::Triangulation::CELL_PERSIST: + { + unpack_callback(dealii_cell, status, ptr); + break; + } + case parallel::distributed::Triangulation::CELL_REFINE: + { + unpack_callback(parent_cell, status, ptr); + break; + } + case parallel::distributed::Triangulation::CELL_COARSEN: + { + unpack_callback(dealii_cell, status, ptr); + break; + } + case parallel::distributed::Triangulation::CELL_INVALID: + { + break; + } + default: + throw ExcInternalError(); + } + } + } + + + + /** + * A data structure that we use to store + * which cells (indicated by + * internal::p4est::types::quadrant objects) shall be + * refined and which shall be coarsened. + */ + template + class RefineAndCoarsenList + { + public: + RefineAndCoarsenList (const Triangulation &triangulation, + const std::vector &p4est_tree_to_coarse_cell_permutation, + const types::subdomain_id_t my_subdomain, + typename internal::p4est::types::forest &forest); + + /** + * A callback function that we + * pass to the p4est data + * structures when a forest is + * to be refined. The p4est + * functions call it back with + * a tree (the index of the + * tree that grows out of a + * given coarse cell) and a + * refinement path from that + * coarse cell to a + * terminal/leaf cell. The + * function returns whether the + * corresponding cell in the + * deal.II triangulation has + * the refined flag set. + */ + static + int + refine_callback (typename internal::p4est::types::forest *forest, + typename internal::p4est::types::topidx coarse_cell_index, + typename internal::p4est::types::quadrant *quadrant); + + /** + * Same as the refine_callback + * function, but return whether + * all four of the given + * children of a non-terminal + * cell are to be coarsened + * away. + */ + static + int + coarsen_callback (typename internal::p4est::types::forest *forest, + typename internal::p4est::types::topidx coarse_cell_index, + typename internal::p4est::types::quadrant *children[]); + + bool pointers_are_at_end () const; + + private: + std::vector::quadrant> refine_list; + typename std::vector::quadrant>::const_iterator current_refine_pointer; + + std::vector::quadrant> coarsen_list; + typename std::vector::quadrant>::const_iterator current_coarsen_pointer; + + typename internal::p4est::types::forest forest; + + void build_lists (const typename Triangulation::cell_iterator &cell, + const typename internal::p4est::types::quadrant &p4est_cell, + const unsigned int myid); + }; + + + + template + bool + RefineAndCoarsenList:: + pointers_are_at_end () const + { + return ((current_refine_pointer == refine_list.end()) + && + (current_coarsen_pointer == coarsen_list.end())); + } + + + + template + RefineAndCoarsenList:: + RefineAndCoarsenList (const Triangulation &triangulation, + const std::vector &p4est_tree_to_coarse_cell_permutation, + const types::subdomain_id_t my_subdomain, + typename internal::p4est::types::forest &forest) + : + forest(forest) + { + // count how many flags are set and + // allocate that much memory + unsigned int n_refine_flags = 0, + n_coarsen_flags = 0; + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) + { + //skip cells that are not local + if (cell->subdomain_id() != my_subdomain) + continue; + + if (cell->refine_flag_set()) + ++n_refine_flags; + else if (cell->coarsen_flag_set()) + ++n_coarsen_flags; + } + + refine_list.reserve (n_refine_flags); + coarsen_list.reserve (n_coarsen_flags); + + + // now build the lists of cells that + // are flagged. note that p4est will + // traverse its cells in the order in + // which trees appear in the + // forest. this order is not the same + // as the order of coarse cells in the + // deal.II Triangulation because we + // have translated everything by the + // coarse_cell_to_p4est_tree_permutation + // permutation. in order to make sure + // that the output array is already in + // the correct order, traverse our + // coarse cells in the same order in + // which p4est will: + for (unsigned int c=0; c::cell_iterator + cell (&triangulation, 0, coarse_cell_index); + + typename internal::p4est::types::quadrant p4est_cell; + internal::p4est::functions:: + quadrant_set_morton (&p4est_cell, + /*level=*/0, + /*index=*/0); + p4est_cell.p.which_tree = c; + build_lists (cell, p4est_cell, my_subdomain); + } + + + Assert(refine_list.size() == n_refine_flags, + ExcInternalError()); + Assert(coarsen_list.size() == n_coarsen_flags, + ExcInternalError()); + + // make sure that our ordering in fact + // worked + for (unsigned int i=1; i= + refine_list[i-1].p.which_tree, + ExcInternalError()); + for (unsigned int i=1; i= + coarsen_list[i-1].p.which_tree, + ExcInternalError()); + + current_refine_pointer = refine_list.begin(); + current_coarsen_pointer = coarsen_list.begin(); + } + + + + template + void + RefineAndCoarsenList:: + build_lists (const typename Triangulation::cell_iterator &cell, + const typename internal::p4est::types::quadrant &p4est_cell, + const types::subdomain_id_t my_subdomain) + { + if (!cell->has_children()) + { + if (cell->subdomain_id() == my_subdomain) + { + if (cell->refine_flag_set()) + refine_list.push_back (p4est_cell); + else if (cell->coarsen_flag_set()) + coarsen_list.push_back (p4est_cell); + } + } + else + { + typename internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + for (unsigned int c=0; c::max_children_per_cell; ++c) + switch (dim) + { + case 2: + P4EST_QUADRANT_INIT(&p4est_child[c]); + break; + case 3: + P8EST_QUADRANT_INIT(&p4est_child[c]); + break; + default: + Assert (false, ExcNotImplemented()); + } + internal::p4est::functions:: + quadrant_childrenv (&p4est_cell, + p4est_child); + for (unsigned int c=0; + c::max_children_per_cell; ++c) + { + p4est_child[c].p.which_tree = p4est_cell.p.which_tree; + build_lists (cell->child(c), + p4est_child[c], + my_subdomain); + } + } + } + + + template + int + RefineAndCoarsenList:: + refine_callback (typename internal::p4est::types::forest *forest, + typename internal::p4est::types::topidx coarse_cell_index, + typename internal::p4est::types::quadrant *quadrant) + { + RefineAndCoarsenList *this_object + = reinterpret_cast*>(forest->user_pointer); + + // if there are no more cells in our + // list the current cell can't be + // flagged for refinement + if (this_object->current_refine_pointer == this_object->refine_list.end()) + return false; + + Assert (coarse_cell_index <= + this_object->current_refine_pointer->p.which_tree, + ExcInternalError()); + + // if p4est hasn't yet reached the tree + // of the next flagged cell the current + // cell can't be flagged for refinement + if (coarse_cell_index < + this_object->current_refine_pointer->p.which_tree) + return false; + + // now we're in the right tree in the + // forest + Assert (coarse_cell_index <= + this_object->current_refine_pointer->p.which_tree, + ExcInternalError()); + + // make sure that the p4est loop over + // cells hasn't gotten ahead of our own + // pointer + Assert (internal::p4est::functions:: + quadrant_compare (quadrant, + &*this_object->current_refine_pointer) <= 0, + ExcInternalError()); + + // now, if the p4est cell is one in the + // list, it is supposed to be refined + if (internal::p4est::functions:: + quadrant_is_equal (quadrant, &*this_object->current_refine_pointer)) + { + ++this_object->current_refine_pointer; + return true; + } + + // p4est cell is not in list + return false; + } + + + + template + int + RefineAndCoarsenList:: + coarsen_callback (typename internal::p4est::types::forest *forest, + typename internal::p4est::types::topidx coarse_cell_index, + typename internal::p4est::types::quadrant *children[]) + { + RefineAndCoarsenList *this_object + = reinterpret_cast*>(forest->user_pointer); + + // if there are no more cells in our + // list the current cell can't be + // flagged for coarsening + if (this_object->current_coarsen_pointer == + this_object->coarsen_list.end()) + return false; + + Assert (coarse_cell_index <= + this_object->current_coarsen_pointer->p.which_tree, + ExcInternalError()); + + // if p4est hasn't yet reached the tree + // of the next flagged cell the current + // cell can't be flagged for coarsening + if (coarse_cell_index < + this_object->current_coarsen_pointer->p.which_tree) + return false; + + // now we're in the right tree in the + // forest + Assert (coarse_cell_index <= + this_object->current_coarsen_pointer->p.which_tree, + ExcInternalError()); + + // make sure that the p4est loop over + // cells hasn't gotten ahead of our own + // pointer + Assert (internal::p4est::functions:: + quadrant_compare (children[0], + &*this_object->current_coarsen_pointer) <= 0, + ExcInternalError()); + + // now, if the p4est cell is one in the + // list, it is supposed to be coarsened + if (internal::p4est::functions:: + quadrant_is_equal (children[0], + &*this_object->current_coarsen_pointer)) + { + // move current pointer one up + ++this_object->current_coarsen_pointer; + + // note that the next 3 cells in + // our list need to correspond to + // the other siblings of the cell + // we have just found + for (unsigned int c=1; c::max_children_per_cell; ++c) + { + Assert (internal::p4est::functions:: + quadrant_is_equal (children[c], + &*this_object->current_coarsen_pointer), + ExcInternalError()); + ++this_object->current_coarsen_pointer; + } + + return true; + } + + // p4est cell is not in list + return false; + } +} + + +// initialize p4est +namespace internal +{ + namespace p4est + { + struct InitFinalize + { + private: + struct Singleton + { + Singleton () + { + // ensure that the + // initialization + // code is run only + // once, even if we + // link with 1d, 2d, + // and 3d libraries + static bool initialized = false; + + if (initialized == false) + { + sc_init (MPI_COMM_WORLD, + 0, 0, 0, SC_LP_STATISTICS); + p4est_init (0, SC_LP_STATISTICS); + + initialized = true; + } + } + + ~Singleton () + { + // same here + static bool deinitialized = false; + + if (deinitialized == false) + { + // p4est has no + // p4est_finalize + // function + sc_finalize (); + + deinitialized = true; + } + } + }; + + public: + // do run the initialization code, at least the first time around we + // get to this function + static void do_initialize () + { + static Singleton singleton; + } + }; + } +} + + +namespace parallel +{ + namespace distributed + { + +/* ---------------------- class Triangulation ------------------------------ */ + + + template + Triangulation:: + Triangulation (MPI_Comm mpi_communicator, + const typename dealii::Triangulation::MeshSmoothing smooth_grid) + : + // do not check + // for distorted + // cells + dealii::Triangulation + (smooth_grid, + false), + mpi_communicator (Utilities::System:: + duplicate_communicator(mpi_communicator)), + my_subdomain (Utilities::System::get_this_mpi_process (this->mpi_communicator)), + triangulation_has_content (false), + connectivity (0), + parallel_forest (0), + refinement_in_progress (false), + attached_data_size(0), + n_attached_datas(0) + { + // initialize p4est. do this in a separate function since it has + // to happen only once, even if we have triangulation objects + // for several different space dimensions + internal::p4est::InitFinalize::do_initialize (); + + number_cache.n_locally_owned_active_cells + .resize (Utilities::System::get_n_mpi_processes (mpi_communicator)); + } + + + + template + Triangulation::~Triangulation () + { + clear (); + + Assert (triangulation_has_content == false, + ExcInternalError()); + Assert (connectivity == 0, ExcInternalError()); + Assert (parallel_forest == 0, ExcInternalError()); + Assert (refinement_in_progress == false, ExcInternalError()); + + // get rid of the unique + // communicator used here again + MPI_Comm_free (&mpi_communicator); + } + + + + + template + void + Triangulation:: + create_triangulation (const std::vector > &vertices, + const std::vector > &cells, + const SubCellData &subcelldata) + { + try + { + dealii::Triangulation:: + create_triangulation (vertices, cells, subcelldata); + } + catch (const typename dealii::Triangulation::DistortedCellList &) + { + // the underlying + // triangulation should not + // be checking for + // distorted cells + AssertThrow (false, ExcInternalError()); + } + + // note that now we have some content in + // the p4est objects and call the + // functions that do the actual work + // (which are dimension dependent, so + // separate) + triangulation_has_content = true; + + setup_coarse_cell_to_p4est_tree_permutation (); + + copy_new_triangulation_to_p4est (internal::int2type()); + + try + { + copy_local_forest_to_triangulation (); + } + catch (const typename Triangulation::DistortedCellList &) + { + // the underlying + // triangulation should not + // be checking for + // distorted cells + AssertThrow (false, ExcInternalError()); + } + + update_number_cache (); + } + + + + template + void + Triangulation::clear () + { + triangulation_has_content = false; + + if (parallel_forest != 0) + { + internal::p4est::functions::destroy (parallel_forest); + parallel_forest = 0; + } + + if (connectivity != 0) + { + internal::p4est::functions::connectivity_destroy (connectivity); + connectivity = 0; + } + + coarse_cell_to_p4est_tree_permutation.resize (0); + p4est_tree_to_coarse_cell_permutation.resize (0); + + dealii::Triangulation::clear (); + } + + + + template + void + Triangulation::setup_coarse_cell_to_p4est_tree_permutation () + { + SparsityPattern cell_connectivity; + GridTools::get_face_connectivity_of_cells (*this, cell_connectivity); + coarse_cell_to_p4est_tree_permutation.resize (this->n_cells(0)); + SparsityTools:: + reorder_Cuthill_McKee (cell_connectivity, + coarse_cell_to_p4est_tree_permutation); + + p4est_tree_to_coarse_cell_permutation + = Utilities::invert_permutation (coarse_cell_to_p4est_tree_permutation); + } + + + + template + void + Triangulation::write_mesh_vtk (const char *file_basename) const + { + Assert (parallel_forest != 0, + ExcMessage ("Can't produce output when no forest is created yet.")); + internal::p4est::functions:: + vtk_write_file (parallel_forest, 0, file_basename); + } + + + + template + unsigned int + Triangulation::get_checksum () const + { + Assert (parallel_forest != 0, + ExcMessage ("Can't produce a check sum when no forest is created yet.")); + return internal::p4est::functions::checksum (parallel_forest); + } + + + template + typename dealii::internal::p4est::types::tree * + Triangulation:: + init_tree(const int dealii_coarse_cell_index) const + { + const unsigned int tree_index + = coarse_cell_to_p4est_tree_permutation[dealii_coarse_cell_index]; + typename dealii::internal::p4est::types::tree *tree + = static_cast::tree*> + (sc_array_index (parallel_forest->trees, + tree_index)); + + return tree; + } + + + +#if deal_II_dimension == 2 + + template + void + Triangulation::copy_new_triangulation_to_p4est (internal::int2type<2>) + { + Assert (this->n_cells(0) > 0, ExcInternalError()); + Assert (this->n_levels() == 1, ExcInternalError()); + + // data structures that counts how many + // cells touch each vertex + // (vertex_touch_count), and which cells + // touch a given vertex (together with + // the local numbering of that vertex + // within the cells that touch it) + std::vector vertex_touch_count; + std::vector< + std::list< + std::pair::active_cell_iterator, + unsigned int> > > + vertex_to_cell; + get_vertex_to_cell_mappings (*this, + vertex_touch_count, + vertex_to_cell); + const internal::p4est::types<2>::locidx + num_vtt = std::accumulate (vertex_touch_count.begin(), + vertex_touch_count.end(), + 0); + + // now create a connectivity + // object with the right sizes + // for all arrays. set vertex + // information only in debug + // mode (saves a few bytes in + // optimized mode) + const bool set_vertex_info +#ifdef DEBUG + = true +#else + = false +#endif + ; + + connectivity + = internal::p4est::functions<2>:: + connectivity_new ((set_vertex_info == true ? this->n_vertices() : 0), + this->n_cells(0), + this->n_vertices(), + num_vtt); + + set_vertex_and_cell_info (*this, + vertex_touch_count, + vertex_to_cell, + coarse_cell_to_p4est_tree_permutation, + set_vertex_info, + connectivity); + + Assert (p4est_connectivity_is_valid (connectivity) == 1, + ExcInternalError()); + + // now create a forest out of the + // connectivity data structure + parallel_forest + = internal::p4est::functions<2>:: + new_forest (mpi_communicator, + connectivity, + /* minimum initial number of quadrants per tree */ 0, + /* minimum level of upfront refinement */ 0, + /* use uniform upfront refinement */ 1, + /* user_data_size = */ 0, + /* user_data_constructor = */ NULL, + /* user_pointer */ this); + } + +#endif + +#if deal_II_dimension == 3 + + template + void + Triangulation::copy_new_triangulation_to_p4est (internal::int2type<3>) + { + Assert (this->n_cells(0) > 0, ExcInternalError()); + Assert (this->n_levels() == 1, ExcInternalError()); + + // data structures that counts how many + // cells touch each vertex + // (vertex_touch_count), and which cells + // touch a given vertex (together with + // the local numbering of that vertex + // within the cells that touch it) + std::vector vertex_touch_count; + std::vector< + std::list< + std::pair::active_cell_iterator, + unsigned int> > > + vertex_to_cell; + get_vertex_to_cell_mappings (*this, + vertex_touch_count, + vertex_to_cell); + const internal::p4est::types<2>::locidx + num_vtt = std::accumulate (vertex_touch_count.begin(), + vertex_touch_count.end(), + 0); + + std::vector edge_touch_count; + std::vector< + std::list< + std::pair::active_cell_iterator, + unsigned int> > > + edge_to_cell; + get_edge_to_cell_mappings (*this, + edge_touch_count, + edge_to_cell); + const internal::p4est::types<2>::locidx + num_ett = std::accumulate (edge_touch_count.begin(), + edge_touch_count.end(), + 0); + // now create a connectivity object with + // the right sizes for all arrays + + const bool set_vertex_info +#ifdef DEBUG + = true +#else + = false +#endif + ; + + connectivity + = internal::p4est::functions<3>:: + connectivity_new ((set_vertex_info == true ? this->n_vertices() : 0), + this->n_cells(0), + this->n_active_lines(), + num_ett, + this->n_vertices(), + num_vtt); + + set_vertex_and_cell_info (*this, + vertex_touch_count, + vertex_to_cell, + coarse_cell_to_p4est_tree_permutation, + set_vertex_info, + connectivity); + + // next to tree-to-edge + // data. note that in p4est lines + // are ordered as follows + // *---3---* *---3---* + // /| | / /| + // 6 | 11 6 7 11 + // / 10 | / / | + // * | | *---2---* | + // | *---1---* | | * + // | / / | 9 / + // 8 4 5 8 | 5 + // |/ / | |/ + // *---0---* *---0---* + // whereas in deal.II they are like this: + // *---7---* *---7---* + // /| | / /| + // 4 | 11 4 5 11 + // / 10 | / / | + // * | | *---6---* | + // | *---3---* | | * + // | / / | 9 / + // 8 0 1 8 | 1 + // |/ / | |/ + // *---2---* *---2---* + + const unsigned int deal_to_p4est_line_index[12] + = { 4, 5, 0, 1, 6, 7, 2, 3, 8, 9, 10, 11 } ; + + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); ++cell) + { + const unsigned int + index = coarse_cell_to_p4est_tree_permutation[cell->index()]; + for (unsigned int e=0; e::lines_per_cell; ++e) + connectivity->tree_to_edge[index*GeometryInfo<3>::lines_per_cell+ + deal_to_p4est_line_index[e]] + = cell->line(e)->index(); + } + + // now also set edge-to-tree + // information + connectivity->ett_offset[0] = 0; + std::partial_sum (edge_touch_count.begin(), + edge_touch_count.end(), + &connectivity->ett_offset[1]); + + Assert (connectivity->ett_offset[this->n_active_lines()] == + num_ett, + ExcInternalError()); + + for (unsigned int v=0; vn_active_lines(); ++v) + { + Assert (edge_to_cell[v].size() == edge_touch_count[v], + ExcInternalError()); + + typename std::list::active_cell_iterator, + unsigned int> >::const_iterator + p = edge_to_cell[v].begin(); + for (unsigned int c=0; cedge_to_tree[connectivity->ett_offset[v]+c] + = coarse_cell_to_p4est_tree_permutation[p->first->index()]; + connectivity->edge_to_edge[connectivity->ett_offset[v]+c] + = deal_to_p4est_line_index[p->second]; + } + } + + Assert (p8est_connectivity_is_valid (connectivity) == 1, + ExcInternalError()); + + // now create a forest out of the + // connectivity data structure + parallel_forest + = internal::p4est::functions<3>:: + new_forest (mpi_communicator, + connectivity, + /* minimum initial number of quadrants per tree */ 0, + /* minimum level of upfront refinement */ 0, + /* use uniform upfront refinement */ 1, + /* user_data_size = */ 0, + /* user_data_constructor = */ NULL, + /* user_pointer */ this); + } + +#endif + + + template + void + Triangulation::copy_local_forest_to_triangulation () + { + // disable mesh smoothing for + // recreating the deal.II + // triangulation, otherwise we might + // not be able to reproduce the p4est + // mesh exactly. We restore the + // original smoothing at the end of + // this function. Note that the + // smoothing flag is used in the normal + // refinement process. + typename Triangulation::MeshSmoothing + save_smooth = this->smooth_grid; + this->smooth_grid = dealii::Triangulation::none; + bool mesh_changed = false; + + // query p4est for the ghost cells + typename internal::p4est::types::ghost * ghostlayer; + ghostlayer = internal::p4est::functions::ghost_new (parallel_forest, + (dim == 2 + ? + typename internal::p4est::types:: + balance_type(P4EST_BALANCE_CORNER) + : + typename internal::p4est::types:: + balance_type(P8EST_BALANCE_CORNER))); + + Assert (ghostlayer, ExcInternalError()); + + + //set all cells to artificial + for (typename Triangulation::cell_iterator + cell = this->begin(0); + cell != this->end(0); + ++cell) + cell->recursively_set_subdomain_id(types::artificial_subdomain_id); + + do + { + for (typename Triangulation::cell_iterator + cell = this->begin(0); + cell != this->end(0); + ++cell) + { + // if this processor + // stores no part of the + // forest that comes out + // of this coarse grid + // cell, then we need to + // delete all children of + // this cell (the coarse + // grid cell remains) + if (tree_exists_locally(parallel_forest, + coarse_cell_to_p4est_tree_permutation[cell->index()]) + == false) + { + delete_all_children (cell); + cell->set_subdomain_id (types::artificial_subdomain_id); + } + + else + { + + // this processor + // stores at least a + // part of the tree + // that comes out of + // this cell. + + + typename internal::p4est::types::quadrant p4est_coarse_cell; + typename internal::p4est::types::tree *tree = + init_tree(cell->index()); + + internal::p4est::init_coarse_quadrant(p4est_coarse_cell); + + match_tree_recursively (*tree, cell, + p4est_coarse_cell, + *parallel_forest, + my_subdomain); + } + } + + // check mesh for ghostcells, + // refine as neccessary. + // iterate over every ghostquadrant, + // find corresponding deal coarsecell + // and recurse. + typename internal::p4est::types::quadrant * quadr; + unsigned int ghost_owner=0; + + for (unsigned int g_idx=0;g_idxghosts.elem_count;++g_idx) + { + while (g_idx >= (unsigned int)ghostlayer->proc_offsets[ghost_owner+1]) + ++ghost_owner; + + quadr = static_cast::quadrant *> + ( sc_array_index(&ghostlayer->ghosts, g_idx) ); + + unsigned int coarse_cell_index = + p4est_tree_to_coarse_cell_permutation[quadr->p.piggy3.which_tree]; + + const typename Triangulation::cell_iterator + cell (this, 0U, coarse_cell_index); + + typename internal::p4est::types::quadrant p4est_coarse_cell; + internal::p4est::init_coarse_quadrant (p4est_coarse_cell); + + match_quadrant_recursively (p4est_coarse_cell, cell, *quadr, + *parallel_forest, ghost_owner); + } + + // fix all the flags to make + // sure we have a consistent + // mesh + this->prepare_coarsening_and_refinement (); + + // see if any flags are still set + mesh_changed = false; + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); + ++cell) + if (cell->refine_flag_set() || cell->coarsen_flag_set()) + { + mesh_changed = true; + break; + } + + // actually do the refinement + // but prevent the refinement + // hook below from taking + // over + const bool saved_refinement_in_progress = refinement_in_progress; + refinement_in_progress = true; + + try + { + this->execute_coarsening_and_refinement(); + } + catch (const typename Triangulation::DistortedCellList &) + { + // the underlying + // triangulation should not + // be checking for + // distorted cells + AssertThrow (false, ExcInternalError()); + } + + refinement_in_progress = saved_refinement_in_progress; + } + while (mesh_changed); + +#ifdef DEBUG + // check if correct number of ghosts is created + unsigned int num_ghosts = 0; + + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); + ++cell) + { + if (cell->subdomain_id() != my_subdomain + && + cell->subdomain_id() != types::artificial_subdomain_id) + ++num_ghosts; + } + + Assert( num_ghosts == ghostlayer->ghosts.elem_count, ExcInternalError()); +#endif + + + + // check that our local copy has + // exactly as many cells as the + // p4est original (at least if we + // are on only one processor); + // for parallel computations, we + // want to check that we have at + // least as many as p4est stores + // locally (in the future we + // should check that we have + // exactly as many non-artificial + // cells as + // parallel_forest->local_num_quadrants) + { + const unsigned int total_local_cells = this->n_active_cells(); + + if (Utilities::System::get_n_mpi_processes (mpi_communicator) == 1) + Assert (static_cast(parallel_forest->local_num_quadrants) == + total_local_cells, + ExcInternalError()) + else + Assert (static_cast(parallel_forest->local_num_quadrants) <= + total_local_cells, + ExcInternalError()); + + // count the number of owned, active + // cells and compare with p4est. + unsigned int n_owned = 0; + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); ++cell) + { + if (cell->subdomain_id() == my_subdomain) + ++n_owned; + } + + Assert(static_cast(parallel_forest->local_num_quadrants) == + n_owned, ExcInternalError()); + + } + + internal::p4est::functions::ghost_destroy (ghostlayer); + + this->smooth_grid = save_smooth; + } + + + + template + void + Triangulation::execute_coarsening_and_refinement () + { + // first make sure that recursive + // calls are handled correctly + if (refinement_in_progress == true) + { + dealii::Triangulation::execute_coarsening_and_refinement (); + return; + } + + Timer t(MPI_COMM_WORLD, true); + t.start(); + + + + // now do the work we're + // supposed to do when we are + // in charge + refinement_in_progress = true; + this->prepare_coarsening_and_refinement (); + + // make sure all flags are + // cleared on cells we don't + // own, since nothing good can + // come of that if they are + // still around + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); ++cell) + if (cell->is_ghost() || cell->is_artificial()) + { + cell->clear_refine_flag (); + cell->clear_coarsen_flag (); + } + + + + // count how many cells will be refined + // and coarsened, and allocate that much + // memory + RefineAndCoarsenList + refine_and_coarsen_list (*this, + p4est_tree_to_coarse_cell_permutation, + my_subdomain, + *parallel_forest); + + // copy refine and coarsen flags into + // p4est and execute the refinement and + // coarsening. this uses the + // refine_and_coarsen_list just built, + // which is communicated to the callback + // functions through the + // user_pointer + Assert (parallel_forest->user_pointer == this, + ExcInternalError()); + parallel_forest->user_pointer = &refine_and_coarsen_list; + + internal::p4est::functions:: + refine (parallel_forest, /* refine_recursive */ false, + &RefineAndCoarsenList::refine_callback, + /*init_callback=*/NULL); + internal::p4est::functions:: + coarsen (parallel_forest, /* coarsen_recursive */ false, + &RefineAndCoarsenList::coarsen_callback, + /*init_callback=*/NULL); + + t.stop(); +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (my_subdomain==0) + { + deallog << "_p4est::ref&coarsen "; + t.print_data(deallog); + } +#endif + + t.reset(); + t.start(); + + + // make sure all cells in the lists have + // been consumed + Assert (refine_and_coarsen_list.pointers_are_at_end(), + ExcInternalError()); + + // reset the pointer + parallel_forest->user_pointer = this; + + // enforce 2:1 hanging node condition + internal::p4est::functions:: + balance (parallel_forest, + /* face and corner balance */ + (dim == 2 + ? + typename internal::p4est::types:: + balance_type(P4EST_BALANCE_FULL) + : + typename internal::p4est::types:: + balance_type(P8EST_BALANCE_FULL)), + /*init_callback=*/NULL); + + + t.stop(); +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (my_subdomain==0) + { + deallog << "_p4est::balance "; + t.print_data(deallog); + } +#endif + t.reset(); + t.start(); + + + // before repartitioning the mesh let + // others attach mesh related info + // (such as SolutionTransfer data) to + // the p4est + attach_mesh_data(); + + + t.stop(); +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (my_subdomain==0) + { + deallog << "_attach_mesh_data "; + t.print_data(deallog); + } +#endif + t.reset(); + t.start(); + + // partition the new mesh between + // all processors + internal::p4est::functions:: + partition (parallel_forest, + /* prepare coarsening */ 1, + /* weight_callback */ NULL); + + t.stop(); +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (my_subdomain==0) + { + deallog << "_p4est::partition "; + t.print_data(deallog); + } +#endif + t.reset(); + t.start(); + + // finally copy back from local + // part of tree to deal.II + // triangulation. before doing + // so, make sure there are no + // refine or coarsen flags + // pending + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); ++cell) + { + cell->clear_refine_flag(); + cell->clear_coarsen_flag(); + } + + try + { + copy_local_forest_to_triangulation (); + } + catch (const typename Triangulation::DistortedCellList &) + { + // the underlying + // triangulation should not + // be checking for + // distorted cells + AssertThrow (false, ExcInternalError()); + } + + + refinement_in_progress = false; + + update_number_cache (); + + t.stop(); +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (my_subdomain==0) + { + deallog << "_copy_to_deal "; + t.print_data(deallog); + } +#endif + t.reset(); + } + + + + template + void + Triangulation::update_number_cache () + { + Assert (number_cache.n_locally_owned_active_cells.size() + == + Utilities::System::get_n_mpi_processes (mpi_communicator), + ExcInternalError()); + + std::fill (number_cache.n_locally_owned_active_cells.begin(), + number_cache.n_locally_owned_active_cells.end(), + 0); + + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(); + cell != this->end(); ++cell) + if (cell->subdomain_id() == my_subdomain) + ++number_cache.n_locally_owned_active_cells[my_subdomain]; + + unsigned int send_value + = number_cache.n_locally_owned_active_cells[my_subdomain]; + MPI_Allgather (&send_value, + 1, + MPI_UNSIGNED, + &number_cache.n_locally_owned_active_cells[0], + 1, + MPI_UNSIGNED, + mpi_communicator); + + number_cache.n_global_active_cells + = std::accumulate (number_cache.n_locally_owned_active_cells.begin(), + number_cache.n_locally_owned_active_cells.end(), + 0); + } + + + + template + types::subdomain_id_t + Triangulation::locally_owned_subdomain () const + { + return my_subdomain; + } + + + + template + unsigned int + Triangulation::n_locally_owned_active_cells () const + { + return number_cache.n_locally_owned_active_cells[my_subdomain]; + } + + + + template + unsigned int + Triangulation::n_global_active_cells () const + { + return number_cache.n_global_active_cells; + } + + + + template + const std::vector & + Triangulation::n_locally_owned_active_cells_per_processor () const + { + return number_cache.n_locally_owned_active_cells; + } + + + + template + unsigned int + Triangulation:: + register_data_attach (const size_t size, + const std_cxx1x::function & pack_callback) + { + Assert(size>0, ExcMessage("register_data_attach(), size==0")); + Assert(attached_data_pack_callbacks.size()==n_attached_datas, + ExcMessage("register_data_attach(), not all data has been unpacked last time?")); + + unsigned int offset = attached_data_size+sizeof(CellStatus); + ++n_attached_datas; + attached_data_size+=size; + attached_data_pack_callbacks.push_back( + std::pair (offset, pack_callback) + ); + return offset; + } + + + + template + void + Triangulation:: + notify_ready_to_unpack (const unsigned int offset, + const std_cxx1x::function & unpack_callback) + { + Assert(offset < attached_data_size, ExcMessage("invalid offset in notify_ready_to_unpack()")); + Assert(n_attached_datas>0, ExcMessage("notify_ready_to_unpack() called too often")); + + // Recurse over p4est and hand the caller the data back + for (typename Triangulation::cell_iterator + cell = this->begin(0); + cell != this->end(0); + ++cell) + { + //skip coarse cells, that are not ours + if (tree_exists_locally(parallel_forest, + coarse_cell_to_p4est_tree_permutation[cell->index()]) + == false) + continue; + + typename internal::p4est::types::quadrant p4est_coarse_cell; + typename internal::p4est::types::tree *tree = + init_tree(cell->index()); + + internal::p4est::init_coarse_quadrant (p4est_coarse_cell); + + // parent_cell is not correct here, + // but is only used in a refined + // cell + post_mesh_data_recursively(*tree, + cell, + cell, + p4est_coarse_cell, + offset, + unpack_callback); + } + + --n_attached_datas; + + if (!n_attached_datas) + { + // everybody got his data, time for cleanup! + attached_data_size=0; + attached_data_pack_callbacks.clear(); + + // and release the data + void * userptr = parallel_forest->user_pointer; + internal::p4est::functions::reset_data (parallel_forest, 0, NULL, NULL); + parallel_forest->user_pointer = userptr; + } + } + + + + template + MPI_Comm + Triangulation::get_communicator () const + { + return mpi_communicator; + } + + + + template + unsigned int + Triangulation::memory_consumption () const + { + unsigned int mem= + this->dealii::Triangulation::memory_consumption() + + MemoryConsumption::memory_consumption(mpi_communicator) + + MemoryConsumption::memory_consumption(my_subdomain) + + MemoryConsumption::memory_consumption(triangulation_has_content) + + MemoryConsumption::memory_consumption(number_cache.n_locally_owned_active_cells) + + MemoryConsumption::memory_consumption(number_cache.n_global_active_cells) + + MemoryConsumption::memory_consumption(connectivity) + + MemoryConsumption::memory_consumption(parallel_forest) + + MemoryConsumption::memory_consumption(refinement_in_progress) + + MemoryConsumption::memory_consumption(attached_data_size) + + MemoryConsumption::memory_consumption(n_attached_datas) +// + MemoryConsumption::memory_consumption(attached_data_pack_callbacks) //TODO[TH]: how? + + MemoryConsumption::memory_consumption(coarse_cell_to_p4est_tree_permutation) + + MemoryConsumption::memory_consumption(p4est_tree_to_coarse_cell_permutation) + + memory_consumption_p4est() + ; + + return mem; + } + + + + template + unsigned int + Triangulation::memory_consumption_p4est () const + { + return internal::p4est::functions::forest_memory_used(parallel_forest) + + internal::p4est::functions::connectivity_memory_used(connectivity); + } + + + + template + void + Triangulation:: + copy_triangulation (const dealii::Triangulation &) + { + Assert (false, ExcNotImplemented()); + } + + + template + void + Triangulation:: + attach_mesh_data() + { + // determine size of memory in bytes to + // attach to each cell. This needs to + // be constant because of p4est. + if (attached_data_size==0) + { + Assert(n_attached_datas==0, ExcInternalError()); + + //nothing to do + return; + } + + // realloc user_data in p4est + void * userptr = parallel_forest->user_pointer; + internal::p4est::functions::reset_data (parallel_forest, + attached_data_size+sizeof(CellStatus), + NULL, NULL); + parallel_forest->user_pointer = userptr; + + + // Recurse over p4est and Triangulation + // to find refined/coarsened/kept + // cells. Then query and attach the data. + for (typename Triangulation::cell_iterator + cell = this->begin(0); + cell != this->end(0); + ++cell) + { + //skip coarse cells, that are not ours + if (tree_exists_locally(parallel_forest, + coarse_cell_to_p4est_tree_permutation[cell->index()]) + == false) + continue; + + typename internal::p4est::types::quadrant p4est_coarse_cell; + typename internal::p4est::types::tree *tree = + init_tree(cell->index()); + + internal::p4est::init_coarse_quadrant(p4est_coarse_cell); + + attach_mesh_data_recursively(*tree, + cell, + p4est_coarse_cell, + attached_data_pack_callbacks); + } + + + + + } + + + + +#if deal_II_dimension == 1 + template + Triangulation<1,spacedim>::Triangulation (MPI_Comm) + { + Assert (false, ExcNotImplemented()); + } + + + template + Triangulation<1,spacedim>::~Triangulation () + { + Assert (false, ExcNotImplemented()); + } + + + + template + types::subdomain_id_t + Triangulation<1,spacedim>::locally_owned_subdomain () const + { + Assert (false, ExcNotImplemented()); + return 0; + } + + + template + MPI_Comm + Triangulation<1,spacedim>::get_communicator () const + { + return MPI_COMM_WORLD; + } +#endif + + } +} + + +#else // DEAL_II_USE_P4EST + +namespace parallel +{ + namespace distributed + { + template + Triangulation::Triangulation () + { + Assert (false, ExcNotImplemented()); + } + + + template + Triangulation::~Triangulation () + { + Assert (false, ExcNotImplemented()); + } + + + + template + types::subdomain_id_t + Triangulation::locally_owned_subdomain () const + { + Assert (false, ExcNotImplemented()); + return 0; + } + } +} + + +#endif // DEAL_II_USE_P4EST + + + +// explicit instantiations +namespace internal +{ + namespace p4est + { +# if deal_II_dimension > 1 + template + void + init_quadrant_children + (const types::quadrant & p4est_cell, + types::quadrant (&p4est_children)[GeometryInfo::max_children_per_cell]); + + template + void + init_coarse_quadrant + (types::quadrant & quad); + + template + bool + quadrant_is_equal + (const types::quadrant & q1, + const types::quadrant & q2); + + template + bool + quadrant_is_ancestor + (const types::quadrant & q1, + const types::quadrant & q2); +#endif + } +} +namespace parallel +{ + namespace distributed + { + template class Triangulation; +# if deal_II_dimension < 3 + template class Triangulation; +# endif + } +} + + +DEAL_II_NAMESPACE_CLOSE + + diff --git a/deal.II/deal.II/source/dofs/dof_accessor.cc b/deal.II/deal.II/source/dofs/dof_accessor.cc index 779665d4c0..2cff208515 100644 --- a/deal.II/deal.II/source/dofs/dof_accessor.cc +++ b/deal.II/deal.II/source/dofs/dof_accessor.cc @@ -45,15 +45,33 @@ DoFCellAccessor::update_cell_dof_indices_cache () const { Assert (static_cast(this->present_level) < this->dof_handler->levels.size(), ExcMessage ("DoFHandler not initialized")); - + + Assert (this->dof_handler != 0, typename BaseClass::ExcInvalidObject()); + Assert (&this->get_fe() != 0, typename BaseClass::ExcInvalidObject()); + + internal::DoFCellAccessor::Implementation:: + update_cell_dof_indices_cache (*this); +} + + + +template +void +DoFCellAccessor::set_dof_indices (const std::vector &local_dof_indices) +{ + Assert (static_cast(this->present_level) < this->dof_handler->levels.size(), + ExcMessage ("DoFHandler not initialized")); + Assert (this->dof_handler != 0, typename BaseClass::ExcInvalidObject()); Assert (&this->get_fe() != 0, typename BaseClass::ExcInvalidObject()); - internal::DoFCellAccessor::Implementation::update_cell_dof_indices_cache (*this); + internal::DoFCellAccessor::Implementation:: + set_dof_indices (*this, local_dof_indices); } + template typename internal::DoFHandler::Iterators::cell_iterator DoFCellAccessor::neighbor_child_on_subface (const unsigned int face, @@ -79,7 +97,7 @@ get_interpolated_dof_values (const InputVector &values, { const FiniteElement &fe = this->get_fe(); const unsigned int dofs_per_cell = fe.dofs_per_cell; - + Assert (this->dof_handler != 0, typename BaseClass::ExcInvalidObject()); Assert (&fe != 0, @@ -98,7 +116,7 @@ get_interpolated_dof_values (const InputVector &values, { Vector tmp1(dofs_per_cell); Vector tmp2(dofs_per_cell); - + interpolated_values = 0; // later on we will have to @@ -161,7 +179,7 @@ get_interpolated_dof_values (const InputVector &values, std::vector restriction_is_additive (dofs_per_cell); for (unsigned int i=0; in_children(); ++child) { // get the values from the present @@ -176,7 +194,7 @@ get_interpolated_dof_values (const InputVector &values, // and add up or set them // in the output vector for (unsigned int i=0; i &local_values, // otherwise distribute them to the children { Vector tmp(dofs_per_cell); - + for (unsigned int child=0; childn_children(); ++child) { // prolong the given data // to the present cell this->get_fe().get_prolongation_matrix(child, this->refinement_case()) .vmult (tmp, local_values); - + this->child(child)->set_dof_values_by_interpolation (tmp, values); } } @@ -324,11 +342,11 @@ template class TriaActiveIterator > >; #if deal_II_dimension != 3 template class DoFCellAccessor >; -template class +template class TriaRawIterator > >; -template class +template class TriaIterator > >; -template class +template class TriaActiveIterator > >; #endif @@ -351,11 +369,11 @@ template class TriaActiveIterator > >; #if deal_II_dimension != 3 template class DoFCellAccessor >; -template class +template class TriaRawIterator > >; -template class +template class TriaIterator > >; -template class +template class TriaActiveIterator > >; #endif diff --git a/deal.II/deal.II/source/dofs/dof_handler.cc b/deal.II/deal.II/source/dofs/dof_handler.cc index 3a0757a1cc..729bee2f4c 100644 --- a/deal.II/deal.II/source/dofs/dof_handler.cc +++ b/deal.II/deal.II/source/dofs/dof_handler.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -71,343 +72,13 @@ namespace internal // namespace internal::DoFHandler using dealii::DoFHandler; + /** * A class with the same purpose as the similarly named class of the * Triangulation class. See there for more information. */ struct Implementation { - /** - * Distribute dofs on the given cell, - * with new dofs starting with index - * @p next_free_dof. Return the next - * unused index number. The finite - * element used is the one given to - * @p distribute_dofs, which is copied - * to @p selected_fe. - * - * This function is excluded from the - * @p distribute_dofs function since - * it can not be implemented dimension - * independent. - */ - template - static - unsigned int - distribute_dofs_on_cell (const DoFHandler<1,spacedim> &dof_handler, - typename DoFHandler<1,spacedim>::active_cell_iterator &cell, - unsigned int next_free_dof) - { - - // distribute dofs of vertices - for (unsigned int v=0; v::vertices_per_cell; ++v) - { - typename DoFHandler<1,spacedim>::cell_iterator - neighbor = cell->neighbor(v); - - if (neighbor.state() == IteratorState::valid) - { - // find true neighbor; may be its - // a child of @p{neighbor} - while (neighbor->has_children()) - neighbor = neighbor->child(v==0 ? 1 : 0); - - // has neighbor already been processed? - if (neighbor->user_flag_set()) - // copy dofs - { - if (v==0) - for (unsigned int d=0; - ddofs_per_vertex; ++d) - cell->set_vertex_dof_index (0, d, - neighbor->vertex_dof_index (1, d)); - else - for (unsigned int d=0; - ddofs_per_vertex; ++d) - cell->set_vertex_dof_index (1, d, - neighbor->vertex_dof_index (0, d)); - - // next neighbor - continue; - } - } - - // otherwise: create dofs newly - for (unsigned int d=0; - ddofs_per_vertex; ++d) - cell->set_vertex_dof_index (v, d, next_free_dof++); - } - - // dofs of line - for (unsigned int d=0; - ddofs_per_line; ++d) - cell->set_dof_index (d, next_free_dof++); - - // note that this cell has been - // processed - cell->set_user_flag (); - - return next_free_dof; - } - - - - template - static - unsigned int - distribute_dofs_on_cell (const DoFHandler<2,spacedim> &dof_handler, - typename DoFHandler<2,spacedim>::active_cell_iterator &cell, - unsigned int next_free_dof) - { - if (dof_handler.selected_fe->dofs_per_vertex > 0) - // number dofs on vertices - for (unsigned int vertex=0; vertex::vertices_per_cell; ++vertex) - // check whether dofs for this - // vertex have been distributed - // (only check the first dof) - if (cell->vertex_dof_index(vertex, 0) == DoFHandler<2,spacedim>::invalid_dof_index) - for (unsigned int d=0; ddofs_per_vertex; ++d) - cell->set_vertex_dof_index (vertex, d, next_free_dof++); - - // for the four sides - if (dof_handler.selected_fe->dofs_per_line > 0) - for (unsigned int side=0; side::faces_per_cell; ++side) - { - typename DoFHandler<2,spacedim>::line_iterator - line = cell->line(side); - - // distribute dofs if necessary: - // check whether line dof is already - // numbered (check only first dof) - if (line->dof_index(0) == DoFHandler<2,spacedim>::invalid_dof_index) - // if not: distribute dofs - for (unsigned int d=0; ddofs_per_line; ++d) - line->set_dof_index (d, next_free_dof++); - } - - - // dofs of quad - if (dof_handler.selected_fe->dofs_per_quad > 0) - for (unsigned int d=0; ddofs_per_quad; ++d) - cell->set_dof_index (d, next_free_dof++); - - - // note that this cell has been processed - cell->set_user_flag (); - - return next_free_dof; - } - - - template - static - unsigned int - distribute_dofs_on_cell (const DoFHandler<3,spacedim> &dof_handler, - typename DoFHandler<3,spacedim>::active_cell_iterator &cell, - unsigned int next_free_dof) - { - if (dof_handler.selected_fe->dofs_per_vertex > 0) - // number dofs on vertices - for (unsigned int vertex=0; vertex::vertices_per_cell; ++vertex) - // check whether dofs for this - // vertex have been distributed - // (only check the first dof) - if (cell->vertex_dof_index(vertex, 0) == DoFHandler<3,spacedim>::invalid_dof_index) - for (unsigned int d=0; ddofs_per_vertex; ++d) - cell->set_vertex_dof_index (vertex, d, next_free_dof++); - - // for the lines - if (dof_handler.selected_fe->dofs_per_line > 0) - for (unsigned int l=0; l::lines_per_cell; ++l) - { - typename DoFHandler<3,spacedim>::line_iterator - line = cell->line(l); - - // distribute dofs if necessary: - // check whether line dof is already - // numbered (check only first dof) - if (line->dof_index(0) == DoFHandler<3,spacedim>::invalid_dof_index) - // if not: distribute dofs - for (unsigned int d=0; ddofs_per_line; ++d) - line->set_dof_index (d, next_free_dof++); - } - - // for the quads - if (dof_handler.selected_fe->dofs_per_quad > 0) - for (unsigned int q=0; q::quads_per_cell; ++q) - { - typename DoFHandler<3,spacedim>::quad_iterator - quad = cell->quad(q); - - // distribute dofs if necessary: - // check whether quad dof is already - // numbered (check only first dof) - if (quad->dof_index(0) == DoFHandler<3,spacedim>::invalid_dof_index) - // if not: distribute dofs - for (unsigned int d=0; ddofs_per_quad; ++d) - quad->set_dof_index (d, next_free_dof++); - } - - - // dofs of hex - if (dof_handler.selected_fe->dofs_per_hex > 0) - for (unsigned int d=0; ddofs_per_hex; ++d) - cell->set_dof_index (d, next_free_dof++); - - - // note that this cell has been - // processed - cell->set_user_flag (); - - return next_free_dof; - } - - - /** - * Implementation of the general template - * of same name. - */ - template - static - void renumber_dofs (const std::vector &new_numbers, - DoFHandler<1,spacedim> &dof_handler) - { - // note that we can not use cell - // iterators in this function since - // then we would renumber the dofs on - // the interface of two cells more - // than once. Anyway, this way it's - // not only more correct but also - // faster; note, however, that dof - // numbers may be invalid_dof_index, - // namely when the appropriate - // vertex/line/etc is unused - for (std::vector::iterator - i=dof_handler.vertex_dofs.begin(); - i!=dof_handler.vertex_dofs.end(); ++i) - if (*i != DoFHandler<1,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - else - // if index is - // invalid_dof_index: - // check if this one - // really is unused - Assert (dof_handler.tria - ->vertex_used((i-dof_handler.vertex_dofs.begin()) / - dof_handler.selected_fe->dofs_per_vertex) - == false, - ExcInternalError ()); - - for (unsigned int level=0; level::iterator - i=dof_handler.levels[level]->lines.dofs.begin(); - i!=dof_handler.levels[level]->lines.dofs.end(); ++i) - if (*i != DoFHandler<1,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - } - - - - template - static - void renumber_dofs (const std::vector &new_numbers, - DoFHandler<2,spacedim> &dof_handler) - { - // note that we can not use cell - // iterators in this function since - // then we would renumber the dofs on - // the interface of two cells more - // than once. Anyway, this way it's - // not only more correct but also - // faster; note, however, that dof - // numbers may be invalid_dof_index, - // namely when the appropriate - // vertex/line/etc is unused - for (std::vector::iterator - i=dof_handler.vertex_dofs.begin(); - i!=dof_handler.vertex_dofs.end(); ++i) - if (*i != DoFHandler<2,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - else - // if index is invalid_dof_index: - // check if this one really is - // unused - Assert (dof_handler.tria - ->vertex_used((i-dof_handler.vertex_dofs.begin()) / - dof_handler.selected_fe->dofs_per_vertex) - == false, - ExcInternalError ()); - - for (std::vector::iterator - i=dof_handler.faces->lines.dofs.begin(); - i!=dof_handler.faces->lines.dofs.end(); ++i) - if (*i != DoFHandler<2,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - - for (unsigned int level=0; level::iterator - i=dof_handler.levels[level]->quads.dofs.begin(); - i!=dof_handler.levels[level]->quads.dofs.end(); ++i) - if (*i != DoFHandler<2,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - } - } - - - template - static - void renumber_dofs (const std::vector &new_numbers, - DoFHandler<3,spacedim> &dof_handler) - { - // note that we can not use cell - // iterators in this function since - // then we would renumber the dofs on - // the interface of two cells more - // than once. Anyway, this way it's - // not only more correct but also - // faster; note, however, that dof - // numbers may be invalid_dof_index, - // namely when the appropriate - // vertex/line/etc is unused - for (std::vector::iterator - i=dof_handler.vertex_dofs.begin(); - i!=dof_handler.vertex_dofs.end(); ++i) - if (*i != DoFHandler<3,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - else - // if index is invalid_dof_index: - // check if this one really is - // unused - Assert (dof_handler.tria - ->vertex_used((i-dof_handler.vertex_dofs.begin()) / - dof_handler.selected_fe->dofs_per_vertex) - == false, - ExcInternalError ()); - - for (std::vector::iterator - i=dof_handler.faces->lines.dofs.begin(); - i!=dof_handler.faces->lines.dofs.end(); ++i) - if (*i != DoFHandler<3,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - for (std::vector::iterator - i=dof_handler.faces->quads.dofs.begin(); - i!=dof_handler.faces->quads.dofs.end(); ++i) - if (*i != DoFHandler<3,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - - for (unsigned int level=0; level::iterator - i=dof_handler.levels[level]->hexes.dofs.begin(); - i!=dof_handler.levels[level]->hexes.dofs.end(); ++i) - if (*i != DoFHandler<3,spacedim>::invalid_dof_index) - *i = new_numbers[*i]; - } - } - - - /** * Implement the function of same name in * the mother class. @@ -692,9 +363,18 @@ DoFHandler::DoFHandler (const Triangulation &tria) : tria(&tria, typeid(*this).name()), selected_fe(0, typeid(*this).name()), - faces(NULL), - used_dofs (0) -{} + faces(NULL) +{ + // decide whether we need a + // sequential or a parallel + // distributed policy + if (dynamic_cast*> + (&tria) + == 0) + policy.reset (new internal::DoFHandler::Policy::Sequential()); + else + policy.reset (new internal::DoFHandler::Policy::ParallelDistributed()); +} template @@ -702,8 +382,7 @@ DoFHandler::DoFHandler () : tria(0, typeid(*this).name()), selected_fe(0, typeid(*this).name()), - faces(NULL), - used_dofs (0) + faces(NULL) {} @@ -722,8 +401,19 @@ DoFHandler::initialize( const FiniteElement& fe) { tria = &t; - faces = NULL; - used_dofs = 0; + faces = 0; + number_cache.n_global_dofs = 0; + + // decide whether we need a + // sequential or a parallel + // distributed policy + if (dynamic_cast*> + (&t) + == 0) + policy.reset (new internal::DoFHandler::Policy::Sequential()); + else + policy.reset (new internal::DoFHandler::Policy::ParallelDistributed()); + distribute_dofs(fe); } @@ -1954,7 +1644,7 @@ DoFHandler::memory_consumption () const MemoryConsumption::memory_consumption (levels) + MemoryConsumption::memory_consumption (*faces) + MemoryConsumption::memory_consumption (faces) + - MemoryConsumption::memory_consumption (used_dofs) + + sizeof (number_cache) + MemoryConsumption::memory_consumption (vertex_dofs)); for (unsigned int i=0; i::memory_consumption () const template void DoFHandler:: - distribute_dofs (const FiniteElement &ff, - const unsigned int offset) +distribute_dofs (const FiniteElement &ff, + const unsigned int offset) { - Assert (tria->n_levels() > 0, ExcInvalidTriangulation()); - selected_fe = &ff; - // delete all levels and set them up - // newly, since vectors are - // troublesome if you want to change - // their size + // delete all levels and set them + // up newly. note that we still + // have to allocate space for all + // degrees of freedom on this mesh + // (including ghost and cells that + // are entirely stored on different + // processors), though we may not + // assign numbers to some of them + // (i.e. they will remain at + // invalid_dof_index). We need to + // allocate the space because we + // will want to be able to query + // the dof_indices on each cell, + // and simply be told that we don't + // know them on some cell (i.e. get + // back invalid_dof_index) clear_space (); internal::DoFHandler::Implementation::reserve_space (*this); - // Clear user flags because we will - // need them. But first we save - // them and make sure that we - // restore them later such that at - // the end of this function the - // Triangulation will be in the - // same state as it was at the - // beginning of this function. - std::vector user_flags; - tria->save_user_flags(user_flags); - const_cast &>(*tria).clear_user_flags (); - - unsigned int next_free_dof = offset; - active_cell_iterator cell = begin_active(), - endc = end(); - - for (; cell != endc; ++cell) - next_free_dof - = internal::DoFHandler::Implementation:: - distribute_dofs_on_cell (*this, cell, next_free_dof); + // hand things off to the policy + number_cache = policy->distribute_dofs (offset, + *this); - used_dofs = next_free_dof; - - // update the cache used - // for cell dof indices - for (cell_iterator cell = begin(); cell != end(); ++cell) - cell->update_cell_dof_indices_cache (); - - // finally restore the user flags - const_cast &>(*tria).load_user_flags(user_flags); - - block_info_object.initialize(*this); + // initialize the block info object + // only if this is a sequential + // triangulation. it doesn't work + // correctly yet if it is parallel + if (dynamic_cast*>(&*tria) == 0) + block_info_object.initialize(*this); } + template void DoFHandler::initialize_local_block_info () { @@ -2022,6 +1701,7 @@ void DoFHandler::initialize_local_block_info () } + template void DoFHandler::clear () { @@ -2039,12 +1719,20 @@ void DoFHandler:: renumber_dofs (const std::vector &new_numbers) { - Assert (new_numbers.size() == n_dofs(), ExcRenumberingIncomplete()); + Assert (new_numbers.size() == n_locally_owned_dofs(), + ExcRenumberingIncomplete()); #ifdef DEBUG // assert that the new indices are - // consecutively numbered - if (true) + // consecutively numbered if we are + // working on a single + // processor. this doesn't need to + // hold in the case of a parallel + // mesh since we map the interval + // [0...n_dofs()) into itself but + // only globally, not on each + // processor + if (n_locally_owned_dofs() == n_dofs()) { std::vector tmp(new_numbers); std::sort (tmp.begin(), tmp.end()); @@ -2053,16 +1741,13 @@ renumber_dofs (const std::vector &new_numbers) for (; p!=tmp.end(); ++p, ++i) Assert (*p == i, ExcNewNumbersNotConsecutive(i)); } + else + for (unsigned int i=0; iupdate_cell_dof_indices_cache (); - + number_cache = policy->renumber_dofs (new_numbers, *this); } diff --git a/deal.II/deal.II/source/dofs/dof_handler_policy.cc b/deal.II/deal.II/source/dofs/dof_handler_policy.cc new file mode 100644 index 0000000000..385eaa564b --- /dev/null +++ b/deal.II/deal.II/source/dofs/dof_handler_policy.cc @@ -0,0 +1,1590 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 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. +// +//--------------------------------------------------------------------------- + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +DEAL_II_NAMESPACE_OPEN + + +namespace internal +{ + namespace DoFHandler + { + namespace Policy + { + // use class dealii::DoFHandler instead + // of namespace internal::DoFHandler in + // the following + using dealii::DoFHandler; + + struct Implementation + { + +/* -------------- distribute_dofs functionality ------------- */ + + /** + * Distribute dofs on the given cell, + * with new dofs starting with index + * @p next_free_dof. Return the next + * unused index number. + * + * This function is excluded from the + * @p distribute_dofs function since + * it can not be implemented dimension + * independent. + */ + template + static + unsigned int + distribute_dofs_on_cell (const DoFHandler<1,spacedim> &dof_handler, + const typename DoFHandler<1,spacedim>::active_cell_iterator &cell, + unsigned int next_free_dof) + { + + // distribute dofs of vertices + for (unsigned int v=0; v::vertices_per_cell; ++v) + { + typename DoFHandler<1,spacedim>::cell_iterator + neighbor = cell->neighbor(v); + + if (neighbor.state() == IteratorState::valid) + { + // find true neighbor; may be its + // a child of @p{neighbor} + while (neighbor->has_children()) + neighbor = neighbor->child(v==0 ? 1 : 0); + + // has neighbor already been processed? + if (neighbor->user_flag_set()) + // copy dofs + { + if (v==0) + for (unsigned int d=0; + dset_vertex_dof_index (0, d, + neighbor->vertex_dof_index (1, d)); + else + for (unsigned int d=0; + dset_vertex_dof_index (1, d, + neighbor->vertex_dof_index (0, d)); + + // next neighbor + continue; + } + } + + // otherwise: create dofs newly + for (unsigned int d=0; + dset_vertex_dof_index (v, d, next_free_dof++); + } + + // dofs of line + for (unsigned int d=0; + dset_dof_index (d, next_free_dof++); + + // note that this cell has been + // processed + cell->set_user_flag (); + + return next_free_dof; + } + + + + template + static + unsigned int + distribute_dofs_on_cell (const DoFHandler<2,spacedim> &dof_handler, + const typename DoFHandler<2,spacedim>::active_cell_iterator &cell, + unsigned int next_free_dof) + { + if (dof_handler.get_fe().dofs_per_vertex > 0) + // number dofs on vertices + for (unsigned int vertex=0; vertex::vertices_per_cell; ++vertex) + // check whether dofs for this + // vertex have been distributed + // (only check the first dof) + if (cell->vertex_dof_index(vertex, 0) == DoFHandler<2,spacedim>::invalid_dof_index) + for (unsigned int d=0; dset_vertex_dof_index (vertex, d, next_free_dof++); + + // for the four sides + if (dof_handler.get_fe().dofs_per_line > 0) + for (unsigned int side=0; side::faces_per_cell; ++side) + { + const typename DoFHandler<2,spacedim>::line_iterator + line = cell->line(side); + + // distribute dofs if necessary: + // check whether line dof is already + // numbered (check only first dof) + if (line->dof_index(0) == DoFHandler<2,spacedim>::invalid_dof_index) + // if not: distribute dofs + for (unsigned int d=0; dset_dof_index (d, next_free_dof++); + } + + + // dofs of quad + if (dof_handler.get_fe().dofs_per_quad > 0) + for (unsigned int d=0; dset_dof_index (d, next_free_dof++); + + + // note that this cell has been processed + cell->set_user_flag (); + + return next_free_dof; + } + + + template + static + unsigned int + distribute_dofs_on_cell (const DoFHandler<3,spacedim> &dof_handler, + const typename DoFHandler<3,spacedim>::active_cell_iterator &cell, + unsigned int next_free_dof) + { + if (dof_handler.get_fe().dofs_per_vertex > 0) + // number dofs on vertices + for (unsigned int vertex=0; vertex::vertices_per_cell; ++vertex) + // check whether dofs for this + // vertex have been distributed + // (only check the first dof) + if (cell->vertex_dof_index(vertex, 0) == DoFHandler<3,spacedim>::invalid_dof_index) + for (unsigned int d=0; dset_vertex_dof_index (vertex, d, next_free_dof++); + + // for the lines + if (dof_handler.get_fe().dofs_per_line > 0) + for (unsigned int l=0; l::lines_per_cell; ++l) + { + const typename DoFHandler<3,spacedim>::line_iterator + line = cell->line(l); + + // distribute dofs if necessary: + // check whether line dof is already + // numbered (check only first dof) + if (line->dof_index(0) == DoFHandler<3,spacedim>::invalid_dof_index) + // if not: distribute dofs + for (unsigned int d=0; dset_dof_index (d, next_free_dof++); + } + + // for the quads + if (dof_handler.get_fe().dofs_per_quad > 0) + for (unsigned int q=0; q::quads_per_cell; ++q) + { + const typename DoFHandler<3,spacedim>::quad_iterator + quad = cell->quad(q); + + // distribute dofs if necessary: + // check whether quad dof is already + // numbered (check only first dof) + if (quad->dof_index(0) == DoFHandler<3,spacedim>::invalid_dof_index) + // if not: distribute dofs + for (unsigned int d=0; dset_dof_index (d, next_free_dof++); + } + + + // dofs of hex + if (dof_handler.get_fe().dofs_per_hex > 0) + for (unsigned int d=0; dset_dof_index (d, next_free_dof++); + + + // note that this cell has been + // processed + cell->set_user_flag (); + + return next_free_dof; + } + + + /** + * Distribute degrees of + * freedom on all cells, or + * on cells with the + * correct subdomain_id if + * the corresponding + * argument is not equal to + * types::invalid_subdomain_id. Return + * the total number of dofs + * returned. + */ + template + static + unsigned int + distribute_dofs (const unsigned int offset, + const unsigned int subdomain_id, + DoFHandler &dof_handler) + { + const dealii::Triangulation & tria + = dof_handler.get_tria(); + Assert (tria.n_levels() > 0, ExcMessage("Empty triangulation")); + + // Clear user flags because we will + // need them. But first we save + // them and make sure that we + // restore them later such that at + // the end of this function the + // Triangulation will be in the + // same state as it was at the + // beginning of this function. + std::vector user_flags; + tria.save_user_flags(user_flags); + const_cast &>(tria).clear_user_flags (); + + unsigned int next_free_dof = offset; + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + + for (; cell != endc; ++cell) + if ((subdomain_id == types::invalid_subdomain_id) + || + (cell->subdomain_id() == subdomain_id)) + next_free_dof + = Implementation::distribute_dofs_on_cell (dof_handler, cell, next_free_dof); + + // update the cache used + // for cell dof indices + for (typename DoFHandler::cell_iterator + cell = dof_handler.begin(); cell != dof_handler.end(); ++cell) + if (cell->subdomain_id() != types::artificial_subdomain_id) + cell->update_cell_dof_indices_cache (); + + // finally restore the user flags + const_cast &>(tria).load_user_flags(user_flags); + + return next_free_dof; + } + + +/* --------------------- renumber_dofs functionality ---------------- */ + + + /** + * Implementation of the + * general template of same + * name. + * + * If the second argument + * has any elements set, + * elements of the then the + * vector of new numbers do + * not relate to the old + * DoF number but instead + * to the index of the old + * DoF number within the + * set of locally owned + * DoFs. + */ + template + static + void + renumber_dofs (const std::vector &new_numbers, + const IndexSet &, + DoFHandler<1,spacedim> &dof_handler, + const bool check_validity) + { + // note that we can not use cell + // iterators in this function since + // then we would renumber the dofs on + // the interface of two cells more + // than once. Anyway, this way it's + // not only more correct but also + // faster; note, however, that dof + // numbers may be invalid_dof_index, + // namely when the appropriate + // vertex/line/etc is unused + for (std::vector::iterator + i=dof_handler.vertex_dofs.begin(); + i!=dof_handler.vertex_dofs.end(); ++i) + if (*i != DoFHandler<1,spacedim>::invalid_dof_index) + *i = new_numbers[*i]; + else if (check_validity) + // if index is + // invalid_dof_index: + // check if this one + // really is unused + Assert (dof_handler.get_tria() + .vertex_used((i-dof_handler.vertex_dofs.begin()) / + dof_handler.selected_fe->dofs_per_vertex) + == false, + ExcInternalError ()); + + for (unsigned int level=0; level::iterator + i=dof_handler.levels[level]->lines.dofs.begin(); + i!=dof_handler.levels[level]->lines.dofs.end(); ++i) + if (*i != DoFHandler<1,spacedim>::invalid_dof_index) + *i = new_numbers[*i]; + + // update the cache + // used for cell dof + // indices + for (typename DoFHandler<1,spacedim>::cell_iterator + cell = dof_handler.begin(); + cell != dof_handler.end(); ++cell) + cell->update_cell_dof_indices_cache (); + } + + + + template + static + void + renumber_dofs (const std::vector &new_numbers, + const IndexSet &indices, + DoFHandler<2,spacedim> &dof_handler, + const bool check_validity) + { + // note that we can not use cell + // iterators in this function since + // then we would renumber the dofs on + // the interface of two cells more + // than once. Anyway, this way it's + // not only more correct but also + // faster; note, however, that dof + // numbers may be invalid_dof_index, + // namely when the appropriate + // vertex/line/etc is unused + for (std::vector::iterator + i=dof_handler.vertex_dofs.begin(); + i!=dof_handler.vertex_dofs.end(); ++i) + if (*i != DoFHandler<2,spacedim>::invalid_dof_index) + *i = (indices.n_elements() == 0)? + (new_numbers[*i]) : + (new_numbers[indices.index_within_set(*i)]); + else if (check_validity) + // if index is invalid_dof_index: + // check if this one really is + // unused + Assert (dof_handler.get_tria() + .vertex_used((i-dof_handler.vertex_dofs.begin()) / + dof_handler.selected_fe->dofs_per_vertex) + == false, + ExcInternalError ()); + + for (std::vector::iterator + i=dof_handler.faces->lines.dofs.begin(); + i!=dof_handler.faces->lines.dofs.end(); ++i) + if (*i != DoFHandler<2,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + + for (unsigned int level=0; level::iterator + i=dof_handler.levels[level]->quads.dofs.begin(); + i!=dof_handler.levels[level]->quads.dofs.end(); ++i) + if (*i != DoFHandler<2,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + } + + // update the cache + // used for cell dof + // indices + for (typename DoFHandler<2,spacedim>::cell_iterator + cell = dof_handler.begin(); + cell != dof_handler.end(); ++cell) + cell->update_cell_dof_indices_cache (); + } + + + template + static + void + renumber_dofs (const std::vector &new_numbers, + const IndexSet &indices, + DoFHandler<3,spacedim> &dof_handler, + const bool check_validity) + { + // note that we can not use cell + // iterators in this function since + // then we would renumber the dofs on + // the interface of two cells more + // than once. Anyway, this way it's + // not only more correct but also + // faster; note, however, that dof + // numbers may be invalid_dof_index, + // namely when the appropriate + // vertex/line/etc is unused + for (std::vector::iterator + i=dof_handler.vertex_dofs.begin(); + i!=dof_handler.vertex_dofs.end(); ++i) + if (*i != DoFHandler<3,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + else if (check_validity) + // if index is invalid_dof_index: + // check if this one really is + // unused + Assert (dof_handler.get_tria() + .vertex_used((i-dof_handler.vertex_dofs.begin()) / + dof_handler.selected_fe->dofs_per_vertex) + == false, + ExcInternalError ()); + + for (std::vector::iterator + i=dof_handler.faces->lines.dofs.begin(); + i!=dof_handler.faces->lines.dofs.end(); ++i) + if (*i != DoFHandler<3,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + for (std::vector::iterator + i=dof_handler.faces->quads.dofs.begin(); + i!=dof_handler.faces->quads.dofs.end(); ++i) + if (*i != DoFHandler<3,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + + for (unsigned int level=0; level::iterator + i=dof_handler.levels[level]->hexes.dofs.begin(); + i!=dof_handler.levels[level]->hexes.dofs.end(); ++i) + if (*i != DoFHandler<3,spacedim>::invalid_dof_index) + *i = ((indices.n_elements() == 0) ? + new_numbers[*i] : + new_numbers[indices.index_within_set(*i)]); + } + + // update the cache + // used for cell dof + // indices + for (typename DoFHandler<3,spacedim>::cell_iterator + cell = dof_handler.begin(); + cell != dof_handler.end(); ++cell) + cell->update_cell_dof_indices_cache (); + } + }; + + + +/* --------------------- class PolicyBase ---------------- */ + + template + PolicyBase::~PolicyBase () + {} + + +/* --------------------- class Sequential ---------------- */ + + + template + NumberCache + Sequential:: + distribute_dofs (const unsigned int offset, + DoFHandler &dof_handler) const + { + const unsigned int n_dofs = + Implementation::distribute_dofs (offset, + types::invalid_subdomain_id, + dof_handler); + + // now set the elements of the + // number cache appropriately + NumberCache number_cache; + number_cache.n_global_dofs = n_dofs; + number_cache.n_locally_owned_dofs = number_cache.n_global_dofs; + + number_cache.locally_owned_dofs + = IndexSet (number_cache.n_global_dofs); + number_cache.locally_owned_dofs.add_range (0, + number_cache.n_global_dofs); + + number_cache.n_locally_owned_dofs_per_processor + = std::vector (1, + number_cache.n_global_dofs); + + number_cache.locally_owned_dofs_per_processor + = std::vector (1, + number_cache.locally_owned_dofs); + return number_cache; + } + + + + template + NumberCache + Sequential:: + renumber_dofs (const std::vector &new_numbers, + dealii::DoFHandler &dof_handler) const + { + Implementation::renumber_dofs (new_numbers, IndexSet(0), + dof_handler, true); + + // in the sequential case, + // the number cache should + // not have changed but we + // have to set the elements + // of the structure + // appropriately anyway + NumberCache number_cache; + number_cache.n_global_dofs = dof_handler.n_dofs(); + number_cache.n_locally_owned_dofs = number_cache.n_global_dofs; + + number_cache.locally_owned_dofs + = IndexSet (number_cache.n_global_dofs); + number_cache.locally_owned_dofs.add_range (0, + number_cache.n_global_dofs); + + number_cache.n_locally_owned_dofs_per_processor + = std::vector (1, + number_cache.n_global_dofs); + + number_cache.locally_owned_dofs_per_processor + = std::vector (1, + number_cache.locally_owned_dofs); + return number_cache; + } + + + +/* --------------------- class ParallelDistributed ---------------- */ + +#ifdef DEAL_II_USE_P4EST + + namespace + { + template + struct types + { + + /** + * A list of tree+quadrant and + * their dof indices. dofs is of + * the form num_dofindices of + * quadrant 0, followed by + * num_dofindices indices, + * num_dofindices of quadrant 1, + * ... + */ + struct cellinfo + { + std::vector tree_index; + std::vector::quadrant> quadrants; + std::vector dofs; + + unsigned int bytes_for_buffer () const + { + return (sizeof(unsigned int) + + tree_index.size() * sizeof(unsigned int) + + quadrants.size() * sizeof(typename dealii::internal::p4est + ::types::quadrant) + + dofs.size() * sizeof(unsigned int)); + } + + void pack_data (std::vector &buffer) const + { + buffer.resize(bytes_for_buffer()); + + char * ptr = &buffer[0]; + + const unsigned int num_cells = tree_index.size(); + std::memcpy(ptr, &num_cells, sizeof(unsigned int)); + ptr += sizeof(unsigned int); + + std::memcpy(ptr, + &tree_index[0], + num_cells*sizeof(unsigned int)); + ptr += num_cells*sizeof(unsigned int); + + std::memcpy(ptr, + &quadrants[0], + num_cells * sizeof(typename dealii::internal::p4est:: + types::quadrant)); + ptr += num_cells*sizeof(typename dealii::internal::p4est::types:: + quadrant); + + std::memcpy(ptr, + &dofs[0], + dofs.size() * sizeof(unsigned int)); + ptr += dofs.size() * sizeof(unsigned int); + + Assert (ptr == &buffer[buffer.size()], + ExcInternalError()); + + } + }; + }; + + + + template + void + fill_dofindices_recursively (const typename parallel::distributed::Triangulation & tria, + const unsigned int tree_index, + const typename DoFHandler::cell_iterator &dealii_cell, + const typename dealii::internal::p4est::types::quadrant &p4est_cell, + const std::map > &vertices_with_ghost_neighbors, + std::map::cellinfo> &needs_to_get_cell) + { + // see if we have to + // recurse... + if (dealii_cell->has_children()) + { + typename dealii::internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + internal::p4est::init_quadrant_children(p4est_cell, p4est_child); + + + for (unsigned int c=0;c::max_children_per_cell; ++c) + fill_dofindices_recursively(tria, + tree_index, + dealii_cell->child(c), + p4est_child[c], + vertices_with_ghost_neighbors, + needs_to_get_cell); + return; + } + + // we're at a leaf cell. see if + // the cell is flagged as + // interesting. note that we + // have only flagged our own + // cells before + if (dealii_cell->user_flag_set() && !dealii_cell->is_ghost()) + { + Assert (!dealii_cell->is_artificial(), ExcInternalError()); + + // check each vertex if + // it is interesting and + // push dofindices if yes + std::set send_to; + for (unsigned int v=0; v::vertices_per_cell; ++v) + { + const std::map >::const_iterator + neighbor_subdomains_of_vertex + = vertices_with_ghost_neighbors.find (dealii_cell->vertex_index(v)); + + if (neighbor_subdomains_of_vertex == + vertices_with_ghost_neighbors.end()) + continue; + + Assert(neighbor_subdomains_of_vertex->second.size()!=0, + ExcInternalError()); + + send_to.insert(neighbor_subdomains_of_vertex->second.begin(), + neighbor_subdomains_of_vertex->second.end()); + } + + if (send_to.size() > 0) + { + // this cell's dof_indices + // need to be sent to + // someone + std::vector + local_dof_indices (dealii_cell->get_fe().dofs_per_cell); + dealii_cell->get_dof_indices (local_dof_indices); + + for (std::set::iterator it=send_to.begin(); + it!=send_to.end();++it) + { + const dealii::types::subdomain_id_t subdomain = *it; + + // get an iterator + // to what needs to + // be sent to that + // subdomain (if + // already exists), + // or create such + // an object + typename std::map::cellinfo>::iterator + p + = needs_to_get_cell.insert (std::make_pair(subdomain, + typename types::cellinfo())) + .first; + + p->second.tree_index.push_back(tree_index); + p->second.quadrants.push_back(p4est_cell); + + p->second.dofs.push_back(dealii_cell->get_fe().dofs_per_cell); + p->second.dofs.insert(p->second.dofs.end(), + local_dof_indices.begin(), + local_dof_indices.end()); + + } + } + } + } + + + template + void + set_dofindices_recursively ( + const parallel::distributed::Triangulation &tria, + const typename dealii::internal::p4est::types::quadrant &p4est_cell, + const typename DoFHandler::cell_iterator &dealii_cell, + const typename dealii::internal::p4est::types::quadrant &quadrant, + unsigned int * dofs) + { + if (internal::p4est::quadrant_is_equal(p4est_cell, quadrant)) + { + Assert(!dealii_cell->has_children(), ExcInternalError()); + Assert(dealii_cell->is_ghost(), ExcInternalError()); + + // update dof indices of cell + std::vector + dof_indices (dealii_cell->get_fe().dofs_per_cell); + dealii_cell->update_cell_dof_indices_cache(); + dealii_cell->get_dof_indices(dof_indices); + + bool complete = true; + for (unsigned int i=0;i::invalid_dof_index) + { + Assert((dof_indices[i] == + (DoFHandler::invalid_dof_index)) + || + (dof_indices[i]==dofs[i]), + ExcInternalError()); + dof_indices[i]=dofs[i]; + } + else + complete=false; + + if (!complete) + const_cast + ::cell_iterator &> + (dealii_cell)->set_user_flag(); + else + const_cast + ::cell_iterator &> + (dealii_cell)->clear_user_flag(); + + const_cast + ::cell_iterator &> + (dealii_cell)->set_dof_indices(dof_indices); + + return; + } + + if (! dealii_cell->has_children()) + return; + + if (! internal::p4est::quadrant_is_ancestor (p4est_cell, quadrant)) + return; + + typename dealii::internal::p4est::types::quadrant + p4est_child[GeometryInfo::max_children_per_cell]; + internal::p4est::init_quadrant_children(p4est_cell, p4est_child); + + for (unsigned int c=0; c::max_children_per_cell; ++c) + set_dofindices_recursively (tria, p4est_child[c], + dealii_cell->child(c), + quadrant, dofs); + } + + /** + * Return a vector in which, + * for every vertex index, we + * mark whether a locally owned + * cell is adjacent. + */ + template + std::vector + mark_locally_active_vertices (const parallel::distributed::Triangulation &triangulation) + { + std::vector locally_active_vertices (triangulation.n_vertices(), + false); + + for (typename dealii::Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) + if (cell->subdomain_id() == triangulation.locally_owned_subdomain()) + for (unsigned int v=0; v::vertices_per_cell; ++v) + locally_active_vertices[cell->vertex_index(v)] = true; + + return locally_active_vertices; + } + + + + template + void + communicate_dof_indices_on_marked_cells + (const DoFHandler<1,spacedim> &, + const std::map > &, + const std::vector &, + const std::vector &) + { + Assert (false, ExcNotImplemented()); + } + + + + template + void + communicate_dof_indices_on_marked_cells + (const DoFHandler &dof_handler, + const std::map > &vertices_with_ghost_neighbors, + const std::vector &coarse_cell_to_p4est_tree_permutation, + const std::vector &p4est_tree_to_coarse_cell_permutation) + { +#ifndef DEAL_II_USE_P4EST + (void)vertices_with_ghost_neighbors; + Assert (false, ExcNotImplemented()); +#else + + const parallel::distributed::Triangulation< dim, spacedim > * tr + = (dynamic_cast*> + (&dof_handler.get_tria())); + Assert (tr != 0, ExcInternalError()); + + // now collect cells and their + // dof_indices for the + // interested neighbors + typedef + std::map::cellinfo> + cellmap_t; + cellmap_t needs_to_get_cells; + + for (typename DoFHandler::cell_iterator + cell = dof_handler.begin(0); + cell != dof_handler.end(0); + ++cell) + { + typename dealii::internal::p4est::types::quadrant p4est_coarse_cell; + internal::p4est::init_coarse_quadrant(p4est_coarse_cell); + + fill_dofindices_recursively + (*tr, + coarse_cell_to_p4est_tree_permutation[cell->index()], + cell, + p4est_coarse_cell, + vertices_with_ghost_neighbors, + needs_to_get_cells); + } + + + + //sending + std::vector > sendbuffers (needs_to_get_cells.size()); + std::vector >::iterator buffer = sendbuffers.begin(); + std::vector requests (needs_to_get_cells.size()); + + unsigned int idx=0; + + for (typename cellmap_t::iterator it=needs_to_get_cells.begin(); + it!=needs_to_get_cells.end(); + ++it, ++buffer, ++idx) + { + const unsigned int num_cells = it->second.tree_index.size(); + + Assert(num_cells==it->second.quadrants.size(), ExcInternalError()); + Assert(num_cells>0, ExcInternalError()); + + // pack all the data into + // the buffer for this + // recipient and send + // it. keep data around + // till we can make sure + // that the packet has been + // received + it->second.pack_data (*buffer); + MPI_Isend(&(*buffer)[0], buffer->size(), + MPI_BYTE, it->first, + 123, tr->get_communicator(), &requests[idx]); + } + + + // mark all own cells, that miss some + // dof_data and collect the neighbors + // that are going to send stuff to us + std::set senders; + { + std::vector local_dof_indices; + typename DoFHandler::active_cell_iterator + cell, endc = dof_handler.end(); + + for (cell = dof_handler.begin_active(); cell != endc; ++cell) + if (!cell->is_artificial()) + { + if (cell->is_ghost()) + { + if (cell->user_flag_set()) + senders.insert(cell->subdomain_id()); + } + else + { + local_dof_indices.resize (cell->get_fe().dofs_per_cell); + cell->get_dof_indices (local_dof_indices); + if (local_dof_indices.end() != + std::find (local_dof_indices.begin(), + local_dof_indices.end(), + DoFHandler::invalid_dof_index)) + cell->set_user_flag(); + else + cell->clear_user_flag(); + } + + } + } + + + //* 5. receive ghostcelldata + std::vector receive; + typename types::cellinfo cellinfo; + for (unsigned int i=0;iget_communicator(), &status); + MPI_Get_count(&status, MPI_BYTE, &len); + receive.resize(len); + + char * ptr = &receive[0]; + MPI_Recv(ptr, len, MPI_BYTE, status.MPI_SOURCE, status.MPI_TAG, + tr->get_communicator(), &status); + + unsigned int cells; + memcpy(&cells, ptr, sizeof(unsigned int)); + ptr+=sizeof(unsigned int); + + //reinterpret too evil? + unsigned int * treeindex=reinterpret_cast(ptr); + ptr+=cells*sizeof(unsigned int); + typename dealii::internal::p4est::types::quadrant * quadrant + =reinterpret_cast::quadrant *>(ptr); + ptr+=cells*sizeof(typename dealii::internal::p4est::types::quadrant); + unsigned int * dofs=reinterpret_cast(ptr); + + for (unsigned int c=0;c::cell_iterator + cell (&dof_handler.get_tria(), + 0, + p4est_tree_to_coarse_cell_permutation[treeindex[c]], + &dof_handler); + + typename dealii::internal::p4est::types::quadrant p4est_coarse_cell; + internal::p4est::init_coarse_quadrant(p4est_coarse_cell); + + Assert(cell->get_fe().dofs_per_cell==dofs[0], ExcInternalError()); + + set_dofindices_recursively (*tr, + p4est_coarse_cell, + cell, + quadrant[c], + (dofs+1)); + } + } + + // complete all sends, so that we can + // safely destroy the buffers. + MPI_Waitall(requests.size(), &requests[0], MPI_STATUSES_IGNORE); + + +#ifdef DEBUG + { + //check all msgs got sent and received + unsigned int sum_send=0; + unsigned int sum_recv=0; + unsigned int sent=needs_to_get_cells.size(); + unsigned int recv=senders.size(); + + MPI_Reduce(&sent, &sum_send, 1, MPI_INT, MPI_SUM, 0, tr->get_communicator()); + MPI_Reduce(&recv, &sum_recv, 1, MPI_INT, MPI_SUM, 0, tr->get_communicator()); + Assert(sum_send==sum_recv, ExcInternalError()); + } +#endif + + //update dofindices + { + typename DoFHandler::active_cell_iterator + cell, endc = dof_handler.end(); + + for (cell = dof_handler.begin_active(); cell != endc; ++cell) + if (!cell->is_artificial()) + cell->update_cell_dof_indices_cache(); + } + + // important, so that sends between two + // calls to this function are not mixed + // up. + // + // this is necessary because above we + // just see if there are messages and + // then receive them, without + // discriminating where they come from + // and whether they were sent in phase + // 1 or 2. the need for a global + // communication step like this barrier + // could be avoided by receiving + // messages specifically from those + // processors from which we expect + // messages, and by using different + // tags for phase 1 and 2 + MPI_Barrier(tr->get_communicator()); +#endif + } + } + +#endif // DEAL_II_USE_P4EST + + + + template + NumberCache + ParallelDistributed:: + distribute_dofs (const unsigned int offset, + DoFHandler &dof_handler) const + { + Assert(offset==0, ExcNotImplemented()); + NumberCache number_cache; + +#ifndef DEAL_II_USE_P4EST + (void)fe; + Assert (false, ExcNotImplemented()); +#else + + + parallel::distributed::Triangulation< dim, spacedim > * tr + = (dynamic_cast*> + (const_cast*> + (&dof_handler.get_tria()))); + Assert (tr != 0, ExcInternalError()); + + const unsigned int + n_cpus = Utilities::System::get_n_mpi_processes (tr->get_communicator()); + + //* 1. distribute on own + //* subdomain + const unsigned int n_initial_local_dofs = + Implementation::distribute_dofs (0, tr->locally_owned_subdomain(), + dof_handler); + + //* 2. iterate over ghostcells and + //kill dofs that are not owned + //by us + std::vector renumbering(n_initial_local_dofs); + for (unsigned int i=0; i local_dof_indices; + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + + for (; cell != endc; ++cell) + if (cell->is_ghost() && + (cell->subdomain_id() < tr->locally_owned_subdomain())) + { + // we found a + // neighboring ghost + // cell whose subdomain + // is "stronger" than + // our own subdomain + + // delete all dofs that + // live there and that + // we have previously + // assigned a number to + // (i.e. the ones on + // the interface) + local_dof_indices.resize (cell->get_fe().dofs_per_cell); + cell->get_dof_indices (local_dof_indices); + for (unsigned int i=0; iget_fe().dofs_per_cell; ++i) + if (local_dof_indices[i] != DoFHandler::invalid_dof_index) + renumbering[local_dof_indices[i]] + = DoFHandler::invalid_dof_index; + } + } + + + // make indices consecutive + number_cache.n_locally_owned_dofs = 0; + for (std::vector::iterator it=renumbering.begin(); + it!=renumbering.end(); ++it) + if (*it != DoFHandler::invalid_dof_index) + *it = number_cache.n_locally_owned_dofs++; + + //* 3. communicate local dofcount and + //shift ids to make them unique + number_cache.n_locally_owned_dofs_per_processor.resize(n_cpus); + + MPI_Allgather ( &number_cache.n_locally_owned_dofs, + 1, MPI_INT, + &number_cache.n_locally_owned_dofs_per_processor[0], + 1, MPI_INT, + tr->get_communicator()); + + const unsigned int + shift = std::accumulate( &number_cache + .n_locally_owned_dofs_per_processor[0], + &number_cache + .n_locally_owned_dofs_per_processor + [tr->locally_owned_subdomain()], + 0 ); + for (std::vector::iterator it=renumbering.begin(); + it!=renumbering.end(); ++it) + if (*it != DoFHandler::invalid_dof_index) + (*it) += shift; + + // now re-enumerate all dofs to + // this shifted and condensed + // numbering form. we renumber + // some dofs as invalid, so + // choose the nocheck-version. + Implementation::renumber_dofs (renumbering, IndexSet(0), + dof_handler, false); + + // now a little bit of + // housekeeping + number_cache.n_global_dofs + = std::accumulate( &number_cache + .n_locally_owned_dofs_per_processor[0], + &number_cache + .n_locally_owned_dofs_per_processor[n_cpus], + 0 ); + + number_cache.locally_owned_dofs = IndexSet(number_cache.n_global_dofs); + number_cache.locally_owned_dofs + .add_range(shift, + shift+number_cache.n_locally_owned_dofs); + + // fill global_dof_indexsets + number_cache.locally_owned_dofs_per_processor.resize(n_cpus); + { + unsigned int lshift = 0; + for (unsigned int i=0;ilocally_owned_subdomain()].n_elements() + == + number_cache.n_locally_owned_dofs, + ExcInternalError()); + Assert(!number_cache.locally_owned_dofs_per_processor + [tr->locally_owned_subdomain()].n_elements() + || + number_cache.locally_owned_dofs_per_processor + [tr->locally_owned_subdomain()].nth_index_in_set(0) + == shift, + ExcInternalError()); + + //* 4. send dofids of cells that are + //ghostcells on other machines + + std::vector user_flags; + tr->save_user_flags(user_flags); + tr->clear_user_flags (); + + //mark all own cells for transfer + typename DoFHandler::active_cell_iterator + cell, endc = dof_handler.end(); + for (cell = dof_handler.begin_active(); cell != endc; ++cell) + if (!cell->is_artificial()) + cell->set_user_flag(); + + //mark the vertices we are interested + //in, i.e. belonging to own and marked cells + const std::vector locally_active_vertices + = mark_locally_active_vertices (*tr); + + // add each ghostcells' + // subdomain to the vertex and + // keep track of interesting + // neighbors + std::map > + vertices_with_ghost_neighbors; + for (typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(); + cell != dof_handler.end(); ++cell) + if (cell->is_ghost ()) + for (unsigned int v=0; v::vertices_per_cell; ++v) + if (locally_active_vertices[cell->vertex_index(v)]) + vertices_with_ghost_neighbors[cell->vertex_index(v)] + .insert (cell->subdomain_id()); + + + /* Send and receive cells. After this, + only the local cells are marked, + that received new data. This has to + be communicated in a second + communication step. */ + communicate_dof_indices_on_marked_cells (dof_handler, + vertices_with_ghost_neighbors, + tr->coarse_cell_to_p4est_tree_permutation, + tr->p4est_tree_to_coarse_cell_permutation); + + communicate_dof_indices_on_marked_cells (dof_handler, + vertices_with_ghost_neighbors, + tr->coarse_cell_to_p4est_tree_permutation, + tr->p4est_tree_to_coarse_cell_permutation); + + tr->load_user_flags(user_flags); + +#ifdef DEBUG + //check that we are really done + { + std::vector local_dof_indices; + typename DoFHandler::active_cell_iterator + cell, endc = dof_handler.end(); + + for (cell = dof_handler.begin_active(); cell != endc; ++cell) + if (!cell->is_artificial()) + { + local_dof_indices.resize (cell->get_fe().dofs_per_cell); + cell->get_dof_indices (local_dof_indices); + if (local_dof_indices.end() != + std::find (local_dof_indices.begin(), + local_dof_indices.end(), + DoFHandler::invalid_dof_index)) + { + if (cell->is_ghost()) + { + Assert(false, ExcMessage ("Not a ghost cell")); + } + else + { + Assert(false, ExcMessage ("Not one of our own cells")); + } + } + } + } +#endif // DEBUG +#endif // DEAL_II_USE_P4EST + + return number_cache; + } + + + + template + NumberCache + ParallelDistributed:: + renumber_dofs (const std::vector &new_numbers, + dealii::DoFHandler &dof_handler) const + { + Assert (new_numbers.size() == dof_handler.locally_owned_dofs().n_elements(), + ExcInternalError()); + + NumberCache number_cache; + +#ifndef DEAL_II_USE_P4EST + Assert (false, ExcNotImplemented()); +#else + + + //calculate new IndexSet. First try + //to find out if the new indices are + //contiguous blocks. This avoids + //inserting each index individually + //into the IndexSet, which is slow. + //If we own no DoFs, we still need to + //go through this function, but we + //can skip this calculation. + + number_cache.locally_owned_dofs = IndexSet (dof_handler.n_dofs()); + if (dof_handler.locally_owned_dofs().n_elements()>0) + { + std::vector::const_iterator it = new_numbers.begin(); + const unsigned int n_blocks = dof_handler.get_fe().n_blocks(); + std::vector > block_indices(n_blocks); + block_indices[0].first = *it++; + block_indices[0].second = 1; + unsigned int current_block = 0, n_filled_blocks = 1; + for ( ; it != new_numbers.end(); ++it) + { + bool done = false; + + // search from the current block onwards + // whether the next index is shifted by one + // from the previous one. + for (unsigned int i=0; i local_dof_indices; + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + + for (; cell != endc; ++cell) + if (!cell->is_artificial()) + { + local_dof_indices.resize (cell->get_fe().dofs_per_cell); + cell->get_dof_indices (local_dof_indices); + for (unsigned int i=0; iget_fe().dofs_per_cell; ++i) + { + if (local_dof_indices[i] == DoFHandler::invalid_dof_index) + continue; + + if (!dof_handler.locally_owned_dofs().is_element(local_dof_indices[i])) + { + //this DoF is not owned + //by us, so set it to + //invalid. + local_dof_indices[i] + = DoFHandler::invalid_dof_index; + } + } + + cell->set_dof_indices (local_dof_indices); + } + } + + + // renumber. Skip when there is + // nothing to do because we own no + // DoF. + if (dof_handler.locally_owned_dofs().n_elements() > 0) + Implementation::renumber_dofs (new_numbers, + dof_handler.locally_owned_dofs(), + dof_handler, + false); + + // communication + { + parallel::distributed::Triangulation< dim, spacedim > * tr + = (dynamic_cast*> + (const_cast*> + (&dof_handler.get_tria()))); + Assert (tr != 0, ExcInternalError()); + + std::vector user_flags; + tr->save_user_flags(user_flags); + tr->clear_user_flags (); + + //mark all own cells for transfer + typename DoFHandler::active_cell_iterator + cell, endc = dof_handler.end(); + for (cell = dof_handler.begin_active(); cell != endc; ++cell) + if (!cell->is_artificial()) + cell->set_user_flag(); + //mark the vertices we are interested + //in, i.e. belonging to own and marked cells + const std::vector locally_active_vertices + = mark_locally_active_vertices (*tr); + + // add each ghostcells' + // subdomain to the vertex and + // keep track of interesting + // neighbors + std::map > + vertices_with_ghost_neighbors; + for (typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(); + cell != dof_handler.end(); ++cell) + if (cell->is_ghost ()) + for (unsigned int v=0; v::vertices_per_cell; ++v) + if (locally_active_vertices[cell->vertex_index(v)]) + vertices_with_ghost_neighbors[cell->vertex_index(v)] + .insert (cell->subdomain_id()); + + // Send and receive cells. After this, only + // the local cells are marked, that received + // new data. This has to be communicated in a + // second communication step. + communicate_dof_indices_on_marked_cells (dof_handler, + vertices_with_ghost_neighbors, + tr->coarse_cell_to_p4est_tree_permutation, + tr->p4est_tree_to_coarse_cell_permutation); + + communicate_dof_indices_on_marked_cells (dof_handler, + vertices_with_ghost_neighbors, + tr->coarse_cell_to_p4est_tree_permutation, + tr->p4est_tree_to_coarse_cell_permutation); + + + // * Create global_dof_indexsets by + // transfering our own owned_dofs to + // every other machine. + const unsigned int n_cpus = Utilities::System:: + get_n_mpi_processes (tr->get_communicator()); + + // Serialize our IndexSet and + // determine size. + std::ostringstream oss; + number_cache.locally_owned_dofs.block_write(oss); + std::string oss_str=oss.str(); + std::vector my_data(oss_str.begin(), oss_str.end()); + unsigned int my_size = my_data.size(); + + // determine maximum size of IndexSet + unsigned int max_size; + + MPI_Allreduce(&my_size, &max_size, 1, MPI_INT, MPI_MAX, tr->get_communicator()); + + // as we are reading past the end, we + // need to increase the size of the + // local buffer. This is filled with + // zeros. + my_data.resize(max_size); + + std::vector buffer(max_size*n_cpus); + MPI_Allgather(&my_data[0], max_size, MPI_BYTE, + &buffer[0], max_size, MPI_BYTE, + tr->get_communicator()); + + number_cache.locally_owned_dofs_per_processor.resize (n_cpus); + number_cache.n_locally_owned_dofs_per_processor.resize (n_cpus); + for (unsigned int i=0;iload_user_flags(user_flags); + } +#endif + + return number_cache; + } + } + } +} + + + + +/*-------------- Explicit Instantiations -------------------------------*/ + +namespace internal +{ + namespace DoFHandler + { + namespace Policy + { + template class PolicyBase; + template class Sequential; + template class ParallelDistributed; + +#if deal_II_dimension==1 || deal_II_dimension==2 + template class PolicyBase; + template class Sequential; + template class ParallelDistributed; +#endif + } + } +} + + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/deal.II/source/dofs/dof_renumbering.cc b/deal.II/deal.II/source/dofs/dof_renumbering.cc index 990948db7e..c6b16a87d7 100644 --- a/deal.II/deal.II/source/dofs/dof_renumbering.cc +++ b/deal.II/deal.II/source/dofs/dof_renumbering.cc @@ -1,5 +1,5 @@ //--------------------------------------------------------------------------- -// $id$ +// $Id$ // Version: $name$ // // Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors @@ -78,6 +78,8 @@ namespace DoFRenumbering class WrapDoFIterator : private T { public: + typedef typename T::AccessorType AccessorType; + WrapDoFIterator (const T& t) : T(t) {} void get_dof_indices (std::vector& v) const @@ -102,6 +104,8 @@ namespace DoFRenumbering class WrapMGDoFIterator : private T { public: + typedef typename T::AccessorType AccessorType; + WrapMGDoFIterator (const T& t) : T(t) {} void get_dof_indices (std::vector& v) const @@ -505,6 +509,7 @@ namespace DoFRenumbering } + template void Cuthill_McKee (MGDoFHandler &dof_handler, const unsigned int level, @@ -532,12 +537,13 @@ namespace DoFRenumbering } + template void component_wise (DoFHandler &dof_handler, const std::vector &component_order_arg) { - std::vector renumbering (dof_handler.n_dofs(), + std::vector renumbering (dof_handler.n_locally_owned_dofs(), DoFHandler::invalid_dof_index); typedef @@ -554,10 +560,21 @@ namespace DoFRenumbering typename DoFHandler::cell_iterator>(renumbering, start, end, component_order_arg); - - if (result == 0) return; - - Assert (result == dof_handler.n_dofs(), + if (result == 0) + return; + + // verify that the last numbered + // degree of freedom is either + // equal to the number of degrees + // of freedom in total (the + // sequential case) or in the + // distributed case at least + // makes sense + Assert ((result == dof_handler.n_locally_owned_dofs()) + || + ((dof_handler.n_locally_owned_dofs() < dof_handler.n_dofs()) + && + (result <= dof_handler.n_dofs())), ExcRenumberingIncomplete()); dof_handler.renumber_dofs (renumbering); @@ -597,6 +614,7 @@ namespace DoFRenumbering } + template void component_wise (MGDoFHandler &dof_handler, @@ -630,6 +648,7 @@ namespace DoFRenumbering } + template void component_wise (MGDoFHandler &dof_handler, @@ -656,11 +675,10 @@ namespace DoFRenumbering - template unsigned int compute_component_wise (std::vector& new_indices, - ITERATOR& start, + const ITERATOR & start, const ENDITERATOR& end, const std::vector &component_order_arg) { @@ -674,10 +692,6 @@ namespace DoFRenumbering return 0; } -// Assert (new_indices.size() == start->dof_handler().n_dofs(), -// ExcDimensionMismatch(new_indices.size(), -// start->dof_handler.n_dofs())); - // Copy last argument into a // writable vector. std::vector component_order (component_order_arg); @@ -692,7 +706,7 @@ namespace DoFRenumbering Assert (component_order.size() == fe_collection.n_components(), ExcDimensionMismatch(component_order.size(), fe_collection.n_components())); - for (unsigned int i=0; i< component_order.size(); ++i) + for (unsigned int i=0; i > component_to_dof_map (fe_collection.n_components()); - for (;start!=end;++start) - { + for (ITERATOR cell=start; cell!=end; ++cell) + if (!cell->is_artificial() && !cell->is_ghost()) + { // on each cell: get dof indices // and insert them into the global // list using their component - const unsigned int fe_index = start->active_fe_index(); - const unsigned int dofs_per_cell =fe_collection[fe_index].dofs_per_cell; - local_dof_indices.resize (dofs_per_cell); - start.get_dof_indices (local_dof_indices); - for (unsigned int i=0; iactive_fe_index(); + const unsigned int dofs_per_cell =fe_collection[fe_index].dofs_per_cell; + local_dof_indices.resize (dofs_per_cell); + cell.get_dof_indices (local_dof_indices); + for (unsigned int i=0; iget_dof_handler().locally_owned_dofs().is_element(local_dof_indices[i])) + component_to_dof_map[component_list[fe_index][i]]. + push_back (local_dof_indices[i]); + } // now we've got all indices sorted // into buckets labelled with their @@ -793,25 +809,88 @@ namespace DoFRenumbering .erase (std::unique (component_to_dof_map[component].begin(), component_to_dof_map[component].end()), component_to_dof_map[component].end()); - }; + } + + // calculate the number of locally owned + // DoFs per bucket + const unsigned int n_buckets = fe_collection.n_components(); + std::vector shifts(n_buckets); + + if (const parallel::distributed::Triangulation * tria + = (dynamic_cast*> + (&start->get_dof_handler().get_tria()))) + { +#ifdef DEAL_II_USE_P4EST + std::vector local_dof_count(n_buckets); + + for (unsigned int c=0; c + all_dof_counts(fe_collection.n_components() * + Utilities::System::get_n_mpi_processes (tria->get_communicator())); + + MPI_Allgather ( &local_dof_count[0], n_buckets, MPI_INT, &all_dof_counts[0], + n_buckets, MPI_INT, tria->get_communicator()); + + for (unsigned int i=0; ilocally_owned_subdomain()+i] + == + local_dof_count[i], + ExcInternalError()); + + //calculate shifts + unsigned int cumulated = 0; + for (unsigned int c=0; clocally_owned_subdomain(); ++i) + shifts[c] += all_dof_counts[c+n_buckets*i]; + for (unsigned int i=0; iget_communicator()); ++i) + cumulated += all_dof_counts[c+n_buckets*i]; + } +#else + Assert (false, ExcInternalError()); +#endif + } + else + { + shifts[0] = 0; + for (unsigned int c=1; c::const_iterator begin_of_component = component_to_dof_map[component].begin(), end_of_component = component_to_dof_map[component].end(); + next_free_index = shifts[component]; + for (typename std::vector::const_iterator dof_index = begin_of_component; dof_index != end_of_component; ++dof_index) - new_indices[*dof_index] = next_free_index++; - }; + { + Assert (start->get_dof_handler().locally_owned_dofs() + .index_within_set(*dof_index) + < + new_indices.size(), + ExcInternalError()); + new_indices[start->get_dof_handler().locally_owned_dofs() + .index_within_set(*dof_index)] + = next_free_index++; + } + } return next_free_index; } @@ -1609,7 +1688,7 @@ namespace DoFRenumbering // first get the association of each dof // with a subdomain and determine the total // number of subdomain ids used - std::vector subdomain_association (n_dofs); + std::vector subdomain_association (n_dofs); DoFTools::get_subdomain_association (dof_handler, subdomain_association); const unsigned int n_subdomains diff --git a/deal.II/deal.II/source/dofs/dof_tools.cc b/deal.II/deal.II/source/dofs/dof_tools.cc index 2a85824601..c6fe150a67 100644 --- a/deal.II/deal.II/source/dofs/dof_tools.cc +++ b/deal.II/deal.II/source/dofs/dof_tools.cc @@ -79,9 +79,13 @@ DoFTools::make_sparsity_pattern (const DH &dof, // current cell is owned by the calling // processor. Otherwise, just continue. for (; cell!=endc; ++cell) - if ((subdomain_id == numbers::invalid_unsigned_int) - || - (subdomain_id == cell->subdomain_id())) + if (((subdomain_id == types::invalid_subdomain_id) + || + (subdomain_id == cell->subdomain_id())) + && + !cell->is_artificial() + && + !cell->is_ghost()) { const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; dofs_on_this_cell.resize (dofs_per_cell); @@ -106,7 +110,7 @@ DoFTools::make_sparsity_pattern (const DH &dof, SparsityPattern &sparsity, const ConstraintMatrix &constraints, const bool keep_constrained_dofs, - const unsigned int subdomain_id) + const types::subdomain_id_t subdomain_id) { const unsigned int n_dofs = dof.n_dofs(); @@ -190,9 +194,13 @@ DoFTools::make_sparsity_pattern (const DH &dof, // current cell is owned by the calling // processor. Otherwise, just continue. for (; cell!=endc; ++cell) - if ((subdomain_id == numbers::invalid_unsigned_int) - || - (subdomain_id == cell->subdomain_id())) + if (((subdomain_id == types::invalid_subdomain_id) + || + (subdomain_id == cell->subdomain_id())) + && + !cell->is_artificial() + && + !cell->is_ghost()) { const unsigned int fe_index = cell->active_fe_index(); const unsigned int dofs_per_cell =fe_collection[fe_index].dofs_per_cell; @@ -1749,6 +1757,18 @@ namespace internal const unsigned int n_master_dofs = master_dofs.size (); const unsigned int n_slave_dofs = slave_dofs.size (); + // check for a couple + // conditions that happened + // in parallel distributed + // mode + for (unsigned int row=0; row!=n_slave_dofs; ++row) + Assert (slave_dofs[row] != numbers::invalid_unsigned_int, + ExcInternalError()); + for (unsigned int col=0; col!=n_master_dofs; ++col) + Assert (master_dofs[col] != numbers::invalid_unsigned_int, + ExcInternalError()); + + for (unsigned int row=0; row!=n_slave_dofs; ++row) if (constraints.is_constrained (slave_dofs[row]) == false) { @@ -1913,7 +1933,7 @@ namespace internal // currently not used but may be in the future: - + // static // void // make_hp_hanging_node_constraints (const dealii::MGDoFHandler<1,2> &, @@ -1986,116 +2006,126 @@ namespace internal typename DH::active_cell_iterator cell = dof_handler.begin_active(), endc = dof_handler.end(); for (; cell!=endc; ++cell) - for (unsigned int face=0; face::faces_per_cell; ++face) - if (cell->face(face)->has_children()) - { - // so now we've found a - // face of an active - // cell that has - // children. that means - // that there are - // hanging nodes here. - - // in any case, faces - // can have at most two - // active fe indices, - // but here the face - // can have only one - // (namely the same as - // that from the cell - // we're sitting on), - // and each of the - // children can have - // only one as - // well. check this - Assert (cell->face(face)->n_active_fe_indices() == 1, - ExcInternalError()); - Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) - == true, - ExcInternalError()); - for (unsigned int c=0; cface(face)->n_children(); ++c) - Assert (cell->face(face)->child(c)->n_active_fe_indices() == 1, + // artificial cells can at + // best neighbor ghost cells, + // but we're not interested + // in these interfaces + if (!cell->is_artificial ()) + for (unsigned int face=0; face::faces_per_cell; ++face) + if (cell->face(face)->has_children()) + { + // in any case, faces + // can have at most two + // active fe indices, + // but here the face + // can have only one + // (namely the same as + // that from the cell + // we're sitting on), + // and each of the + // children can have + // only one as + // well. check this + Assert (cell->face(face)->n_active_fe_indices() == 1, ExcInternalError()); + Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) + == true, + ExcInternalError()); + for (unsigned int c=0; cface(face)->n_children(); ++c) + if (!cell->neighbor_child_on_subface(face,c)->is_artificial()) + Assert (cell->face(face)->child(c)->n_active_fe_indices() == 1, + ExcInternalError()); - // right now, all that - // is implemented is - // the case that both - // sides use the same - // fe - for (unsigned int c=0; cface(face)->n_children(); ++c) - Assert (cell->face(face)->child(c) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcNotImplemented()); - - // ok, start up the work - const FiniteElement &fe = cell->get_fe(); - const unsigned int fe_index = cell->active_fe_index(); + // right now, all that + // is implemented is + // the case that both + // sides use the same + // fe + for (unsigned int c=0; cface(face)->n_children(); ++c) + if (!cell->neighbor_child_on_subface(face,c)->is_artificial()) + Assert (cell->face(face)->child(c) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcNotImplemented()); + + // ok, start up the work + const FiniteElement &fe = cell->get_fe(); + const unsigned int fe_index = cell->active_fe_index(); + + const unsigned int + n_dofs_on_mother = 2*fe.dofs_per_vertex + fe.dofs_per_line, + n_dofs_on_children = fe.dofs_per_vertex + 2*fe.dofs_per_line; + + dofs_on_mother.resize (n_dofs_on_mother); + dofs_on_children.resize (n_dofs_on_children); + + Assert(n_dofs_on_mother == fe.constraints().n(), + ExcDimensionMismatch(n_dofs_on_mother, + fe.constraints().n())); + Assert(n_dofs_on_children == fe.constraints().m(), + ExcDimensionMismatch(n_dofs_on_children, + fe.constraints().m())); + + const typename DH::line_iterator this_face = cell->face(face); + + // fill the dofs indices. Use same + // enumeration scheme as in + // @p{FiniteElement::constraints()} + unsigned int next_index = 0; + for (unsigned int vertex=0; vertex<2; ++vertex) + for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) + dofs_on_mother[next_index++] = this_face->vertex_dof_index(vertex,dof, + fe_index); + for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) + dofs_on_mother[next_index++] = this_face->dof_index(dof, fe_index); + AssertDimension (next_index, dofs_on_mother.size()); - const unsigned int - n_dofs_on_mother = 2*fe.dofs_per_vertex + fe.dofs_per_line, - n_dofs_on_children = fe.dofs_per_vertex + 2*fe.dofs_per_line; - - dofs_on_mother.resize (n_dofs_on_mother); - dofs_on_children.resize (n_dofs_on_children); - - Assert(n_dofs_on_mother == fe.constraints().n(), - ExcDimensionMismatch(n_dofs_on_mother, - fe.constraints().n())); - Assert(n_dofs_on_children == fe.constraints().m(), - ExcDimensionMismatch(n_dofs_on_children, - fe.constraints().m())); - - const typename DH::line_iterator this_face = cell->face(face); - - // fill the dofs indices. Use same - // enumeration scheme as in - // @p{FiniteElement::constraints()} - unsigned int next_index = 0; - for (unsigned int vertex=0; vertex<2; ++vertex) + next_index = 0; for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) - dofs_on_mother[next_index++] = this_face->vertex_dof_index(vertex,dof, - fe_index); - for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) - dofs_on_mother[next_index++] = this_face->dof_index(dof, fe_index); - AssertDimension (next_index, dofs_on_mother.size()); - - next_index = 0; - for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) - dofs_on_children[next_index++] - = this_face->child(0)->vertex_dof_index(1,dof,fe_index); - for (unsigned int child=0; child<2; ++child) - for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) dofs_on_children[next_index++] - = this_face->child(child)->dof_index(dof, fe_index); - AssertDimension (next_index, dofs_on_children.size()); + = this_face->child(0)->vertex_dof_index(1,dof,fe_index); + for (unsigned int child=0; child<2; ++child) + for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) + dofs_on_children[next_index++] + = this_face->child(child)->dof_index(dof, fe_index); + AssertDimension (next_index, dofs_on_children.size()); - // for each row in the constraint - // matrix for this line: - for (unsigned int row=0; row!=dofs_on_children.size(); ++row) - { - constraints.add_line (dofs_on_children[row]); - for (unsigned int i=0; i!=dofs_on_mother.size(); ++i) - constraints.add_entry (dofs_on_children[row], - dofs_on_mother[i], - fe.constraints()(row,i)); + // for each row in the constraint + // matrix for this line: + for (unsigned int row=0; row!=dofs_on_children.size(); ++row) + { + constraints.add_line (dofs_on_children[row]); + for (unsigned int i=0; i!=dofs_on_mother.size(); ++i) + constraints.add_entry (dofs_on_children[row], + dofs_on_mother[i], + fe.constraints()(row,i)); - constraints.set_inhomogeneity (dofs_on_children[row], 0.); - } - } - else - { - // this face has no - // children, but it - // could still be that - // it is shared by two - // cells that use a - // different fe index - Assert (cell->face(face)->n_active_fe_indices() == 1, - ExcNotImplemented()); - Assert (cell->face(face) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcInternalError()); - } + constraints.set_inhomogeneity (dofs_on_children[row], 0.); + } + } + else + { + // this face has no + // children, but it + // could still be + // that it is shared + // by two cells that + // use a different fe + // index. check a + // couple of things, + // but ignore the + // case that the + // neighbor is an + // artificial cell + if (!cell->at_boundary(face) && + !cell->neighbor(face)->is_artificial()) + { + Assert (cell->face(face)->n_active_fe_indices() == 1, + ExcNotImplemented()); + Assert (cell->face(face) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcInternalError()); + } + } } #endif @@ -2132,205 +2162,214 @@ namespace internal typename DH::active_cell_iterator cell = dof_handler.begin_active(), endc = dof_handler.end(); for (; cell!=endc; ++cell) - for (unsigned int face=0; face::faces_per_cell; ++face) - if (cell->face(face)->has_children()) - { - // first of all, make sure that - // we treat a case which is - // possible, i.e. either no dofs - // on the face at all or no - // anisotropic refinement - if (cell->get_fe().dofs_per_face == 0) - continue; - - Assert(cell->face(face)->refinement_case()==RefinementCase::isotropic_refinement, - ExcNotImplemented()); - - // so now we've found a - // face of an active - // cell that has - // children. that means - // that there are - // hanging nodes here. - - // in any case, faces - // can have at most two - // active fe indices, - // but here the face - // can have only one - // (namely the same as - // that from the cell - // we're sitting on), - // and each of the - // children can have - // only one as - // well. check this - AssertDimension (cell->face(face)->n_active_fe_indices(), 1); - Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) - == true, - ExcInternalError()); - for (unsigned int c=0; cface(face)->n_children(); ++c) - AssertDimension (cell->face(face)->child(c)->n_active_fe_indices(), 1); - - // right now, all that - // is implemented is - // the case that both - // sides use the same - // fe, and not only - // that but also that - // all lines bounding - // this face and the - // children have the - // same fe - for (unsigned int c=0; cface(face)->n_children(); ++c) - { - Assert (cell->face(face)->child(c) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcNotImplemented()); - for (unsigned int e=0; e<4; ++e) + // artificial cells can at + // best neighbor ghost cells, + // but we're not interested + // in these interfaces + if (!cell->is_artificial ()) + for (unsigned int face=0; face::faces_per_cell; ++face) + if (cell->face(face)->has_children()) + { + // first of all, make sure that + // we treat a case which is + // possible, i.e. either no dofs + // on the face at all or no + // anisotropic refinement + if (cell->get_fe().dofs_per_face == 0) + continue; + + Assert(cell->face(face)->refinement_case()==RefinementCase::isotropic_refinement, + ExcNotImplemented()); + + // in any case, faces + // can have at most two + // active fe indices, + // but here the face + // can have only one + // (namely the same as + // that from the cell + // we're sitting on), + // and each of the + // children can have + // only one as + // well. check this + AssertDimension (cell->face(face)->n_active_fe_indices(), 1); + Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) + == true, + ExcInternalError()); + for (unsigned int c=0; cface(face)->n_children(); ++c) + AssertDimension (cell->face(face)->child(c)->n_active_fe_indices(), 1); + + // right now, all that + // is implemented is + // the case that both + // sides use the same + // fe, and not only + // that but also that + // all lines bounding + // this face and the + // children have the + // same fe + for (unsigned int c=0; cface(face)->n_children(); ++c) + if (!cell->neighbor_child_on_subface(face,c)->is_artificial()) { - Assert (cell->face(face)->child(c)->line(e) - ->n_active_fe_indices() == 1, - ExcNotImplemented()); - Assert (cell->face(face)->child(c)->line(e) + Assert (cell->face(face)->child(c) ->fe_index_is_active(cell->active_fe_index()) == true, ExcNotImplemented()); + for (unsigned int e=0; e<4; ++e) + { + Assert (cell->face(face)->child(c)->line(e) + ->n_active_fe_indices() == 1, + ExcNotImplemented()); + Assert (cell->face(face)->child(c)->line(e) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcNotImplemented()); + } } - } - for (unsigned int e=0; e<4; ++e) - { - Assert (cell->face(face)->line(e) - ->n_active_fe_indices() == 1, - ExcNotImplemented()); - Assert (cell->face(face)->line(e) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcNotImplemented()); - } - - // ok, start up the work - const FiniteElement &fe = cell->get_fe(); - const unsigned int fe_index = cell->active_fe_index(); - - const unsigned int - n_dofs_on_mother = (4*fe.dofs_per_vertex+ - 4*fe.dofs_per_line+ - fe.dofs_per_quad), - n_dofs_on_children = (5*fe.dofs_per_vertex+ - 12*fe.dofs_per_line+ - 4*fe.dofs_per_quad); - //TODO[TL]: think about this and the following in case of anisotropic refinement - - dofs_on_mother.resize (n_dofs_on_mother); - dofs_on_children.resize (n_dofs_on_children); - - Assert(n_dofs_on_mother == fe.constraints().n(), - ExcDimensionMismatch(n_dofs_on_mother, - fe.constraints().n())); - Assert(n_dofs_on_children == fe.constraints().m(), - ExcDimensionMismatch(n_dofs_on_children, - fe.constraints().m())); - - const typename DH::face_iterator this_face = cell->face(face); - - // fill the dofs indices. Use same - // enumeration scheme as in - // @p{FiniteElement::constraints()} - unsigned int next_index = 0; - for (unsigned int vertex=0; vertex<4; ++vertex) - for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) - dofs_on_mother[next_index++] = this_face->vertex_dof_index(vertex,dof, - fe_index); - for (unsigned int line=0; line<4; ++line) - for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) - dofs_on_mother[next_index++] - = this_face->line(line)->dof_index(dof, fe_index); - for (unsigned int dof=0; dof!=fe.dofs_per_quad; ++dof) - dofs_on_mother[next_index++] = this_face->dof_index(dof, fe_index); - AssertDimension (next_index, dofs_on_mother.size()); - - next_index = 0; - - // assert some consistency - // assumptions - //TODO[TL]: think about this in case of anisotropic refinement - Assert (dof_handler.get_tria().get_anisotropic_refinement_flag() || - ((this_face->child(0)->vertex_index(3) == - this_face->child(1)->vertex_index(2)) && - (this_face->child(0)->vertex_index(3) == - this_face->child(2)->vertex_index(1)) && - (this_face->child(0)->vertex_index(3) == - this_face->child(3)->vertex_index(0))), - ExcInternalError()); - for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) - dofs_on_children[next_index++] - = this_face->child(0)->vertex_dof_index(3,dof); + for (unsigned int e=0; e<4; ++e) + { + Assert (cell->face(face)->line(e) + ->n_active_fe_indices() == 1, + ExcNotImplemented()); + Assert (cell->face(face)->line(e) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcNotImplemented()); + } - // dof numbers on the centers of - // the lines bounding this face - for (unsigned int line=0; line<4; ++line) + // ok, start up the work + const FiniteElement &fe = cell->get_fe(); + const unsigned int fe_index = cell->active_fe_index(); + + const unsigned int + n_dofs_on_mother = (4*fe.dofs_per_vertex+ + 4*fe.dofs_per_line+ + fe.dofs_per_quad), + n_dofs_on_children = (5*fe.dofs_per_vertex+ + 12*fe.dofs_per_line+ + 4*fe.dofs_per_quad); + //TODO[TL]: think about this and the following in case of anisotropic refinement + + dofs_on_mother.resize (n_dofs_on_mother); + dofs_on_children.resize (n_dofs_on_children); + + Assert(n_dofs_on_mother == fe.constraints().n(), + ExcDimensionMismatch(n_dofs_on_mother, + fe.constraints().n())); + Assert(n_dofs_on_children == fe.constraints().m(), + ExcDimensionMismatch(n_dofs_on_children, + fe.constraints().m())); + + const typename DH::face_iterator this_face = cell->face(face); + + // fill the dofs indices. Use same + // enumeration scheme as in + // @p{FiniteElement::constraints()} + unsigned int next_index = 0; + for (unsigned int vertex=0; vertex<4; ++vertex) + for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) + dofs_on_mother[next_index++] = this_face->vertex_dof_index(vertex,dof, + fe_index); + for (unsigned int line=0; line<4; ++line) + for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) + dofs_on_mother[next_index++] + = this_face->line(line)->dof_index(dof, fe_index); + for (unsigned int dof=0; dof!=fe.dofs_per_quad; ++dof) + dofs_on_mother[next_index++] = this_face->dof_index(dof, fe_index); + AssertDimension (next_index, dofs_on_mother.size()); + + next_index = 0; + + // assert some consistency + // assumptions + //TODO[TL]: think about this in case of anisotropic refinement + Assert (dof_handler.get_tria().get_anisotropic_refinement_flag() || + ((this_face->child(0)->vertex_index(3) == + this_face->child(1)->vertex_index(2)) && + (this_face->child(0)->vertex_index(3) == + this_face->child(2)->vertex_index(1)) && + (this_face->child(0)->vertex_index(3) == + this_face->child(3)->vertex_index(0))), + ExcInternalError()); for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) dofs_on_children[next_index++] - = this_face->line(line)->child(0)->vertex_dof_index(1,dof, fe_index); - - // next the dofs on the lines interior - // to the face; the order of these - // lines is laid down in the - // FiniteElement class documentation - for (unsigned int dof=0; dofchild(0)->line(1)->dof_index(dof, fe_index); - for (unsigned int dof=0; dofchild(2)->line(1)->dof_index(dof, fe_index); - for (unsigned int dof=0; dofchild(0)->line(3)->dof_index(dof, fe_index); - for (unsigned int dof=0; dofchild(1)->line(3)->dof_index(dof, fe_index); + = this_face->child(0)->vertex_dof_index(3,dof); - // dofs on the bordering lines - for (unsigned int line=0; line<4; ++line) - for (unsigned int child=0; child<2; ++child) - for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) + // dof numbers on the centers of + // the lines bounding this face + for (unsigned int line=0; line<4; ++line) + for (unsigned int dof=0; dof!=fe.dofs_per_vertex; ++dof) dofs_on_children[next_index++] - = this_face->line(line)->child(child)->dof_index(dof, fe_index); + = this_face->line(line)->child(0)->vertex_dof_index(1,dof, fe_index); - // finally, for the dofs interior - // to the four child faces - for (unsigned int child=0; child<4; ++child) - for (unsigned int dof=0; dof!=fe.dofs_per_quad; ++dof) + // next the dofs on the lines interior + // to the face; the order of these + // lines is laid down in the + // FiniteElement class documentation + for (unsigned int dof=0; dofchild(child)->dof_index(dof, fe_index); - AssertDimension (next_index, dofs_on_children.size()); + = this_face->child(0)->line(1)->dof_index(dof, fe_index); + for (unsigned int dof=0; dofchild(2)->line(1)->dof_index(dof, fe_index); + for (unsigned int dof=0; dofchild(0)->line(3)->dof_index(dof, fe_index); + for (unsigned int dof=0; dofchild(1)->line(3)->dof_index(dof, fe_index); + + // dofs on the bordering lines + for (unsigned int line=0; line<4; ++line) + for (unsigned int child=0; child<2; ++child) + for (unsigned int dof=0; dof!=fe.dofs_per_line; ++dof) + dofs_on_children[next_index++] + = this_face->line(line)->child(child)->dof_index(dof, fe_index); + + // finally, for the dofs interior + // to the four child faces + for (unsigned int child=0; child<4; ++child) + for (unsigned int dof=0; dof!=fe.dofs_per_quad; ++dof) + dofs_on_children[next_index++] + = this_face->child(child)->dof_index(dof, fe_index); + AssertDimension (next_index, dofs_on_children.size()); - // for each row in the constraint - // matrix for this line: - for (unsigned int row=0; row!=dofs_on_children.size(); ++row) - { - constraints.add_line (dofs_on_children[row]); - for (unsigned int i=0; i!=dofs_on_mother.size(); ++i) - constraints.add_entry (dofs_on_children[row], - dofs_on_mother[i], - fe.constraints()(row,i)); + // for each row in the constraint + // matrix for this line: + for (unsigned int row=0; row!=dofs_on_children.size(); ++row) + { + constraints.add_line (dofs_on_children[row]); + for (unsigned int i=0; i!=dofs_on_mother.size(); ++i) + constraints.add_entry (dofs_on_children[row], + dofs_on_mother[i], + fe.constraints()(row,i)); - constraints.set_inhomogeneity(dofs_on_children[row], 0.); - } - } - else - { - // this face has no - // children, but it - // could still be that - // it is shared by two - // cells that use a - // different fe index - Assert (cell->face(face)->n_active_fe_indices() == 1, - ExcNotImplemented()); - Assert (cell->face(face) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcInternalError()); - } + constraints.set_inhomogeneity(dofs_on_children[row], 0.); + } + } + else + { + // this face has no + // children, but it + // could still be + // that it is shared + // by two cells that + // use a different fe + // index. check a + // couple of things, + // but ignore the + // case that the + // neighbor is an + // artificial cell + if (!cell->at_boundary(face) && + !cell->neighbor(face)->is_artificial()) + { + Assert (cell->face(face)->n_active_fe_indices() == 1, + ExcNotImplemented()); + Assert (cell->face(face) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcInternalError()); + } + } } #endif @@ -2418,638 +2457,742 @@ namespace internal typename DH::active_cell_iterator cell = dof_handler.begin_active(), endc = dof_handler.end(); for (; cell!=endc; ++cell) - for (unsigned int face=0; face::faces_per_cell; ++face) - if (cell->face(face)->has_children()) - { - // first of all, make sure that - // we treat a case which is - // possible, i.e. either no dofs - // on the face at all or no - // anisotropic refinement - if (cell->get_fe().dofs_per_face == 0) - continue; - - Assert(cell->face(face)->refinement_case()==RefinementCase::isotropic_refinement, - ExcNotImplemented()); - - // check to see if the child elements - // have no dofs on the - // shared face. if none of them do, we - // we can simply continue to the next face. - // if only some of them have dofs, while - // others do not, then we don't know - // what to do and throw an exception. - bool any_are_zero = false; - bool all_are_zero = true; - - for (unsigned int c=0; cface(face)->n_children(); ++c) - { - if(cell->neighbor_child_on_subface (face, c)->get_fe().dofs_per_face == 0) - any_are_zero = true; - else - all_are_zero = false; - } - - if(all_are_zero) - continue; - - Assert( all_are_zero || !any_are_zero, ExcNotImplemented() ); - - // so now we've found a - // face of an active - // cell that has - // children. that means - // that there are - // hanging nodes here. - - // in any case, faces - // can have at most two - // sets of active fe - // indices, but here - // the face can have - // only one (namely the - // same as that from - // the cell we're - // sitting on), and - // each of the children - // can have only one as - // well. check this - Assert (cell->face(face)->n_active_fe_indices() == 1, - ExcInternalError()); - Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) - == true, - ExcInternalError()); - for (unsigned int c=0; cface(face)->n_children(); ++c) - Assert (cell->face(face)->child(c)->n_active_fe_indices() == 1, - ExcInternalError()); + // artificial cells can at + // best neighbor ghost cells, + // but we're not interested + // in these interfaces + if (!cell->is_artificial ()) + for (unsigned int face=0; face::faces_per_cell; ++face) + if (cell->face(face)->has_children()) + { + // first of all, make sure that + // we treat a case which is + // possible, i.e. either no dofs + // on the face at all or no + // anisotropic refinement + if (cell->get_fe().dofs_per_face == 0) + continue; + + Assert(cell->face(face)->refinement_case()==RefinementCase::isotropic_refinement, + ExcNotImplemented()); + + // check to see if the child elements + // have no dofs on the + // shared face. if none of them do, we + // we can simply continue to the next face. + // if only some of them have dofs, while + // others do not, then we don't know + // what to do and throw an exception. + // + // ignore all + // interfaces with + // artificial cells + bool any_are_zero = false; + bool all_are_zero = true; + + for (unsigned int c=0; cface(face)->n_children(); ++c) + if (!cell->neighbor_child_on_subface(face, c)->is_artificial()) + { + if (cell->neighbor_child_on_subface(face, c)->get_fe().dofs_per_face == 0) + any_are_zero = true; + else + all_are_zero = false; + } - // first find out - // whether we can - // constrain each of - // the subfaces to the - // mother face. in the - // lingo of the hp - // paper, this would be - // the simple - // case. note that we - // can short-circuit - // this decision if the - // dof_handler doesn't - // support hp at all - FiniteElementDomination::Domination - mother_face_dominates = FiniteElementDomination::either_element_can_dominate; - - if (DoFHandlerSupportsDifferentFEs::value == true) - for (unsigned int c=0; cface(face)->number_of_children(); ++c) - mother_face_dominates = mother_face_dominates & - (cell->get_fe().compare_for_face_domination - (cell->neighbor_child_on_subface (face, c)->get_fe())); - - switch (mother_face_dominates) - { - case FiniteElementDomination::this_element_dominates: - case FiniteElementDomination::either_element_can_dominate: + if(all_are_zero) + continue; + + Assert( all_are_zero || !any_are_zero, ExcNotImplemented() ); + + // so now we've found a + // face of an active + // cell that has + // children. that means + // that there are + // hanging nodes here. + + // in any case, faces + // can have at most two + // sets of active fe + // indices, but here + // the face can have + // only one (namely the + // same as that from + // the cell we're + // sitting on), and + // each of the children + // can have only one as + // well. check this + Assert (cell->face(face)->n_active_fe_indices() == 1, + ExcInternalError()); + Assert (cell->face(face)->fe_index_is_active(cell->active_fe_index()) + == true, + ExcInternalError()); + for (unsigned int c=0; cface(face)->n_children(); ++c) + Assert (cell->face(face)->child(c)->n_active_fe_indices() == 1, + ExcInternalError()); + + // first find out + // whether we can + // constrain each of + // the subfaces to the + // mother face. in the + // lingo of the hp + // paper, this would be + // the simple + // case. note that we + // can short-circuit + // this decision if the + // dof_handler doesn't + // support hp at all + // + // ignore all + // interfaces with + // artificial cells + FiniteElementDomination::Domination + mother_face_dominates = FiniteElementDomination::either_element_can_dominate; + + if (DoFHandlerSupportsDifferentFEs::value == true) + for (unsigned int c=0; cface(face)->number_of_children(); ++c) + if (!cell->neighbor_child_on_subface (face, c)->is_artificial()) + mother_face_dominates = mother_face_dominates & + (cell->get_fe().compare_for_face_domination + (cell->neighbor_child_on_subface (face, c)->get_fe())); + + switch (mother_face_dominates) { - // Case 1 (the - // simple case - // and the only - // case that can - // happen for - // non-hp - // DoFHandlers): - // The coarse - // element - // dominates the - // elements on - // the subfaces - // (or they are - // all the same) - master_dofs.resize (cell->get_fe().dofs_per_face); - - cell->face(face)->get_dof_indices (master_dofs, - cell->active_fe_index ()); - - // Now create constraint matrix for - // the subfaces and assemble it. - for (unsigned int c=0; cface(face)->n_children(); ++c) - { - const typename DH::active_face_iterator - subface = cell->face(face)->child(c); - - Assert (subface->n_active_fe_indices() == 1, - ExcInternalError()); - - const unsigned int - subface_fe_index = subface->nth_active_fe_index(0); - - // Same procedure as for the - // mother cell. Extract the face - // DoFs from the cell DoFs. - slave_dofs.resize (subface->get_fe(subface_fe_index) - .dofs_per_face); - subface->get_dof_indices (slave_dofs, subface_fe_index); - - // Now create the - // element constraint - // for this subface. - // - // As a side remark, - // one may wonder the - // following: - // neighbor_child is - // clearly computed - // correctly, - // i.e. taking into - // account - // face_orientation - // (just look at the - // implementation of - // that - // function). however, - // we don't care about - // this here, when we - // ask for - // subface_interpolation - // on subface c. the - // question rather is: - // do we have to - // translate 'c' here - // as well? - // - // the answer is in - // fact 'no'. if one - // does that, results - // are wrong: - // constraints are - // added twice for the - // same pair of nodes - // but with differing - // weights. in - // addition, one can - // look at the - // deal.II/project_*_03 - // tests that look at - // exactly this case: - // there, we have a - // mesh with at least - // one - // face_orientation==false - // and hanging nodes, - // and the results of - // those tests show - // that the result of - // projection verifies - // the approximation - // properties of a - // finite element onto - // that mesh - ensure_existence_of_subface_matrix - (cell->get_fe(), - subface->get_fe(subface_fe_index), - c, - subface_interpolation_matrices - [cell->active_fe_index()][subface_fe_index][c]); - - // Add constraints to global constraint - // matrix. + case FiniteElementDomination::this_element_dominates: + case FiniteElementDomination::either_element_can_dominate: + { + // Case 1 (the + // simple case + // and the only + // case that can + // happen for + // non-hp + // DoFHandlers): + // The coarse + // element + // dominates the + // elements on + // the subfaces + // (or they are + // all the same) + // + // so we are + // going to + // constrain + // the DoFs on + // the face + // children + // against the + // DoFs on the + // face itself + master_dofs.resize (cell->get_fe().dofs_per_face); + + cell->face(face)->get_dof_indices (master_dofs, + cell->active_fe_index ()); + + // Now create + // constraint matrix + // for the subfaces and + // assemble it. ignore + // all interfaces with + // artificial cells + // because we can only + // get to such + // interfaces if the + // current cell is a + // ghost cell. also + // ignore the interface + // if the neighboring + // cell is a ghost cell + // in 2d: what we would + // compute here are the + // constraints on the + // ghost cell's DoFs, + // but we are not + // interested in those: + // we only want + // constraints on + // *locally active* + // DoFs, not on + // *locally relevant* + // DoFs. However, in 3d + // we must still + // compute those + // constraints because + // it might happen that + // a constraint is + // related to an edge + // where the hanging + // node is only + // detected if we also + // look between ghosts + for (unsigned int c=0; cface(face)->n_children(); ++c) + { + if (cell->neighbor_child_on_subface (face, c)->is_artificial() + || + (dim == 2 && cell->neighbor_child_on_subface (face, c)->is_ghost())) + continue; + + const typename DH::active_face_iterator + subface = cell->face(face)->child(c); + + Assert (subface->n_active_fe_indices() == 1, + ExcInternalError()); + + const unsigned int + subface_fe_index = subface->nth_active_fe_index(0); + + // Same procedure as for the + // mother cell. Extract the face + // DoFs from the cell DoFs. + slave_dofs.resize (subface->get_fe(subface_fe_index) + .dofs_per_face); + subface->get_dof_indices (slave_dofs, subface_fe_index); + + for (unsigned int i=0; iget_fe(), + subface->get_fe(subface_fe_index), + c, + subface_interpolation_matrices + [cell->active_fe_index()][subface_fe_index][c]); + + // Add constraints to global constraint + // matrix. #ifdef WOLFGANG - std::cout << "Constraints for cell=" << cell - << ", face=" << face - << ", subface=" << c - << std::endl; + std::cout << "Constraints for cell=" << cell + << ", face=" << face + << ", subface=" << c + << std::endl; #endif - filter_constraints (master_dofs, - slave_dofs, - *(subface_interpolation_matrices - [cell->active_fe_index()][subface_fe_index][c]), - constraints); - } + filter_constraints (master_dofs, + slave_dofs, + *(subface_interpolation_matrices + [cell->active_fe_index()][subface_fe_index][c]), + constraints); + } - break; - } + break; + } - case FiniteElementDomination::other_element_dominates: - case FiniteElementDomination::neither_element_dominates: - { - // Case 2 (the "complex" - // case): at least one - // (the neither_... case) - // of the finer elements - // or all of them (the - // other_... case) is - // dominating. See the hp - // paper for a way how to - // deal with this - // situation - // - // since this is - // something that - // can only - // happen for hp - // dof handlers, - // add a check - // here... - Assert (DoFHandlerSupportsDifferentFEs::value == true, - ExcInternalError()); - - // we first have - // to find the - // finite element - // that is able - // to generate a - // space that all - // the other ones - // can be - // constrained to - const unsigned int dominating_fe_index - = get_most_dominating_subface_fe_index (cell->face(face)); - - const FiniteElement &dominating_fe - = dof_handler.get_fe()[dominating_fe_index]; - - // check also - // that it is - // able to - // constrain the - // mother - // face. it - // should be, or - // we wouldn't - // have gotten - // into the - // branch for the - // 'complex' case - Assert ((dominating_fe.compare_for_face_domination - (cell->face(face)->get_fe(cell->face(face)->nth_active_fe_index(0))) - == FiniteElementDomination::this_element_dominates) - || - (dominating_fe.compare_for_face_domination - (cell->face(face)->get_fe(cell->face(face)->nth_active_fe_index(0))) - == FiniteElementDomination::either_element_can_dominate), - ExcInternalError()); + case FiniteElementDomination::other_element_dominates: + case FiniteElementDomination::neither_element_dominates: + { + // Case 2 (the "complex" + // case): at least one + // (the neither_... case) + // of the finer elements + // or all of them (the + // other_... case) is + // dominating. See the hp + // paper for a way how to + // deal with this + // situation + // + // since this is + // something that + // can only + // happen for hp + // dof handlers, + // add a check + // here... + Assert (DoFHandlerSupportsDifferentFEs::value == true, + ExcInternalError()); + // we first have + // to find the + // finite element + // that is able + // to generate a + // space that all + // the other ones + // can be + // constrained to + const unsigned int dominating_fe_index + = get_most_dominating_subface_fe_index (cell->face(face)); + + const FiniteElement &dominating_fe + = dof_handler.get_fe()[dominating_fe_index]; + + // check also + // that it is + // able to + // constrain the + // mother + // face. it + // should be, or + // we wouldn't + // have gotten + // into the + // branch for the + // 'complex' case + Assert ((dominating_fe.compare_for_face_domination + (cell->face(face)->get_fe(cell->face(face)->nth_active_fe_index(0))) + == FiniteElementDomination::this_element_dominates) + || + (dominating_fe.compare_for_face_domination + (cell->face(face)->get_fe(cell->face(face)->nth_active_fe_index(0))) + == FiniteElementDomination::either_element_can_dominate), + ExcInternalError()); - // first get the - // interpolation matrix - // from the mother to the - // virtual dofs - Assert (dominating_fe.dofs_per_face <= - cell->get_fe().dofs_per_face, - ExcInternalError()); - ensure_existence_of_face_matrix - (dominating_fe, - cell->get_fe(), - face_interpolation_matrices - [dominating_fe_index][cell->active_fe_index()]); - - // split this matrix into - // master and slave - // components. invert the - // master component - ensure_existence_of_master_dof_mask - (cell->get_fe(), - dominating_fe, - (*face_interpolation_matrices - [dominating_fe_index] - [cell->active_fe_index()]), - master_dof_masks - [dominating_fe_index] - [cell->active_fe_index()]); - - ensure_existence_of_split_face_matrix - (*face_interpolation_matrices - [dominating_fe_index][cell->active_fe_index()], - (*master_dof_masks - [dominating_fe_index][cell->active_fe_index()]), - split_face_interpolation_matrices - [dominating_fe_index][cell->active_fe_index()]); - - const FullMatrix &restrict_mother_to_virtual_master_inv - = (split_face_interpolation_matrices - [dominating_fe_index][cell->active_fe_index()]->first); - - const FullMatrix &restrict_mother_to_virtual_slave - = (split_face_interpolation_matrices - [dominating_fe_index][cell->active_fe_index()]->second); - - // now compute - // the constraint - // matrix as the - // product - // between the - // inverse matrix - // and the slave - // part - constraint_matrix.reinit (cell->get_fe().dofs_per_face - - dominating_fe.dofs_per_face, - dominating_fe.dofs_per_face); - restrict_mother_to_virtual_slave - .mmult (constraint_matrix, - restrict_mother_to_virtual_master_inv); - - // then figure - // out the global - // numbers of - // master and - // slave dofs and - // apply - // constraints - scratch_dofs.resize (cell->get_fe().dofs_per_face); - cell->face(face)->get_dof_indices (scratch_dofs, - cell->active_fe_index ()); - - // split dofs into master - // and slave components - master_dofs.clear (); - slave_dofs.clear (); - for (unsigned int i=0; iget_fe().dofs_per_face; ++i) - if ((*master_dof_masks - [dominating_fe_index][cell->active_fe_index()])[i] == true) - master_dofs.push_back (scratch_dofs[i]); - else - slave_dofs.push_back (scratch_dofs[i]); + // first get the + // interpolation matrix + // from the mother to the + // virtual dofs + Assert (dominating_fe.dofs_per_face <= + cell->get_fe().dofs_per_face, + ExcInternalError()); - AssertDimension (master_dofs.size(), dominating_fe.dofs_per_face); - AssertDimension (slave_dofs.size(), - cell->get_fe().dofs_per_face - dominating_fe.dofs_per_face); + ensure_existence_of_face_matrix + (dominating_fe, + cell->get_fe(), + face_interpolation_matrices + [dominating_fe_index][cell->active_fe_index()]); + + // split this matrix into + // master and slave + // components. invert the + // master component + ensure_existence_of_master_dof_mask + (cell->get_fe(), + dominating_fe, + (*face_interpolation_matrices + [dominating_fe_index] + [cell->active_fe_index()]), + master_dof_masks + [dominating_fe_index] + [cell->active_fe_index()]); + + ensure_existence_of_split_face_matrix + (*face_interpolation_matrices + [dominating_fe_index][cell->active_fe_index()], + (*master_dof_masks + [dominating_fe_index][cell->active_fe_index()]), + split_face_interpolation_matrices + [dominating_fe_index][cell->active_fe_index()]); + + const FullMatrix &restrict_mother_to_virtual_master_inv + = (split_face_interpolation_matrices + [dominating_fe_index][cell->active_fe_index()]->first); + + const FullMatrix &restrict_mother_to_virtual_slave + = (split_face_interpolation_matrices + [dominating_fe_index][cell->active_fe_index()]->second); + + // now compute + // the constraint + // matrix as the + // product + // between the + // inverse matrix + // and the slave + // part + constraint_matrix.reinit (cell->get_fe().dofs_per_face - + dominating_fe.dofs_per_face, + dominating_fe.dofs_per_face); + restrict_mother_to_virtual_slave + .mmult (constraint_matrix, + restrict_mother_to_virtual_master_inv); + + // then figure + // out the global + // numbers of + // master and + // slave dofs and + // apply + // constraints + scratch_dofs.resize (cell->get_fe().dofs_per_face); + cell->face(face)->get_dof_indices (scratch_dofs, + cell->active_fe_index ()); + + // split dofs into master + // and slave components + master_dofs.clear (); + slave_dofs.clear (); + for (unsigned int i=0; iget_fe().dofs_per_face; ++i) + if ((*master_dof_masks + [dominating_fe_index][cell->active_fe_index()])[i] == true) + master_dofs.push_back (scratch_dofs[i]); + else + slave_dofs.push_back (scratch_dofs[i]); + + AssertDimension (master_dofs.size(), dominating_fe.dofs_per_face); + AssertDimension (slave_dofs.size(), + cell->get_fe().dofs_per_face - dominating_fe.dofs_per_face); #ifdef WOLFGANG - std::cout << "Constraints for cell=" << cell - << ", face=" << face - << " (complicated case, mother)" - << std::endl; + std::cout << "Constraints for cell=" << cell + << ", face=" << face + << " (complicated case, mother)" + << std::endl; #endif - filter_constraints (master_dofs, - slave_dofs, - constraint_matrix, - constraints); + filter_constraints (master_dofs, + slave_dofs, + constraint_matrix, + constraints); + + + + // next we have + // to deal with + // the + // subfaces. do + // as discussed + // in the hp + // paper + for (unsigned int sf=0; + sfface(face)->n_children(); ++sf) + { + // ignore + // interfaces + // with + // artificial + // cells as + // well as + // interfaces + // between + // ghost + // cells in 2d + if (cell->neighbor_child_on_subface (face, sf)->is_artificial() + || + (dim==2 && cell->is_ghost() + && + cell->neighbor_child_on_subface (face, sf)->is_ghost())) + continue; + + Assert (cell->face(face)->child(sf) + ->n_active_fe_indices() == 1, + ExcInternalError()); + + const unsigned int subface_fe_index + = cell->face(face)->child(sf)->nth_active_fe_index(0); + const FiniteElement &subface_fe + = dof_handler.get_fe()[subface_fe_index]; + + // first get the + // interpolation + // matrix from the + // subface to the + // virtual dofs + Assert (dominating_fe.dofs_per_face <= + subface_fe.dofs_per_face, + ExcInternalError()); + ensure_existence_of_subface_matrix + (dominating_fe, + subface_fe, + sf, + subface_interpolation_matrices + [dominating_fe_index][subface_fe_index][sf]); + const FullMatrix &restrict_subface_to_virtual + = *(subface_interpolation_matrices + [dominating_fe_index][subface_fe_index][sf]); + constraint_matrix.reinit (subface_fe.dofs_per_face, + dominating_fe.dofs_per_face); - // next we have to - // deal with the - // subfaces. do as - // discussed in the - // paper - for (unsigned int sf=0; - sfface(face)->n_children(); ++sf) - { - Assert (cell->face(face)->child(sf) - ->n_active_fe_indices() == 1, - ExcInternalError()); - - const unsigned int subface_fe_index - = cell->face(face)->child(sf)->nth_active_fe_index(0); - const FiniteElement &subface_fe - = dof_handler.get_fe()[subface_fe_index]; - - // first get the - // interpolation - // matrix from the - // subface to the - // virtual dofs - Assert (dominating_fe.dofs_per_face <= - subface_fe.dofs_per_face, - ExcInternalError()); - ensure_existence_of_subface_matrix - (dominating_fe, - subface_fe, - sf, - subface_interpolation_matrices - [dominating_fe_index][subface_fe_index][sf]); - - const FullMatrix &restrict_subface_to_virtual - = *(subface_interpolation_matrices - [dominating_fe_index][subface_fe_index][sf]); - - constraint_matrix.reinit (subface_fe.dofs_per_face, - dominating_fe.dofs_per_face); - - restrict_subface_to_virtual - .mmult (constraint_matrix, - restrict_mother_to_virtual_master_inv); - - slave_dofs.resize (subface_fe.dofs_per_face); - cell->face(face)->child(sf)->get_dof_indices (slave_dofs, - subface_fe_index); + restrict_subface_to_virtual + .mmult (constraint_matrix, + restrict_mother_to_virtual_master_inv); + + slave_dofs.resize (subface_fe.dofs_per_face); + cell->face(face)->child(sf)->get_dof_indices (slave_dofs, + subface_fe_index); #ifdef WOLFGANG - std::cout << "Constraints for cell=" << cell - << ", face=" << face - << ", subface=" << sf - << " (complicated case, children)" - << std::endl; + std::cout << "Constraints for cell=" << cell + << ", face=" << face + << ", subface=" << sf + << " (complicated case, children)" + << std::endl; #endif - filter_constraints (master_dofs, - slave_dofs, - constraint_matrix, - constraints); - } + filter_constraints (master_dofs, + slave_dofs, + constraint_matrix, + constraints); + } - break; + break; + } + + default: + // we shouldn't get here + Assert (false, ExcInternalError()); } + } + else + { + // this face has no + // children, but it + // could still be that + // it is shared by two + // cells that use a + // different fe index + Assert (cell->face(face) + ->fe_index_is_active(cell->active_fe_index()) == true, + ExcInternalError()); - default: - // we shouldn't get here - Assert (false, ExcInternalError()); - } - } - else - { - // this face has no - // children, but it - // could still be that - // it is shared by two - // cells that use a - // different fe index - Assert (cell->face(face) - ->fe_index_is_active(cell->active_fe_index()) == true, - ExcInternalError()); - - // Only if there is - // a neighbor with - // a different - // active_fe_index - // and the same h-level, - // some action has - // to be taken. - if ((DoFHandlerSupportsDifferentFEs::value == true) - && - !cell->face(face)->at_boundary () - && - (cell->neighbor(face)->active_fe_index () != - cell->active_fe_index ()) - && - (!cell->face(face)->has_children() && - !cell->neighbor_is_coarser(face) )) - { - const typename DH::cell_iterator neighbor = cell->neighbor (face); + // see if there is a + // neighbor that is + // an artificial + // cell. in that + // case, we're not + // interested in this + // interface. we test + // this case first + // since artificial + // cells may not have + // an active_fe_index + // set, etc + if (!cell->at_boundary(face) + && + cell->neighbor(face)->is_artificial()) + continue; + + // Only if there is + // a neighbor with + // a different + // active_fe_index + // and the same h-level, + // some action has + // to be taken. + if ((DoFHandlerSupportsDifferentFEs::value == true) + && + !cell->face(face)->at_boundary () + && + (cell->neighbor(face)->active_fe_index () != + cell->active_fe_index ()) + && + (!cell->face(face)->has_children() && + !cell->neighbor_is_coarser(face) )) + { + const typename DH::cell_iterator neighbor = cell->neighbor (face); - // see which side of the - // face we have to - // constrain - switch (cell->get_fe().compare_for_face_domination (neighbor->get_fe ())) - { - case FiniteElementDomination::this_element_dominates: + // see which side of the + // face we have to + // constrain + switch (cell->get_fe().compare_for_face_domination (neighbor->get_fe ())) { - // Get DoFs on - // dominating and - // dominated side of - // the face - master_dofs.resize (cell->get_fe().dofs_per_face); - cell->face(face)->get_dof_indices (master_dofs, - cell->active_fe_index ()); - - slave_dofs.resize (neighbor->get_fe().dofs_per_face); - cell->face(face)->get_dof_indices (slave_dofs, - neighbor->active_fe_index ()); - - // break if the n_master_dofs == 0, - // because we are attempting to - // constrain to an element that has - // has no face dofs - if(master_dofs.size() == 0) break; - - // make sure - // the element - // constraints - // for this - // face are - // available - ensure_existence_of_face_matrix - (cell->get_fe(), - neighbor->get_fe(), - face_interpolation_matrices - [cell->active_fe_index()][neighbor->active_fe_index()]); - - // Add constraints to global constraint - // matrix. + case FiniteElementDomination::this_element_dominates: + { + // Get DoFs on + // dominating and + // dominated side of + // the face + master_dofs.resize (cell->get_fe().dofs_per_face); + cell->face(face)->get_dof_indices (master_dofs, + cell->active_fe_index ()); + + slave_dofs.resize (neighbor->get_fe().dofs_per_face); + cell->face(face)->get_dof_indices (slave_dofs, + neighbor->active_fe_index ()); + + // break if the n_master_dofs == 0, + // because we are attempting to + // constrain to an element that has + // has no face dofs + if(master_dofs.size() == 0) break; + + // make sure + // the element + // constraints + // for this + // face are + // available + ensure_existence_of_face_matrix + (cell->get_fe(), + neighbor->get_fe(), + face_interpolation_matrices + [cell->active_fe_index()][neighbor->active_fe_index()]); + + // Add constraints to global constraint + // matrix. #ifdef WOLFGANG - std::cout << "p-constraints for cell=" << cell - << ", face=" << face << std::endl; + std::cout << "p-constraints for cell=" << cell + << ", face=" << face << std::endl; #endif - filter_constraints (master_dofs, - slave_dofs, - *(face_interpolation_matrices - [cell->active_fe_index()] - [neighbor->active_fe_index()]), - constraints); + filter_constraints (master_dofs, + slave_dofs, + *(face_interpolation_matrices + [cell->active_fe_index()] + [neighbor->active_fe_index()]), + constraints); + + break; + } - break; - } + case FiniteElementDomination::other_element_dominates: + { + // we don't do anything + // here since we will + // come back to this + // face from the other + // cell, at which time + // we will fall into + // the first case + // clause above + break; + } - case FiniteElementDomination::other_element_dominates: - { - // we don't do anything - // here since we will - // come back to this - // face from the other - // cell, at which time - // we will fall into - // the first case - // clause above - break; - } + case FiniteElementDomination::either_element_can_dominate: + { + // it appears as if + // neither element has + // any constraints on + // its neighbor. this + // may be because + // neither element has + // any DoFs on faces at + // all. or that the two + // elements are + // actually the same, + // although they happen + // to run under + // different fe_indices + // (this is what + // happens in + // hp/hp_hanging_nodes_01 + // for example). + // + // another possibility + // is what happens in + // crash_13. there, we + // have + // FESystem(FE_Q(1),FE_DGQ(0)) + // vs. FESystem(FE_Q(1),FE_DGQ(1)). + // neither of them + // dominates the + // other. the point is + // that it doesn't + // matter, since + // hopefully for this + // case, both sides' + // dofs should have + // been unified. + // + // make sure this is + // actually true. this + // actually only + // matters, of course, + // if either of the two + // finite elements + // actually do have + // dofs on the face + if ((cell->get_fe().dofs_per_face != 0) + || + (cell->neighbor(face)->get_fe().dofs_per_face != 0)) + { + Assert (cell->get_fe().dofs_per_face + == + cell->neighbor(face)->get_fe().dofs_per_face, + ExcNotImplemented()); + + // (ab)use the master + // and slave dofs + // arrays for a + // moment here + master_dofs.resize (cell->get_fe().dofs_per_face); + cell->face(face) + ->get_dof_indices (master_dofs, + cell->active_fe_index ()); + + slave_dofs.resize (cell->neighbor(face)->get_fe().dofs_per_face); + cell->face(face) + ->get_dof_indices (slave_dofs, + cell->neighbor(face)->active_fe_index ()); + + for (unsigned int i=0; iget_fe().dofs_per_face; ++i) + AssertDimension (master_dofs[i], slave_dofs[i]); + } - case FiniteElementDomination::either_element_can_dominate: - { - // it appears as if - // neither element has - // any constraints on - // its neighbor. this - // may be because - // neither element has - // any DoFs on faces at - // all. or that the two - // elements are - // actually the same, - // although they happen - // to run under - // different fe_indices - // (this is what - // happens in - // hp/hp_hanging_nodes_01 - // for example). - // - // another possibility - // is what happens in - // crash_13. there, we - // have - // FESystem(FE_Q(1),FE_DGQ(0)) - // vs. FESystem(FE_Q(1),FE_DGQ(1)). - // neither of them - // dominates the - // other. the point is - // that it doesn't - // matter, since - // hopefully for this - // case, both sides' - // dofs should have - // been unified. - // - // make sure this is - // actually true. this - // actually only - // matters, of course, - // if either of the two - // finite elements - // actually do have - // dofs on the face - if ((cell->get_fe().dofs_per_face != 0) - || - (cell->neighbor(face)->get_fe().dofs_per_face != 0)) - { - Assert (cell->get_fe().dofs_per_face - == - cell->neighbor(face)->get_fe().dofs_per_face, - ExcNotImplemented()); - - // (ab)use the master - // and slave dofs - // arrays for a - // moment here - master_dofs.resize (cell->get_fe().dofs_per_face); - cell->face(face) - ->get_dof_indices (master_dofs, - cell->active_fe_index ()); - - slave_dofs.resize (cell->neighbor(face)->get_fe().dofs_per_face); - cell->face(face) - ->get_dof_indices (slave_dofs, - cell->neighbor(face)->active_fe_index ()); - - for (unsigned int i=0; iget_fe().dofs_per_face; ++i) - AssertDimension (master_dofs[i], slave_dofs[i]); - } + break; + } - break; - } + case FiniteElementDomination::neither_element_dominates: + { + // we don't presently + // know what exactly to + // do here. it isn't quite + // clear what exactly we + // would have to do + // here. sit tight until + // someone trips over the + // following statement + // and see what exactly + // is going on + Assert (false, ExcNotImplemented()); + break; + } - case FiniteElementDomination::neither_element_dominates: - { - // we don't presently - // know what exactly to - // do here. it isn't quite - // clear what exactly we - // would have to do - // here. sit tight until - // someone trips over the - // following statement - // and see what exactly - // is going on - Assert (false, ExcNotImplemented()); - break; + default: + // we shouldn't get + // here + Assert (false, ExcInternalError()); } - - default: - // we shouldn't get - // here - Assert (false, ExcInternalError()); - } - } - } + } + } } } } @@ -3108,8 +3251,9 @@ namespace internal const dealii::hp::FECollection fe_collection (dof.get_fe()); Assert (fe_collection.n_components() < 256, ExcNotImplemented()); - Assert(dofs_by_component.size() == dof.n_dofs(), - ExcDimensionMismatch(dofs_by_component.size(), dof.n_dofs())); + Assert (dofs_by_component.size() == dof.n_locally_owned_dofs(), + ExcDimensionMismatch(dofs_by_component.size(), + dof.n_locally_owned_dofs())); // next set up a table for the // degrees of freedom on each of @@ -3131,7 +3275,8 @@ namespace internal else for (unsigned int i=0; i indices; for (typename DH::active_cell_iterator c=dof.begin_active(); c!=dof.end(); ++ c) - { - const unsigned int fe_index = c->active_fe_index(); - const unsigned int dofs_per_cell = c->get_fe().dofs_per_cell; - indices.resize(dofs_per_cell); - c->get_dof_indices(indices); - for (unsigned int i=0; iis_artificial() && !c->is_ghost()) + { + const unsigned int fe_index = c->active_fe_index(); + const unsigned int dofs_per_cell = c->get_fe().dofs_per_cell; + indices.resize(dofs_per_cell); + c->get_dof_indices(indices); + for (unsigned int i=0; i component_dofs (dof_handler.n_dofs()); + std::vector component_dofs (dof_handler.n_locally_owned_dofs()); std::vector component_mask (dof_handler.get_fe().n_components(), false); component_mask[component] = true; @@ -3306,8 +3454,8 @@ DoFTools::extract_dofs ( ExcDimensionMismatch(component_select.size(), n_components(dof))); } - Assert(selected_dofs.size() == dof.n_dofs(), - ExcDimensionMismatch(selected_dofs.size(), dof.n_dofs())); + Assert(selected_dofs.size() == dof.n_locally_owned_dofs(), + ExcDimensionMismatch(selected_dofs.size(), dof.n_locally_owned_dofs())); // two special cases: no component // is selected, and all components @@ -3316,19 +3464,19 @@ DoFTools::extract_dofs ( if (std::count (component_select.begin(), component_select.end(), true) == 0) { - std::fill_n (selected_dofs.begin(), dof.n_dofs(), false); + std::fill_n (selected_dofs.begin(), dof.n_locally_owned_dofs(), false); return; - }; - if (std::count (component_select.begin(), component_select.end(), true) + } + else if (std::count (component_select.begin(), component_select.end(), true) == static_cast(component_select.size())) { - std::fill_n (selected_dofs.begin(), dof.n_dofs(), true); + std::fill_n (selected_dofs.begin(), dof.n_locally_owned_dofs(), true); return; - }; + } // preset all values by false - std::fill_n (selected_dofs.begin(), dof.n_dofs(), false); + std::fill_n (selected_dofs.begin(), dof.n_locally_owned_dofs(), false); // if we count by blocks, we need to extract // the association of blocks with local dofs, @@ -3337,11 +3485,11 @@ DoFTools::extract_dofs ( // info. Otherwise, we let the function // extract_dofs_by_component function do the // job. - std::vector dofs_by_component (dof.n_dofs()); + std::vector dofs_by_component (dof.n_locally_owned_dofs()); internal::extract_dofs_by_component (dof, component_select, count_by_blocks, dofs_by_component); - for (unsigned int i=0; i &dof_handler, template void -DoFTools::extract_subdomain_dofs (const DH &dof_handler, - const unsigned int subdomain_id, - std::vector &selected_dofs) +DoFTools::extract_subdomain_dofs (const DH &dof_handler, + const types::subdomain_id_t subdomain_id, + std::vector &selected_dofs) { Assert(selected_dofs.size() == dof_handler.n_dofs(), ExcDimensionMismatch(selected_dofs.size(), dof_handler.n_dofs())); @@ -4166,6 +4314,96 @@ DoFTools::extract_subdomain_dofs (const DH &dof_handler, +template +void +DoFTools::extract_locally_owned_dofs (const DH & dof_handler, + IndexSet & dof_set) +{ + // collect all the locally owned dofs + dof_set = dof_handler.locally_owned_dofs(); + dof_set.compress (); +} + + + +template +void +DoFTools::extract_locally_active_dofs (const DH & dof_handler, + IndexSet & dof_set) +{ + // collect all the locally owned dofs + dof_set = dof_handler.locally_owned_dofs(); + + // add the DoF on the adjacent ghost cells + // to the IndexSet, cache them in a + // set. need to check each dof manually + // because we can't be sure that the dof + // range of locally_owned_dofs is really + // contiguous. + std::vector dof_indices; + std::set global_dof_indices; + + typename DH::active_cell_iterator cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + if (!cell->is_ghost() && !cell->is_artificial()) + { + dof_indices.resize(cell->get_fe().dofs_per_cell); + cell->get_dof_indices(dof_indices); + + for (std::vector::iterator it=dof_indices.begin(); + it!=dof_indices.end(); + ++it) + if (!dof_set.is_element(*it)) + global_dof_indices.insert(*it); + } + + dof_set.add_indices(global_dof_indices.begin(), global_dof_indices.end()); + + dof_set.compress(); +} + + + +template +void +DoFTools::extract_locally_relevant_dofs (const DH & dof_handler, + IndexSet & dof_set) +{ + // collect all the locally owned dofs + dof_set = dof_handler.locally_owned_dofs(); + + // add the DoF on the adjacent ghost cells + // to the IndexSet, cache them in a + // set. need to check each dof manually + // because we can't be sure that the dof + // range of locally_owned_dofs is really + // contiguous. + std::vector dof_indices; + std::set global_dof_indices; + + typename DH::active_cell_iterator cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + if (cell->is_ghost()) + { + dof_indices.resize(cell->get_fe().dofs_per_cell); + cell->get_dof_indices(dof_indices); + + for (std::vector::iterator it=dof_indices.begin(); + it!=dof_indices.end(); + ++it) + if (!dof_set.is_element(*it)) + global_dof_indices.insert(*it); + } + + dof_set.add_indices(global_dof_indices.begin(), global_dof_indices.end()); + + dof_set.compress(); +} + + + template void DoFTools::extract_constant_modes (const DH &dof_handler, @@ -4183,7 +4421,7 @@ DoFTools::extract_constant_modes (const DH &dof_handler, if (component_select[i] == true) localized_component[i] = n_components_selected++; - std::vector dofs_by_component (dof_handler.n_dofs()); + std::vector dofs_by_component (dof_handler.n_locally_owned_dofs()); internal::extract_dofs_by_component (dof_handler, component_select, false, dofs_by_component); unsigned int n_selected_dofs = 0; @@ -4201,7 +4439,7 @@ DoFTools::extract_constant_modes (const DH &dof_handler, component_list[d] = component_select[d]; unsigned int counter = 0; - for (unsigned int i=0; i void DoFTools::get_subdomain_association (const DH &dof_handler, - std::vector &subdomain_association) + std::vector &subdomain_association) { + // if the Triangulation is distributed, the + // only thing we can usefully ask is for + // its locally owned subdomain + Assert ((dynamic_cast*> + (&dof_handler.get_tria()) == 0), + ExcMessage ("For parallel::distributed::Triangulation objects and " + "associated DoF handler objects, asking for any subdomain other " + "than the locally owned one does not make sense.")); + Assert(subdomain_association.size() == dof_handler.n_dofs(), ExcDimensionMismatch(subdomain_association.size(), dof_handler.n_dofs())); // preset all values by an invalid value std::fill_n (subdomain_association.begin(), dof_handler.n_dofs(), - numbers::invalid_unsigned_int); + types::invalid_subdomain_id); std::vector local_dof_indices; local_dof_indices.reserve (max_dofs_per_cell(dof_handler)); @@ -4260,7 +4508,11 @@ DoFTools::get_subdomain_association (const DH &dof_handler, endc = dof_handler.end(); for (; cell!=endc; ++cell) { - const unsigned int subdomain_id = cell->subdomain_id(); + Assert (cell->is_artificial() == false, + ExcMessage ("You can't call this function for meshes that " + "have artificial cells.")); + + const types::subdomain_id_t subdomain_id = cell->subdomain_id(); const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; local_dof_indices.resize (dofs_per_cell); cell->get_dof_indices (local_dof_indices); @@ -4289,7 +4541,7 @@ DoFTools::get_subdomain_association (const DH &dof_handler, Assert (std::find (subdomain_association.begin(), subdomain_association.end(), - numbers::invalid_unsigned_int) + types::invalid_subdomain_id) == subdomain_association.end(), ExcInternalError()); } @@ -4299,9 +4551,9 @@ DoFTools::get_subdomain_association (const DH &dof_handler, template unsigned int DoFTools::count_dofs_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain) + const types::subdomain_id_t subdomain) { - std::vector subdomain_association (dof_handler.n_dofs()); + std::vector subdomain_association (dof_handler.n_dofs()); get_subdomain_association (dof_handler, subdomain_association); return std::count (subdomain_association.begin(), @@ -4314,31 +4566,43 @@ DoFTools::count_dofs_with_subdomain_association (const DH &dof_handler template IndexSet DoFTools::dof_indices_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain) + const types::subdomain_id_t subdomain) { - std::vector subdomain_association (dof_handler.n_dofs()); - get_subdomain_association (dof_handler, subdomain_association); + // if the DoFHandler is distributed, the + // only thing we can usefully ask is for + // its locally owned subdomain + Assert ((dynamic_cast*> + (&dof_handler.get_tria()) == 0) + || + (subdomain == + dynamic_cast*> + (&dof_handler.get_tria())->locally_owned_subdomain()), + ExcMessage ("For parallel::distributed::Triangulation objects and " + "associated DoF handler objects, asking for any subdomain other " + "than the locally owned one does not make sense.")); IndexSet index_set (dof_handler.n_dofs()); - unsigned int index = 0; - while (index < dof_handler.n_dofs()) - { - // find first_of - while ((index < dof_handler.n_dofs()) && - (subdomain_association[index] != subdomain)) - ++index; - const unsigned int begin = index; - - // find first_not_of - while ((index < dof_handler.n_dofs()) && - (subdomain_association[index] == subdomain)) - ++index; - const unsigned int end = index; - - if ((begin < end) && (begin < dof_handler.n_dofs())) - index_set.add_range (begin, end); - } + std::vector local_dof_indices; + local_dof_indices.reserve (max_dofs_per_cell(dof_handler)); + + typename DH::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + if ((cell->is_artificial() == false) + && + (cell->subdomain_id() == subdomain)) + { + const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; + local_dof_indices.resize (dofs_per_cell); + cell->get_dof_indices (local_dof_indices); + index_set.add_indices (local_dof_indices.begin(), + local_dof_indices.end()); + } + index_set.compress (); return index_set; } @@ -4351,7 +4615,7 @@ DoFTools::dof_indices_with_subdomain_association (const DH &dof_handle template void DoFTools::count_dofs_with_subdomain_association (const DH &dof_handler, - const unsigned int subdomain, + const types::subdomain_id_t subdomain, std::vector &n_dofs_on_subdomain) { Assert (n_dofs_on_subdomain.size() == dof_handler.get_fe().n_components(), @@ -4481,7 +4745,7 @@ DoFTools::count_dofs_per_component ( // computations if (n_components == 1) { - dofs_per_component[0] = dof_handler.n_dofs(); + dofs_per_component[0] = dof_handler.n_locally_owned_dofs(); return; } @@ -4489,7 +4753,7 @@ DoFTools::count_dofs_per_component ( // otherwise determine the number // of dofs in each component // separately. do so in parallel - std::vector dofs_by_component (dof_handler.n_dofs()); + std::vector dofs_by_component (dof_handler.n_locally_owned_dofs()); internal::extract_dofs_by_component (dof_handler, std::vector(), false, dofs_by_component); @@ -4507,11 +4771,25 @@ DoFTools::count_dofs_per_component ( || (std::accumulate (dofs_per_component.begin(), dofs_per_component.end(), 0U) - == dof_handler.n_dofs()), + == dof_handler.n_locally_owned_dofs()), ExcInternalError()); + + // reduce information from all CPUs +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + if (const parallel::distributed::Triangulation * tria + = (dynamic_cast*> + (&dof_handler.get_tria()))) + { + std::vector local_dof_count = dofs_per_component; + + MPI_Allreduce ( &local_dof_count[0], &dofs_per_component[0], n_target_components, + MPI_INT, MPI_SUM, tria->get_communicator()); + } +#endif } + template void DoFTools:: @@ -4558,19 +4836,35 @@ count_dofs_per_block (const DoFHandler& dof_handler, } // otherwise determine the number // of dofs in each block - // separately. do so in parallel - std::vector dofs_by_block (dof_handler.n_dofs()); + // separately. + std::vector dofs_by_block (dof_handler.n_locally_owned_dofs()); internal::extract_dofs_by_component (dof_handler, std::vector(), true, dofs_by_block); // next count what we got - for (unsigned int block=0;block * tria + = (dynamic_cast*> + (&dof_handler.get_tria()))) + { + std::vector local_dof_count = dofs_per_block; + MPI_Allreduce ( &local_dof_count[0], &dofs_per_block[0], n_target_blocks, + MPI_INT, MPI_SUM, tria->get_communicator()); + } +#endif } + template void DoFTools:: @@ -5770,7 +6064,7 @@ DoFTools::make_zero_boundary_constraints (const DH &dof, ++face_no) { const FiniteElement &fe = cell->get_fe(); - + typename DH::face_iterator face = cell->face(face_no); if (face->boundary_indicator () == 0) // face is of the right component @@ -5799,7 +6093,7 @@ DoFTools::make_zero_boundary_constraints (const DH &dof, nonzero = true; break; } - + if (nonzero) zero_boundary_constraints.add_line (face_dofs[i]); } @@ -6080,15 +6374,34 @@ template void DoFTools::extract_subdomain_dofs > (const DoFHandler &dof_handler, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, std::vector &selected_dofs); template void DoFTools::extract_subdomain_dofs > (const hp::DoFHandler &dof_handler, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, std::vector &selected_dofs); +template +void +DoFTools::extract_locally_owned_dofs > +(const DoFHandler & dof_handler, + IndexSet & dof_set); + +template +void +DoFTools::extract_locally_active_dofs > +(const DoFHandler & dof_handler, + IndexSet & dof_set); + + +template +void +DoFTools::extract_locally_relevant_dofs > +(const DoFHandler & dof_handler, + IndexSet & dof_set); + template void DoFTools::extract_constant_modes > @@ -6112,63 +6425,63 @@ template void DoFTools::get_subdomain_association > (const DoFHandler &dof_handler, - std::vector &subdomain_association); + std::vector &subdomain_association); template void DoFTools::get_subdomain_association > (const hp::DoFHandler &dof_handler, - std::vector &subdomain_association); + std::vector &subdomain_association); template unsigned int DoFTools::count_dofs_with_subdomain_association > (const DoFHandler &, - const unsigned int); + const types::subdomain_id_t); template IndexSet DoFTools::dof_indices_with_subdomain_association > (const DoFHandler &, - const unsigned int); + const types::subdomain_id_t); template void DoFTools::count_dofs_with_subdomain_association > (const DoFHandler &, - const unsigned int, + const types::subdomain_id_t, std::vector &); template unsigned int DoFTools::count_dofs_with_subdomain_association > (const hp::DoFHandler &, - const unsigned int); + const types::subdomain_id_t); template IndexSet DoFTools::dof_indices_with_subdomain_association > (const hp::DoFHandler &, - const unsigned int); + const types::subdomain_id_t); template void DoFTools::count_dofs_with_subdomain_association > (const hp::DoFHandler &, - const unsigned int, + const types::subdomain_id_t, std::vector &); template unsigned int DoFTools::count_dofs_with_subdomain_association > (const MGDoFHandler &, - const unsigned int); + const types::subdomain_id_t); template IndexSet DoFTools::dof_indices_with_subdomain_association > (const MGDoFHandler &, - const unsigned int); + const types::subdomain_id_t); template void DoFTools::count_dofs_with_subdomain_association > (const MGDoFHandler &, - const unsigned int, + const types::subdomain_id_t, std::vector &); @@ -6196,6 +6509,7 @@ DoFTools::count_dofs_per_block ( const DoFHandler&, std::vector&, std::vector); + template void DoFTools::count_dofs_per_component ( diff --git a/deal.II/deal.II/source/dofs/number_cache.cc b/deal.II/deal.II/source/dofs/number_cache.cc new file mode 100644 index 0000000000..bff02e10d4 --- /dev/null +++ b/deal.II/deal.II/source/dofs/number_cache.cc @@ -0,0 +1,45 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 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. +// +//--------------------------------------------------------------------------- + + +#include +#include + +DEAL_II_NAMESPACE_OPEN + +namespace internal +{ + namespace DoFHandler + { + NumberCache::NumberCache () + : + n_global_dofs (0), + n_locally_owned_dofs (0) + {} + + + + unsigned int + NumberCache::memory_consumption () const + { + return + MemoryConsumption::memory_consumption (n_global_dofs) + + MemoryConsumption::memory_consumption (n_locally_owned_dofs) + + MemoryConsumption::memory_consumption (locally_owned_dofs) + + MemoryConsumption::memory_consumption (n_locally_owned_dofs_per_processor) + + MemoryConsumption::memory_consumption (locally_owned_dofs_per_processor); + } + } +} + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/deal.II/source/fe/fe_tools.cc b/deal.II/deal.II/source/fe/fe_tools.cc index 25fbdcb63e..ec1298b4fc 100644 --- a/deal.II/deal.II/source/fe/fe_tools.cc +++ b/deal.II/deal.II/source/fe/fe_tools.cc @@ -11,8 +11,6 @@ // //--------------------------------------------------------------------------- -#include - #include #include #include @@ -47,6 +45,8 @@ #include +#include + DEAL_II_NAMESPACE_OPEN diff --git a/deal.II/deal.II/source/grid/grid_refinement.cc b/deal.II/deal.II/source/grid/grid_refinement.cc index c96a80f034..8e36fa9af3 100644 --- a/deal.II/deal.II/source/grid/grid_refinement.cc +++ b/deal.II/deal.II/source/grid/grid_refinement.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by the deal.II authors +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -11,9 +11,14 @@ // //--------------------------------------------------------------------------- +#include #include +#include +#include #include +#include #include +#include #include #include @@ -29,73 +34,121 @@ DEAL_II_NAMESPACE_OPEN -namespace +namespace { - template - inline - number - max_element (const Vector &criteria) + namespace internal { - return *std::max_element(criteria.begin(), criteria.end()); - } + template + inline + number + max_element (const Vector &criteria) + { + return *std::max_element(criteria.begin(), criteria.end()); + } - - template - inline - number - min_element (const Vector &criteria) - { - return *std::min_element(criteria.begin(), criteria.end()); + + template + inline + number + min_element (const Vector &criteria) + { + return *std::min_element(criteria.begin(), criteria.end()); + } + + +#ifdef DEAL_II_USE_PETSC + PetscScalar + max_element (const PETScWrappers::Vector &criteria) + { + // this is horribly slow (since we have + // to get the array of values from PETSc + // in every iteration), but works + PetscScalar m = 0; + for (unsigned int i=0; i + typename constraint_and_return_value::value, + typename Vector::value_type>::type + min_element (const Vector &criteria) { - // this is horribly slow (since we have - // to get the array of values from PETSc - // in every iteration), but works - PetscScalar m = 0; - for (unsigned int i=0; i + typename constraint_and_return_value::value, + typename Vector::value_type>::type + max_element (const Vector &criteria) { - // this is horribly slow (since we have - // to get the array of values from PETSc - // in every iteration), but works - PetscScalar m = criteria(0); - for (unsigned int i=1; i + typename constraint_and_return_value::value, + typename Vector::value_type>::type + min_element (const Vector &criteria) { - TrilinosScalar m = 0; - criteria.trilinos_vector().MaxValue(&m); - return m; + typename Vector::value_type t = internal::min_element(criteria.block(0)); + for (unsigned int b=1; b + typename constraint_and_return_value::value, + typename Vector::value_type>::type + max_element (const Vector &criteria) { - TrilinosScalar m = 0; - criteria.trilinos_vector().MinValue(&m); - return m; + typename Vector::value_type t = internal::max_element(criteria.block(0)); + for (unsigned int b=1; b0)); - + if (i &tria, Assert (criteria.size() == tria.n_active_cells(), ExcDimensionMismatch(criteria.size(), tria.n_active_cells())); Assert (criteria.is_non_negative (), ExcNegativeCriteria()); - + // when all indicators are zero we // do not need to refine but only // to coarsen if (criteria.all_zero()) return; - + typename Triangulation::active_cell_iterator cell = tria.begin_active(); const unsigned int n_cells = criteria.size(); //TODO: This is undocumented, looks fishy and seems unnecessary - + double new_threshold=threshold; // when threshold==0 find the // smallest value in criteria @@ -185,7 +238,7 @@ void GridRefinement::refine (Triangulation &tria, && (criteria(index)= new_threshold) cell->set_refine_flag(); @@ -204,7 +257,7 @@ void GridRefinement::coarsen (Triangulation &tria, typename Triangulation::active_cell_iterator cell = tria.begin_active(); const unsigned int n_cells = criteria.size(); - + for (unsigned int index=0; indexrefine_flag_set()) @@ -240,7 +293,7 @@ GridRefinement::refine_and_coarsen_fixed_number (Triangulation &tr // refining cells and instead try to // only coarsen as many as it would // take to get to the target - + // as we have no information on cells // being refined isotropically or // anisotropically, assume isotropic @@ -293,10 +346,10 @@ GridRefinement::refine_and_coarsen_fixed_number (Triangulation &tr refine_cells = static_cast (refine_cells * alpha); coarsen_cells = static_cast (coarsen_cells * alpha); } - + if (refine_cells || coarsen_cells) { - dealii::Vector tmp(criteria); + dealii::Vector tmp (criteria); if (refine_cells) { std::nth_element (tmp.begin(), tmp.begin()+refine_cells, @@ -332,12 +385,13 @@ GridRefinement::refine_and_coarsen_fixed_fraction (Triangulation & Assert ((bottom_fraction>=0) && (bottom_fraction<=1), ExcInvalidParameterValue()); Assert (top_fraction+bottom_fraction <= 1, ExcInvalidParameterValue()); Assert (criteria.is_non_negative (), ExcNegativeCriteria()); - + // let tmp be the cellwise square of the // error, which is what we have to sum // up and compare with // @p{fraction_of_error*total_error}. - dealii::Vector tmp(criteria); + dealii::Vector tmp; + tmp = criteria; const double total_error = tmp.l1_norm(); // sort the largest criteria to the @@ -397,7 +451,7 @@ GridRefinement::refine_and_coarsen_fixed_fraction (Triangulation & return; } } - + // in some rare cases it may happen that // both thresholds are the same (e.g. if @@ -433,14 +487,14 @@ GridRefinement::refine_and_coarsen_fixed_fraction (Triangulation & if ((top_threshold == max_element(criteria)) && (top_fraction != 1)) top_threshold *= 0.999; - + if (bottom_threshold>=top_threshold) bottom_threshold = 0.999*top_threshold; - + // actually flag cells if (top_threshold < max_element(criteria)) refine (tria, criteria, top_threshold); - + if (bottom_threshold > min_element(criteria)) coarsen (tria, criteria, bottom_threshold); } @@ -455,21 +509,21 @@ GridRefinement::refine_and_coarsen_optimize (Triangulation &tria, Assert (criteria.size() == tria.n_active_cells(), ExcDimensionMismatch(criteria.size(), tria.n_active_cells())); Assert (criteria.is_non_negative (), ExcNegativeCriteria()); - + // get an increasing order on // the error indicator std::vector tmp(criteria.size()); for (unsigned int i=0;i &tria, // N+3*M (N is the current number // of cells) double min = (3.*(1.+M)+N) * (E-s0); - + unsigned int minArg = N-1; - + for (M=1;M &tria, // explicit instantiations -template -void -GridRefinement:: -refine,deal_II_dimension> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -refine,deal_II_dimension> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -coarsen,deal_II_dimension> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -coarsen,deal_II_dimension> - (Triangulation &, - const Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number,deal_II_dimension> -(Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number,deal_II_dimension> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction,deal_II_dimension> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction,deal_II_dimension> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize,deal_II_dimension> -(Triangulation &, - const Vector &); - -template -void -GridRefinement:: -refine_and_coarsen_optimize,deal_II_dimension> -(Triangulation &, - const Vector &); - -#ifdef DEAL_II_USE_PETSC -template -void -GridRefinement:: -refine - (Triangulation &, - const PETScWrappers::Vector &, - const double); - -template -void -GridRefinement:: -coarsen -(Triangulation &, - const PETScWrappers::Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number -(Triangulation &, - const PETScWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction -(Triangulation &, - const PETScWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize -(Triangulation &, - const PETScWrappers::Vector &); -#endif - -#ifdef DEAL_II_USE_TRILINOS -template -void -GridRefinement:: -refine - (Triangulation &, - const TrilinosWrappers::Vector &, - const double); - -template -void -GridRefinement:: -coarsen -(Triangulation &, - const TrilinosWrappers::Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number -(Triangulation &, - const TrilinosWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction -(Triangulation &, - const TrilinosWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize -(Triangulation &, - const TrilinosWrappers::Vector &); -#endif - - - -#if deal_II_dimension != 3 -// explicit instantiations -template -void -GridRefinement:: -refine,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -refine,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -coarsen,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double); - -template -void -GridRefinement:: -coarsen,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number,deal_II_dimension+1> -(Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction,deal_II_dimension+1> - (Triangulation &, - const Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize,deal_II_dimension+1> -(Triangulation &, - const Vector &); - -template -void -GridRefinement:: -refine_and_coarsen_optimize,deal_II_dimension+1> -(Triangulation &, - const Vector &); - -#ifdef DEAL_II_USE_PETSC -template -void -GridRefinement:: -refine - (Triangulation &, - const PETScWrappers::Vector &, - const double); - -template -void -GridRefinement:: -coarsen -(Triangulation &, - const PETScWrappers::Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number -(Triangulation &, - const PETScWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction -(Triangulation &, - const PETScWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize -(Triangulation &, - const PETScWrappers::Vector &); -#endif - -#ifdef DEAL_II_USE_TRILINOS -template -void -GridRefinement:: -refine - (Triangulation &, - const TrilinosWrappers::Vector &, - const double); - -template -void -GridRefinement:: -coarsen -(Triangulation &, - const TrilinosWrappers::Vector &, - const double); - - -template -void -GridRefinement:: -refine_and_coarsen_fixed_number -(Triangulation &, - const TrilinosWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_fixed_fraction -(Triangulation &, - const TrilinosWrappers::Vector &, - const double, - const double, - const unsigned int); - -template -void -GridRefinement:: -refine_and_coarsen_optimize -(Triangulation &, - const TrilinosWrappers::Vector &); -#endif - - -#endif +#include "grid_refinement.inst" DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/deal.II/source/grid/grid_refinement.inst.in b/deal.II/deal.II/source/grid/grid_refinement.inst.in new file mode 100644 index 0000000000..d4619e799d --- /dev/null +++ b/deal.II/deal.II/source/grid/grid_refinement.inst.in @@ -0,0 +1,105 @@ +//--------------------------------------------------------------------------- +// $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 (S : REAL_SCALARS) +{ + template + void + GridRefinement:: + refine,deal_II_dimension> + (Triangulation &, + const dealii::Vector &, + const double); + + template + void + GridRefinement:: + coarsen,deal_II_dimension> + (Triangulation &, + const dealii::Vector &, + const double); + + template + void + GridRefinement:: + refine_and_coarsen_fixed_number,deal_II_dimension> + (Triangulation &, + const dealii::Vector &, + const double, + const double, + const unsigned int); + + template + void + GridRefinement:: + refine_and_coarsen_fixed_fraction,deal_II_dimension> + (Triangulation &, + const dealii::Vector &, + const double, + const double, + const unsigned int); + + template + void + GridRefinement:: + refine_and_coarsen_optimize,deal_II_dimension> + (Triangulation &, + const dealii::Vector &); + +#if deal_II_dimension < 3 + template + void + GridRefinement:: + refine,deal_II_dimension+1> + (Triangulation &, + const dealii::Vector &, + const double); + + template + void + GridRefinement:: + coarsen,deal_II_dimension+1> + (Triangulation &, + const dealii::Vector &, + const double); + + template + void + GridRefinement:: + refine_and_coarsen_fixed_number,deal_II_dimension+1> + (Triangulation &, + const dealii::Vector &, + const double, + const double, + const unsigned int); + + template + void + GridRefinement:: + refine_and_coarsen_fixed_fraction,deal_II_dimension+1> + (Triangulation &, + const dealii::Vector &, + const double, + const double, + const unsigned int); + + template + void + GridRefinement:: + refine_and_coarsen_optimize,deal_II_dimension+1> + (Triangulation &, + const dealii::Vector &); +#endif +} diff --git a/deal.II/deal.II/source/grid/grid_tools.cc b/deal.II/deal.II/source/grid/grid_tools.cc index d845d538cd..77b81cc56e 100644 --- a/deal.II/deal.II/source/grid/grid_tools.cc +++ b/deal.II/deal.II/source/grid/grid_tools.cc @@ -989,7 +989,7 @@ template void GridTools:: get_subdomain_association (const Triangulation &triangulation, - std::vector &subdomain) + std::vector &subdomain) { Assert (subdomain.size() == triangulation.n_active_cells(), ExcDimensionMismatch (subdomain.size(), @@ -1009,7 +1009,7 @@ template unsigned int GridTools:: count_cells_with_subdomain_association (const Triangulation &triangulation, - const unsigned int subdomain) + const types::subdomain_id_t subdomain) { unsigned int count = 0; for (typename Triangulation::active_cell_iterator @@ -1018,8 +1018,6 @@ count_cells_with_subdomain_association (const Triangulation &tria if (cell->subdomain_id() == subdomain) ++count; - Assert (count != 0, ExcNonExistentSubdomain(subdomain)); - return count; } @@ -1863,6 +1861,14 @@ GridTools:: get_face_connectivity_of_cells (const Triangulation &triangulation, SparsityPattern &cell_connectivity); +#if deal_II_dimension < 3 +template +void +GridTools:: +get_face_connectivity_of_cells (const Triangulation &triangulation, + SparsityPattern &cell_connectivity); +#endif + template void GridTools::partition_triangulation (const unsigned int, @@ -1878,13 +1884,13 @@ template void GridTools:: get_subdomain_association (const Triangulation &, - std::vector &); + std::vector &); template unsigned int GridTools:: count_cells_with_subdomain_association (const Triangulation &, - const unsigned int ); + const types::subdomain_id_t); template diff --git a/deal.II/deal.II/source/grid/tria.cc b/deal.II/deal.II/source/grid/tria.cc index 7e13a79f6b..6e390afd5f 100644 --- a/deal.II/deal.II/source/grid/tria.cc +++ b/deal.II/deal.II/source/grid/tria.cc @@ -4042,6 +4042,8 @@ namespace internal subcells[i]->set_parent (cell->index ()); } + + // set child index for // even children children // i=0,2 (0) @@ -9152,9 +9154,9 @@ Triangulation:: Triangulation (const MeshSmoothing smooth_grid, const bool check_for_distorted_cells) : + smooth_grid(smooth_grid), faces(NULL), anisotropic_refinement(false), - smooth_grid(smooth_grid), check_for_distorted_cells(check_for_distorted_cells) { // set default boundary for all @@ -9178,6 +9180,7 @@ Triangulation (const Triangulation &) } + template Triangulation::~Triangulation () { @@ -9188,6 +9191,7 @@ Triangulation::~Triangulation () } + template void Triangulation::clear () { @@ -11648,6 +11652,8 @@ unsigned int Triangulation::n_lines () const #if deal_II_dimension == 1 + + template <> unsigned int Triangulation<1,1>::n_raw_lines (const unsigned int level) const { @@ -12013,6 +12019,7 @@ Triangulation::get_used_vertices () const #if deal_II_dimension == 1 + template <> unsigned int Triangulation<1,1>::max_adjacent_cells () const { @@ -12060,6 +12067,13 @@ Triangulation::execute_coarsening_and_refinement () { prepare_coarsening_and_refinement (); + // verify a case with which we have had + // some difficulty in the past (see the + // deal.II/coarsening_* tests) + if (smooth_grid & limit_level_difference_at_vertices) + Assert (satisfies_level1_at_vertex_rule (*this) == true, + ExcInternalError()); + // Inform RefinementListeners // about beginning of refinement. typename std::list::iterator ref_listener = @@ -12071,8 +12085,7 @@ Triangulation::execute_coarsening_and_refinement () execute_coarsening(); const DistortedCellList - cells_with_distorted_children - = execute_refinement(); + cells_with_distorted_children = execute_refinement(); // verify a case with which we have had // some difficulty in the past (see the @@ -12274,6 +12287,8 @@ void Triangulation::fix_coarsen_flags () std::vector previous_coarsen_flags (n_active_cells()); save_coarsen_flags (previous_coarsen_flags); + std::vector vertex_level (vertices.size(), 0); + bool continue_iterating = true; do @@ -12288,7 +12303,7 @@ void Triangulation::fix_coarsen_flags () // store highest level one // of the cells adjacent to // a vertex belongs to - std::vector vertex_level (vertices.size(), 0); + std::fill (vertex_level.begin(), vertex_level.end(), 0); active_cell_iterator cell = begin_active(), endc = end(); for (; cell!=endc; ++cell) @@ -12553,6 +12568,62 @@ bool Triangulation<1,2>::prepare_coarsening_and_refinement () namespace { + + // check if the given @param cell marked + // for coarsening would produce an + // unrefined island. To break up long + // chains of these cells we recursively + // check our neighbors in case we change + // this cell. This reduces the number of + // outer iterations dramatically. + template + void + possibly_do_not_produce_unrefined_islands( + const typename Triangulation::cell_iterator &cell) + { + Assert (cell->has_children(), ExcInternalError()); + + unsigned int n_neighbors=0; + // count all neighbors + // that will be refined + // along the face of our + // cell after the next + // step + unsigned int count=0; + for (unsigned int n=0; n::faces_per_cell; ++n) + { + const typename Triangulation::cell_iterator neighbor = cell->neighbor(n); + if (neighbor.state() == IteratorState::valid) + { + ++n_neighbors; + if (face_will_be_refined_by_neighbor(cell,n)) + ++count; + } + } + // clear coarsen flags if + // either all existing + // neighbors will be + // refined or all but one + // will be and the cell + // is in the interior of + // the domain + if (count==n_neighbors || + (count>=n_neighbors-1 && + n_neighbors == GeometryInfo::faces_per_cell) ) + { + for (unsigned int c=0; cn_children(); ++c) + cell->child(c)->clear_coarsen_flag(); + + for (unsigned int face=0; face::faces_per_cell; ++face) + if (!cell->at_boundary(face) + && + ( !cell->neighbor(face)->active() ) + && (cell_will_be_coarsened(cell->neighbor(face))) ) + possibly_do_not_produce_unrefined_islands( cell->neighbor(face) ); + } + } + + // see if the current cell needs to // be refined to avoid unrefined // islands. @@ -12926,11 +12997,9 @@ bool Triangulation::prepare_coarsening_and_refinement () cell->clear_coarsen_flag(); } - bool mesh_changed_in_this_loop = false; do { - ////////////////////////////////////// // STEP 1: // do not coarsen a cell if 'most of @@ -12947,47 +13016,12 @@ bool Triangulation::prepare_coarsening_and_refinement () for (cell=begin(); cell!=endc; ++cell) { - if (!cell->active()) - { // only do something if this // cell will be coarsened - if (cell_will_be_coarsened(cell)) - { - unsigned int n_neighbors=0; - // count all neighbors - // that will be refined - // along the face of our - // cell after the next - // step - unsigned int count=0; - for (unsigned int n=0; - n::faces_per_cell; ++n) - { - const cell_iterator neighbor = cell->neighbor(n); - if (neighbor.state() == IteratorState::valid) - { - ++n_neighbors; - if (face_will_be_refined_by_neighbor(cell,n)) - ++count; - } - } - // clear coarsen flags if - // either all existing - // neighbors will be - // refined or all but one - // will be and the cell - // is in the interior of - // the domain - if (count==n_neighbors || - (count>=n_neighbors-1 && - n_neighbors== - GeometryInfo::faces_per_cell)) - for (unsigned int c=0; cn_children(); ++c) - cell->child(c)->clear_coarsen_flag(); - } - } // if (!cell->active()) - } // for (all cells) - } // if (smooth_grid & ...) + if (!cell->active() && cell_will_be_coarsened(cell)) + possibly_do_not_produce_unrefined_islands(cell); + } + } ////////////////////////////////////// @@ -13000,6 +13034,31 @@ bool Triangulation::prepare_coarsening_and_refinement () // priority. // If patch_level_1 is set, this will // be automatically fulfilled. + // + // there is one corner case + // to consider: if this is a + // distributed + // triangulation, there may + // be refined islands on the + // boundary of which we own + // only part (e.g. a single + // cell in the corner of a + // domain). the rest of the + // island is ghost cells and + // it *looks* like the area + // around it (artificial + // cells) are coarser but + // this is only because they + // may actually be equally + // fine on other + // processors. it's hard to + // detect this case but we + // can do the following: + // only set coarsen flags to + // remove this refined + // island if all cells we + // want to set flags on are + // locally owned if (smooth_grid & (eliminate_refined_inner_islands | eliminate_refined_boundary_islands) && !(smooth_grid & patch_level_1)) @@ -13008,7 +13067,11 @@ bool Triangulation::prepare_coarsening_and_refinement () const cell_iterator endc = end(); for (cell=begin(); cell!=endc; ++cell) - if (!cell->active() || (cell->active() && cell->refine_flag_set())) + if (!cell->active() || + (cell->active() && + cell->refine_flag_set() && + !cell->is_ghost() && + !cell->is_artificial())) { // check whether all // children are @@ -13027,7 +13090,9 @@ bool Triangulation::prepare_coarsening_and_refinement () bool all_children_active = true; if (!cell->active()) for (unsigned int c=0; cn_children(); ++c) - if (!cell->child(c)->active()) + if (!cell->child(c)->active() || + cell->child(c)->is_ghost() || + cell->child(c)->is_artificial()) { all_children_active = false; break; diff --git a/deal.II/deal.II/source/grid/tria_accessor.cc b/deal.II/deal.II/source/grid/tria_accessor.cc index b8c5d20815..4d9214845e 100644 --- a/deal.II/deal.II/source/grid/tria_accessor.cc +++ b/deal.II/deal.II/source/grid/tria_accessor.cc @@ -1264,7 +1264,7 @@ void CellAccessor::recursively_set_material_id (const unsigned ch template -unsigned int CellAccessor::subdomain_id () const +types::subdomain_id_t CellAccessor::subdomain_id () const { Assert (this->used(), TriaAccessorExceptions::ExcCellNotUsed()); return this->tria->levels[this->present_level]->subdomain_ids[this->present_index]; @@ -1274,7 +1274,7 @@ unsigned int CellAccessor::subdomain_id () const template void -CellAccessor::set_subdomain_id (const unsigned int new_subdomain_id) const +CellAccessor::set_subdomain_id (const types::subdomain_id_t new_subdomain_id) const { Assert (this->used(), TriaAccessorExceptions::ExcCellNotUsed()); this->tria->levels[this->present_level]->subdomain_ids[this->present_index] @@ -1295,6 +1295,20 @@ CellAccessor::parent () const } +template +void +CellAccessor:: +recursively_set_subdomain_id (const types::subdomain_id_t new_subdomain_id) const +{ + set_subdomain_id (new_subdomain_id); + + if (this->has_children()) + for (unsigned int c=0; cn_children(); ++c) + this->child(c)->recursively_set_subdomain_id (new_subdomain_id); +} + + + template void CellAccessor::set_neighbor (const unsigned int i, const TriaIterator > &pointer) const diff --git a/deal.II/deal.II/source/hp/dof_handler.cc b/deal.II/deal.II/source/hp/dof_handler.cc index 7513dae2be..2995838cdf 100644 --- a/deal.II/deal.II/source/hp/dof_handler.cc +++ b/deal.II/deal.II/source/hp/dof_handler.cc @@ -31,6 +31,15 @@ DEAL_II_NAMESPACE_OPEN +namespace parallel +{ + namespace distributed + { + template class Triangulation; + } +} + + namespace internal { namespace hp @@ -1587,9 +1596,14 @@ namespace hp DoFHandler::DoFHandler (const Triangulation &tria) : tria(&tria, typeid(*this).name()), - faces (NULL), - used_dofs (0) - { + faces (NULL) + { + Assert ((dynamic_cast*> + (&tria) + == 0), + ExcMessage ("The given triangulation is parallel distributed but " + "this class does not currently support this.")); + create_active_fe_table (); tria.add_refinement_listener (*this); } @@ -2924,7 +2938,7 @@ namespace hp MemoryConsumption::memory_consumption (tria) + MemoryConsumption::memory_consumption (levels) + MemoryConsumption::memory_consumption (*faces) + - MemoryConsumption::memory_consumption (used_dofs) + + MemoryConsumption::memory_consumption (number_cache) + MemoryConsumption::memory_consumption (vertex_dofs) + MemoryConsumption::memory_consumption (vertex_dofs_offsets) + MemoryConsumption::memory_consumption (has_children)); @@ -3491,7 +3505,7 @@ namespace hp = internal::hp::DoFHandler::Implementation::template distribute_dofs_on_cell (cell, next_free_dof); - used_dofs = next_free_dof; + number_cache.n_global_dofs = next_free_dof; } @@ -3505,7 +3519,7 @@ namespace hp // lower-dimensional objects // where elements come together std::vector - constrained_indices (used_dofs, numbers::invalid_unsigned_int); + constrained_indices (number_cache.n_global_dofs, numbers::invalid_unsigned_int); compute_vertex_dof_identities (constrained_indices); compute_line_dof_identities (constrained_indices); compute_quad_dof_identities (constrained_indices); @@ -3514,9 +3528,9 @@ namespace hp // new numbers to those which are // not constrained std::vector - new_dof_indices (used_dofs, numbers::invalid_unsigned_int); + new_dof_indices (number_cache.n_global_dofs, numbers::invalid_unsigned_int); unsigned int next_free_dof = 0; - for (unsigned int i=0; i()); - used_dofs = next_free_dof; + // now set the elements of the + // number cache appropriately + number_cache.n_global_dofs = next_free_dof; + number_cache.n_locally_owned_dofs = number_cache.n_global_dofs; + + number_cache.locally_owned_dofs + = IndexSet (number_cache.n_global_dofs); + number_cache.locally_owned_dofs.add_range (0, + number_cache.n_global_dofs); + + number_cache.n_locally_owned_dofs_per_processor + = std::vector (1, + number_cache.n_global_dofs); + number_cache.locally_owned_dofs_per_processor + = std::vector (1, + number_cache.locally_owned_dofs); // finally restore the user flags const_cast &>(*tria).load_user_flags(user_flags); diff --git a/deal.II/deal.II/source/multigrid/mg_dof_handler.cc b/deal.II/deal.II/source/multigrid/mg_dof_handler.cc index 7ec46d3036..cc03d6bdf3 100644 --- a/deal.II/deal.II/source/multigrid/mg_dof_handler.cc +++ b/deal.II/deal.II/source/multigrid/mg_dof_handler.cc @@ -30,6 +30,14 @@ DEAL_II_NAMESPACE_OPEN +namespace parallel +{ + namespace distributed + { + template class Triangulation; + } +} + //TODO[WB]: this class is currently only implemented for dim==spacedim. to //make the general case happen it should undergo a similar transformation as @@ -489,7 +497,8 @@ namespace internal { const unsigned int dim = 1; - Assert (mg_dof_handler.get_tria().n_levels() > 0, DoFHandler<1>::ExcInvalidTriangulation()); + Assert (mg_dof_handler.get_tria().n_levels() > 0, + ExcMessage("Invalid triangulation")); ////////////////////////// // DESTRUCTION @@ -567,7 +576,8 @@ namespace internal void reserve_space (MGDoFHandler<2,spacedim> &mg_dof_handler) { const unsigned int dim = 2; - Assert (mg_dof_handler.get_tria().n_levels() > 0, DoFHandler<2>::ExcInvalidTriangulation()); + Assert (mg_dof_handler.get_tria().n_levels() > 0, + ExcMessage("Invalid triangulation")); //////////////////////////// // DESTRUCTION @@ -656,7 +666,8 @@ namespace internal { const unsigned int dim = 3; - Assert (mg_dof_handler.get_tria().n_levels() > 0, DoFHandler<3>::ExcInvalidTriangulation()); + Assert (mg_dof_handler.get_tria().n_levels() > 0, + ExcMessage("Invalid triangulation")); //////////////////////////// // DESTRUCTION @@ -831,7 +842,13 @@ MGDoFHandler::MGDoFHandler (const Triangulation &tri : DoFHandler (tria), mg_faces (NULL) -{} +{ + Assert ((dynamic_cast*> + (&tria) + == 0), + ExcMessage ("The given triangulation is parallel distributed but " + "this class does not currently support this.")); +} diff --git a/deal.II/deal.II/source/numerics/data_out.cc b/deal.II/deal.II/source/numerics/data_out.cc index 0926a72d99..9a346cbf82 100644 --- a/deal.II/deal.II/source/numerics/data_out.cc +++ b/deal.II/deal.II/source/numerics/data_out.cc @@ -970,8 +970,8 @@ void DataOut::build_patches (const Mappingdofs->get_tria().n_levels(); ++l) { unsigned int max_index = 0; - for (cell_iterator cell=first_cell(); cell != this->dofs->end(); - cell = next_cell(cell)) + for (cell_iterator cell=first_locally_owned_cell(); cell != this->dofs->end(); + cell = next_locally_owned_cell(cell)) if (static_cast(cell->level()) == l) max_index = std::max (max_index, static_cast(cell->index())); @@ -983,19 +983,19 @@ void DataOut::build_patches (const Mapping > all_cells; { // set the index of the first - // cell. if first_cell/next_cell + // cell. if first_locally_owned_cell/next_locally_owned_cell // returns non-active cells, then // the index is not usable // anyway, but otherwise we // should keep track where we are unsigned int index; - if (first_cell()->has_children()) + if (first_locally_owned_cell()->has_children()) index = 0; else index = std::distance (this->dofs->begin_active(), - active_cell_iterator(first_cell())); - for (cell_iterator cell=first_cell(); cell != this->dofs->end(); - cell = next_cell(cell)) + active_cell_iterator(first_locally_owned_cell())); + for (cell_iterator cell=first_locally_owned_cell(); cell != this->dofs->end(); + cell = next_locally_owned_cell(cell)) { Assert (static_cast(cell->level()) < cell_to_patch_index_map.size(), @@ -1020,10 +1020,10 @@ void DataOut::build_patches (const Mappinghas_children() && - next_cell(cell) != this->dofs->end() && - !next_cell(cell)->has_children()) + next_locally_owned_cell(cell) != this->dofs->end() && + !next_locally_owned_cell(cell)->has_children()) index += std::distance (active_cell_iterator(cell), - active_cell_iterator(next_cell(cell))); + active_cell_iterator(next_locally_owned_cell(cell))); } } @@ -1117,6 +1117,41 @@ DataOut::next_cell (const typename DataOut::cell_iterator &cell) } + +template +typename DataOut::cell_iterator +DataOut::first_locally_owned_cell () +{ + typename DataOut::cell_iterator + cell = this->dofs->begin_active (); + + // skip cells if the current one + // has no children (is active) and + // is a ghost or artificial cell + while ((cell != this->dofs->end()) && + (cell->has_children() == false) && + (cell->is_ghost() || cell->is_artificial())) + cell = next_cell(cell); + + return cell; +} + + + +template +typename DataOut::cell_iterator +DataOut::next_locally_owned_cell (const typename DataOut::cell_iterator &old_cell) +{ + typename DataOut::cell_iterator + cell = next_cell(old_cell); + while ((cell != this->dofs->end()) && + (cell->has_children() == false) && + (cell->is_ghost() || cell->is_artificial())) + cell = next_cell(cell); + return cell; +} + + // explicit instantiations #include "data_out.inst" diff --git a/deal.II/deal.II/source/numerics/data_out_faces.cc b/deal.II/deal.II/source/numerics/data_out_faces.cc index 3b4b62e340..8760e59d56 100644 --- a/deal.II/deal.II/source/numerics/data_out_faces.cc +++ b/deal.II/deal.II/source/numerics/data_out_faces.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by the deal.II authors +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -87,6 +87,10 @@ build_one_patch (const FaceDescriptor *cell_and_face, internal::DataOutFaces::ParallelData &data, DataOutBase::Patch &patch) { + Assert (!cell_and_face->first->is_ghost() && + !cell_and_face->first->is_artificial(), + ExcNotImplemented()); + // we use the mapping to transform the // vertices. However, the mapping works on // cells, not faces, so transform the face diff --git a/deal.II/deal.II/source/numerics/data_out_rotation.cc b/deal.II/deal.II/source/numerics/data_out_rotation.cc index 148c5b8627..94a0e8f587 100644 --- a/deal.II/deal.II/source/numerics/data_out_rotation.cc +++ b/deal.II/deal.II/source/numerics/data_out_rotation.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by the deal.II authors +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -103,6 +103,10 @@ build_one_patch (const cell_iterator *cell, internal::DataOutRotation::ParallelData &data, std::vector > &patches) { + Assert (!(*cell)->is_ghost() && + !(*cell)->is_artificial(), + ExcNotImplemented()); + const unsigned int n_patches_per_circle = data.n_patches_per_circle; // another abbreviation denoting diff --git a/deal.II/deal.II/source/numerics/data_out_stack.cc b/deal.II/deal.II/source/numerics/data_out_stack.cc index 048e6ebda8..8c24e7c3a9 100644 --- a/deal.II/deal.II/source/numerics/data_out_stack.cc +++ b/deal.II/deal.II/source/numerics/data_out_stack.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -302,6 +302,10 @@ void DataOutStack::build_patches (const unsigned int nnnn_subdi for (typename DH::active_cell_iterator cell=dof_handler->begin_active(); cell != dof_handler->end(); ++cell, ++patch, ++cell_number) { + Assert (!cell->is_ghost() && + !cell->is_artificial(), + ExcNotImplemented()); + Assert (patch != patches.end(), ExcInternalError()); // first fill in the vertices of the patch diff --git a/deal.II/deal.II/source/numerics/error_estimator.cc b/deal.II/deal.II/source/numerics/error_estimator.cc index cfa015a050..fac5c18221 100644 --- a/deal.II/deal.II/source/numerics/error_estimator.cc +++ b/deal.II/deal.II/source/numerics/error_estimator.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by the deal.II authors +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -226,7 +226,7 @@ namespace internal * The subdomain id we are to care * for. */ - const unsigned int subdomain_id; + const types::subdomain_id_t subdomain_id; /** * The material id we are to care * for. @@ -251,7 +251,7 @@ namespace internal const dealii::hp::MappingCollection &mapping, const bool need_quadrature_points, const unsigned int n_solution_vectors, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id, const typename FunctionMap::type *neumann_bc, const std::vector component_mask, @@ -275,7 +275,7 @@ namespace internal const dealii::hp::MappingCollection &mapping, const bool need_quadrature_points, const unsigned int n_solution_vectors, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id, const typename FunctionMap::type *neumann_bc, const std::vector component_mask, @@ -383,6 +383,7 @@ namespace internal // exists in the global map Assert (face_integrals.find (p->first) == face_integrals.end(), ExcInternalError()); + for (unsigned int i=0; isecond.size(); ++i) Assert (p->second[i] >= 0, ExcInternalError()); @@ -828,7 +829,7 @@ namespace internal { const unsigned int n_solution_vectors = solutions.size(); - const unsigned int subdomain_id = parallel_data.subdomain_id; + const types::subdomain_id_t subdomain_id = parallel_data.subdomain_id; const unsigned int material_id = parallel_data.material_id; // empty our own copy of the local face @@ -901,7 +902,7 @@ namespace internal // material_id), or if one of the // neighbors behind the face is on // the subdomain we care for - if ( ! ( ((subdomain_id == numbers::invalid_unsigned_int) + if ( ! ( ((subdomain_id == types::invalid_subdomain_id) || (cell->subdomain_id() == subdomain_id)) && @@ -921,7 +922,7 @@ namespace internal if (face->has_children() == false) care_for_cell |= ((cell->neighbor(face_no)->subdomain_id() == subdomain_id) || - (subdomain_id == numbers::invalid_unsigned_int)) + (subdomain_id == types::invalid_subdomain_id)) && ((cell->neighbor(face_no)->material_id() == material_id) || @@ -939,7 +940,7 @@ namespace internal ->material_id() == material_id) && (subdomain_id == - numbers::invalid_unsigned_int))) + types::invalid_subdomain_id))) { care_for_cell = true; break; @@ -1015,7 +1016,7 @@ estimate (const Mapping<1,spacedim> &mapping, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { // just pass on to the other function @@ -1039,7 +1040,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1061,7 +1062,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1084,7 +1085,7 @@ estimate (const Mapping<1,spacedim> &mapping, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { // just pass on to the other function @@ -1107,7 +1108,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1129,7 +1130,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function<1> *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1152,7 +1153,7 @@ estimate (const Mapping<1,spacedim> &/*mapping*/, const std::vector &/*component_mask_*/, const Function<1> */*coefficient*/, const unsigned int, - const unsigned int /*subdomain_id*/, + const types::subdomain_id_t /*subdomain_id*/, const unsigned int /*material_id*/) { Assert (false, ExcInternalError()); @@ -1172,9 +1173,32 @@ estimate (const Mapping<1,spacedim> &mapping, const std::vector &component_mask_, const Function<1> *coefficient, const unsigned int, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id_, const unsigned int material_id) { + if (dynamic_cast*> + (&dof_handler.get_tria()) + != 0) + Assert ((subdomain_id_ == types::invalid_subdomain_id) + || + (subdomain_id_ == + dynamic_cast&> + (dof_handler.get_tria()).locally_owned_subdomain()), + ExcMessage ("For parallel distributed triangulations, the only " + "valid subdomain_id that can be passed here is the " + "one that corresponds to the locally owned subdomain id.")); + + const types::subdomain_id_t subdomain_id + = ((dynamic_cast*> + (&dof_handler.get_tria()) + != 0) + ? + dynamic_cast&> + (dof_handler.get_tria()).locally_owned_subdomain() + : + subdomain_id_); + + const unsigned int n_components = dof_handler.get_fe().n_components(); const unsigned int n_solution_vectors = solutions.size(); @@ -1273,7 +1297,7 @@ estimate (const Mapping<1,spacedim> &mapping, typename DH::active_cell_iterator cell = dof_handler.begin_active(); for (unsigned int cell_index=0; cell != dof_handler.end(); ++cell, ++cell_index) - if (((subdomain_id == numbers::invalid_unsigned_int) + if (((subdomain_id == types::invalid_subdomain_id) || (cell->subdomain_id() == subdomain_id)) && @@ -1416,7 +1440,7 @@ estimate (const Mapping &mapping, const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { // just pass on to the other function @@ -1439,7 +1463,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1462,7 +1486,7 @@ estimate (const Mapping &mapping, const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { // just pass on to the other function @@ -1485,7 +1509,7 @@ estimate (const DH &dof_handler, const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1510,9 +1534,32 @@ estimate (const Mapping &mapping, const std::vector &component_mask_, const Function *coefficients, const unsigned int , - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id_, const unsigned int material_id) { + if (dynamic_cast*> + (&dof_handler.get_tria()) + != 0) + Assert ((subdomain_id_ == types::invalid_subdomain_id) + || + (subdomain_id_ == + dynamic_cast&> + (dof_handler.get_tria()).locally_owned_subdomain()), + ExcMessage ("For parallel distributed triangulations, the only " + "valid subdomain_id that can be passed here is the " + "one that corresponds to the locally owned subdomain id.")); + + const types::subdomain_id_t subdomain_id + = ((dynamic_cast*> + (&dof_handler.get_tria()) + != 0) + ? + dynamic_cast&> + (dof_handler.get_tria()).locally_owned_subdomain() + : + subdomain_id_); + + const unsigned int n_components = dof_handler.get_fe().n_components(); // sanity checks @@ -1610,7 +1657,7 @@ estimate (const Mapping &mapping, for (typename DH::active_cell_iterator cell=dof_handler.begin_active(); cell!=dof_handler.end(); ++cell, ++present_cell) - if ( ((subdomain_id == numbers::invalid_unsigned_int) + if ( ((subdomain_id == types::invalid_subdomain_id) || (cell->subdomain_id() == subdomain_id)) && @@ -1660,7 +1707,7 @@ estimate (const Mapping &mapping, const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { // forward to the function with the QCollection @@ -1682,7 +1729,7 @@ void KellyErrorEstimator::estimate (const DH const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1703,7 +1750,7 @@ void KellyErrorEstimator::estimate (const DH const std::vector &component_mask, const Function *coefficients, const unsigned int n_threads, - const unsigned int subdomain_id, + const types::subdomain_id_t subdomain_id, const unsigned int material_id) { Assert (DEAL_II_COMPAT_MAPPING, ExcCompatibility("mapping")); @@ -1806,6 +1853,10 @@ INSTANTIATE(PETScWrappers::BlockVector,DoFHandler); INSTANTIATE(PETScWrappers::Vector,hp::DoFHandler); INSTANTIATE(PETScWrappers::BlockVector,hp::DoFHandler); + +INSTANTIATE(PETScWrappers::MPI::Vector,DoFHandler); +INSTANTIATE(PETScWrappers::MPI::BlockVector,DoFHandler); + #endif #ifdef DEAL_II_USE_TRILINOS diff --git a/deal.II/deal.II/source/numerics/solution_transfer.cc b/deal.II/deal.II/source/numerics/solution_transfer.cc index a9a64caa2b..ceddc927c3 100644 --- a/deal.II/deal.II/source/numerics/solution_transfer.cc +++ b/deal.II/deal.II/source/numerics/solution_transfer.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -32,7 +32,8 @@ DEAL_II_NAMESPACE_OPEN template -SolutionTransfer::SolutionTransfer(const DH &dof): +SolutionTransfer::SolutionTransfer(const DH &dof) + : dof_handler(&dof, typeid(*this).name()), n_dofs_old(0), prepared_for(none) @@ -67,9 +68,9 @@ void SolutionTransfer::clear () template void SolutionTransfer::prepare_for_pure_refinement() -{ +{ Assert(prepared_for!=pure_refinement, ExcAlreadyPrepForRef()); - Assert(prepared_for!=coarsening_and_refinement, + Assert(prepared_for!=coarsening_and_refinement, ExcAlreadyPrepForCoarseAndRef()); clear(); @@ -84,7 +85,7 @@ void SolutionTransfer::prepare_for_pure_refinement() typename DH::active_cell_iterator cell = dof_handler->begin_active(), endc = dof_handler->end(); - for (unsigned int i=0; cell!=endc; ++cell, ++i) + for (unsigned int i=0; cell!=endc; ++cell, ++i) { indices_on_cell[i].resize(cell->get_fe().dofs_per_cell); // on each cell store the indices of the @@ -100,7 +101,7 @@ void SolutionTransfer::prepare_for_pure_refinement() } -template +template void SolutionTransfer::refine_interpolate(const VECTOR &in, VECTOR &out) const @@ -122,10 +123,10 @@ SolutionTransfer::refine_interpolate(const VECTOR &in, pointerstruct, cell_map_end=cell_map.end(); - for (; cell!=endc; ++cell) + for (; cell!=endc; ++cell) { pointerstruct=cell_map.find(std::make_pair(cell->level(),cell->index())); - + if (pointerstruct!=cell_map_end) // this cell was refined or not // touched at all, so we can get @@ -160,9 +161,9 @@ SolutionTransfer:: prepare_for_coarsening_and_refinement(const std::vector &all_in) { Assert(prepared_for!=pure_refinement, ExcAlreadyPrepForRef()); - Assert(!prepared_for!=coarsening_and_refinement, + Assert(!prepared_for!=coarsening_and_refinement, ExcAlreadyPrepForCoarseAndRef()); - + const unsigned int in_size=all_in.size(); Assert(in_size!=0, ExcNoInVectorsGiven()); @@ -185,7 +186,7 @@ prepare_for_coarsening_and_refinement(const std::vector &all_in) typename DH::active_cell_iterator act_cell = dof_handler->begin_active(), endc = dof_handler->end(); - for (; act_cell!=endc; ++act_cell) + for (; act_cell!=endc; ++act_cell) { if (act_cell->coarsen_flag_set()) ++n_cells_to_coarsen; @@ -201,7 +202,7 @@ prepare_for_coarsening_and_refinement(const std::vector &all_in) for (; cell!=endc; ++cell) if (!cell->active() && cell->child(0)->coarsen_flag_set()) ++n_coarsen_fathers; - + if (n_cells_to_coarsen) Assert(n_cells_to_coarsen>=2*n_coarsen_fathers, ExcInternalError()); @@ -211,18 +212,18 @@ prepare_for_coarsening_and_refinement(const std::vector &all_in) std::vector > (n_cells_to_stay_or_refine) .swap(indices_on_cell); - + std::vector > > (n_coarsen_fathers, std::vector > (in_size)) .swap(dof_values_on_cell); - + // we need counters for // the 'to_stay_or_refine' cells 'n_sr' and // the 'coarsen_fathers' cells 'n_cf', unsigned int n_sr=0, n_cf=0; - cell = dof_handler->begin(); - for (; cell!=endc; ++cell) + cell = dof_handler->begin(); + for (; cell!=endc; ++cell) { if (cell->active() && !cell->coarsen_flag_set()) { @@ -250,7 +251,7 @@ prepare_for_coarsening_and_refinement(const std::vector &all_in) std::vector >(in_size, Vector(dofs_per_cell)) .swap(dof_values_on_cell[n_cf]); - + for (unsigned int j=0; j &all_in, typename DH::cell_iterator cell = dof_handler->begin(), endc = dof_handler->end(); - for (; cell!=endc; ++cell) + for (; cell!=endc; ++cell) { pointerstruct=cell_map.find(std::make_pair(cell->level(),cell->index())); - + if (pointerstruct!=cell_map_end) { const std::vector * const indexptr @@ -320,7 +321,7 @@ interpolate (const std::vector &all_in, =pointerstruct->second.dof_values_ptr; const unsigned int dofs_per_cell=cell->get_fe().dofs_per_cell; - + // cell stayed or is // refined if (indexptr) diff --git a/deal.II/doc/development/Makefile b/deal.II/doc/development/Makefile index 9ce2b295f7..8fa9834712 100644 --- a/deal.II/doc/development/Makefile +++ b/deal.II/doc/development/Makefile @@ -64,7 +64,8 @@ makefiles.html: Makefile makefiles.1.html $D/common/Make.global_options makefile @echo '
  • INCLUDE=$(INCLUDE)' >> $@ @echo '
  • CXXFLAGS.g=$(CXXFLAGS.g)' >> $@ @echo '
  • CXXFLAGS.o=$(CXXFLAGS.o)' >> $@ - @echo '
  • CFLAGS=$(CFLAGS)' >> $@ + @echo '
  • CFLAGS.g=$(CFLAGS.g)' >> $@ + @echo '
  • CFLAGS.o=$(CFLAGS.o)' >> $@ @echo '
  • F77FLAGS.g=$(F77FLAGS.g)' >> $@ @echo '
  • F77FLAGS.o=$(F77FLAGS.o)' >> $@ @echo '
  • LDFLAGS=$(LDFLAGS)' >> $@ diff --git a/deal.II/doc/development/makefiles.1.html b/deal.II/doc/development/makefiles.1.html index ece06b10e5..3fc1566bb4 100644 --- a/deal.II/doc/development/makefiles.1.html +++ b/deal.II/doc/development/makefiles.1.html @@ -471,12 +471,16 @@

    -
    CCFLAGS
    +
    CCFLAGS.g

    - C compiler flags. Since we only compiler C code for - third-party libraries for which we assume that they and our - interfaces to them are bug free, these flags are always for - optimized mode. + C compiler flags for debug mode. +

    +
    + + +
    CCFLAGS.o
    +

    + C compiler flags for optimized mode.

    diff --git a/deal.II/doc/doxygen/headers/distributed.h b/deal.II/doc/doxygen/headers/distributed.h new file mode 100644 index 0000000000..2c5df2d112 --- /dev/null +++ b/deal.II/doc/doxygen/headers/distributed.h @@ -0,0 +1,364 @@ +//------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2009, 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. +// +//------------------------------------------------------------------------- + +/** + * @defgroup distributed Parallel computing with multiple processors using distributed memory + * @ingroup Parallel + * + * @brief A module discussing the use of parallelism on distributed memory + * clusters. + * + *

    Overview

    + * + * deal.II can use multiple machine connected via MPI to parallelize + * computations, in addition to the parallelization within a shared + * memory machine discussed in the @ref threads module. There are + * essentially two ways to utilize multiple machines: + * + * - Each machine keeps the entire mesh and DoF handler locally, but + * only a share of the global matrix, sparsity pattern, and solution + * vector is stored on each machine. + * - The mesh and DoFhandler are also distributed, i.e. each processor + * stores only a share of the cells and degrees of freedom. No + * processor has knowledge of the entire mesh, matrix, or solution, + * and in fact problems solved in this mode are usually so large + * (say, 100s of millions to billions of degrees of freedom) that no + * processor can or should store even a single solution vector. + * + * The first of these two options is relatively straightforward + * because most of the things one wants to do in a finite element + * program still work in essentially the same way, and handling + * distributed matrices, vectors, and linear solvers is something for + * which good external libraries such as @ref SoftwareTrilinos or @ref + * SoftwarePETSc exist that can make things look almost exactly the + * same as they would if everything was available locally. The use of + * this mode of parallelization is explained in the tutorial programs + * step-17, and step-18 and will not be discussed here in more detail. + * + * The use of truly distributed meshes is somewhat more complex because it + * changes or makes impossible some of the things that can otherwise be done + * with deal.II triangulations, DoF handlers, etc. This module documents these + * issues with a vantage point at 50,000 ft above ground without going into + * too many details. All the algorithms described below are implement in + * classes and functions in namespace parallel::distributed. + * + * + *

    Other resources

    + * + * A complete discussion of the algorithms used in this namespace, as well as + * a thorough description of many of the terms used here, can be found in the + * @ref distributed_paper "Distributed Computing paper". In particular, the + * paper shows that the methods discussed in this module scale to thousands of + * processors and well over a billion degrees of freedom (they may scale to + * even bigger problems but at the time of writing this, we do not have + * solvers that are capable of more than $2^{31}$ degrees of freedom due to + * the use of signed int as index). The paper also gives a + * concise definition of many of the terms that are used here and in other + * places of the library related to distributed computing. The step-40 + * tutorial program shows an application of the classes and methods of this + * namespace to the Laplace equation, while step-32 extends the step-31 + * program to massively parallel computations and thereby explains the use of + * the topic discussed here to more complicated applications. + * + * + *

    Distributed triangulations

    + * + * In %parallel %distributed mode, objects of type + * parallel::distributed::Triangulation on each processor only store + * a subset of cells. In particular, the global mesh can be thought of + * as decomposed so that each MPI process "owns" a number of + * cells. The mesh each process then stores locally consists of + * exactly those cells that it owns, as well as one layer of @ref + * GlossGhostCell "ghost cells" around the ones it locally owns, and a + * number of cells we call @ref GlossArtificialCell "artificial". The + * latter are cells that ensure that each processor has a mesh that + * has all the coarse level cells and that respects the invariant that + * neighboring cells can not differ by more than one level of + * refinement. The following pictures show such a mesh, %distributed + * across four processors, and the collection of cells each of these + * processors stores locally: + * + * + * + * + * + * + * + * + * + * + *
    @image html distributed_mesh_0.png @image html distributed_mesh_1.png
    @image html distributed_mesh_2.png @image html distributed_mesh_3.png
    + * + * The cells are colored based on the @ref GlossSubdomainId "subdomain id", + * which identifies which processor owns a cell: turquoise for + * processor 0, green for processor 1, yellow for processor 2, and red + * for processor 3. As can be seen, each process has one layer of + * ghost cells around its own cells, which are correctly colored by + * the subdomain id that identifies the processor that owns each of + * these cells. Note also how each processor stores a number of + * artificial cells, indicated in blue, that only exist to ensure that + * each processor knows about all coarse grid cells and that the + * meshes have the 2:1 refinement property; however, in the area + * occupied by these artificial cells, a processor has no knowledge + * how refined the mesh there really is, as these are areas that are + * owned by other processors. As a consequence, all algorithms we will + * develop can only run over the locally owned cells and if necessary + * the ghost cells; trying to access data on any of the artificial + * cells is most likely an error. Note that we can determine whether + * we own a cell by testing that cell-@>subdomain_id() == + * triangulation.locally_owned_subdomain(). + * + * The "real" mesh one has to think of here is the one that would + * result from forming the union of cells each of the processes own, + * i.e. from the overlap of the turquoise, green, yellow and red + * areas, disregarding the blue areas. + * + * + *

    Distributed degree of freedom handler

    + * + * The DoFHandler class builds on the Triangulation class, but it can + * detect whenever we actually use an object of type + * parallel::distributed::Triangulation as triangulation. In that + * case, it assigns global %numbers for all degrees of freedom that + * exist, given a finite element, on the global mesh, but each + * processor will only know about those that are defined on locally + * relevant cells (i.e. cells either locally owned or that are ghost + * cells). Internally, the algorithm essentially works by just looping + * over all cells we own locally and assigning DoF indices to the + * degrees of freedom defined on them and, in the case of degrees of + * freedom at the interface between subdomains owned by different + * processors, that are not owned by the neighboring processor. All + * processors then exchange how many degrees of freedom they locally + * own and shift their own indices in such a way that every degree of + * freedom on all subdomains are uniquely identified by an index + * between zero and DoFHandler::n_dofs() (this function returns the + * global number of degrees of freedom, accumulated over all + * processors). Note that after this step, the degrees of freedom + * owned by each process form a contiguous range that can, for + * example, be obtained by the contiguous index set returned by + * DoFHandler::locally_owned_dofs(). After + * assigning unique indices to all degrees of freedom, the + * DoFHandler::distribute_dofs() function then + * loops over all ghost cells and communicates with neighboring + * processors to ensure that the global indices of degrees of freedom + * on these ghost cells match the ones that the neighbor has assigned + * to them. + * + * Through this scheme, we can make sure that each cell we locally own + * as well as all the ghost cells can be asked to yield the globally + * correct indices for the degrees of freedom defined on + * them. However, asking for degrees of freedom on artificial cells is + * likely going to lead to nothing good, as no information is + * available for these cells (in fact, it isn't even known whether + * these cells are active on the global mesh, or are further refined). + * + * As usual, degrees of freedom can be renumbered after being enumerated, + * using the functions in namespace DoFRenumbering. + * + * + *

    Linear systems for %distributed computations

    + * + * One thing one learns very quickly when working with very large + * numbers of processors is that one can not store information about + * every degree of freedom on each processor, even if this information + * is "this degree of freedom doesn't live here". An example for this + * is that we can create an object for a (compressed) sparsity pattern + * that has DoFHandler::n_dofs() rows, + * but for which we fill only those rows that correspond to the + * DoFHandler::n_locally_owned_dofs() locally + * owned degrees of freedom. The reason is simple: for the sake of + * example, let's assume we have 1 billion degrees of freedom + * distributed across 100 processors; if we even only hold 16 bytes + * per line in this sparsity pattern (whether we own the corresponding + * DoF or not), we'll need 16 GB for this object even if every single + * line is empty. Of course, only 10 million lines will be non-empty, + * for which we need 160 MB plus whatever is necessary to store the + * actual column indices of nonzero entries. Let's say we have a + * moderately complex problem with 50 entries per row, for each of + * which we store the column index worth 4 bytes, then we'll need 216 + * bytes for each of the 10 million lines that correspond to the + * degrees of freedom we own, for a total of 2.16 GB. And we'll need + * 16 bytes for each of the 990 million lines that we don't own, for a + * total of 15.840 GB. It is clear that this ratio doesn't become any + * better if we go to even higher %numbers of processors. + * + * The solution to this problem is to really only use any memory at + * all for those parts of the linear system that we own, or need for + * some other reason. For all other parts, we must know that they + * exist, but we can not set up any part of our data structure. To + * this end, there exists a class called IndexSet that denotes a set + * of indices which we care for, and for which we may have to allocate + * memory. The data structures for sparsity patterns, constraint + * matrices, matrices and vector can be initialized with these + * IndexSet objects to really only care for those rows or entries that + * correspond to indices in the index set, and not care about all + * others. These objects will then ask how many indices exist in the + * set, allocate memory for each one of them (e.g. initialize the data + * structures for a line of a sparsity pattern), and when you want to + * access data for global degree of freedom i you will be + * redirected to the result of calling IndexSet::index_within_set() + * with index i instead. Accessing data for elements + * i for which IndexSet::is_element() is false will yield + * an error. + * + * The remaining question is how to identify the set of indices that + * correspond to degrees of freedom we need to worry about on each + * processor. To this end, you can use the + * DoFTools::extract_locally_owned_dofs() function to get at all the + * indices a processor owns. Note that this is a subset of the degrees + * of freedom that are defined on the locally owned cells (since some + * of the degrees of freedom at the interface between two different + * subdomains may be owned by the neighbor). This set of degrees of + * freedom defined on cells we own can be obtained using the function + * DoFTools::extract_locally_active_dofs(). Finally, one + * sometimes needs the set of all degrees of freedom on the locally + * owned subdomain as well as the adjacent ghost cells. This + * information is provided by the + * DoFTools::extract_locally_relevant_dofs() function. + * + * + *
    Sparsity patterns
    + * + * At the time of writing this, the only class equipped to deal with the + * situation just explained is CompressedSimpleSparsityPattern. A version of + * the function CompressedSimpleSparsityPattern::reinit() exists that takes an + * IndexSet argument that indicates which lines of the sparsity pattern to + * allocate memory for. In other words, it is safe to create such an object + * that will report as its size 1 billion, but in fact only stores only as + * many rows as the index set has elements. You can then use the usual + * function DoFTools::make_sparsity_pattern to build the sparsity pattern that + * results from assembling on the locally owned portion of the mesh. The + * resulting object can be used to initialize a PETSc or Trilinos matrix which + * support very large object sizes through completely distributed storage. The + * matrix can then be assembled by only looping over those cells owned by the + * current processor. + * + * The only thing to pay attention to is for which degrees of freedom the + * sparsity needs to store entries. These are, in essence, the ones we could + * possibly store values to in the matrix upon assembly. It is clear that + * these are certainly the locally active degrees of freedom (which live on + * the cells we locally own) but through constraints, it may also be possible + * to write to entries that are located on ghost cells. Consequently, you need + * to pass the index set that results from + * DoFTools::extract_locally_relevant_dofs() upon initializing the sparsity + * pattern. + * + * + *

    Constraints on degrees of freedom

    + * + * When creating the sparsity pattern as well as when assembling the linear + * system, we need to know about constraints on degrees of freedom, for + * example resulting from hanging nodes or boundary conditions. Like the + * CompressedSimpleSparsityPattern class, the ConstraintMatrix can also take + * an IndexSet upon construction that indicates for which of the possibly very + * large number of degrees of freedom it should actually store + * constraints. Unlike for the sparsity pattern, these are now only those + * degrees of freedom which we work on locally when assembling, namely those + * returned by DoFTools::extract_locally_active_dofs() (a superset of the + * locally owned ones). + * + * There are, however, situations where more complicated constraints appear in + * finite element programs. An example is in $hp$ adaptive computations where + * degrees of freedom can be constrained against other degrees of freedom that + * are themselves constrained. In a case like this, in order to fully resolve + * this chain of constraints, it may not be sufficient to only store + * constraints on locally active degrees of freedom but one may also need to + * have constraints available on locally relevant ones. In that case, the + * ConstraintMatrix object needs to be initialized with the IndexSet produced + * by DoFTools::extract_locally_relevant_dofs() . + * + * In general, your program will continue to do something if you happen to not + * store all necessary constraints on each processor: you will just generate + * wrong matrix entries, but the program will not abort. This is opposed to + * the situation of the sparsity pattern: there, if the IndexSet passed to the + * CompressedSimpleSparsityPattern indicates that it should store too few rows + * of the matrix, the program will either abort when you attempt to write into + * matrix entries that do not exist or the matrix class will silently allocate + * more memory to accomodate them. As a consequence, it is useful to err on + * the side of caution when indicating which constraints to store and use the + * result of DoFTools::extract_locally_relevant_dofs() rather than + * DoFTools::extract_locally_active_dofs() . This is also affordable since the + * set of locally relevant degrees of freedom is only marginally larger than + * the set of locally active degrees of freedom. We choose this strategy in + * both step-32 and step-40. + * + * + *

    Postprocessing

    + * + * Like everything else, you can only do postprocessing on cells a + * local processor owns. The DataOut and KellyErrorEstimator classes + * do this automatically: they only operate on locally owned cells + * without the need to do anything in particular. At least for large + * computations, there is also no way to merge the results of all + * these local computations on a single machine, i.e. each processor + * has to be self-sufficient. For example, each processor has to + * generate its own parallel output files that have to be visualizated + * by a program that can deal with multiple input files rather than + * merging the results of calling DataOut to a single processor before + * generating a single output file. The latter can be achieved, for + * example, using the DataOutBase::write_vtu() and + * DataOutBase::write_pvtu_record() functions. + * + * These same considerations hold for all other postprocessing actions + * as well: while it is, for example, possible to compute a global + * energy dissipation rate by doing the computations locally and + * accumulating the resulting single number processor to a single + * number for the entire communication, it is in general not possible + * to do the same if the volume of data produced by every processor is + * significant. + * + * There is one particular consideration for postprocessing, however: whatever + * you do on each cell a processor owns, you need access to at least all those + * values of the solution vector that are active on these cells (i.e. to the + * set of all locally active degrees of freedom, in the language of the + * @ref distributed_paper "Distributed Computing paper"), which is a superset + * of the degrees of freedom this processor actually owns (because it may not + * own all degrees of freedom on the interface between its own cells and those + * cells owned by other processors). Sometimes, however, you need even more + * information: for example, to compute the KellyErrorIndicator results, one + * needs to evaluate the gradient at the interface on the current as well as + * its neighbor cell; the latter may be owned by another processor, so we need + * those degrees of freedom as well. In general, therefore, one needs access + * to the solution values for all degrees of freedom that are locally + * relevant. On the other hand, both of the packages we can use for + * parallel linear algebra (PETSc and Trilinos) subdivide vectors into chunks + * each processor owns and chunks stored on other processors. To postprocess + * stuff therefore means that we have to tell PETSc or Trilinos that it should + * also import ghost elements, i.e. additional vector elements of the + * solution vector other than the ones we store locally. Both the + * PETScWrappers::MPI::Vector and TrilinosWrappers::MPI::Vector class support + * specifying this information (see step-40 and step-32, respectively) through + * the PETScWrappers::MPI::Vector::update_ghost_values() function or, in the + * case of Trilinos, construction of a vector with an the locally relevant + * degrees of freedom index set. + */ + + + +namespace parallel +{ + /** + * A namespace for class and + * functions that support %parallel + * computing on %distributed memory + * machines. See the @ref + * distributed module for an + * overview of the facilities this + * namespace offers. + * + * @ingroup distributed + */ + namespace distributed + { + } +} diff --git a/deal.II/doc/doxygen/headers/glossary.h b/deal.II/doc/doxygen/headers/glossary.h index d1a395fae5..c349e47699 100644 --- a/deal.II/doc/doxygen/headers/glossary.h +++ b/deal.II/doc/doxygen/headers/glossary.h @@ -426,13 +426,14 @@ *
    The "distributed computing paper" is a paper by W. Bangerth, * C. Burstedde, T. Heister and M. Kronbichler titled "Algorithms and Data * Structures for Massively Parallel Generic Finite Element Codes" that - * described the implementation of parallel distributed computing in deal.II, + * describes the implementation of parallel distributed computing in deal.II, * i.e. computations where not only the linear system is split onto different * machines as in, for example, step-18, but also the Triangulation and * DoFHandler objects. In essence, it is a guide to the parallel::distributed * namespace. * - * The paper is currently in preparation. + * The paper is submitted to the ACM Transactions on Mathematical Software + * (ACM TOMS). *
    * * @@ -608,21 +609,21 @@ Article{JK10, * polynomials. Let us give some examples: * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * *
    ElementFunction spaceNode values
    FE_Q, FE_DGQQkvalues in support points
    FE_DGPPkmoments with respect to Legendre polynomials
    FE_RaviartThomas (2d)Qk+1,k x Qk,k+1moments on edges and in the interior
    FE_RaviartThomasNodalQk+1,k x Qk,k+1Gauss points on edges(faces) and anisotropic Gauss points in the interior
    ElementFunction spaceNode values
    FE_Q, FE_DGQQkvalues in support points
    FE_DGPPkmoments with respect to Legendre polynomials
    FE_RaviartThomas (2d)Qk+1,k x Qk,k+1moments on edges and in the interior
    FE_RaviartThomasNodalQk+1,k x Qk,k+1Gauss points on edges(faces) and anisotropic Gauss points in the interior
    * *
    @anchor GlossPrimitive Primitive finite @@ -633,7 +634,7 @@ Article{JK10, * a vector-valued element has exactly one nonzero component if an element is * primitive. This includes, in particular, all scalar elements as well as * vector-valued elements assembled via the FESystem class from other - * primitive (for example scalar) elements as shown in step-8, + * primitive (for example scalar) elements as shown in step-8, * step-29, step-22 and several others. On the other hand, * the FE_RaviartThomas class used in step-20 and step-21, or the FE_Nedelec * class provide non-primitive finite elements because there, each @@ -660,16 +661,15 @@ Article{JK10, * cell is associated with. * * For programs that are parallelized based on MPI but where each processor - * stores the entire triangulation (as in, for example, step-18 - * or step-32, subdomain ids are assigned to cells by + * stores the entire triangulation (as in, for example, step-18, but not in + * step-32), subdomain ids are assigned to cells by * partitioning a mesh, and each MPI process then only works on those cells it - * "owns", i.e. that belong to a subdomain that it is associated with + * "owns", i.e. that belong to a subdomain that the processor is associated with * (traditionally, this is the case for the subdomain id whose numerical value * coincides with the rank of the MPI process within the MPI * communicator). Partitioning is typically done using the * GridTools::partition() function, but any other method can also be used to - * do this though most simple ideas will likely lead to less well balanced - * numbers of degrees of freedom on the various subdomains. + * do this. * * On the other hand, for programs that are parallelized using MPI but * where meshes are held distributed across several processors using diff --git a/deal.II/doc/doxygen/headers/parallel.h b/deal.II/doc/doxygen/headers/parallel.h index 7189a61040..b61f9f58ea 100644 --- a/deal.II/doc/doxygen/headers/parallel.h +++ b/deal.II/doc/doxygen/headers/parallel.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2009, 2010 by the deal.II authors +// Copyright (C) 2009 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -17,9 +17,7 @@ * @brief A module discussing the use of multiple processor. * * This module contains information on %parallel computing. It is - * subdivided into parts on @ref threads and on distributed - * computing. The latter part will become available in a future - * public release. + * subdivided into parts on @ref threads and on @ref distributed. */ @@ -27,7 +25,8 @@ * A namespace in which we define classes and algorithms that deal * with running in %parallel on shared memory machines when deal.II is * configured to use multiple threads (see @ref threads), as well as - * running things in %parallel on %distributed memory machines. + * running things in %parallel on %distributed memory machines (see @ref + * distributed). * * @ingroup threads * @author Wolfgang Bangerth, 2008, 2009 diff --git a/deal.II/doc/doxygen/images/distributed_mesh_0.png b/deal.II/doc/doxygen/images/distributed_mesh_0.png new file mode 100644 index 0000000000000000000000000000000000000000..0975c9a7c18ee9d8086ab6aeb2faa3a05a0627f9 GIT binary patch literal 41469 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 literal 0 HcmV?d00001 diff --git a/deal.II/doc/doxygen/images/distributed_mesh_1.png b/deal.II/doc/doxygen/images/distributed_mesh_1.png new file mode 100644 index 0000000000000000000000000000000000000000..12864ae955be4d267e9a06df90a0f7f084b776b0 GIT binary patch literal 52087 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 z

    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 literal 0 HcmV?d00001 diff --git a/deal.II/doc/doxygen/images/distributed_mesh_3.png b/deal.II/doc/doxygen/images/distributed_mesh_3.png new file mode 100644 index 0000000000000000000000000000000000000000..9b97ad2d8394a60ccf86b30afec8ef282bfe2486 GIT binary patch literal 37749 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

    4H=hy%BingWE|*l$6iW%dB~yq72Ei>Pg;y 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%0Ds2f#yhlqB(_uI=bQ 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 literal 0 HcmV?d00001 diff --git a/deal.II/doc/doxygen/tutorial/Makefile b/deal.II/doc/doxygen/tutorial/Makefile index a2641da2bf..64c314d503 100644 --- a/deal.II/doc/doxygen/tutorial/Makefile +++ b/deal.II/doc/doxygen/tutorial/Makefile @@ -44,7 +44,7 @@ all: $(example-doxygen) toc.html # # make steps.png # -# by hand. This way, we avoid testing for neato during +# by hand. This way, we avoid testing for dot during # configuration. The map only needs to be generated after changes in # the structure of the tutorials. toc.html: steps.cmapx toc.html.in @@ -52,13 +52,13 @@ toc.html: steps.cmapx toc.html.in @perl make_toc.pl > toc.html # generate dot file -steps.dot: steps.pl $D/examples/*/doc/tooltip +steps.dot: steps.pl $D/examples/*/doc/tooltip $D/examples/*/doc/builds-on @echo ================== Making $@ @perl $< > $@ steps.png: steps.dot @echo ================== Making $@ - @neato -Tpng -Tcmapx -O $< + @dot -Tpng -Tcmapx -O $< @mv steps.dot.png steps.png @mv steps.dot.cmapx steps.cmapx diff --git a/deal.II/doc/doxygen/tutorial/steps.cmapx b/deal.II/doc/doxygen/tutorial/steps.cmapx index 45c6f8238b..56f83e58d0 100644 --- a/deal.II/doc/doxygen/tutorial/steps.cmapx +++ b/deal.II/doc/doxygen/tutorial/steps.cmapx @@ -1,38 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deal.II/doc/doxygen/tutorial/steps.pl b/deal.II/doc/doxygen/tutorial/steps.pl index 3b59d4290c..b5701aa516 100644 --- a/deal.II/doc/doxygen/tutorial/steps.pl +++ b/deal.II/doc/doxygen/tutorial/steps.pl @@ -11,18 +11,18 @@ use strict; 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, 33,34,35,36, 39, - 45); + 30,31,32,33,34,35,36, 39, + 40,45); ; # List of additional node attributes to highlight purpose and state of the example my %style = ( - "basic" => ',height=.7,width=.7,shape="octagon",fillcolor="green"', - "techniques" => ',fillcolor="orange"', - "fluids" => ',fillcolor="yellow"', - "solids" => ',fillcolor="lightblue"', - "time dependent" => ',fillcolor="blue"', - "unfinished" => ',style="dashed"' + "basic" => ',height=.8,width=.8,shape="octagon",fillcolor="green"', + "techniques" => ',height=.35,width=.35,fillcolor="orange"', + "fluids" => ',height=.25,width=.25,fillcolor="yellow"', + "solids" => ',height=.25,width=.25,fillcolor="lightblue"', + "time dependent" => ',height=.25,width=.25,fillcolor="blue"', + "unfinished" => ',height=.25,width=.25,style="dashed"' ); diff --git a/deal.II/doc/doxygen/tutorial/steps.png b/deal.II/doc/doxygen/tutorial/steps.png index 0894d21d1575f75147971ce934a9ec62eae33f47..44435ded1ad7d0e0d7414beb72d25d229abaec38 100644 GIT binary patch literal 77138 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 literal 51820 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

    rA+L${x1ZQ7ILH*=GwGtn5GBtXECC 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

    EFo6t`s5)JTK>P7*P+*2aON500{{ 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 " +} +function bdie () { + echo "Error: $@" + exit 1 +} + +if test -z "$CFLAGS" -a -z "$P4EST_CFLAGS_FAST" ; then + export CFLAGS_FAST="-O2" +else + export CFLAGS_FAST="$CFLAGS $P4EST_CFLAGS_FAST" +fi +echo "CFLAGS_FAST: $CFLAGS_FAST" +if test -z "$CFLAGS" -a -z "$P4EST_CFLAGS_DEBUG" ; then + export CFLAGS_DEBUG="-O0 -g" +else + export CFLAGS_DEBUG="$CFLAGS $P4EST_CFLAGS_DEBUG" +fi +echo "CFLAGS_DEBUG: $CFLAGS_DEBUG" + +TGZ="$1"; shift +if test ! -f "$TGZ" ; then + busage + bdie "File not found" +fi +if ! (echo "$TGZ" | grep -q 'p4est.*.tar.gz') ; then + busage + bdie "File name mismatch" +fi + +# choose names for fast and debug installation directories +INSTALL_DIR=$1 +shift +if test -z "$INSTALL_DIR" ; then + INSTALL_FAST="$UNPACK/p4est-install/FAST" + INSTALL_DEBUG="$UNPACK/p4est-install/DEBUG" +else + INSTALL_FAST="$INSTALL_DIR/FAST" + INSTALL_DEBUG="$INSTALL_DIR/DEBUG" +fi + +echo +echo "This script tries to unpack, configure and build the p4est library." +echo "Build FAST: $BUILD_FAST" +echo "Build DEBUG: $BUILD_DEBUG" +echo "Install FAST: $INSTALL_FAST" +echo "Install DEBUG: $INSTALL_DEBUG" +echo "Checking environment: CFLAGS P4EST_CFLAGS_FAST P4EST_CFLAGS_DEBUG" + + +if test -d $UNPACK/p4est-build ; then + rm -rf $UNPACK/p4est-build +fi + +DIR=`echo "$TGZ" | sed 's/\(p4est.*\).tar.gz/\1/'` +DIR=`basename $DIR` +echo "Unpack directory: $UNPACK/$DIR" +if test -d "$UNPACK/$DIR" ; then + echo \ + "Directory found (remove it and also the build directories" \ + "to start over)" +else + echo -n "Unpacking... " + tar -xvz -f "$TGZ" -C "$UNPACK" >/dev/null + echo "done" +fi +test -f "$UNPACK/$DIR/src/p4est.h" || bdie "Main header file missing" +test -f "$UNPACK/$DIR/configure" || bdie "Configure script missing" + +echo +echo "See output in files .../config.output and .../make.output" +echo "Build FAST version in $BUILD_FAST" +mkdir -p "$BUILD_FAST" +cd "$BUILD_FAST" +("$UNPACK/$DIR/configure" --enable-mpi --enable-shared --disable-vtk-binary --without-blas \ + --prefix="$INSTALL_FAST" CFLAGS="$CFLAGS_FAST" \ + "$@" || bdie "Error in configure" ) | tee config.output +(make -C sc -j 8 || bdie "Error in make sc") | tee make.output +(make src/libp4est.la -j 8 \ + || bdie "Error in make p4est") | tee -a make.output +(make install || bdie "Error in make install") | tee -a make.output +echo "FAST version installed in $INSTALL_FAST" + +echo +echo "Build DEBUG version in $BUILD_DEBUG" +mkdir -p "$BUILD_DEBUG" +cd "$BUILD_DEBUG" +if test -z "$CFLAGS" ; then + export CFLAGS="-g -O0" +fi +("$UNPACK/$DIR/configure" --enable-mpi --enable-shared --disable-vtk-binary --without-blas --enable-debug \ + --prefix="$INSTALL_DEBUG" CFLAGS="$CFLAGS_DEBUG" \ + "$@" || bdie "Error in configure") | tee config.output +(make -C sc -j 8 || bdie "Error in make sc") | tee make.output +(make src/libp4est.la -j 8 \ + || bdie "Error in make p4est") | tee -a make.output +(make install || bdie "Error in make install") | tee -a make.output +echo "DEBUG version installed in $INSTALL_DEBUG" +echo diff --git a/deal.II/doc/external-libs/p4est.html b/deal.II/doc/external-libs/p4est.html new file mode 100644 index 0000000000..a18ef34ef1 --- /dev/null +++ b/deal.II/doc/external-libs/p4est.html @@ -0,0 +1,62 @@ + + + + + The deal.II Readme + + + + + + + +

    Using and installing instructions for the p4est library

    + +

    + p4est is a + library that manages meshes that are distributed across multiple + processors. It forms the basis of deal.II's implementation of + finite element solvers that can use meshes that are too large to + be held on each processor individually. +

    + +

    + You need to install p4est before deal.II. To do so, you can + download it + from here, copy it to a fresh directory + into which you should also copy + the p4est-setup.sh script. Then + call the script as follows: + +

    +	  ./p4est-setup.sh p4est-x-y-z.tar.gz /path/to/installation
    +	
    + + where p4est-x-y-z.tar.gz is the name of the p4est + distribution file, and /path/to/installation is a + directory into which you want to install p4est. +

    + +

    + After this, you need to configure and build deal.II using a line + like + +

    +	  ./configure --with-p4est=/path/to/installation --enable-mpi
    +	  make all
    +	
    + + Obviously, you can also add additional flags + to ./configure as described in the + general ReadMe file. +

    + +
    + +
    + The deal.II mailing list +
    + + diff --git a/deal.II/doc/readme.html b/deal.II/doc/readme.html index 5718222251..7d00c4e0a8 100644 --- a/deal.II/doc/readme.html +++ b/deal.II/doc/readme.html @@ -471,30 +471,34 @@ processor cores. If this is not desired, use --disable-threads. +
  • +

    + --enable-mpi: If given this + flag, ./configure chooses mpiCC + and mpicc as the C++ and C compilers, respectively, + unless the $CXX and $CC environment + variables have specifically been set to something else. If these + compilers exist and indeed support MPI, then this also switches + on support for MPI in the library. +

    +
  • --with-cpu=...: Enable specific optimization flags for a particular processor. Programs compiled with these options - might not execute on an other system, but will be faster by up to - 30 percent on the CPU selected. + might not execute on an other system, but may be faster on the + particular CPU selected.

    For a complete list of supported values of this switch, you may take a look at the file aclocal.m4 in the top-level directory. However, the most commonly used value - is --with-cpu=native indicating that the compiler + is --with-cpu=native, indicating that the compiler should determine the CPU we are running on by itself and optimize for it.

    -

    - Optimizations currently only work in conjunction with gcc. Without the - option --with-cpu=xxx, no cpu specific optimization is applied. - Furthermore, CPU specific optimization is switched on only for the compilation - of the optimized library versions. -

    -
  • --with-doxygen=...: Select the specified executable @@ -657,6 +661,21 @@

    + +
    p4est
    +
    +

    + p4est is a library that deal.II uses to + distribute very large meshes across multiple processors (think + meshes with a billion cells on 10,000 processors). Using and + installing p4est is discussed here. + To configure deal.II with p4est, you will + need to use the --with-path=/path/to/path + switch to the ./configure script. +

    +
    +
    BLAS, LAPACK
    @@ -730,10 +749,10 @@
    MUltifrontal Massively Parallel sparse direct Solver (MUMPS)
    - It is possible to make use some subroutines that make use of the + It is possible to make use some subroutines that make use of the MUltifrontal Massively Parallel sparse direct Solver - (MUMPS). For a detailed description of how to compile MUMPS (and + target="_top">MUltifrontal Massively Parallel sparse direct Solver + (MUMPS). For a detailed description of how to compile MUMPS (and some dependencies) and linking with deal.II, see this page.
    @@ -754,7 +773,7 @@
    ARPACK
    - There is a wrapper for the + There is a wrapper for the ARPACK library, which has its own license; if you want to use it with deal.II, please read it and make sure that you agree with it. diff --git a/deal.II/examples/step-18/doc/builds-on b/deal.II/examples/step-18/doc/builds-on index e820585ca6..1b9190dc94 100644 --- a/deal.II/examples/step-18/doc/builds-on +++ b/deal.II/examples/step-18/doc/builds-on @@ -1 +1 @@ -step-17 +step-17 step-19 diff --git a/deal.II/examples/step-32/Makefile b/deal.II/examples/step-32/Makefile index 770b78a94b..fce563c47d 100644 --- a/deal.II/examples/step-32/Makefile +++ b/deal.II/examples/step-32/Makefile @@ -30,7 +30,7 @@ D = ../../ # 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 +clean-up-files = *gmv *gnuplot *gpl *eps *pov *vtk *d2 diff --git a/deal.II/examples/step-32/doc/builds-on b/deal.II/examples/step-32/doc/builds-on index 11e000dda8..63490605a0 100644 --- a/deal.II/examples/step-32/doc/builds-on +++ b/deal.II/examples/step-32/doc/builds-on @@ -1 +1 @@ -step-31 step-17 +step-31 step-17 step-40 diff --git a/deal.II/examples/step-32/step-32.cc b/deal.II/examples/step-32/step-32.cc index 9ca65c24a7..6eaac2ef88 100644 --- a/deal.II/examples/step-32/step-32.cc +++ b/deal.II/examples/step-32/step-32.cc @@ -1,6 +1,7 @@ //TODO: - adjust stopping criteria for solvers // - better refinement at the start? // - check solver stability +// - Q2 Mapping useful? /* $Id$ */ @@ -31,12 +32,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -74,11 +77,86 @@ // matrices. #include +#include +#include +#include +#include + +#include "timeblock.h" + + + + #include + #include + #include + #include + +class BadConversion : public std::runtime_error { + public: + BadConversion(std::string const& s) + : std::runtime_error(s) + { } +}; + +inline std::string stringify(double x) +{ + std::ostringstream o; + if (!(o << x)) + throw BadConversion("stringify(double)"); + return o.str(); +} + + + +#define CONF_LOG_SOLVER +//#define CONF_COMPARE_SOLVER +//#define CONF_BENCHMARK - // Next, we import all deal.II - // names into global namespace: using namespace dealii; +void print_it(Utilities::System::MinMaxAvg & result) +{ + std::cout// << "sum: " << result.sum + << " avg: " << (long)result.avg/1024 + << " min: " << (long)result.min/1024 << " @" << result.min_index + << " max: " << (long)result.max/1024 << " @" << result.max_index + << std::endl; +} + +void print_memory_stats() +{ + int myid = Utilities::System::get_this_mpi_process(MPI_COMM_WORLD); + Utilities::System::MemoryStats stats; + Utilities::System::get_memory_stats(stats); + Utilities::System::MinMaxAvg r; + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmPeak, r); + if (myid==0) + { + std::cout << "MEM: VmPeak: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmSize, r); + if (myid==0) + { + std::cout << "MEM: VmSize: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmHWM, r); + if (myid==0) + { + std::cout << "MEM: VmHWM: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmRSS, r); + if (myid==0) + { + std::cout << "MEM: VmRSS: "; + print_it(r); + } +} + + +static int out_index=0; // @sect3{Equation data} // In the following namespace, we define the @@ -145,8 +223,17 @@ namespace EquationData const double h = R1-R0; const double s = (r-R0)/h; +// see http://www.wolframalpha.com/input/?i=plot+(sqrt(x^2%2By^2)*0.95%2B0.05*sin(6*atan2(x,y))),+x%3D-1+to+1,+y%3D-1+to+1 - return T1+(T0-T1)*((1-s)*(1-s)); + double s_mod = s*0.95 + 0.05*sin(6.0*atan2(p(0),p(1))); +//alternative: http://www.wolframalpha.com/input/?i=plot+atan((sqrt(x^2%2By^2)*0.95%2B0.05*sin(6*atan2(x,y))-0.5)*10)/pi%2B0.5,+x%3D-1+to+1,+y%3D-1+to+1 +// s_mod = atan((s_mod-0.5)*10.0)/dealii::numbers::PI+0.5; + + return T1+(T0-T1)*(1.0-s_mod); + + //old: +// return T1+(T0-T1)*((1-s)*(1-s)); +// return T1+(T0-T1)*((1-s)); } @@ -224,6 +311,103 @@ namespace EquationData // name. namespace LinearSolvers { + template + class RightPrecond : public Subscriptor + { + public: + RightPrecond ( + const TrilinosWrappers::BlockSparseMatrix &S, + const TrilinosWrappers::BlockSparseMatrix &Spre, + const PreconditionerMp &Mppreconditioner, + const PreconditionerA &Apreconditioner) + : + stokes_matrix (&S), + stokes_preconditioner_matrix (&Spre), + mp_preconditioner (Mppreconditioner), + a_preconditioner (Apreconditioner) + {} + + void solve_S(TrilinosWrappers::MPI::Vector &dst, + const TrilinosWrappers::MPI::Vector &src) const + { + SolverControl cn(5000, 1e-5);//src.l2_norm()*1e-5); + + TrilinosWrappers::SolverBicgstab solver(cn); + + solver.solve(stokes_preconditioner_matrix->block(1,1), + dst, src, + mp_preconditioner); + +#ifdef CONF_LOG_SOLVER + TrilinosWrappers::MPI::Vector res(src); + double resid = + stokes_preconditioner_matrix->block(1,1).residual(res,dst,src); + + double srcl2 = src.l2_norm(); +#endif + dst*=-1.0; + +#ifdef CONF_LOG_SOLVER + int myid=Utilities::System::get_this_mpi_process(MPI_COMM_WORLD); + if (myid==0) + { + std::cout << " solve_S it=" << cn.last_step() + << " startres=" << stringify(srcl2) + << " res=" << stringify(resid) + << " reduction=" << stringify(resid / srcl2) + << std::endl; + + } +#endif + + } + + void solve_A(TrilinosWrappers::MPI::Vector &dst, + const TrilinosWrappers::MPI::Vector &src) const + { + SolverControl cn(5000, src.l2_norm()*1e-4); + TrilinosWrappers::SolverBicgstab solver(cn); + solver.solve(stokes_matrix->block(0,0), dst, src, a_preconditioner); + +#ifdef CONF_LOG_SOLVER + double srcl2 = src.l2_norm(); + TrilinosWrappers::MPI::Vector res(src); + double resid = stokes_matrix->block(0,0).residual(res,dst,src); + + int myid=Utilities::System::get_this_mpi_process(MPI_COMM_WORLD); + if (myid==0) + { + std::cout << " solve_A it=" << cn.last_step() + << " startres=" << stringify(srcl2) + << " res=" << stringify(resid) + << " reduction=" << stringify(resid / srcl2) + << std::endl; + + } +#endif + } + + void vmult (TrilinosWrappers::MPI::BlockVector &dst, + const TrilinosWrappers::MPI::BlockVector &src) const + { + TrilinosWrappers::MPI::Vector utmp(src.block(0)); + + solve_S(dst.block(1), src.block(1)); + + stokes_matrix->block(0,1).vmult(utmp, dst.block(1)); //B^T + utmp*=-1.0; + utmp.add(src.block(0)); + + solve_A(dst.block(0), utmp); + } + + private: + const SmartPointer stokes_matrix; + const SmartPointer stokes_preconditioner_matrix; + const PreconditionerMp &mp_preconditioner; + const PreconditionerA &a_preconditioner; + }; + template class BlockSchurPreconditioner : public Subscriptor { @@ -801,7 +985,7 @@ class BoussinesqFlowProblem { public: BoussinesqFlowProblem (); - void run (); + void run (unsigned int ref); private: void setup_dofs (); @@ -834,34 +1018,34 @@ class BoussinesqFlowProblem ConditionalOStream pcout; - Triangulation triangulation; + parallel::distributed::Triangulation triangulation; double global_Omega_diameter; const unsigned int stokes_degree; FESystem stokes_fe; - DoFHandler stokes_dof_handler; + DoFHandler stokes_dof_handler; ConstraintMatrix stokes_constraints; TrilinosWrappers::BlockSparseMatrix stokes_matrix; TrilinosWrappers::BlockSparseMatrix stokes_preconditioner_matrix; - TrilinosWrappers::BlockVector stokes_solution; - TrilinosWrappers::BlockVector old_stokes_solution; + TrilinosWrappers::MPI::BlockVector stokes_solution; + TrilinosWrappers::MPI::BlockVector old_stokes_solution; TrilinosWrappers::MPI::BlockVector stokes_rhs; const unsigned int temperature_degree; FE_Q temperature_fe; - DoFHandler temperature_dof_handler; + DoFHandler temperature_dof_handler; ConstraintMatrix temperature_constraints; TrilinosWrappers::SparseMatrix temperature_mass_matrix; TrilinosWrappers::SparseMatrix temperature_stiffness_matrix; TrilinosWrappers::SparseMatrix temperature_matrix; - TrilinosWrappers::Vector temperature_solution; - TrilinosWrappers::Vector old_temperature_solution; - TrilinosWrappers::Vector old_old_temperature_solution; + TrilinosWrappers::MPI::Vector temperature_solution; + TrilinosWrappers::MPI::Vector old_temperature_solution; + TrilinosWrappers::MPI::Vector old_old_temperature_solution; TrilinosWrappers::MPI::Vector temperature_rhs; @@ -977,7 +1161,11 @@ BoussinesqFlowProblem::BoussinesqFlowProblem () get_this_mpi_process(MPI_COMM_WORLD) == 0)), - triangulation (Triangulation::maximum_smoothing), + triangulation (MPI_COMM_WORLD, + typename Triangulation::MeshSmoothing + (Triangulation::smoothing_on_refinement | Triangulation::smoothing_on_coarsening + ) + ), stokes_degree (1), stokes_fe (FE_Q(stokes_degree+1), dim, @@ -1435,8 +1623,9 @@ void BoussinesqFlowProblem::project_temperature_field () cg.solve (temperature_mass_matrix, solution, rhs, preconditioner_mass); - old_temperature_solution = solution; - temperature_constraints.distribute (old_temperature_solution); + temperature_constraints.distribute (solution); +// old_temperature_solution = solution; + old_temperature_solution.reinit(solution, false, true); } @@ -1537,7 +1726,8 @@ void BoussinesqFlowProblem:: else coupling[c][d] = DoFTools::none; - DoFTools::make_sparsity_pattern (stokes_dof_handler, coupling, sp, + DoFTools::make_sparsity_pattern (static_cast&>(stokes_dof_handler), + coupling, sp, stokes_constraints, false, Utilities::System:: get_this_mpi_process(MPI_COMM_WORLD)); @@ -1568,7 +1758,8 @@ void BoussinesqFlowProblem:: else coupling[c][d] = DoFTools::none; - DoFTools::make_sparsity_pattern (stokes_dof_handler, coupling, sp, + DoFTools::make_sparsity_pattern (static_cast&>(stokes_dof_handler), + coupling, sp, stokes_constraints, false, Utilities::System:: get_this_mpi_process(MPI_COMM_WORLD)); @@ -1589,7 +1780,7 @@ void BoussinesqFlowProblem:: TrilinosWrappers::SparsityPattern sp (temperature_partitioner, MPI_COMM_WORLD); - DoFTools::make_sparsity_pattern (temperature_dof_handler, sp, + DoFTools::make_sparsity_pattern (static_cast&>(temperature_dof_handler), sp, temperature_constraints, false, Utilities::System:: get_this_mpi_process(MPI_COMM_WORLD)); @@ -1684,55 +1875,20 @@ template void BoussinesqFlowProblem::setup_dofs () { computing_timer.enter_section("Setup dof systems"); + TimeBlock t(pcout, "**dof_setup", false); + std::vector stokes_sub_blocks (dim+1,0); stokes_sub_blocks[dim] = 1; - - GridTools::partition_triangulation (Utilities::System:: - get_n_mpi_processes(MPI_COMM_WORLD), - triangulation); - { + TimeBlock t(pcout, "***dof_distribute"); + stokes_dof_handler.distribute_dofs (stokes_fe); - DoFRenumbering::subdomain_wise (stokes_dof_handler); DoFRenumbering::component_wise (stokes_dof_handler, stokes_sub_blocks); - stokes_constraints.clear (); - DoFTools::make_hanging_node_constraints (stokes_dof_handler, - stokes_constraints); - - std::vector velocity_mask (dim+1, true); - velocity_mask[dim] = false; - VectorTools::interpolate_boundary_values (stokes_dof_handler, - 0, - ZeroFunction(dim+1), - stokes_constraints, - velocity_mask); - - std::set no_normal_flux_boundaries; - no_normal_flux_boundaries.insert (1); - VectorTools::compute_no_normal_flux_constraints (stokes_dof_handler, 0, - no_normal_flux_boundaries, - stokes_constraints); - stokes_constraints.close (); - } - { temperature_dof_handler.distribute_dofs (temperature_fe); - DoFRenumbering::subdomain_wise (temperature_dof_handler); - - temperature_constraints.clear (); - VectorTools::interpolate_boundary_values (temperature_dof_handler, - 0, - EquationData::TemperatureInitialValues(), - temperature_constraints); - VectorTools::interpolate_boundary_values (temperature_dof_handler, - 1, - EquationData::TemperatureInitialValues(), - temperature_constraints); - DoFTools::make_hanging_node_constraints (temperature_dof_handler, - temperature_constraints); - temperature_constraints.close (); } + std::vector stokes_dofs_per_block (2); DoFTools::count_dofs_per_block (stokes_dof_handler, stokes_dofs_per_block, stokes_sub_blocks); @@ -1742,7 +1898,7 @@ void BoussinesqFlowProblem::setup_dofs () n_T = temperature_dof_handler.n_dofs(); pcout << "Number of active cells: " - << triangulation.n_active_cells() + << triangulation.n_global_active_cells() << " (on " << triangulation.n_levels() << " levels)" @@ -1753,20 +1909,80 @@ void BoussinesqFlowProblem::setup_dofs () << std::endl << std::endl; - std::vector stokes_partitioning; - IndexSet temperature_partitioning (n_T); + + + std::vector stokes_partitioning, stokes_relevant_partitioning; + IndexSet temperature_partitioning (n_T), temperature_relevant_partitioning (n_T); + IndexSet stokes_relevant_set; { + TimeBlock t(pcout, "***index_sets"); const unsigned int my_id = Utilities::System::get_this_mpi_process(MPI_COMM_WORLD); - IndexSet stokes_index_set = - DoFTools::dof_indices_with_subdomain_association(stokes_dof_handler, - my_id); + IndexSet stokes_index_set = stokes_dof_handler.locally_owned_dofs(); stokes_partitioning.push_back(stokes_index_set.get_view(0,n_u)); stokes_partitioning.push_back(stokes_index_set.get_view(n_u,n_u+n_p)); - temperature_partitioning = - DoFTools::dof_indices_with_subdomain_association(temperature_dof_handler, - my_id); + DoFTools::extract_locally_relevant_dofs (stokes_dof_handler, + stokes_relevant_set); + stokes_relevant_partitioning.push_back(stokes_relevant_set.get_view(0,n_u)); + stokes_relevant_partitioning.push_back(stokes_relevant_set.get_view(n_u,n_u+n_p)); + + temperature_partitioning = temperature_dof_handler.locally_owned_dofs(); + DoFTools::extract_locally_relevant_dofs (temperature_dof_handler, + temperature_relevant_partitioning); + } + + { + + stokes_constraints.clear (); +// IndexSet stokes_la; +// DoFTools::extract_locally_active_dofs (stokes_dof_handler, +// stokes_la); + stokes_constraints.reinit(stokes_relevant_set); + + { + TimeBlock t(pcout, "***make_hanging_nodes_vel"); + + DoFTools::make_hanging_node_constraints (static_cast&>(stokes_dof_handler), + stokes_constraints); + } + + TimeBlock t(pcout, "***boundary_values_vel"); + + std::vector velocity_mask (dim+1, true); + velocity_mask[dim] = false; + VectorTools::interpolate_boundary_values (static_cast&>(stokes_dof_handler), + 0, + ZeroFunction(dim+1), + stokes_constraints, + velocity_mask); + + std::set no_normal_flux_boundaries; + no_normal_flux_boundaries.insert (1); + VectorTools::compute_no_normal_flux_constraints (static_cast&>(stokes_dof_handler), 0, + no_normal_flux_boundaries, + stokes_constraints); + stokes_constraints.close (); + } + { + TimeBlock t(pcout, "***hanging_nodes_and_bv_temperature"); + temperature_constraints.clear (); +// IndexSet temp_locally_active; +// DoFTools::extract_locally_active_dofs (temperature_dof_handler, +// temp_locally_active); + temperature_constraints.reinit(temperature_relevant_partitioning);//temp_locally_active); + + VectorTools::interpolate_boundary_values (static_cast&>(temperature_dof_handler), + 0, + EquationData::TemperatureInitialValues(), + temperature_constraints); + VectorTools::interpolate_boundary_values (static_cast&>(temperature_dof_handler), + 1, + EquationData::TemperatureInitialValues(), + temperature_constraints); + DoFTools::make_hanging_node_constraints (static_cast&>(temperature_dof_handler), + temperature_constraints); + temperature_constraints.close (); } if (Utilities::System::job_supports_mpi() == false) @@ -1785,17 +2001,26 @@ void BoussinesqFlowProblem::setup_dofs () } else { + TimeBlock t(pcout, "***setup_stokes_matrix"); setup_stokes_matrix (stokes_partitioning); + t.close(); + + TimeBlock t2(pcout, "***setup_stokes_preconditioner"); setup_stokes_preconditioner (stokes_partitioning); + t2.close(); + + TimeBlock t3(pcout, "***setup_temperature_matrix"); setup_temperature_matrices (temperature_partitioning); } + TimeBlock t2(pcout, "***init_vectors"); + stokes_rhs.reinit (stokes_partitioning, MPI_COMM_WORLD); - stokes_solution.reinit (stokes_rhs); + stokes_solution.reinit (stokes_relevant_partitioning, MPI_COMM_WORLD); old_stokes_solution.reinit (stokes_solution); temperature_rhs.reinit (temperature_partitioning, MPI_COMM_WORLD); - temperature_solution.reinit (temperature_rhs); + temperature_solution.reinit (temperature_relevant_partitioning, MPI_COMM_WORLD); old_temperature_solution.reinit (temperature_solution); old_old_temperature_solution.reinit (temperature_solution); @@ -1890,7 +2115,7 @@ local_assemble_stokes_preconditioner (const typename DoFHandler::active_cel (1./EquationData::eta) * EquationData::pressure_scaling * EquationData::pressure_scaling * - scratch.phi_p[i] * scratch.phi_p[j]) + (scratch.phi_p[i] * scratch.phi_p[j])) * scratch.stokes_fe_values.JxW(q); } } @@ -2016,11 +2241,13 @@ template void BoussinesqFlowProblem::build_stokes_preconditioner () { + TimeBlock t(pcout, "*build_stokes_preconditioner"); + if (rebuild_stokes_preconditioner == false) return; - computing_timer.enter_section (" Build Stokes preconditioner"); - pcout << " Rebuilding Stokes preconditioner..." << std::flush; +// computing_timer.enter_section (" Build Stokes preconditioner"); +// pcout << " Rebuilding Stokes preconditioner..." << std::flush; assemble_stokes_preconditioner (); @@ -2040,7 +2267,7 @@ BoussinesqFlowProblem::build_stokes_preconditioner () Amg_data.elliptic = true; Amg_data.higher_order_elements = true; Amg_data.smoother_sweeps = 2; - Amg_data.aggregation_threshold = 0.02; +// Amg_data.aggregation_threshold = 0.02; Mp_preconditioner->initialize (stokes_preconditioner_matrix.block(1,1)); Amg_preconditioner->initialize (stokes_preconditioner_matrix.block(0,0), @@ -2048,8 +2275,8 @@ BoussinesqFlowProblem::build_stokes_preconditioner () rebuild_stokes_preconditioner = false; - pcout << std::endl; - computing_timer.exit_section(); +// pcout << std::endl; +// computing_timer.exit_section(); } // @sect5{Stokes system assembly} @@ -2166,9 +2393,9 @@ copy_local_to_global_stokes_system (const Assembly::CopyData::StokesSystem template void BoussinesqFlowProblem::assemble_stokes_system () { - pcout << " Assembling..." << std::flush; + TimeBlock t(pcout, "*assemble_stokes"); - computing_timer.enter_section (" Assemble Stokes system"); +// computing_timer.enter_section (" Assemble Stokes system"); if (rebuild_stokes_matrix == true) stokes_matrix=0; @@ -2218,8 +2445,8 @@ void BoussinesqFlowProblem::assemble_stokes_system () rebuild_stokes_matrix = false; - pcout << std::endl; - computing_timer.exit_section(); +// pcout << std::endl; +// computing_timer.exit_section(); } @@ -2295,10 +2522,11 @@ copy_local_to_global_temperature_matrix (const Assembly::CopyData::TemperatureMa template void BoussinesqFlowProblem::assemble_temperature_matrix () { + TimeBlock t(pcout, "*assemble_temp_matrix"); if (rebuild_temperature_matrices == false) return; - computing_timer.enter_section (" Assemble temperature matrices"); +// computing_timer.enter_section (" Assemble temperature matrices"); temperature_mass_matrix = 0; temperature_stiffness_matrix = 0; @@ -2336,7 +2564,7 @@ void BoussinesqFlowProblem::assemble_temperature_matrix () rebuild_temperature_matrices = false; rebuild_temperature_preconditioner = true; - computing_timer.exit_section(); +// computing_timer.exit_section(); } @@ -2658,17 +2886,26 @@ void BoussinesqFlowProblem::assemble_temperature_system (const double maxim template void BoussinesqFlowProblem::solve () { - computing_timer.enter_section (" Solve Stokes system"); - pcout << " Solving..." << std::endl; +// computing_timer.enter_section (" Solve Stokes system"); { + TimeBlock t(pcout, "*solve_stokes", false); + const LinearSolvers::BlockSchurPreconditioner preconditioner (stokes_matrix, *Mp_preconditioner, *Amg_preconditioner); + const LinearSolvers::RightPrecond + preconditioner_right (stokes_matrix, stokes_preconditioner_matrix, + *Mp_preconditioner, *Amg_preconditioner); + TrilinosWrappers::MPI::BlockVector distributed_stokes_solution (stokes_rhs); - distributed_stokes_solution = stokes_solution; +// distributed_stokes_solution = stokes_solution; + distributed_stokes_solution.block(0).reinit(stokes_solution.block(0),false,true); + distributed_stokes_solution.block(1).reinit(stokes_solution.block(1),false,true); + const unsigned int start = (distributed_stokes_solution.block(0).size() + @@ -2679,39 +2916,94 @@ void BoussinesqFlowProblem::solve () if (stokes_constraints.is_constrained (i)) distributed_stokes_solution(i) = 0; - SolverControl solver_control (stokes_matrix.m(), 1e-22*stokes_rhs.l2_norm()); - SolverBicgstab - bicgstab (solver_control, false); +#ifdef CONF_COMPARE_SOLVER + { + TrilinosWrappers::MPI::BlockVector x(distributed_stokes_solution); + { + TimeBlock t(pcout, "*TMP: solve bicg", false); + + PrimitiveVectorMemory< TrilinosWrappers::MPI::BlockVector > mem; + SolverControl solver_control (stokes_matrix.m(), 1e-24*stokes_rhs.l2_norm()); + SolverBicgstab + solver (solver_control, mem, false); + solver.solve(stokes_matrix, x, stokes_rhs, + preconditioner); + + pcout << " xx BICG: " + << solver_control.last_step() + << " iterations," + << " reduced res by " << stringify(solver_control.last_value()/solver_control.initial_value()) + << std::endl; + } + { + TrilinosWrappers::MPI::BlockVector + res (stokes_rhs); + double residual = stokes_matrix.residual(res, x, stokes_rhs); - bicgstab.solve(stokes_matrix, distributed_stokes_solution, stokes_rhs, - preconditioner); + pcout << " x BICG: real reduced res by " << stringify(residual + /stokes_rhs.l2_norm()) + << std::endl; + } + } +#endif + + SolverControl solver_control (stokes_matrix.m(), 1e-7*stokes_rhs.l2_norm()); + { +#ifdef CONF_COMPARE_SOLVER + TimeBlock t(pcout, "*TMP: solve fgmres", false); +#endif + PrimitiveVectorMemory< TrilinosWrappers::MPI::BlockVector > mem; + SolverFGMRES + solver(solver_control, mem, + SolverFGMRES::AdditionalData(50, true)); + solver.solve(stokes_matrix, distributed_stokes_solution, stokes_rhs, + preconditioner_right); + + pcout << " xx FGMRES: " + << solver_control.last_step() + << " iterations," + << " reduced res by " << stringify(solver_control.last_value()/solver_control.initial_value()) + << std::endl; + } + { + TrilinosWrappers::MPI::BlockVector + res (stokes_rhs); + double residual = stokes_matrix.residual(res, distributed_stokes_solution, stokes_rhs); + + pcout << " x FGMRES: real reduced res by " << stringify(residual + /stokes_rhs.l2_norm()) + << std::endl; + } + + stokes_constraints.distribute (distributed_stokes_solution); + //stokes_solution = distributed_stokes_solution; + stokes_solution.block(0).reinit(distributed_stokes_solution.block(0), false, true); + stokes_solution.block(1).reinit(distributed_stokes_solution.block(1), false, true); - stokes_solution = distributed_stokes_solution; pcout << " " << solver_control.last_step() - << " BiCGStab iterations for Stokes subsystem." + << " iterations for Stokes subsystem." + << " reduced res by " << stringify(solver_control.last_value()/solver_control.initial_value()) << std::endl; - stokes_constraints.distribute (stokes_solution); + } - computing_timer.exit_section(); +// computing_timer.exit_section(); - computing_timer.enter_section (" Assemble temperature rhs"); +// computing_timer.enter_section (" Assemble temperature rhs"); old_time_step = time_step; const double maximal_velocity = get_maximal_velocity(); - if (maximal_velocity > 1e-10) - time_step = 1./(1.6*dim*std::sqrt(1.*dim)) / - temperature_degree * - GridTools::minimal_cell_diameter(triangulation) / - maximal_velocity; - else - time_step = 1./(1.6*dim*std::sqrt(1.*dim)) / - temperature_degree * - GridTools::minimal_cell_diameter(triangulation) / - 1e-10; + double local_time_step = 1./(1.6*dim*std::sqrt(1.*dim)) / + temperature_degree * + GridTools::minimal_cell_diameter(triangulation) / + std::max(1e-10,maximal_velocity); + + // calculate the minimum allowed time step + // size + MPI_Allreduce (&local_time_step, &time_step, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD); pcout << " Maximal velocity: " << maximal_velocity * EquationData::year_in_seconds * 100 @@ -2723,51 +3015,69 @@ void BoussinesqFlowProblem::solve () << std::endl; temperature_solution = old_temperature_solution; + { + TimeBlock t(pcout, "*assemble_temp"); - assemble_temperature_system (maximal_velocity); + assemble_temperature_system (maximal_velocity); + } - computing_timer.exit_section (); - computing_timer.enter_section (" Solve temperature system"); +// computing_timer.exit_section (); + +// computing_timer.enter_section (" Solve temperature system"); { + TimeBlock t(pcout, "*solve_temp", false); SolverControl solver_control (temperature_matrix.m(), 1e-12*temperature_rhs.l2_norm()); SolverCG cg (solver_control); TrilinosWrappers::MPI::Vector distributed_temperature_solution (temperature_rhs); - distributed_temperature_solution = temperature_solution; +// distributed_temperature_solution = temperature_solution; + distributed_temperature_solution.reinit(temperature_solution, false, true); cg.solve (temperature_matrix, distributed_temperature_solution, temperature_rhs, *T_preconditioner); - temperature_solution = distributed_temperature_solution; - temperature_constraints.distribute (temperature_solution); + temperature_constraints.distribute (distributed_temperature_solution); +// temperature_solution = distributed_temperature_solution; + temperature_solution.reinit(distributed_temperature_solution, false, true); pcout << " " << solver_control.last_step() << " CG iterations for temperature" << std::endl; - computing_timer.exit_section(); +// computing_timer.exit_section(); - double min_temperature = temperature_solution(0), - max_temperature = temperature_solution(0); - for (unsigned int i=1; i temperature (2), global_temperature (2); + temperature[0] = distributed_temperature_solution.trilinos_vector()[0][0], + temperature[1] = temperature[0]; + for (unsigned int i=1; i (min_temperature, - temperature_solution(i)); - max_temperature = std::max (max_temperature, - temperature_solution(i)); + temperature[0] = std::min (temperature[0], + distributed_temperature_solution.trilinos_vector()[0][i]); + temperature[1] = std::max (temperature[1], + distributed_temperature_solution.trilinos_vector()[0][i]); } +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + temperature[0] *= -1.0; + MPI_Allreduce (&temperature[0], &global_temperature[0], + 2, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + global_temperature[0] *= -1.0; +#else + global_temperature = local_temperature; +#endif pcout << " Temperature range: " - << min_temperature << ' ' << max_temperature + << global_temperature[0] << ' ' << global_temperature[1] << std::endl; } } + // @sect4{BoussinesqFlowProblem::output_results} // This function does mostly what the @@ -2839,49 +3149,61 @@ void BoussinesqFlowProblem::solve () template void BoussinesqFlowProblem::output_results () { - if (timestep_number % 25 != 0) - return; + pcout << "outputting " << out_index << std::endl; computing_timer.enter_section ("Postprocessing"); + Vector estimated_error_per_cell (triangulation.n_active_cells()); + + KellyErrorEstimator::estimate (static_cast&>(temperature_dof_handler), + QGauss(temperature_degree+1), + typename FunctionMap::type(), + temperature_solution, + estimated_error_per_cell); + + const FESystem joint_fe (stokes_fe, 1, + temperature_fe, 1, + FE_DGQ(0), 1, + FE_DGQ(0), 1, + temperature_fe, 1); + + DoFHandler joint_dof_handler (triangulation); + joint_dof_handler.distribute_dofs (joint_fe); + Assert (joint_dof_handler.n_global_dofs() == + stokes_dof_handler.n_global_dofs() + + temperature_dof_handler.n_global_dofs() + + 2*triangulation.n_global_active_cells() + + temperature_dof_handler.n_global_dofs(), + ExcInternalError()); + + TrilinosWrappers::MPI::Vector joint_solution; + IndexSet locally_relevant_dofs(joint_dof_handler.n_dofs()); + DoFTools::extract_locally_relevant_dofs (joint_dof_handler, locally_relevant_dofs); + joint_solution.reinit (locally_relevant_dofs, MPI_COMM_WORLD); - if (Utilities::System::get_this_mpi_process(MPI_COMM_WORLD) == 0) - { - - const FESystem joint_fe (stokes_fe, 1, - temperature_fe, 1, - FE_DGQ(0), 1); - DoFHandler joint_dof_handler (triangulation); - joint_dof_handler.distribute_dofs (joint_fe); - Assert (joint_dof_handler.n_dofs() == - stokes_dof_handler.n_dofs() + - temperature_dof_handler.n_dofs() + - triangulation.n_active_cells(), - ExcInternalError()); - - Vector joint_solution (joint_dof_handler.n_dofs()); - - { - double minimal_pressure = stokes_solution.block(1)(0); - for (unsigned int i=0; i (stokes_solution.block(1)(i), - minimal_pressure); - - std::vector local_joint_dof_indices (joint_fe.dofs_per_cell); - std::vector local_stokes_dof_indices (stokes_fe.dofs_per_cell); - std::vector local_temperature_dof_indices (temperature_fe.dofs_per_cell); - - typename DoFHandler::active_cell_iterator - joint_cell = joint_dof_handler.begin_active(), - joint_endc = joint_dof_handler.end(), - stokes_cell = stokes_dof_handler.begin_active(), - temperature_cell = temperature_dof_handler.begin_active(); - for (; joint_cell!=joint_endc; ++joint_cell, ++stokes_cell, ++temperature_cell) - { - joint_cell->get_dof_indices (local_joint_dof_indices); - stokes_cell->get_dof_indices (local_stokes_dof_indices); - temperature_cell->get_dof_indices (local_temperature_dof_indices); + { + //double minimal_pressure = stokes_solution.block(1)(0); + //for (unsigned int i=0; i (stokes_solution.block(1)(i), + // minimal_pressure); + + std::vector local_joint_dof_indices (joint_fe.dofs_per_cell); + std::vector local_stokes_dof_indices (stokes_fe.dofs_per_cell); + std::vector local_temperature_dof_indices (temperature_fe.dofs_per_cell); + + typename DoFHandler::active_cell_iterator + joint_cell = joint_dof_handler.begin_active(), + joint_endc = joint_dof_handler.end(), + stokes_cell = stokes_dof_handler.begin_active(), + temperature_cell = temperature_dof_handler.begin_active(); + for (unsigned int cell_index=0; joint_cell!=joint_endc; ++joint_cell, ++stokes_cell, ++temperature_cell, ++cell_index) + if (!joint_cell->is_artificial() && !joint_cell->is_ghost()) + { + joint_cell->get_dof_indices (local_joint_dof_indices); + stokes_cell->get_dof_indices (local_stokes_dof_indices); + temperature_cell->get_dof_indices (local_temperature_dof_indices); - for (unsigned int i=0; i::output_results () joint_solution(local_joint_dof_indices[i]) = ((stokes_solution(local_stokes_dof_indices [joint_fe.system_to_base_index(i).second]) - - - minimal_pressure) + ) * EquationData::pressure_scaling); } } - else if (joint_fe.system_to_base_index(i).first.first == 1) - { - Assert (joint_fe.system_to_base_index(i).second - < - local_temperature_dof_indices.size(), - ExcInternalError()); - joint_solution(local_joint_dof_indices[i]) - = temperature_solution(local_temperature_dof_indices - [joint_fe.system_to_base_index(i).second]); - } + else if (joint_fe.system_to_base_index(i).first.first == 1) + { + Assert (joint_fe.system_to_base_index(i).second + < + local_temperature_dof_indices.size(), + ExcInternalError()); + joint_solution(local_joint_dof_indices[i]) + = temperature_solution(local_temperature_dof_indices + [joint_fe.system_to_base_index(i).second]); + } + else if (joint_fe.system_to_base_index(i).first.first == 2) + { + Assert (joint_fe.system_to_base_index(i).second + == 0, + ExcInternalError()); + joint_solution(local_joint_dof_indices[i]) + = joint_cell->subdomain_id(); + } + else if (joint_fe.system_to_base_index(i).first.first == 3) + { + Assert (joint_fe.system_to_base_index(i).first.first == 3, + ExcInternalError()); + Assert (joint_fe.system_to_base_index(i).second + == 0, + ExcInternalError()); + joint_solution(local_joint_dof_indices[i]) + = estimated_error_per_cell (cell_index); + } else { - Assert (joint_fe.system_to_base_index(i).first.first == 2, - ExcInternalError()); - Assert (joint_fe.system_to_base_index(i).second - == 0, - ExcInternalError()); - joint_solution(local_joint_dof_indices[i]) - = joint_cell->subdomain_id(); - } - } - } + Assert (joint_fe.system_to_base_index(i).first.first == 4, + ExcInternalError()); - std::vector joint_solution_names (dim, "velocity"); - joint_solution_names.push_back ("p"); - joint_solution_names.push_back ("T"); - joint_solution_names.push_back ("partition"); + joint_solution(local_joint_dof_indices[i]) + = local_temperature_dof_indices + [joint_fe.system_to_base_index(i).second]; - DataOut data_out; - - data_out.attach_dof_handler (joint_dof_handler); - - std::vector - data_component_interpretation - (dim+3, DataComponentInterpretation::component_is_scalar); - for (unsigned int i=0; i::type_dof_data, - data_component_interpretation); - data_out.build_patches (std::min(stokes_degree, temperature_degree)); + } + } + } - std::ostringstream filename; - filename << "solution-" << Utilities::int_to_string(timestep_number, 5) - << ".vtk"; + std::vector joint_solution_names (dim, "velocity"); + joint_solution_names.push_back ("p"); + joint_solution_names.push_back ("T"); + joint_solution_names.push_back ("partition"); + joint_solution_names.push_back ("error"); + joint_solution_names.push_back ("TDofIndex"); + + DataOut data_out; + + data_out.attach_dof_handler (joint_dof_handler); + + std::vector + data_component_interpretation + (dim+5, DataComponentInterpretation::component_is_scalar); + for (unsigned int i=0; i::type_dof_data, + data_component_interpretation); + data_out.build_patches (1+std::min(stokes_degree, temperature_degree)); + + const std::string filename = ("out/solution-" + + Utilities::int_to_string (out_index, 5) + + "." + + Utilities::int_to_string + (triangulation.locally_owned_subdomain(), 4) + + ".vtu"); + std::ofstream output (filename.c_str()); + data_out.write_vtu (output); - std::ofstream output (filename.str().c_str()); - data_out.write_vtk (output); + if (Utilities::System::get_this_mpi_process(MPI_COMM_WORLD) == 0) + { + std::vector filenames; + for (unsigned int i=0; i::output_results () template void BoussinesqFlowProblem::refine_mesh (const unsigned int max_grid_level) { + TimeBlock t(pcout, "*refine_mesh", false); + computing_timer.enter_section ("Refine mesh structure, part 1"); - Vector local_estimated_error_per_cell (triangulation.n_active_cells()); + Vector estimated_error_per_cell (triangulation.n_active_cells()); + + { + TimeBlock t(pcout, "**kelly"); - KellyErrorEstimator::estimate (temperature_dof_handler, + KellyErrorEstimator::estimate (static_cast&>(temperature_dof_handler), QGauss(temperature_degree+1), typename FunctionMap::type(), temperature_solution, - local_estimated_error_per_cell, + estimated_error_per_cell, std::vector(), 0, 0, - Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)); + triangulation.locally_owned_subdomain()); + + } + + +#ifdef CONF_BENCHMARK + /* pure refinement to double the number of + cells in benchmark mode if this is + prerefinement. Keep the number of cells + constant later on. */ + if (timestep_number==0) + { + pcout << "refine_mesh: prerefine, factor 2." << std::endl; + parallel::distributed::GridRefinement:: + refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3333, 0.0); + } + else + { + pcout << "refine_mesh: keeping mesh constant." << std::endl; + TimeBlock t(pcout, "**refine&coarsen_fixed_nb"); + + parallel::distributed::GridRefinement:: + refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3333, 0.0);//0.1, 0.4 ? + } - Vector x_local_estimated_error_per_cell (triangulation.n_active_cells()); - x_local_estimated_error_per_cell = local_estimated_error_per_cell; - Vector estimated_error_per_cell (triangulation.n_active_cells()); -#ifdef DEAL_II_COMPILER_SUPPORTS_MPI - MPI_Allreduce (&x_local_estimated_error_per_cell(0), - &estimated_error_per_cell(0), - triangulation.n_active_cells(), MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); #else - estimated_error_per_cell = x_local_estimated_error_per_cell; -#endif + parallel::distributed::GridRefinement:: + refine_and_coarsen_fixed_fraction (triangulation, + estimated_error_per_cell, + 0.6, 0.2); - GridRefinement::refine_and_coarsen_fixed_fraction (triangulation, - estimated_error_per_cell, - 0.6, 0.2); + // limit maximum refinement level if (triangulation.n_levels() > max_grid_level) for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(max_grid_level); cell != triangulation.end(); ++cell) cell->clear_refine_flag (); +#endif - std::vector x_temperature (2); - x_temperature[0] = temperature_solution; - x_temperature[1] = old_temperature_solution; - TrilinosWrappers::BlockVector x_stokes = stokes_solution; + TimeBlock t_a(pcout, "**prepare_solution_transfer", false); + std::vector x_temperature (2); + x_temperature[0] = &temperature_solution; + x_temperature[1] = &old_temperature_solution; + TrilinosWrappers::MPI::BlockVector x_stokes = stokes_solution; - SolutionTransfer + parallel::distributed::SolutionTransfer temperature_trans(temperature_dof_handler); - SolutionTransfer + parallel::distributed::SolutionTransfer stokes_trans(stokes_dof_handler); triangulation.prepare_coarsening_and_refinement(); temperature_trans.prepare_for_coarsening_and_refinement(x_temperature); stokes_trans.prepare_for_coarsening_and_refinement(x_stokes); + t_a.close(); + TimeBlock t_c(pcout, "**execute_c&r", false); + triangulation.execute_coarsening_and_refinement (); computing_timer.exit_section(); + t_c.close(); + setup_dofs (); + TimeBlock t_b(pcout, "**do_solution_transfer"); computing_timer.enter_section ("Refine mesh structure, part 2"); - std::vector tmp (2); - tmp[0].reinit (temperature_solution); - tmp[1].reinit (temperature_solution); - temperature_trans.interpolate(x_temperature, tmp); + TrilinosWrappers::MPI::Vector + distributed_temp1 (temperature_rhs); + TrilinosWrappers::MPI::Vector + distributed_temp2 (temperature_rhs); + + std::vector tmp (2); + tmp[0] = &(distributed_temp1); + tmp[1] = &(distributed_temp2); + temperature_trans.interpolate(tmp); + +// temperature_solution = distributed_temp1; + temperature_solution.reinit(distributed_temp1, false, true); +// old_temperature_solution = distributed_temp2; + old_temperature_solution.reinit(distributed_temp2, false, true); + + TrilinosWrappers::MPI::BlockVector + distributed_stokes (stokes_rhs); - temperature_solution = tmp[0]; - old_temperature_solution = tmp[1]; - temperature_constraints.distribute(temperature_solution); - temperature_constraints.distribute(old_temperature_solution); + stokes_trans.interpolate (distributed_stokes); +// stokes_solution = distributed_stokes; + stokes_solution.block(0).reinit(distributed_stokes.block(0), false, true); + stokes_solution.block(1).reinit(distributed_stokes.block(1), false, true); - TrilinosWrappers::BlockVector x_stokes_new = stokes_solution; - stokes_trans.interpolate (x_stokes, x_stokes_new); - stokes_solution = x_stokes_new; rebuild_stokes_matrix = true; rebuild_stokes_preconditioner = true; @@ -3159,10 +3564,17 @@ void BoussinesqFlowProblem::refine_mesh (const unsigned int max_grid_level) // VectorTools::project, the // rest is as before. template -void BoussinesqFlowProblem::run () +void BoussinesqFlowProblem::run (unsigned int ref) { - const unsigned int initial_refinement = (dim == 2 ? 5 : 2); - const unsigned int n_pre_refinement_steps = (dim == 2 ? 2 : 2); + pcout << "this is step-32. ref=" << ref << std::endl; + #ifdef CONF_BENCHMARK + // create approximately 12*4^(ref/2) cells: + const unsigned int initial_refinement = (ref-1)/2; + const unsigned int n_pre_refinement_steps = 2+(ref-1)%2; +#else + const unsigned int initial_refinement = ref;//(dim == 2 ? 5 : 2); + const unsigned int n_pre_refinement_steps = 2;//(dim == 2 ? 4 : 2); +#endif GridGenerator::hyper_shell (triangulation, Point(), @@ -3170,11 +3582,12 @@ void BoussinesqFlowProblem::run () EquationData::R1, 12, true); - static HyperShellBoundary boundary; triangulation.set_boundary (0, boundary); triangulation.set_boundary (1, boundary); + //GridGenerator::hyper_cube (triangulation, EquationData::R0, EquationData::R1); + global_Omega_diameter = GridTools::diameter (triangulation); triangulation.refine_global (initial_refinement); @@ -3207,6 +3620,18 @@ void BoussinesqFlowProblem::run () pcout << std::endl; +#ifdef CONF_BENCHMARK + static int bench_step=0; + if (time>=1e5*EquationData::year_in_seconds) + { + ++bench_step; + if (bench_step>1) + break; + pcout << "***BEGIN BENCHSTEP" << std::endl; + refine_mesh (initial_refinement + n_pre_refinement_steps); + } + else +#endif if ((timestep_number == 0) && (pre_refinement_step < n_pre_refinement_steps)) { @@ -3214,11 +3639,19 @@ void BoussinesqFlowProblem::run () ++pre_refinement_step; goto start_time_iteration; } +#ifndef CONF_BENCHMARK else if ((timestep_number > 0) && (timestep_number % 10 == 0)) refine_mesh (initial_refinement + n_pre_refinement_steps); +#endif - output_results (); +#ifdef CONF_BENCHMARK + if (false)//bench_step) +#else + if (timestep_number % 10 == 0 && + Utilities::System::get_n_mpi_processes(MPI_COMM_WORLD) <= 10) +#endif + output_results (); time += time_step; ++timestep_number; @@ -3244,7 +3677,15 @@ int main (int argc, char *argv[]) Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); BoussinesqFlowProblem<2> flow_problem; - flow_problem.run (); + + unsigned int ref=5; + if (argc>=2) + { + ref = (unsigned int)Utilities::string_to_int(argv[1]); + } + + flow_problem.run (ref); + print_memory_stats(); } catch (std::exception &exc) { diff --git a/deal.II/examples/step-32/timeblock.h b/deal.II/examples/step-32/timeblock.h new file mode 100644 index 0000000000..0aaa5ffab3 --- /dev/null +++ b/deal.II/examples/step-32/timeblock.h @@ -0,0 +1,64 @@ +/** + * copyright: + * Research Group on Numerical Methods for PDEs + * University of Göttingen + * + * lube@math.uni-goettingen.de + */ + +#ifndef TIMEBLOCK_H +#define TIMEBLOCK_H + +template +class TimeBlock +{ + public: + TimeBlock(STREAM & stream_, const char* blockname, bool singleline=true) + : m_blockname(blockname), + m_singleline(singleline), + m_running(true), + m_timer(MPI_COMM_WORLD, true), + stream(stream_) + { + MPI_Barrier(MPI_COMM_WORLD); + stream << blockname << " ... "; + if (singleline) + stream << std::flush; + else + stream << std::endl; + m_timer.start(); + } + + ~TimeBlock() + { + close(); + } + + void close() + { + if (!m_running) + return; + m_running = false; + + m_timer.stop(); + + if (!m_singleline) + stream << m_blockname << " took "; + + stream << std::fixed << std::setprecision(4); + + m_timer.print_data(stream); + + MPI_Barrier(MPI_COMM_WORLD); + } + + + private: + const char* m_blockname; + bool m_singleline; + bool m_running; + dealii::Timer m_timer; + STREAM & stream; +}; + +#endif diff --git a/deal.II/examples/step-40/Makefile b/deal.II/examples/step-40/Makefile new file mode 100644 index 0000000000..083bb751a1 --- /dev/null +++ b/deal.II/examples/step-40/Makefile @@ -0,0 +1,191 @@ +# $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 *d2 + + + + +# +# +# Usually, you will not need to change anything beyond this point. +# +# +# The next statement tell 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 + +################################################################ +# This example program will only work if PETSc is installed. If this +# is not the case, then simply redefine the main targets to do nothing +# +# The same holds true if we use threads: PETSc and threads doesn't +# seem to work well together... +ifneq ($(USE_CONTRIB_PETSC)$(enable-threads),yesno) + ifneq ($(USE_CONTRIB_PETSC),yes) +default run clean: + @echo + @echo "===========================================================" + @echo "= This program cannot be compiled without PETSc. Make =" + @echo "= sure you have PETSc installed and detected during =" + @echo "= configuration of deal.II =" + @echo "===========================================================" + @echo + else +default run clean: + @echo + @echo "===========================================================" + @echo "= PETSc can not be used when running programs on =" + @echo "= multiple threads. Make sure you have specified the =" + @echo "= --disable-threads flag upon configuration of deal.II =" + @echo "===========================================================" + @echo + endif +else +# +################################################################ + + + + +# 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, and there need +# to be two sets of libraries: one for the debug mode version of the +# application and one for the optimized mode. Here we have selected +# the versions for 2d. Note that the order in which the libraries are +# given here is important and that your applications won't link +# properly if they are given in another order. +# +# You may need to augment the lists of libraries when compiling your +# program for other dimensions, or when using third party libraries +libs.g = $(lib-deal2-2d.g) \ + $(lib-deal2-3d.g) \ + $(lib-lac.g) \ + $(lib-base.g) +libs.o = $(lib-deal2-2d.o) \ + $(lib-deal2-3d.o) \ + $(lib-lac.o) \ + $(lib-base.o) + + +# We now use the variable defined above which 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 the 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) : $(libraries) + @echo ============================ Linking $@ + @$(CXX) -o $@$(EXEEXT) $^ $(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) + @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========= $( $@ \ + || (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 + + +endif # USE_CONTRIB_TRILINOS diff --git a/deal.II/examples/step-40/doc/builds-on b/deal.II/examples/step-40/doc/builds-on new file mode 100644 index 0000000000..c27eb152ec --- /dev/null +++ b/deal.II/examples/step-40/doc/builds-on @@ -0,0 +1 @@ +step-6 step-17 diff --git a/deal.II/examples/step-40/doc/intro.dox b/deal.II/examples/step-40/doc/intro.dox new file mode 100644 index 0000000000..51b95cbae4 --- /dev/null +++ b/deal.II/examples/step-40/doc/intro.dox @@ -0,0 +1,115 @@ +
    + +This program was contributed by Timo Heister, Martin Kronbichler and Wolfgang +Bangerth. +
    +This material is based upon work partly supported by the National +Science Foundation under Award No. EAR-0426271 and The California Institute of +Technology. Any opinions, findings, and conclusions or recommendations +expressed in this publication are those of the author and do not +necessarily reflect the views of the National Science Foundation or of The +California Institute of Technology. +
    + + + +

    Introduction

    + +Given today's computers, most finite element computations can be done on +a single machine. The majority of previous tutorial programs therefore +shows only this, possibly splitting up work among a number of +processors that, however, can all access the same, shared memory +space. That said, there are problems that are simply too big for a +single machine and in that case the problem has to be split up in a +suitable way among multiple machines each of which contributes its +part to the whole. A simple way to do that was shown in step-17 and +step-18, where we show how a program can use MPI to parallelize +assembling the linear system, storing it, solving it, and computing +error estimators. All of these operations scale relatively trivially, +but there was one significant drawback: for this to be moderately +simple to implement, each MPI processor had to keep its own copy of +the entire Triangulation and DoFHandler objects. Consequently, while +we can suspect (with good reasons) that the operations listed above +can scale to thousands of computers and problem sizes of billions of +cells and billions of degrees of freedom, building the one big mesh for the +entire problem these thousands of computers are solving on every last +processor is clearly not going to scale: it is going to take forever, +and maybe more importantly no single machine will have enough memory +to store a mesh that has a billion cells (at least not at the time of +writing this). In reality, programs like step-17 and step-18 can +therefore not be run on more than maybe 100 or 200 processors and even +there storing the Triangulation and DoFHandler objects consumes the +vast majority of memory on each machine. + +Consequently, we need to approach the problem differently: to scale to +very large problems each processor can only store its own little piece +of the Triangulation and DoFHandler objects. deal.II implements such a +scheme in the parallel::distributed namespace and the classes +therein. It builds on an external library, p4est (a play on the expression +parallel forest that describes the parallel storage of a +hierarchically constructed mesh as a forest of quad- or +oct-trees). You need to install and configure p4est +but apart from that all of its workings are hidden under the surface +of deal.II. + +In essence, what the parallel::distributed::Triangulation class and +associated parallel::distributed::DoFHandler objects do is to split +the global mesh so that every processor only stores a small bit it +"owns" along with one layer of "ghost" cells that surround the ones it +owns. What happens in the rest of the domain on which we want to solve +the partial differential equation is unknown to each processor and can +only be inferred through communication with other machines if such +information is needed. This implies that we also have to think about +problems in a different way than we did in, for example, step-17 and +step-18: no processor can have the entire solution vector for +postprocessing, for example, and every part of a program has to be +parallelized because no processor has all the information necessary +for sequential operations. + +A general overview of how this parallelization happens is described in +the @ref distributed documentation module. You should read it for a +top-level overview before reading through the source code of this +program. A concise discussion of many terms we will use in the program +is also provided in the @ref distributed_paper "Distributed Computing paper". +It is probably worthwhile reading it for background information on how +things work internally in this program. + + +

    The testcase

    + +This program solves essentially re-solves what we already do in +step-6, i.e. it solves the Laplace equation +@f{align*} + -\Delta u &= f \qquad &&\text{in}\ \Omega=[0,1]^2, \\ + u &= 0 \qquad &&\text{on}\ \partial\Omega. +@f} +The difference of course is now that we want to do so on a mesh that +may have a billion cells, with a billion or so degrees of +freedom. There is no doubt that doing so is completely silly for such +a simple problem, but the point of a tutorial program is, after all, +not to do something useful but to show how useful programs can be +implemented using deal.II. Be that as it may, to make things at least +a tiny bit interesting, we choose the right hand side as a +discontinuous function, +@f{align*} + f(x,y) + = + \left\{ + \begin{array}{ll} + 1 & \text{if}\ y < \frac 12 + \frac 14 \sin(4\pi x), \\ + -1 & \text{otherwise}, + \end{array} + \right. +@f} +so that the solution has a singularity along the sinusoidal line +snaking its way through the domain. As a consequence, mesh refinement +will be concentrated along this line. You can see this in the mesh +picture shown below in the results section. + +Rather than continuing here and giving a long introduction, let us go +straight to the program code. If you have read through step-6 and the +@ref distributed documentation module, most of things that are going +to happen should be familiar to you already. diff --git a/deal.II/examples/step-40/doc/kind b/deal.II/examples/step-40/doc/kind new file mode 100644 index 0000000000..c1d9154931 --- /dev/null +++ b/deal.II/examples/step-40/doc/kind @@ -0,0 +1 @@ +techniques diff --git a/deal.II/examples/step-40/doc/results.dox b/deal.II/examples/step-40/doc/results.dox new file mode 100644 index 0000000000..56bb1e36f6 --- /dev/null +++ b/deal.II/examples/step-40/doc/results.dox @@ -0,0 +1,117 @@ +

    Results

    + +DOCUMENT HOW TO RUN THE PROGRAM + +When run on a sufficiently large number of machines (say a few +thousand), this program can relatively easily solve problems with well +over one billion unknowns in less than a minute. On the other hand, +such big problems can no longer be visualized, so we also ran the +program on only 16 processors. Here are a mesh, along with its +partitioning onto the 16 processors, and the corresponding solution: + + + + + + +
    + @image html step-40.mesh.png + + @image html step-40.solution.png +
    + +The mesh on the left has a mere 7,069 cells. This is of course a +problem we would easily have been able to solve already on a single +processor using step-6, but the point of the program was to show how +to write a program that scales to many more machines. For example, +here are two graphs that show how the run time of a large number of parts +of the program scales on problems with around 52 and 375 million degrees of +freedom if we take more and more processors (these and the next couple of +graphs are taken from the @ref distributed_paper "Distributed Computing paper"): + + + + + + +
    + @image html step-40.strong2.png + + @image html step-40.strong.png +
    + +As can clearly be seen, the program scales nicely to very large +numbers of processors. The curves, in particular the linear solver, become a +bit wobble at the right end of the graphs since each processor has too little +to do to offset the cost of communication (the part of the whole problem each +processor has to solve in the above two examples is only 13,000 and 90,000 +degrees of freedom when 4,096 processors are used). + +While the strong scaling graphs above show that we can solve a problem of +fixed size faster and faster if we take more and more processors, the more +interesting question may be how big problems can become so that they can still +be solved within a reasonable time on a machine of a particular size. We show +this in the following two graphs for 256 and 4096 processors: + + + + + + +
    + @image html step-40.256.png + + @image html step-40.4096.png +
    + +What these graphs show is that all parts of the program scale linearly with +the number of degrees of freedom. This time, lines are wobbly at the left as +the size of local problems is too small. For more discussions of these results +we refer to the @ref distributed_paper "Distributed Computing paper". + +So how large are the largest problems one can solve? At the time of writing +this problem, the +limiting factor is that the program uses the BoomerAMG algebraic +multigrid method from the hypre package as +a preconditioner, which unfortunately uses signed 32-bit integers to +index the elements of a %distributed matrix. This limits the size of +problems to $2^31-1=2,147,483,647$ degrees of freedom. From the graphs +above it is obvious that the scalability would extend beyond this +number, and one could expect that given more than the 4,096 machines +shown above would also further reduce the compute time. That said, one +can certainly expect that this limit will eventually be lifted by the +hypre developers. + + + + +

    Possibilities for extensions

    + +In a sense, this program is the ultimate solver for the Laplace +equation: it can essentially solve the equation to whatever accuracy +you want, if only you have enough processors available. Since the +Laplace equation by itself is not terribly interesting at this level +of accuracy, the more interesting possibilities for extension +therefore concern not so much this program but what comes beyond +it. For example, several of the other programs in this tutorial have +significant run times, especially in 3d. It would therefore be +interesting to use the techniques explained here to extend other +programs to support parallel distributed computations. We have done +this for step-31 in the step-32 tutorial program, but the same would +apply to, for example, step-23 and step-25 for hyperbolic time +dependent problems, step-33 for gas dynamics, or step-35 for the +Navier-Stokes equations. + +Maybe equally interesting is the problem of postprocessing. As +mentioned above, we only show pictures of the solution and the mesh +for 16 processors because 4,096 processors solving 1 billion unknowns +would produce graphical output on the order of several 10 +gigabyte. Currently, no program is able to visualize this amount of +data in any reasonable way unless it also runs on at least several +hundred processors. There are, however, approaches where visualization +program directly communicate with solvers on each processor with each +visualization process rendering the part of the scene computed by the +solver on this processor. Implementing such an interface would allow +to quickly visualize things that are otherwise not amenable to +graphical display. diff --git a/deal.II/examples/step-40/doc/step-40.256.png b/deal.II/examples/step-40/doc/step-40.256.png new file mode 100644 index 0000000000000000000000000000000000000000..e71abac31d21505a5f357ea09fdb55c230fddd5d GIT binary patch literal 11910 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 ptCjC$l8V`9T6+yPdN000OE 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 literal 0 HcmV?d00001 diff --git a/deal.II/examples/step-40/doc/step-40.strong.png b/deal.II/examples/step-40/doc/step-40.strong.png new file mode 100644 index 0000000000000000000000000000000000000000..2cae6b5db9ac99255f75c4ae61ede7d890179257 GIT binary patch literal 11211 zcmaiacQ{;6^!HkeWtG*IsH?XndX!+T8ojeR(R&g?5_MV8iQYSr=rwu^QKI*V5)!?a z2qC<_zvun){XOqDv2@xsHZ`(%s!1Ko9^(006830962h763p71pxqg0Du($zzhI%0RVCU!0P=| zPR`28s;jGOb#;}QnK>^nud}l={|@edhaBy$>ACC4ZyG(mJ0AU%dw17;cNcuu-F=tu zb(e2_myf&)Mg|81g01g@t-JqABB_F@?r(sh>%#;ct{>u)EIz?&bm2hlh7fJ%`JfA9r`hhR2l)n+b;r$HKz*z24Z^7#SJq?(VLy zug}QH2nq^vaB$Gn)D#gBp`)X_A0_0EiTklB;pGjwo4M}|GWb6mg4V&^!FPaQM*g{JdQy1`;}W-j^@%`2g?Q41jr}Pam%aDLj>a4)wi}${NcH6 z%P$g`eL*NU8twno@y;NlTATin23|iqjUtO>HnMA51eFr63=iLXd~ols#p=ULCmX$GBhZ-X;S6 z^_h_t(d^!x_tNa*pCmn;;pItB(^}UP8~2~>>nbk1L%GE>C&57pl;3~WOx?_Xjyi3< zaJK2HUS43gTrbkx&*ro*l8BPfzkyZeGCMlPQ0`uhO+U3Vx+T2&aIkbPmz=oRh>CcC zds6<5e7VyvGe2K|ASCdL{x;8l;-Hz~>|#RRBp$Wb+Z7ghGc7*0KVN%B;BWZo#>?EL zmf*J4C{U=eHEuJ_;@M3OZ6*c_KD>^Wn|CUQ_BB-5KPVDxzzS{Fq zZ4X>9Zv0zhlKx9q=~>oAV+J7Ziv$D1P(ki?T;{UwU^jEi{t$ZM3r{HSL<=^Sd_TF0~ zy%wWnqNiSs5XcKxXk2w(fwt^s9qV6(Nu&;FPw_(9c{D2i zewX>QZE6L$`U}1&#aU31csX|sZr_gk)t>NjB{}QubICr~a~!7i^^y;`P;v1KQ{Ni} zKQ}*wbug4c?AU5tc-#@{stR2o_&4k#FZ8HE|nwsps-g2T%(J-@@h_*797i_s%?d z_|KD+S$@ZxnN}*OxP2#*-)4-hVpBKfv=GH!8Ii8i{jlK`D9~kK@9j3-apDaH;_n1X zhV6W2jMb7ccp^=>YNJvNDJY7hWu=1?2e^#~E>nDCMHx+Zm{xihV;g$GQ-$JB_96T<=BVv-%({pIhRkjsX_QQ zjyQ&7hl>AnZpz_W5(`4EOz7pEEzJ%e9Nv4XxA>hmbd0@s%rePt|Eb-o=dkjnCsKpA$jif;trW zRY;niZWVZ$LC+P(_O56;vmWH2#XSR5g8zEZDGq5+c|b6OH5K+brRxtee#eZt5k8i| z6-E95;B3_V@Yw&GM2Ja!@VDR(xniA@Op!>I*n}`LO?HS+esGuHKR+wj3t~Zn!Y4|{ zw9#U3eMZZA>a-t2h(mf%omC8CCR!XVdlEW>#t-3kMS>=S`f~7m8(5WrF}#eMeMV#i z6)uH~6Hvuz`@-#0;JHPBfNatPGkBX)j9lv>)9M#(Q15#o$U_p;3cpfE;V(1t`Jx(u z%&*_H@|*_+t)`L!TMet$-zIYa#&6v!6!D-^nJP8&K34VJ^PC%x;*Uu{HyUd?X-#KKo(4sdX!BQfSz=0>f8TUtXJNcfNccYIdQtNOLDDtc%cg3%QKxn zc4AiJ$w0=+?Jx4p%Dc-M;{A2&3Jj6l-YeDTp*wvY*Yb82`6H-KMDj1o>BtPog%jKw zx)}Iz_|mRx-A?*U3xP3sKv#v7m#7#Xc;TS9HWwwf7XV^s_9kfjXD~~GK)o|1nwVHY zcpVAjfn+4bC-mkDmYQ_AwCtw5csN)|`|4^D{c6m)BOf*I2B&PxOu!Ajn#d2uFfNpPVOPzY%&6bfo2JN#5CT_@Bfmf>lyqHHN1f2+rY%bM5qjJe>` z#hNgzjF0Y*@ioDlrslkT)ff8HXLYZraDc52Gdj`O^Y608ScVaVOURloyQ`3z;4y9n=9LWp`YkwRZue5009ga&JPC5u4< z!oMATSxt?^NdaBG6r&RYe+geAVqDk6wW}&;C;BwlY+Y_7L_`v!s{rjQD`-S>CqTLY#aTVfOk;iW zy_<&c3u>-~yH!*!sf39=spur=oMcwP{mso4i(YxHx%*{w=1^Bcpk7YXH4^@*%}8ge zDZ%z5phjl|L8ZQA6ozFS`sE}A{`i*ewn;8dZg4$Bdb~co5~q(v9%t=y@2EjoAb>TvQA~JtbLs zNCphKst<62Gpz3AUFTgqWaQm@Kn|VTIWaI|9?=;taUMX$idH|74n@M_2!m{)vfFYu z5!tXF9W`bf8L9WG&(go@tb2m`P^Pn)L=HEj&MemOgstB*qJ4kJk&em2{&pead}pPT zd>V%7SXPIE=wIWUV0K@+RX9_#+;@UNv)}}27q1*$Aowh_Rm0geJ&I`gFZsvjSTuJg zm24~?Wtip&TN&9>-oaD-vzE;`tkxFVo|R_m3ceeoQQc zAX{j@GK&-J^lAAUGy8o#T`1?A3IJqwwhZ;8i`ne+WuXNMIrJRto3b{w_MV3Rc^Z-@ z@IY2;+}*ZQg`d5k zkj-f4Z!wyqBWk4J3!Y8dtc6H)ex&qIJ!!K~byj#8A!ILM2)GmOWjH)Sw%4=@eP(#_ zubpQMVGtd4dGO@K-7GCBqtDPmzDSA7oofiV5-I?0o!)nrZn+dz%9ILzKLEEP_@|{< zDz;l`^bBVL^Sf34GS2=RQwT~4w$!1#)tY@)YA4T@_@e%@oHaT`P1m^Iu;OoIIh1FL zDjH5v_3m{-rgN+jjyZs;hx7Rrp(~{t9u*lX8;$IxTqJ*z5sq=ZdOqKpP3bWnD~;R} zx-jLsz$P6;p`j^}ymnEdD=H=_VXhU~iO%k+Tez1R`B~EI`cpic&vE|n!1E>Q;cTV* zn=CzuV_8fr-a&Z2d0G-@0|k1bx%4+}WVP@f`HF&#JoC17Y27ucB*oc$!Q-*$tq-TC>Td zSUej`RB&v_e=(udnB|LA^#$lx6+VI2**}_wVSq7o?fjtQ5j1wwlA9Q{G+08Il27%_Pf-`IS8#?uOrRL)Ayhys@na?#`h0Y+8MfkE}M62Wa z1q$I1i&C@>8&3yby{Gy=BYEiF^QpW8eSxhf*lM<+pe9cQS!O4}Xs)u-^0Y~m=tkX% z%-}VBo{Gsmm&w)IueZx53hE!)ujLl$fAcNZH;O#z{NO$Gu~=1IC+clt7s@zodegwH zY|G?3g1thK{<41)Yh|NmJW^WAh?}G7!To%5^(jB_Pi0H#DO`N3S zj|Fb)YQ322bruA@dr<1omDSZ0<4H}$z9%!@P83nLJ%MN=|5Y1pWI;PYmPStK!t%)y z(Q=+L0ZVHnw+l?I-$(&nV@26d#vX_5ZBig+{>I?Ed;>l@;v^)zuO&lN2r{AEi-Wl( z8be(v7cW2Vy>5#I8B?x(EWx6?%k4(aGh)iIZ!$OCN)fYHzWqKLR4`I|6%~JUKQ*UM zjm3^{=hxohN-%$0W-BlELxL`1xN{s2#~ckwh!-czETY>(D`W%%GwTqjZ-Tw|)}GED zZ77kN&v}54PM1!ed=3Pezg@h{=^lg!s{=5-$+%3yqY@+=~FDDFJ zbiC?o&&U5>tn@vLQ7XQ_tseI!)= z;>tljx>8()q_sZ7q;9&-fXg<}p&|Y_%bN~8EJPAET~!!$CXHGwP=twh6{?W9af|RD zGcHpr^VUapN7=FH{FuMnJ_Cz8)>A(qwYz!x)AufvN)`chXFh)gI(0haEw8D#`>=pl z>r*ZoU&D7-#}@oJ z2vOWMdgC%+KKhs>vFJTRz*p)&O(u#QREw26=bEq1Av222TX#W9_-#7~ts znOaq6*Wr~wFbL$#N{f#-iVF29zeb}r&CS}xas+YLQsucNb_NN#yHSFJOw`^^ygCr0 zHO|6~Os&;2!w8$6I^E_zzDeOjW!C#u(Qv$0o659mr;k1y49nIG<1ZGMk*D-)S=VzY zKyoNNn&U<~ z-uSSg$NgOE+KaLZd0~u!y28#Sc*bp01z<$0(SZ34J?&>NzN}kbb;K?Fqs#(}70;PA zfg!hltcDTiUezpMs;OY`d!A$K_5oAYc(9=o^dkB_~|UavRx5@r^fMZAPF5c?A@fDmw^+z8VxSxXo| zb!i0AS39xD3X%`bz_U`V^u&5gLyjBz*BZHX=UQjy`3=owr-uH`(YR0}5#40q(Uxv| zNJ=E(2fUhudXL|R+@i7GwqT@5#EzuYp;GhLJd+6G4_JgTaphixt5IbqcU6G_We>G0 zOao@C;L_p1@@9E=y*v?U;zYM|UV39u5Gmn)nu+I*VPPqr8$g9L?we&{Q(>XR89(W zP@Z+W3jhB8$^2QS8^2(69cT5OYz4k6F2v#bEjf)d4H^LAyI(B5!V+kFAO zNg8oj)q!MdDl6h1Ck*L=R8z>?*Gzc0JCT5rR}_E!VZgmM$&6^ODw0kVCd8XlDH}n3 zZY&^yo@Rob+Aogm3PLF*rt) zWWxw_Qp*ky)KwLPh&D;NQsD?fo*7s4<=zM8skQ|Hc(;j*KQBKx8tN^km16!)8SmXRd zYNVGio>?v?XSs1!6q13MxB<`qCNCzYazhw6v2LH8GBi-mxzj$K@k_1mbRX&(F-bvr?EyAQu$L3O0fHUO6L}`bdD{ zj6?X$>kZ++leNnLvJ-(s;0<$=;I&7pI^f`q zhO*H^U|>8Nh`p*cqItdaFF}tzY~)xHPLzSLZh2ONP~z4B80?!?r=OFnRno6VWC`mX zZ!&)z1Ew?y_3&dmJM(Nmzn2lNMZ%D)`T% ze<2qwT8J`bW}85h^?g1y5s3eBf*WkOF1X(d9zVC>B0UDDQ*hfNTMwnDl6NqHfzHGj)tozefZ>mW!)L4dkDD@M)~mj$qGWq`|X! zGa~Wi&Zy9NP%4LZ=xEiq-c+|QSoo^g?X_BClQHuuvNSHRKDPSCs)HQ?##UC*DLHR1hxBTP zqsU>zw1NLnV+o6=0byFoB&9%ykZ-tx5WKO$NMHbO7-mIHqHwE0_w&W`B7A+zd2t-% z$&9;)^E096;FEe$BGtBd_q=F?TzrC+k`hjx9GerHE&!_^L3RBZSzw*E_HfGBD^#$0 zeBjhCBSR@q3sK`(WOA}mvj4oWJNHR0R!;_>=<}pXP3W=9AG%&l|5ymIf*4gGFE>>k zp-B*n3ys$6#M_O8@m~Poh6wMr6)jtsoL9T$b8-S^#xn z7k=TBVJ1lA*8>&%?8`xg4>nQ0$(~1mh!uu<|q-(suz%Zy^N#J1e}G}bew)r=?6oVgTI-0!92jdnq0}jwn>^SFmBv9$!!TLxT{|7 ze8yRg^#?l*ZjQ)$0QasU&KM@VyG`kwQ4m6!jSqf8Bwgzzd8E-_X|(UmknOgR6@i1Y z5rXyNE&K9(_CLO2{eclx*J&s zMQpxwcIoaNLSzq>u4>{v`o?ZkoQqT7K2AKd>t1z)dK0?9)4u?9~3ne05k zBux4Ai;+HFZwYUm^`8i)M>!w4ohWp;uRS@a5} zc0S{RRin%3E!^h;1s`Vo76Fiz*HP7GGRS5vKkl7Z^r_J$`R+9~f=H*rM{ADpxH$b$rWEY#JaWqKVd+QAXJ_3~wOo07f|RTMDidsinAj znt@H-8&vI}1EV^ok2h^_ni=eMVU{pZVW)HVqY?%L()a`aK(A{kaz=>6GZ}C1O|0>U ze;_#2=X=VzD7g+*RLwCOnZH}lx8uP|OT_;rId4Ycg9zP9HUf~w_hs06E$I!nj}q=P z$GTd8MHMaITdX!QP*+a0T?iI4db|-8sfWHiheLlqhkSK`Ej;f(Hcd34g_pH5g+GEo ztW0YJkrUoBgpaSnhk;!AoDkfwq$*GLl`({ud|Crnl#NZW_57@0O?pJN-f0Iff*8us znhEMh&@x!3INAjNn4FCT9Z8sWWD?yI7`&rLJD3BeV1HUq%yG|ikWUMG0#!}|XGMDr zXp%sYtu^nwK;P8i6#JNd31aAvRxo~06CMgT8|%rw9vGbPeor+R^esq|OaSpWd_Ux# zWU3aEs>j3@!5hv8MVf|j62myCD1kj8WNHEXxCi{6FFwwzLOQ5DL0Y8ACQk-Fc&OEl z{ap>~2PtFsM^w0Bk1cQ&hIbnbaKlVn#}LJ}KX%nfp+wt-%D^VQ+Lj@qVp@3e6vxkd zJ`M-h2wc|B~zkvJG70+QHql7NCKtQw{`?VZ-LA%3uo|i&@5Z|i#HaEJ#X!QUE;*yJU_PZ=kllg(ny1=F(YVmLR`i{bZz{R+}6)k zs>M-W-63`lKL%#!K=uG%HIDa5G1T9!E>AyD-=AvTyzj40UVos<_oVHmo7L|nhv(l_ z+g>)x{~&e$H8^eC@`9R0H?n!HcD>y8Ss_++MNBfsuK4n{H%+^8TWBBzeqkts>wMpU zxI?~i605o$XUn!l_%^BgK;AD)rFCO2-d9!oM+5tOJ85OV{1a;(`z{`JmgO*V*mDNX zyF5){w~%hCE{n|_wfzt0MCvYbCB&TStnNyu$PrKAx|JFui}@em`+K1O)v@{wy#@g1 zh|l$BO0l_gXLXroGY!A{HF(!2e{*L$zp|dRqokmmBiS zk%v>0pV5;6zZnPmv?qqSa$Qh2RyK=7UB~}zbS3Pi%ro#{UgI%qTD~DE9c(YU)!6gl z*8I%MSGb&TIbCENHOt8t+4XX`bA4&>_{95w4ugF7Y}5FCpmzHnnj%b*bSy_n@7*yt zhEj&mGOL39c5x`+cN~H#WqZEr0k~a^)2`GMq)T{bP%NJ}l^L$oA^P{p7xf3S9u7+H z7>)yc#*ruGTFoz?Up5O3H@$y#U>YKL(nI9D^=pt{tuQfmykg{Q_7Zw&X6L~__Mb{z z$;}V*EOl?fD5hK}Q;IFyEGkLH)!t=r{99}U+K`J2KaE0{hXsc51YC0SN#@x#ytHJK zr0*zfCSo*pQQ1r#Op?|X(vE!_ui!iJ|Bnm$-@=BB@k8DON;4`Tnb`I^9Tg{+lpm4+ zU;G`$W#I$=lzr_C<)kZ0AVd|^@0i~!qD|N1ez1?>wjF8?;NBJ-O$Cs7_IqkWeD^GN zICtJOSDUijMqL8N)EFfg2t9v{tA5vZJJ7gyNxT`XVALef?m|IF&*e!zYEZcT_VXwG%r?9KFE zC{o~B=(Uv_Og=}&!?rf@gNA7`Z*M$jY3Z6nX9n(x5MR kVn~4NeHhsJBK$@!eYojif6%kem38E}wi5{Yt)gxM>22s{pEm$PcyND!ujVNJ- zkdUkry(XeWZ_(rD{rUXy`{(;T-*fLdcjlRSX6DZGoSA!{^NKal(_~`cWB>pFOj=lV zBLILJ3;s93=jQ-}JODx&fCT_b0f0aNKqU;|Jqxh=2w>rZ z1NZ^~c_hF`TL54d08j=1_yB;s3qo1h*4Fmp$B(nKvwVDfd3kwlZEgAIh>&xY{`RVl z^N#$6{;l(^{>I$%^Y-)e@bmWe^L+2~e7o~}mGf|w@Nhu5-FdiO`+uYgTR7Xr4KR5q zK{8O8&Yyk`7}~&ZoWBC(ha>j)r`{78TN@v4Ek0X$8FU=}_*?_96&`*bW^m4y3ivi~ z4!F3^qra^J0Q~?!_?0W(06;tda4sHiDiALKI1d9jUT}c(Q9$L!#(6`>#x#EJ{Cvw~ zt9)#cv_aaElDf#i@87@s`uf`2+iPoUGcq#5!or-Lopp6}rKP1gIXN#frZPvl$kQuP z(XjL3i?>k;|IY{7F1$Vb91yN@-i}axFyGSyXjcIMHa6NX{9TOy_WSe5pW9c_ zz_ZH=R8&Bz6sN57Tfgg&gof{`;h2K3`PTz7ao;1Dety5%1kyR*W!KY`PIG*Nm39#H zL!D_qsBiw$m9{5eEXc;0F?{UtzDkRG$w@Zr1H~Ua^wHh6zP_UN^!exRrFWK9vA~~B zDH<+8<~I8Ha;1+vJIt3jPMj7ZZ(6Sv8gRXiD`NH&fDU)%(LP{egK0OXsz@-z-P6i{ zf!ge9S(ey2jvwmUD|33n40VjG;vGX~(hV4ku{w-OB5=7265%VGHR$wP-+ta3*KaBj zs}GIITgV?ZW2!&mUnwjRm(@9z`;0gDH*)u&oX7-9uiszGjnkM&S(VUH6R(!L#&}IR zOHih{dzf>TU~1HJo?O~$XI1~|Udi^=KkCTRcOPTUh1kCaDRuv*)=D46>^fGE5$6#Z zMgO|cyZm~q0!kx39-}Oy4`nOpOXIDZlz#s#463yPJGUK5x0Z`EX-rKrvwoKS64pRC zc8SZdmL7^XHTv^-0I8i&Q(Ca9g=L8r{GM{$D{?7C720=|WoXb4s+S_?gDx3LMugP} z9_XiM_TDl|^Wn|7F6Pn%^;fziI&3)*?Dq9!68E;n>U&w)KMQD!pw*2-p;rVKv`s&i zSgnZY)98H}MKJi`LzhIXc{|RmB%IKj?pvYXGS}VDT~@S$-6|2T?cSGCfT>t-Skcx_ z70ZQDJ5AA96isKX?z<&9T`%~TY41EbyhpDspYQ>k`t?U5#BTGQpD=W!h?T&2_+mS| z;wuYj?%ZiHOS&xnMwSS;&%eDQbydF5Powsy)?|wGdv;&HFomLtI`i?$+4aoKw(ykP zy-y}t{2ms$1j7FD!$YBeLw!H=L8Q5H0{B$Bt1o`l{*?!Qg#MK9#+9|CSQn5LIr7e( z)m>{-AaO+B47QQBR~%=n%lP*DcrD4>T>QAQ;mO;hvTfEfmxL4qhsCwk^Ppe%c@Ak5 zmr!%%Gz0o(ro>SE)i3Y4u2P?rw_m>dtPzoPV*hB9Lr9cUQDmWCb68b|T}-Mf`qTbAQ5)N<57P6t}ds+K9@+akwrEFPfJWq+f@zwJT2ayi%(aA?EVYE6QO#8A@u;nwi1M#R>#aZsOzfNPxn-WUxzG3>2r14 zn*a2AoUnCwUs$hR-~SIS)OhL9b{kUGHr;&6pr5RV`7OLQ%!GW#aaLSgKnI=1hZu(QNR*ZS+%>3++$#Uz0)cn_f zg{W#7P2LI@yt8RM1b_PDnCGBrcN!EsM%?gu|kE^_Jd_?Oju5o#dIEFnw$8tzBN2iXnyDfC={^GkvYq2%$Crjh&8gPpvY@(J@%KO$RlpN$lCsC5KbOx09w0`9jSW3;9fV(*+rv5a#*D=Su z<#SreBgipx(QH7w+Ei}gCQE5un#Vf1T12cP5TE`sE!ffX*^{f!P<@J!M-Dg2)e@Nb z%<-ZbQ6rDGue*&42LJ^3sY+=4&JTgqjJiynX9nKQAG4(gj;6H`GLF~R$e+^nga_E{)j!#LF1ZOZocx$dy`zFc(f7wyk%O+vgk^Cil{YrETn zgrhB050`bd19%+V$z%^S7G|++NBwO$0d%xtRP+pYHOsj_8J+&ajHQ-#!)EfLiVNNG z@}7QDcdLWC`k$;=onXhES3Ot#z`xud$VzpUm`n3PbdDq+ioK2VrSyh!7CLNd{4?Hh zDLB!VVe~L_J#eodzmf;seWCHnF0-Blya=N3Ene-P3CjKZrFyV(tNyzzGglJF2kUMS zSJxkky_<0qDi>a`D#MMB7rG4>Eo(!`y>9}Vg!0y_1)F@hyQ<=> zs~x>;vG&yB8H{>qzm^Er^$5|oSMm(#glSdf9cNd?i@B)eTg7qt(G zu@S%R>S{={ZEQ#>QiG50e<{;6i2R_Yl@*>$a*Xg+=zq=cEjc+JwMbOee4QZ|H6cf2 zwt1Gx!Q=_^%=BXh@NCN_8>}oQfw05+w}G+oK*-%UlNl!+ zHvd&sG7IpZ^-|rr7swFSosxHE`Ec6FfSxLfU3kRoRGL9GsxQkR4EN>6U3~hxGhlk_ z^`d4{em*mn9nvS$WpvF;2j^LIyb0&it3%W#opo8^cf(jqOOI*YQu^YqgkTXRdF z(go{TkW}{J4$!~`!OMI)OpGx&*NNmgxo0;oCb5#8XWB0 z9FE{&IkqSxO@U>-scy__3nc&^HzO2mDG2S3?^U=W9$|zSu0kkBHr>FTVg&~##JQO} z#m6T(lf#o_9Z;64Do*pS>li;(9TxxfLQc%4^$a(v{cR8q#grVoe!C>J{h}M=WENr+ zT}5Fd{<@bq8xQ1UtjBFX;AzL)>)g^tBFzA3w$7iEd!j2Z*oJ%1Q@_^7np0SEeuV>{ zgZ}V<%UL;zSrz%XxyIuOw@QY=Q0hoG)2A{nujXh-23_PfW;G2Vq08b3#x@BnHM>=` zN$(TJ5BB%8peZMTKGPBRS(KDQvtL9JQB6CerzdiZqlY|Vp4F!2(^0?&+%4nLwN)qd zG^oz}u)V13(rwMZyvy?1l?lV9qKtJ0%koQ-vXI~^(nFz7^gGJu(bZvpHvPXMT$zw| z4xM|m@BFs#<`caMsF*Cn=_lnN)sRx96XFHU?h$h&|1^a4bWkkh zl_P7J6>cRJeAE=ALfY+OtK1+{SIVI#+5P? zl}ZGw6M#nniP-cal{>zHLHAax;^R;$J$PtB6jKMq72v+B@B(*61(xc}%)lwDP|M!M zFUx&;8ckt+A_5hFJw7NAUBg4`lS!fE%QQ*($KiyoMARW4f+|ahicgn3&pb5~lG+?FpOe|0J7!)~N%G=(j$oW} zC=d`+Vu$F;LR>|OC_7d9c`Ch(0cQ`^iA2;en_gA8W>*?Ftwo}@df@%Ur3*P@1Z5Bj znb1cl#_O8?&WJZ-38-Yfge?}#@`Mn(rRx-9ttmU?Ld6W51@D|OUylhgL0>uUccz3XO0u4B*wLkE%u_(7cFpN6Yl1i=lF=Fesgz)rc*dP(Nr_ZP0x^ zf45;Q=_z57aGNcstnyCAi^5PsunPXC1RK5}Rt&j#A~uKYqo#KA%I;$AtOM5i*Qo{| zBf|ETQW-XM&j77ouuXcu7cUcimE^usheqwe(bxH480zKEvr&X6X_DQgZIxv4=h!WC zdat691jU&-ba=7#TvTow4(^h6O_!Z#PWxw8qKdQ^)JgO+XX@+ELPonM+ipyt>?sPs zM)^Z$mRjpJp@Dt%&o6V^Bd+hV%ogHfhCZa=$8U2%c-)o&z3uV?LpXeyk$EW4qa-k|q+NxCMgAwD9XJ6oX)cV1>Pyxa3QtBeC}jMMEm zW=44=5FYZAID+JxS*DmXM|sB-m#>L0HPhD(-0!{)mQBvwW_6aho+ZBY+rh`gpu$XQ zhB5Vu$IN_0h`Y<|NCQrz01txyE&VsbBY1>8nG z9A;hWZmT13!zAO%{JlwZ7R2TT_Aj52YJTanJe8HhVbPg!cD&pJc1Ptxvxx9HnvajH zSz+I66V!z{EwVs)Kn{Ickf~LR9EfZheq71;Li)m1C2cLS*~6nDS$IA;hZK3Eyi=Ul zLGiB2*548oR;>NdG}jAhgxgm9j1lLwD4a{3``IfVg3Ya~53dJrvFAU2vxFK`ce4?4 zW^!d+(_4GU>TfuxpI^%og<)i0$NUy-XmZA+U-&+98A;pb90&fA*Hdk%%ZM22;06mP zb6SaxhKG`0j^HBms^Td*CM*W#Dq7wN>TBqW;&IgAx~#8)Pe7#(4Z){!QXcWHZiaY| z56*GQe&&?E{7^nQGoZ%ZLblo}q&>v_#xKbJQ@Uf32T_!qv%>Qfv%~gWl7aRWqLrEA z_bSp%tbQJlOTQ%az3IjTz0=z0u1IZhwT8>x$cJOx5Bt>1R@B{QdZixr{Eok(pMFFO z75((Lg(Fs`z_ty}76QZGj%A_EjtVhT)HD;hqvjPf&T^!^o`NmE@9Fp;Fa1^ENSXvM zLn6qiQtHF)-#4KDrZ8q9%V>kbg7`}QcymN*)m?#lCj01av8PAWhNUk2IJhVs{H-RO zzOtCnEI+rG2l~cis6cl9%sP2%qd)lFit*!NbdNGSSoxJ(biqOB*Q4n!<^+$IA~BcH zjo)xFOllOYffRw!gjwz0jPX&A*!wmbh5XH}HlwP4to^`r+i|QTgyxoIi?Oxaa%S2~ zC0+%`2?- zhF-VWhN^o+$x{O^pK-tUZI&}N8`uefQsu@P5+vf4GQS}glWxe;?RwLuTjIs7y*Tk^ zX0Kdjs;mbmOhPbOPNFf|Hwb7EO3KmP1qf|Bb^ql?VtYnY-h{2jHF0kKg*gR#BEfNgR&Z4~wai`<9tNX2|37r8UANU%tyVnQ(8gl<$Ri;+ywt1sj zl)@2xCu^ePG7e$%YCTKq>xqRslD>n&4X0&+@UVX}izzBtdf+RuefoJeENa^&?CH)& zVdav(Bw?SZvzv{$8f1+8)Z51|O+o*DQUdR(Ggobizf-pRl8Az=Qia1;Cp9K4<2nsE z4i62#;$Jv-_?9@OHp!-+ABNd$TS>4g3R}j^@#KAA<|q?h;=GlG#+#_lzc+gNQGSMP zWvV2XZwRAhcFAl?E$ixjs)h{i7OYJ>x}eZ@#VLYadNo_K9}k8+qH+y8SBOzQdB`$D zy_2YaRoN_IzKAmLwt?Zat6+Nyh75chknexFN$4v?7EE8t9Es=Gp|<=^?VD-*Y2xGA zbV~dA=5f&x*Lj^OoCJvcv7kXSQ*=yk%EU;A{N&T=b5gF?6Oo4pRzPUJkVhLU*~Qwu z8xzFRCqFHwl@|4euFxDm-*$PYdzhv5(^WYTURDye$jk6e`0yPj3vkV8Y^qx}LIL`T zwWej=Bl=4s79SM{Fe^Y*2(6$O%-)|yKZZZcLnJwPEQd*szB5iZSX!DTk94s4`4e|@Y-+dcQ@y7w214exN<>3oI? z6eCp9?K3z?FefaD2PdG=o3>v+tg=@D$Uj#SfSKKl@nI%mTkUTUi{(y<5945t> z;kqTmINwKC%UF;FIA$K&4;Kk2xs3s?TK`(PB*o@xK2Kt_Z>Y2&HLZ+10`zT*O~J$fkPFZfGQxbr7#HKpU) zRaSX6+E2P9WR>?f+G>8A>N8b-NrG5OVuT>iYb@Ei$}{?JXX~<%bBo_WB6Nig9(LoSI#v}Zc4MRHeCA(j4b0lh zMF)QTRm7;Ru7iEC;o?919o`{a)d8!NQcq(zBaU9ID?r`8;Gub`&ZK(8yg~GQge9F& zCv^<5f-BnQs*1E)k(j$Lyb3uFgeUs(>hrw8keKbM1}HE1tKRfcUab6FO(mq^#v(ye zcf%h&J9tX#_D%qX@1wL(uyf<#sS?FsY1K?oV2Ok4S+xcTT%Jolvy}Tw5S)6omLv&@ zZJ~TY8<)f{S*8#;>~0n(c!!cRas$Q?tjlmhyO9}oiC(|jfDdA=2i1$@H(hkj*|<`g z@K_9(QSc@KDBDJ|i?}GdeImkOi4N~@Tp)86!tvy?>YQM8G7A|J=?$+!A>Ho4MYPCI zsEGH)il+#vN;LJl&Qp@yK@hVsdpHI*yhOZABtPttfw-!uvBFXiYbTK5`2)k5S;mZl zR~A+Ibx#QVMf$9hczDe8L`;L=-ADVuCfSC!r0Wb6V6seV9Jc!D5ZAE*+dvG|%6WO1 zRk~#uTjx+9rS;xq!euNT#4#|@_OF>Ra}yfqt6xY)BeOLZx~N*Fu*OKR+%dAMYW}oj z8p!e6#`dA%LG1AUW5TYzprIGsvFBH=7WvilJW4Kz8eDH;xrnU{QO??m8G#O(sxqD zsRPt?j+KRuPiw)6Yw{-wNzFmeE)IX^zSO8N1b@ob1k=bi? z9{)9&$(Lair*1fG^V#jsYHmtK-1gvc)$Fvhhe5hXDhjc^1zhS`!Y@@bW-J2%Jo+HVe0Ym)sKKiF8vn4mrvab0Zr6f7~1x(P}k z97D{ooJ1se5Z7+kqp7w#hF*v)cwrHM^sfh_{%8<)xWF#lB4Qy6g$1v0DxP}36YwnE z82>1XW)k21npSM<;Gux5>X#eP3Tf4PMr}t}+yDjBhC=|=7=MgKwS0spO180q|K|82 zXl0!XWb-wu0e1PtM3|HqOJu>5yGw?osa;Vsu@(bsfD^s=A;O%Q&hQp* zISauEkeX@X7W9Cp22)rt>&6AO5errBO`|=Xkj&pz;DDV>K;>(Mc=QrCSsc9|rk2J3Xwqm}f87E@^>3o+c zM%9FzH}qE~M1^4H)CCJP`N9If$Uqxz@wePo`Ld~yk7hIT7QEZMEQt<}t344~!-z}! z3f%C$8Svv^GlFp*meJ&wKrjM!=G9o+sW#Y*!{4-%O^y6rd+`6% z;(Zyl=D3(>sMQ9=eWBh?IhsSV#&Ft`D7F>Y6;MHfnEF{Pka1ccE82;t3BTD5;EaDP zh*OC%qvwoWE4h6ZfzCH=o+La=5i?eNRF0rOX5I5%uClC-1+7T@L?=42hR|nWQgqmh|qiY$UsBY44wIhbP#f0aStyL2Fw!wVF8Mjb&VuGRcg1@OD>monk=T#SZN(C+wUrpf0kG(Z`$b(; zy-fYp^ChK35@^D|-pV~*Rh3j_btic!?7^hG;+Ixzp}XTfDe1b)>p4qC4AB&H$oap5 z77&^PO!Seg5`||nNAhhhCL?BKly%BH5_4XYReC{c!O~t)RIARJTJ$t9_z|mf2=bCz za31C*;r1Q@xePFaL;5Y-+Y6*uA`Bh*dk*1nqCKGKKA-v95rq_Fc4Gw216jq4LPNl? zh1VH-=B50&58r}|jh|RRDad)un{Vpd^-HO4TA;n>RC2hTZEeZ|z} zO4eR`aqSTKOQ#N4yI;))AbR!bpH+n`s&%atL5##FuDc^45F9t7E6>z{?E^V}EwXpc zL>uSE6RmJ?gd~hK_F44cGEhm_0zB;JYvg*W#Yv?6Oy@5D8mIbDbN-3e77{`;f+ak$ z(|yibK7!bi9MEPno9n)5C#If|gur##S>#H2?MiBM(q5wFIJ!gVpj9eF!#fR}Rk`Ts zX%>k+%k>R%MQ0yu)gQoQAYKcKVbv}7saH&)1}u*~KShHJ84O6W^AjHAa-#%b+uHRw zk2lU#QL0ynW#)i?KP&TQUM0<7htCMk+VT)H_q*%Zx@I-@_I4pgGqF07Ccb!XR3gj7 z=G!x^IZoo+)@v(W*q(i4;Mq_^LEAhU+lk;S>%@9rOTXc0Dxf{m;dSw|i(34c6`EZL zmC(d|NNzV$nz`u5@X($f*fSs!ZxocoKuaJzYO^W1+Xesi{zFEj&yCM_L#Ay>zzVKd z*NVi%N(O?)-gR(f9fRrvl8&`iQw z-z6&b4WjScO@qqbGVvSsqi0cCr7rUg_sJ1+_4IF;qwb?2tr@_@r$pOS9~m-H}3wp(yB{^ua0xuW%}-hMPE*7fWuAhJ@uAg(1QS*Y?r3ps!3 z#>n5;=P|_lWI?{)E+5DT?d^xj*H;BFm^pvs`vK>JVZZ#k)GfE0J+7|eob<)9?RT=I zB9a1m5KUQj1$B2x1j|z(o}s+++Wgi(jis&QpRZ#7szX9PU1d3@Wj!kw8P1&!RzBJh zxcUBCDOYSv^j3o7Ky$?q8~p=jF{aJUFp(Mj>)=#`pCYZ#k|Ltwc)BKNa5FVKHk}iFBAf%Uz#+ZS~0NYqa*%_sk z{$NH{%G-SS;e0hN7z(BPt@wdUd#A|%y_R+Z>ovv&cD+0JL1FGS_K#h2d}Dg^t=d$P z_GGV{Z$I7>nV}q&(7~ zCx>EfZ)8I%IDz9ne|poKH~Yb;164F;fg{va28>6w?G zYOmZDh?1)%Y5#Nmp>;Eo1i8LxhOT3YjY_wh_e0vs2@GqA{6TcG^kG42K1PBO>oq$n zx8Vr(r33i)g0n~1nFh}hY{+QqWf0AK{ej!v^6R>4HK>Tbsf(7ibN#(TSMaLAYZ?fR zd~mSMoXYGpLIX8_e|<_Xq(jZl5SBvZuZ@2hrH5f$ze~eQZP|hG-)swOtZ+ch@4f9g zYbr=S^!#wy&T~`2eI${{e}y_r3UZ@K*=f&b>(pvfA=|{HgVMrH&801KS#{VLH7~pF z1aA{@p|T{RI2EJ@iz#*Ja1yO(3v{{(cxT&+!-1$zI z8}u;O)>oA6eX8x2Iblffj4!Gn!(RsS<_%;7nFAXa^ z&mo+#E2diS-ZZT+qhQAxzq{5 zq79o++d^V6vOyJN4uQ_F7mdVE<4u{JL|2fCQKbW(S77qsOT~4cGkU4H;6&tw(h^C4 zUxOi>zXOAsA9#+2kOfY)N`TBW9?z;NT7j?!w617`KGqk?YcwwTR)&!mGQbMWA-EWK zqv7zjol3790?N=8-tKsr$WD-(wOvLkCXr!+o%nE7bI?0Ia^$t(d)Jnf9(UlQF&}C6 zmqn*$bw1Xm1S9qo4D<_iiTp7U>ldN@GX~YWc38f+uskqwpIn-{CG}{~$+h z(8X9?X(>rPvuW!~MHbNR2Xmdr64`KxZ9h$|(r$FtSn5N9?OVA=Z=crOGeK4b zI*h$!^)ZtEYFTDBrd2vc$hm+xK@Uepa2R10=m%ac55+sr2^DlhM4JV?&P6(H`~>fE zNT$mOu;vh=N4p1{yXPrBsQLN&NuJUQx_kc@tWecE9_dz_cq2_x4sHQM{YETrYVu^j*frZW4?{t(oD9a zo*MB;O51snBOz$(s5pG=l%GFm@(eU2^!M|9Wt-3Yvm{Z^uS^_{k%yeU9_+reJmUR7i|pfUCj zieB0!%mgWDFA8hn&;n2e6rGk=Haym9U~ZWDU1&Kvm^}7BR*GO!AKj0=KiN0pucpbf z)J|341QSXe&Va2>!jr|PVABtJsb@d5;_sJE8<*L;zd8B!HqZ2+qUwsF-VvMH5!PMR z(Xw|MxA9Iq>lSzQSjF6zc{=;F$s5M2?Q6*w8>ro4-v7dIW!2e!#ou3J^=a0+0 zYX+0>-_ILFnTqFvvD8lnqoJvYuO*i>#r;QrJip5wW9>Q0qIZPI#y&-Arp~p!bP`*6 zF=v$?XR>RhfiKIQLj^W>lxmWzJYL;>yB-{!bBnuvG6-bKtjyxoG)-93_#k*I9q6hX=swcZguNaW$N+r^SFP}N-2?A zr2Yj1Pafgsj~RMx5@wRq!s&m0>kB!sy5FjGcqN!|D$U&ZVupv~GX0nCXa`s%3+5n8 zpPPPnlq3=>$@LUTmx|UC>~*lJ_%Nli7C?^cMUMS(+Wiw0THxN)1(|xD=(w#JICrsp z4uaIU@n!Eddlt;{DdAHDI*dsYC={K(m&~n$=N9e?V0Ft#=a$bZ?dCPs5$-U_x3CwUaQ45xIE;)!X*og5ZR|i=To*p?eOR!T>9!Gk*Ic;=bDH za7O&nP<5(%x=!CtnR8P*Mm7@TtW66_wBh-wg_P_({cU$!;Xplz3c9wdoCw_3X**2s zvU27Xl>44qdu_C!)bnMzXVho*ny*U_%H4NN)0?t!|HxnZr=(ZdvG6?~heY7fFPV@& ztJ}&!H*B+gMDj$lB$UAUBx}#YbZZ{rwx{$DIa`}t2CtFYE7eq}50uiMg!CzCE@o51 z`td$*CTHSI}L#G@(QfJZp(*uJHx+x*dF>{Fw&DXQG$TBrW zZ{Z-}MTxTBzT2rb1mKgIQJYi3I512Rw>bCNO_R#qFJ8RA{qZ*UYaV?Tzp|d*0ft?U y5yz!mtiC?RA!jXm${>dz{~xq7H;r%(db#qno90W|w+l=;KubeUy#iy0|Nj8~GtpE4 literal 0 HcmV?d00001 diff --git a/deal.II/examples/step-40/doc/tooltip b/deal.II/examples/step-40/doc/tooltip new file mode 100644 index 0000000000..a8090e4c75 --- /dev/null +++ b/deal.II/examples/step-40/doc/tooltip @@ -0,0 +1 @@ +Solving the Laplace equation on adaptive meshes on thousands of processors. diff --git a/deal.II/examples/step-40/step-40-commented.cc b/deal.II/examples/step-40/step-40-commented.cc new file mode 100644 index 0000000000..b94923467c --- /dev/null +++ b/deal.II/examples/step-40/step-40-commented.cc @@ -0,0 +1,406 @@ +/* $Id$ */ +/* Author: Wolfgang Bangerth, Texas A&M University, 2009, 2010 */ +/* Timo Heister, University of Goettingen, 2009, 2010 */ + +/* $Id$ */ +/* */ +/* Copyright (C) 2009, 2010 by Timo Heister and 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace dealii; + + +template +class LaplaceProblem +{ + public: + LaplaceProblem (); + ~LaplaceProblem (); + + void run (const unsigned int initial_global_refine); + + private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + MPI_Comm mpi_communicator; + + parallel::distributed::Triangulation triangulation; + + DoFHandler dof_handler; + FE_Q fe; + + IndexSet locally_owned_dofs; + IndexSet locally_relevant_dofs; + + ConstraintMatrix constraints; + + PETScWrappers::MPI::SparseMatrix system_matrix; + PETScWrappers::MPI::Vector locally_relevant_solution; + PETScWrappers::MPI::Vector system_rhs; + + ConditionalOStream pcout; +}; + + + +template +LaplaceProblem::LaplaceProblem () + : + mpi_communicator (MPI_COMM_WORLD), + triangulation (mpi_communicator, + typename Triangulation::MeshSmoothing + (Triangulation::smoothing_on_refinement | + Triangulation::smoothing_on_coarsening)), + dof_handler (triangulation), + fe (2), + pcout (std::cout, + (Utilities::System:: + get_this_mpi_process(mpi_communicator) + == 0)) +{} + + + +template +LaplaceProblem::~LaplaceProblem () +{ + dof_handler.clear (); +} + + + +template +void LaplaceProblem::setup_system () +{ + locally_relevant_solution.reinit (mpi_communicator, + locally_owned_dofs, + locally_relevant_dofs); + system_rhs.reinit (mpi_communicator, + dof_handler.n_dofs(), + dof_handler.n_locally_owned_dofs()); + + locally_relevant_solution = 0; + system_rhs = 0; + + constraints.clear (); + constraints.reinit (locally_relevant_dofs); + + DoFTools::make_hanging_node_constraints (dof_handler, constraints); + VectorTools::interpolate_boundary_values (dof_handler, + 0, + ZeroFunction(), + constraints); + constraints.close (); + + + CompressedSimpleSparsityPattern csp (dof_handler.n_dofs(), + dof_handler.n_dofs(), + locally_relevant_dofs); + DoFTools::make_sparsity_pattern (dof_handler, + csp, + constraints, false); + SparsityTools::distribute_sparsity_pattern (csp, + dof_handler.n_locally_owned_dofs_per_processor(), + mpi_communicator, + locally_relevant_dofs); + system_matrix.reinit (mpi_communicator, + csp, + dof_handler.n_locally_owned_dofs_per_processor(), + dof_handler.n_locally_owned_dofs_per_processor(), + Utilities::System::get_this_mpi_process(mpi_communicator)); +} + + + +template +void LaplaceProblem::assemble_system () +{ + const QGauss quadrature_formula(3); + + FEValues fe_values (fe, quadrature_formula, + update_values | update_gradients | + 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(); + + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + Vector cell_rhs (dofs_per_cell); + + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + if (cell->subdomain_id() == triangulation.locally_owned_subdomain()) + { + cell_matrix = 0; + cell_rhs = 0; + + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_point + 0.5+0.25*sin(4.0*numbers::PI*fe_values.quadrature_point(q_point)[0]) + ? 1 : -1) * + fe_values.JxW(q_point)); + } + + cell->get_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (cell_matrix, + cell_rhs, + local_dof_indices, + system_matrix, + system_rhs); + } + + system_matrix.compress (); + system_rhs.compress (); +} + + + + + +template +void LaplaceProblem::solve () +{ + PETScWrappers::MPI::Vector + completely_distributed_solution (mpi_communicator, + dof_handler.n_dofs(), + dof_handler.n_locally_owned_dofs()); + + SolverControl solver_control (dof_handler.n_dofs(), 1e-12); + + PETScWrappers::SolverCG solver(solver_control, mpi_communicator); + PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix); + + solver.solve (system_matrix, completely_distributed_solution, system_rhs, + preconditioner); + + pcout << " Solved in " << solver_control.last_step() + << " iterations." << std::endl; + + constraints.distribute (completely_distributed_solution); + + locally_relevant_solution = completely_distributed_solution; +} + + + +template +void LaplaceProblem::refine_grid () +{ + Vector estimated_error_per_cell (triangulation.n_active_cells()); + KellyErrorEstimator::estimate (dof_handler, + QGauss(3), + typename FunctionMap::type(), + locally_relevant_solution, + estimated_error_per_cell); + parallel::distributed::GridRefinement:: + refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3, 0.03); + triangulation.execute_coarsening_and_refinement (); +} + + + + +template +void LaplaceProblem::output_results (const unsigned int cycle) const +{ + DataOut data_out; + data_out.attach_dof_handler (dof_handler); + data_out.add_data_vector (locally_relevant_solution, "u"); + + Vector subdomain (triangulation.n_active_cells()); + // could just fill entire vector with subdomain_id() + { + unsigned int index = 0; + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell, ++index) + subdomain(index) = (cell->is_ghost() || cell->is_artificial() + ? + -1 + : + cell->subdomain_id()); + } + data_out.add_data_vector (subdomain, "subdomain"); + data_out.build_patches (); + + const std::string filename = ("solution-" + + Utilities::int_to_string (cycle, 2) + + "." + + Utilities::int_to_string + (triangulation.locally_owned_subdomain(), 4)); + + std::ofstream output ((filename + ".vtu").c_str()); + data_out.write_vtu (output); + + if (Utilities::System::get_this_mpi_process(mpi_communicator) == 0) + { + std::vector filenames; + for (unsigned int i=0; + i +void LaplaceProblem::run (const unsigned int initial_global_refine) +{ + const unsigned int n_cycles = 12; + for (unsigned int cycle=0; cycle1) + { + refine = (unsigned int)Utilities::string_to_int(argv[1]); + } + + { + LaplaceProblem<2> laplace_problem_2d; + laplace_problem_2d.run (refine); + } + + PetscFinalize(); + } + 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; +} diff --git a/deal.II/examples/step-40/step-40.cc b/deal.II/examples/step-40/step-40.cc new file mode 100644 index 0000000000..b8683b8024 --- /dev/null +++ b/deal.II/examples/step-40/step-40.cc @@ -0,0 +1,715 @@ +/* $Id$ */ +/* Author: Wolfgang Bangerth, Texas A&M University, 2009 */ +/* Timo Heister, University of Goettingen, 2009 */ + +/* $Id$ */ +/* */ +/* Copyright (C) 2009, 2010 by Timo Heister and 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. */ + +// comment to use PETSc: +//#define USE_TRILINOS + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_TRILINOS +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "timeblock.h" + +using namespace dealii; + +void print_it(Utilities::System::MinMaxAvg & result) +{ + std::cout// << "sum: " << result.sum + << " avg: " << (long)result.avg/1024 + << " min: " << (long)result.min/1024 << " @" << result.min_index + << " max: " << (long)result.max/1024 << " @" << result.max_index + << std::endl; +} + +void print_memory_stats() +{ + int myid = Utilities::System::get_this_mpi_process(MPI_COMM_WORLD); + Utilities::System::MemoryStats stats; + Utilities::System::get_memory_stats(stats); + Utilities::System::MinMaxAvg r; + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmPeak, r); + if (myid==0) + { + std::cout << "MEM: VmPeak: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmSize, r); + if (myid==0) + { + std::cout << "MEM: VmSize: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmHWM, r); + if (myid==0) + { + std::cout << "MEM: VmHWM: "; + print_it(r); + } + Utilities::System::calculate_collective_mpi_min_max_avg(MPI_COMM_WORLD, stats.VmRSS, r); + if (myid==0) + { + std::cout << "MEM: VmRSS: "; + print_it(r); + } +} + + +template +class LaplaceProblem +{ + public: + LaplaceProblem (); + ~LaplaceProblem (); + + void run (unsigned int refine); + + private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + parallel::distributed::Triangulation triangulation; + + DoFHandler dof_handler; + FE_Q fe; + + IndexSet locally_owned_dofs; + IndexSet locally_relevant_dofs; + + ConstraintMatrix constraints; + +#ifdef USE_TRILINOS + TrilinosWrappers::SparseMatrix system_matrix; + TrilinosWrappers::MPI::Vector solution; + TrilinosWrappers::MPI::Vector system_rhs; + parallel::distributed::SolutionTransfer soltrans; +#else + PETScWrappers::MPI::SparseMatrix system_matrix; + PETScWrappers::MPI::Vector solution; + PETScWrappers::MPI::Vector system_rhs; + parallel::distributed::SolutionTransfer soltrans; +#endif + + ConditionalOStream pcout; + + +}; + + + +template +LaplaceProblem::LaplaceProblem () + : + triangulation (MPI_COMM_WORLD + ,typename Triangulation::MeshSmoothing (Triangulation::smoothing_on_refinement | Triangulation::smoothing_on_coarsening) + ), + dof_handler (triangulation), + fe (2), + soltrans(dof_handler), + pcout (std::cout, + (Utilities::System:: + get_this_mpi_process(MPI_COMM_WORLD) + == 0)) +{} + + + +template +LaplaceProblem::~LaplaceProblem () +{ + dof_handler.clear (); +} + + + +template +void LaplaceProblem::setup_system () +{ + TimeBlock t(pcout, "setup_system", false); + { + TimeBlock t(pcout, "_init_vectors"); +#ifdef USE_TRILINOS + solution.reinit (locally_relevant_dofs, MPI_COMM_WORLD); + system_rhs.reinit (locally_owned_dofs, MPI_COMM_WORLD); +#else + solution.reinit ( MPI_COMM_WORLD, locally_owned_dofs, locally_relevant_dofs); + system_rhs.reinit (MPI_COMM_WORLD, dof_handler.n_dofs(), + dof_handler.n_locally_owned_dofs()); + + solution = 0; + system_rhs = 0; +#endif + } + + constraints.clear (); + constraints.reinit (locally_relevant_dofs); + + { + + TimeBlock t(pcout, "_make_hn"); + + DoFTools::make_hanging_node_constraints (static_cast&>(dof_handler), + constraints); + } + + { + TimeBlock t(pcout, "_interpol_bv"); + VectorTools::interpolate_boundary_values (static_cast&>(dof_handler), + 0, + ZeroFunction(), + constraints); + constraints.close (); + } + + + CompressedSimpleSparsityPattern csp (dof_handler.n_dofs(), + dof_handler.n_dofs(), + locally_relevant_dofs); + { + TimeBlock t(pcout, "_make_sp", false); + + DoFTools::make_sparsity_pattern (static_cast&>(dof_handler), + csp, + constraints, false); + } + + { + TimeBlock t(pcout, "_init_matrix", false); +#ifdef USE_TRILINOS + system_matrix.reinit (locally_owned_dofs, csp, MPI_COMM_WORLD, true); +#else + + { + TimeBlock t(pcout, "__prep_csp", false); + SparsityTools::distribute_sparsity_pattern<>(csp, + dof_handler.n_locally_owned_dofs_per_processor(), + MPI_COMM_WORLD, + locally_relevant_dofs); + } + + system_matrix.reinit (MPI_COMM_WORLD, + csp, + dof_handler.n_locally_owned_dofs_per_processor(), + dof_handler.n_locally_owned_dofs_per_processor(), + Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)); +#if (PETSC_VERSION_MAJOR <= 2) +// MatSetOption (system_matrix, MAT_YES_NEW_NONZERO_LOCATIONS); +#else +// MatSetOption (system_matrix, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_FALSE); +// MatSetOption (system_matrix, MAT_NEW_NONZERO_LOCATIONS, PETSC_TRUE); +#endif + +#endif + } +} + + + +template +void LaplaceProblem::assemble_system () +{ + TimeBlock t(pcout, "assemble"); + + const QGauss quadrature_formula(3); + + FEValues fe_values (fe, quadrature_formula, + update_values | update_gradients | + 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(); + + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + Vector cell_rhs (dofs_per_cell); + + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + if (cell->subdomain_id() == triangulation.locally_owned_subdomain()) + { + cell_matrix = 0; + cell_rhs = 0; + + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); + + constraints.distribute_local_to_global (cell_matrix, + cell_rhs, + local_dof_indices, + system_matrix, + system_rhs); + } + +#ifdef USE_TRILINOS + system_rhs.compress (Add); +#else + system_rhs.compress (); +#endif + system_matrix.compress (); +} + + + + + +template +void LaplaceProblem::solve () +{ + // we do not want to get spammed with solver info. + deallog.depth_console (0); + + TimeBlock t(pcout, "solve", false); + +#ifdef USE_TRILINOS + TrilinosWrappers::MPI::Vector distributed_solution (locally_owned_dofs, + MPI_COMM_WORLD); + + distributed_solution.reinit(solution, false, true); +#else + + PETScWrappers::MPI::Vector distributed_solution(MPI_COMM_WORLD, + dof_handler.n_dofs(), + dof_handler.n_locally_owned_dofs()); + + distributed_solution = solution; +#endif + + print_memory_stats(); + + SolverControl solver_control (solution.size(), 1e-12); +#ifdef USE_TRILINOS + SolverCG solver (solver_control); + + TrilinosWrappers::PreconditionAMG preconditioner; + preconditioner.initialize(system_matrix); +#else + PETScWrappers::SolverCG solver(solver_control, MPI_COMM_WORLD); + PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix); + +#endif + + { // memory consumption + pcout << "Mem: Tria (p4est) DofH Constraints Mat X Rhs I_lo I_lr" + << std::endl; +// MPI_Barrier(MPI_COMM_WORLD); + + pcout << "MEM: " + << " " << triangulation.memory_consumption() + << " " << triangulation.memory_consumption_p4est() + << " " << dof_handler.memory_consumption() + << " " << constraints.memory_consumption() + << " " << system_matrix.memory_consumption() + << " " << solution.memory_consumption() + << " " << system_rhs.memory_consumption() + << " " << locally_owned_dofs.memory_consumption() + << " " << locally_relevant_dofs.memory_consumption() +// << " " << preconditioner.memory_consumption() + << " ~sum=" << (triangulation.memory_consumption() + dof_handler.memory_consumption() + + constraints.memory_consumption() + system_matrix.memory_consumption() + + solution.memory_consumption() + system_rhs.memory_consumption() + + locally_owned_dofs.memory_consumption() + + locally_relevant_dofs.memory_consumption() )/1024 + << " kB" + << std::endl; + +// MPI_Barrier(MPI_COMM_WORLD); + } + + + solver.solve (system_matrix, distributed_solution, system_rhs, + preconditioner); + + print_memory_stats(); + + pcout << " Solved in " << solver_control.last_step() + << " iterations." << std::endl; + + constraints.distribute (distributed_solution); + solution = distributed_solution; + +#ifndef USE_TRILINOS + solution.update_ghost_values(); +#endif + + deallog.depth_console (1); +} + + + +template +void LaplaceProblem::refine_grid () +{ + TimerOutput computing_timer(pcout, TimerOutput::summary, + TimerOutput::wall_times); + + computing_timer.enter_section ("Estimating"); + + Vector estimated_error_per_cell (triangulation.n_active_cells()); + + { + + TimeBlock t(pcout, "kelly"); + + KellyErrorEstimator::estimate (static_cast&>(dof_handler), + QGauss(3), + typename FunctionMap::type(), + solution, + estimated_error_per_cell); + } + + computing_timer.exit_section(); + + computing_timer.enter_section ("Marking"); + { + TimeBlock t(pcout, "marking"); + + parallel::distributed::GridRefinement:: + refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3, 0.03); + } + + computing_timer.exit_section(); + + computing_timer.enter_section ("Refining"); + + + { + TimeBlock t(pcout, "prep_c&r"); + + triangulation.prepare_coarsening_and_refinement(); + } + + if (0) + { + //output refinement information for + //debugging + + const std::string filename = ("ref." + + Utilities::int_to_string + (triangulation.locally_owned_subdomain(), 4) + + ".vtk"); + std::ofstream output (filename.c_str()); + + DataOut data_out; + data_out.attach_dof_handler (dof_handler); + + unsigned int n_coarse=0; + Vector subdomain (triangulation.n_active_cells()); + { + unsigned int index = 0; + + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell, ++index) + { + subdomain(index)=0; + + if (cell->is_ghost() || cell->is_artificial()) + subdomain(index)=-4; + + if (cell->refine_flag_set()) + subdomain(index)+=1; + if (cell->coarsen_flag_set()) + { + subdomain(index)+=2; + ++n_coarse; + } + } + } + std::cout << "id=" << triangulation.locally_owned_subdomain() << " n_coarsen=" << n_coarse << std::endl; + + data_out.add_data_vector (subdomain, "info"); + data_out.build_patches (); + data_out.write_vtk (output); + } + + { + TimeBlock t(pcout, "soltrans.prep"); + + soltrans.prepare_for_coarsening_and_refinement(solution); + } + + { + TimeBlock t(pcout, "c&r", false); + + triangulation.execute_coarsening_and_refinement (); + } + + computing_timer.exit_section(); +} + + + + +template +void LaplaceProblem::output_results (const unsigned int cycle) const +{ + const std::string filename = ("solution-" + + Utilities::int_to_string (cycle, 2) + + "." + + Utilities::int_to_string + (triangulation.locally_owned_subdomain(), 4) + + ".d2"); + std::ofstream output (filename.c_str()); + + DataOut data_out; + data_out.attach_dof_handler (dof_handler); + data_out.add_data_vector (solution, "u"); + + Vector subdomain (triangulation.n_active_cells()); + { + unsigned int index = 0; + for (typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell, ++index) + subdomain(index) = (cell->is_ghost() || cell->is_artificial() + ? + -1 + : + cell->subdomain_id()); + } + data_out.add_data_vector (subdomain, "subdomain"); + + data_out.build_patches (); + + data_out.write_deal_II_intermediate (output); +} + + + +template +void LaplaceProblem::run (unsigned int refine) +{ + { + TimeBlock t(pcout, "empty"); + } + + unsigned int cycles = 12; + + pcout << "running " << cycles << " cycles with refine= " << refine << +#ifdef USE_TRILINOS + " with Trilinos" +#else + " with PETSc" +#endif + << std::endl; + + for (unsigned int cycle=0; cycle t(pcout, "dist_dofs"); + dof_handler.distribute_dofs (fe); + } + + + locally_owned_dofs = dof_handler.locally_owned_dofs (); + DoFTools::extract_locally_relevant_dofs (dof_handler, + locally_relevant_dofs); + + pcout << " Number of degrees of freedom: " + << dof_handler.n_dofs() + << std::endl; +/* pcout << " ("; + for (unsigned int i=0; i0) + { + TimeBlock t(pcout, "soltrans.interp"); + + soltrans.interpolate(solution); + } + + + computing_timer.exit_section(); + + computing_timer.enter_section ("Assembling system"); + assemble_system (); + computing_timer.exit_section(); + + computing_timer.enter_section ("Solving system"); + solve (); + computing_timer.exit_section(); + +// output_results (cycle); + + pcout << std::endl; + } +} + + + +int main(int argc, char *argv[]) +{ + try + { +#ifdef USE_TRILINOS + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); +#else + PetscInitialize(&argc, &argv); +#endif + + deallog.depth_console (1); + + int refine=5; + if (argc>1) + { + refine = (unsigned int)Utilities::string_to_int(argv[1]); + } + + { + LaplaceProblem<2> laplace_problem_2d; + laplace_problem_2d.run (refine); + } + + print_memory_stats(); + +#ifndef USE_TRILINOS + PetscLogPrintSummary(MPI_COMM_WORLD, "petsc.log" ); + PetscFinalize(); +#endif + + } + 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; +} diff --git a/deal.II/examples/step-40/timeblock.h b/deal.II/examples/step-40/timeblock.h new file mode 100644 index 0000000000..d86eff8ae0 --- /dev/null +++ b/deal.II/examples/step-40/timeblock.h @@ -0,0 +1,54 @@ +/** + * copyright: + * Research Group on Numerical Methods for PDEs + * University of Göttingen + * + * lube@math.uni-goettingen.de + */ + +#ifndef TIMEBLOCK_H +#define TIMEBLOCK_H + +template +class TimeBlock +{ + public: + TimeBlock(STREAM & stream_, const char* blockname, bool singleline=true) + : m_blockname(blockname), + m_singleline(singleline), + m_timer(MPI_COMM_WORLD, true), + stream(stream_) + { + MPI_Barrier(MPI_COMM_WORLD); + stream << blockname << " ... "; + if (singleline) + stream << std::flush; + else + stream << std::endl; + m_timer.start(); + } + + ~TimeBlock() + { + m_timer.stop(); + + if (!m_singleline) + stream << m_blockname << " took "; + + stream << std::fixed << std::setprecision(4); + + m_timer.print_data(stream); + + MPI_Barrier(MPI_COMM_WORLD); + } + + + private: + const char* m_blockname; + bool m_singleline; + bool m_issection; + dealii::Timer m_timer; + STREAM & stream; +}; + +#endif diff --git a/deal.II/lac/include/lac/block_matrix_base.h b/deal.II/lac/include/lac/block_matrix_base.h index bdda476633..4da101c56a 100644 --- a/deal.II/lac/include/lac/block_matrix_base.h +++ b/deal.II/lac/include/lac/block_matrix_base.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -898,6 +899,16 @@ class BlockMatrixBase : public Subscriptor */ const BlockIndices & get_column_indices () const; + /** + * Determine an estimate for the memory + * consumption (in bytes) of this + * object. Note that only the memory + * reserved on the current processor is + * returned in case this is called in + * an MPI-based program. + */ + unsigned int memory_consumption () const; + /** @addtogroup Exceptions * @{ */ @@ -1710,6 +1721,29 @@ copy_from (const BlockMatrixType &source) } +template +unsigned int +BlockMatrixBase::memory_consumption () const +{ + unsigned int mem = + MemoryConsumption::memory_consumption(row_block_indices)+ + MemoryConsumption::memory_consumption(column_block_indices)+ + MemoryConsumption::memory_consumption(sub_objects)+ + MemoryConsumption::memory_consumption(counter_within_block)+ + MemoryConsumption::memory_consumption(column_indices)+ + MemoryConsumption::memory_consumption(column_values); + + for (unsigned int r=0; rsub_objects[r][c]; + mem += MemoryConsumption::memory_consumption(*p); + } + + return mem; +} + + template inline diff --git a/deal.II/lac/include/lac/block_sparsity_pattern.h b/deal.II/lac/include/lac/block_sparsity_pattern.h index 29fbdb146c..3a4612e954 100644 --- a/deal.II/lac/include/lac/block_sparsity_pattern.h +++ b/deal.II/lac/include/lac/block_sparsity_pattern.h @@ -916,12 +916,26 @@ class BlockCompressedSimpleSparsityPattern : public BlockSparsityPatternBase& row_block_sizes, const std::vector& col_block_sizes); - + /** - * Resize the matrix to a tensor - * product of matrices with - * dimensions defined by the - * arguments. + * Initialize the pattern with symmetric + * blocks. The number of IndexSets in the + * vector determine the number of rows + * and columns of blocks. The size of + * each block is determined by the size() + * of the respective IndexSet. Each block + * only stores the rows given by the + * values in the IndexSet, which is + * useful for distributed memory parallel + * computations and usually corresponds + * to the locally owned DoFs. + */ + BlockCompressedSimpleSparsityPattern (const std::vector & partitioning); + + /** + * Resize the pattern to a tensor product + * of matrices with dimensions defined by + * the arguments. * * The matrix will have as many * block rows and columns as @@ -935,6 +949,15 @@ class BlockCompressedSimpleSparsityPattern : public BlockSparsityPatternBase &row_block_sizes, const std::vector< unsigned int > &col_block_sizes); + /** + * Resize the pattern with symmetric + * blocks determined by the size() of + * each IndexSet. See the constructor + * taking a vector of IndexSets for + * details. + */ + void reinit(const std::vector & partitioning); + /** * Allow the use of the reinit * functions of the base class as @@ -1030,17 +1053,14 @@ namespace TrilinosWrappers /** * Initialize the pattern with an array - * of index sets that specifies both - * rows and columns of the matrix (so - * the final matrix will be a square - * matrix), where the IndexSet - * specifies the parallel distribution - * of the degrees of freedom on the - * individual block. This function is - * equivalent to calling the second - * constructor with the length of the - * mapping vector and then entering the - * index values. + * of index sets that specifies both rows + * and columns of the matrix (so the + * final matrix will be a square matrix), + * where the size() of the IndexSets + * specifies the size of the blocks and + * the values in each IndexSet denotes + * the rows that are going to be saved in + * each block. */ BlockSparsityPattern (const std::vector& parallel_partitioning, const MPI_Comm &communicator = MPI_COMM_WORLD); @@ -1074,10 +1094,9 @@ namespace TrilinosWrappers /** * Resize the matrix to a square tensor - * product of matrices with parallel - * distribution according to the - * specifications in the array of - * Epetra_Maps. + * product of matrices. See the + * constructor that takes a vector of + * IndexSets for details. */ void reinit (const std::vector& parallel_partitioning, const MPI_Comm & communicator = MPI_COMM_WORLD); diff --git a/deal.II/lac/include/lac/constraint_matrix.h b/deal.II/lac/include/lac/constraint_matrix.h index 33f4ab8a68..eb03db3550 100644 --- a/deal.II/lac/include/lac/constraint_matrix.h +++ b/deal.II/lac/include/lac/constraint_matrix.h @@ -243,11 +243,9 @@ class ConstraintMatrix : public Subscriptor */ /** - * Add a new line to the - * matrix. If the line already - * exists, then the function - * simply returns without doing - * anything. + * Add a new line to the matrix. If the + * line already exists, then the function + * simply returns without doing anything. */ void add_line (const unsigned int line); @@ -1371,6 +1369,10 @@ class ConstraintMatrix : public Subscriptor * or Trilinos vector wrapper class, or * any other type having the same * interface. + * + * Note that if called with a @p + * TrilinosWrappers::MPI::Vector it may + * not contain ghost elements. */ template void distribute (VectorType &vec) const; diff --git a/deal.II/lac/include/lac/petsc_parallel_vector.h b/deal.II/lac/include/lac/petsc_parallel_vector.h index bfec7d151a..5fe82f7319 100644 --- a/deal.II/lac/include/lac/petsc_parallel_vector.h +++ b/deal.II/lac/include/lac/petsc_parallel_vector.h @@ -187,7 +187,7 @@ namespace PETScWrappers explicit Vector (const MPI_Comm &communicator, const unsigned int n, const unsigned int local_size); - + /** * Copy-constructor from deal.II @@ -229,7 +229,7 @@ namespace PETScWrappers const unsigned int local_size); - /** + /** * Constructs a new parallel PETSc * vector from an Indexset. Note that * @p local must be contiguous and @@ -375,7 +375,7 @@ namespace PETScWrappers void reinit (const Vector &v, const bool fast = false); - /** + /** * Reinit as a ghosted vector. See * constructor with same signature * for more details. diff --git a/deal.II/lac/include/lac/petsc_vector_base.h b/deal.II/lac/include/lac/petsc_vector_base.h index 0b93a15330..9870b3a289 100644 --- a/deal.II/lac/include/lac/petsc_vector_base.h +++ b/deal.II/lac/include/lac/petsc_vector_base.h @@ -664,7 +664,7 @@ namespace PETScWrappers void ratio (const VectorBase &a, const VectorBase &b); - /** + /** * Updates the ghost values of this * vector. This is necessary after any * modification before reading ghost @@ -753,7 +753,7 @@ namespace PETScWrappers */ bool ghosted; - /** + /** * This vector contains the global * indices of the ghost values. The * location in this vector denotes the diff --git a/deal.II/lac/include/lac/sparsity_tools.h b/deal.II/lac/include/lac/sparsity_tools.h index dabf7e35e6..32225be219 100644 --- a/deal.II/lac/include/lac/sparsity_tools.h +++ b/deal.II/lac/include/lac/sparsity_tools.h @@ -199,21 +199,42 @@ namespace SparsityTools #ifdef DEAL_II_COMPILER_SUPPORTS_MPI /** * Communciate rows in a compressed - * sparsity pattern over MPI. The parameter - * @p csp is modified inline. All entries - * in rows that belong to a different - * processor are send to them and added - * there. The ownership is determined by - * the parameter @p rows_per_cpu. The - * IndexSet @p myrange should be the one - * used in the constructor of the + * sparsity pattern over MPI. + * + * @param csp is the sparsity + * pattern that has been built + * locally and for which we need to + * exchange entries with other + * processors to make sure that + * each processor knows all the + * elements of the rows of a matrix + * it stores and that may + * eventually be written to. This + * sparsity pattern will be changes + * as a result of this function: + * All entries in rows that belong + * to a different processor are + * sent to them and added there. + * + * @param rows_per_cpu determines ownership of rows. + * + * @param mpi_comm is the MPI + * communicator that is shared + * between the processors that all + * participate in this operation. + * + * @param myrange indicates the + * range of elements stored locally + * and should be the one used in + * the constructor of the * CompressedSimpleSparsityPattern. Only - * the rows in @p csp contained in @p - * myrange are checked for transfer. This - * function needs to be used with - * PETScWrappers::MPI::SparseMatrix for it - * to work correctly in a parallel - * calculation. + * rows contained in myrange are + * checked in csp for transfer. + * This function needs to be used + * with + * PETScWrappers::MPI::SparseMatrix + * for it to work correctly in a + * parallel computation. */ template void distribute_sparsity_pattern(CSP_t & csp, @@ -222,7 +243,7 @@ namespace SparsityTools const IndexSet & myrange); #endif - + /** * Exception */ diff --git a/deal.II/lac/include/lac/trilinos_sparse_matrix.h b/deal.II/lac/include/lac/trilinos_sparse_matrix.h index b5428cae27..569bcdbaa7 100644 --- a/deal.II/lac/include/lac/trilinos_sparse_matrix.h +++ b/deal.II/lac/include/lac/trilinos_sparse_matrix.h @@ -3231,7 +3231,9 @@ namespace TrilinosWrappers // the content -- this is what we need // since we're going to overwrite that // anyway in the vmult operation. - temp_vector.reinit(dst, true); + // TODO[TH]: workaround, because fast is + // not reliable with MPI right now. + temp_vector.reinit(dst, false); vmult (temp_vector, src); dst += temp_vector; diff --git a/deal.II/lac/include/lac/trilinos_vector.h b/deal.II/lac/include/lac/trilinos_vector.h index 8e730fe177..551ace6446 100644 --- a/deal.II/lac/include/lac/trilinos_vector.h +++ b/deal.II/lac/include/lac/trilinos_vector.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2008, 2009 by the deal.II authors +// Copyright (C) 2008, 2009, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -68,7 +68,7 @@ namespace TrilinosWrappers * to avoid using the wrong data, you need to call Vector::compress() * before you actually use the vectors. * - *

    Parallel communication model

    + *

    Parallel communication model

    * * The parallel functionality of Trilinos is built on top of the Message * Passing Interface (MPI). MPI's communication model is built on diff --git a/deal.II/lac/include/lac/trilinos_vector_base.h b/deal.II/lac/include/lac/trilinos_vector_base.h index adcfaa6410..21d13c78ff 100644 --- a/deal.II/lac/include/lac/trilinos_vector_base.h +++ b/deal.II/lac/include/lac/trilinos_vector_base.h @@ -18,6 +18,7 @@ #ifdef DEAL_II_USE_TRILINOS +#include # include # include # include @@ -896,8 +897,7 @@ namespace TrilinosWrappers /** * Estimate for the memory - * consumption (not implemented - * for this class). + * consumption in bytes. */ unsigned int memory_consumption () const; //@} @@ -1132,11 +1132,29 @@ namespace TrilinosWrappers void VectorBase::compress (const Epetra_CombineMode given_last_action) { + Epetra_CombineMode mode = (last_action != Zero) ? + last_action : given_last_action; + #ifdef DEBUG + #ifdef DEAL_II_COMPILER_SUPPORTS_MPI + // check that every process has decided + // to use the same mode. This will + // otherwise result in undefined + // behaviour in the call to + // GlobalAssemble(). + double double_mode = mode; + struct Utilities::System::MinMaxAvg result; + Utilities::System::calculate_collective_mpi_min_max_avg( + dynamic_cast(&vector_partitioner().Comm())->GetMpiComm(), + double_mode, + result); + Assert(result.max-result.min<1e-5, ExcTrilinosError(0)); + + #endif + #endif + // Now pass over the information about // what we did last to the vector. - const int ierr = vector->GlobalAssemble(last_action != Zero ? - last_action : - given_last_action); + const int ierr = vector->GlobalAssemble(mode); AssertThrow (ierr == 0, ExcTrilinosError(ierr)); last_action = Zero; @@ -1193,6 +1211,11 @@ namespace TrilinosWrappers const unsigned int *indices, const TrilinosScalar *values) { + // if we have ghost values, do not allow + // writing to this vector at all. + Assert (vector->Map().UniqueGIDs()==true, + ExcGhostsPresent()); + if (last_action == Add) vector->GlobalAssemble(Add); @@ -1250,6 +1273,11 @@ namespace TrilinosWrappers const unsigned int *indices, const TrilinosScalar *values) { + // if we have ghost values, do not allow + // writing to this vector at all. + Assert (vector->Map().UniqueGIDs()==true, + ExcGhostsPresent()); + if (last_action != Add) { if (last_action == Insert) diff --git a/deal.II/lac/source/block_sparsity_pattern.cc b/deal.II/lac/source/block_sparsity_pattern.cc index 50e3c59605..55eeb571a9 100644 --- a/deal.II/lac/source/block_sparsity_pattern.cc +++ b/deal.II/lac/source/block_sparsity_pattern.cc @@ -265,6 +265,33 @@ BlockSparsityPatternBase::print(std::ostream& out) const } +template <> +void +BlockSparsityPatternBase::print(std::ostream& out) const +{ + unsigned int k=0; + for (unsigned int ib=0;ib void BlockSparsityPatternBase::print_gnuplot(std::ostream &out) const @@ -598,6 +625,21 @@ BlockCompressedSimpleSparsityPattern (const std::vector& row_indic } +BlockCompressedSimpleSparsityPattern:: +BlockCompressedSimpleSparsityPattern (const std::vector & partitioning) + : + BlockSparsityPatternBase(partitioning.size(), + partitioning.size()) +{ + for (unsigned int i=0;iblock(i,j).reinit(partitioning[i].size(), + partitioning[j].size(), + partitioning[i]); + this->collect_sizes(); +} + + void BlockCompressedSimpleSparsityPattern::reinit ( @@ -612,6 +654,19 @@ BlockCompressedSimpleSparsityPattern::reinit ( this->collect_sizes(); } +void +BlockCompressedSimpleSparsityPattern::reinit ( + const std::vector< IndexSet > & partitioning) +{ + BlockSparsityPatternBase:: + reinit(partitioning.size(), partitioning.size()); + for (unsigned int i=0;iblock(i,j).reinit(partitioning[i].size(), + partitioning[j].size(), + partitioning[i]); + this->collect_sizes(); +} #ifdef DEAL_II_USE_TRILINOS diff --git a/deal.II/lac/source/compressed_simple_sparsity_pattern.cc b/deal.II/lac/source/compressed_simple_sparsity_pattern.cc index db3404a9e2..ede39e6b38 100644 --- a/deal.II/lac/source/compressed_simple_sparsity_pattern.cc +++ b/deal.II/lac/source/compressed_simple_sparsity_pattern.cc @@ -279,6 +279,8 @@ CompressedSimpleSparsityPattern::reinit (const unsigned int m, cols = n; rowset=rowset_; + Assert(rowset.size()==0 || rowset.size() == m, ExcInvalidConstructorCall()); + std::vector new_lines (rowset.size()==0 ? rows : rowset.n_elements()); lines.swap (new_lines); } diff --git a/deal.II/lac/source/constraint_matrix.cc b/deal.II/lac/source/constraint_matrix.cc index ea5f067941..cc6aa46a28 100644 --- a/deal.II/lac/source/constraint_matrix.cc +++ b/deal.II/lac/source/constraint_matrix.cc @@ -1924,7 +1924,11 @@ ConstraintMatrix::distribute (TrilinosWrappers::MPI::Vector &vec) const vec(it->line) = new_value; } - vec.compress (); + // some processes might not apply + // constraints, so we need to explicitly + // state, that the others are doing an + // insert here: + vec.compress (Insert); } @@ -2018,6 +2022,9 @@ ConstraintMatrix::distribute (TrilinosWrappers::MPI::BlockVector &vec) const } } + // force every processor to write something + unsigned int idx = vec.block(0).local_range().first; + vec(idx) = vec(idx); vec.compress (); } diff --git a/deal.II/lac/source/petsc_parallel_sparse_matrix.cc b/deal.II/lac/source/petsc_parallel_sparse_matrix.cc index ac4163e11d..5fa947c3c7 100644 --- a/deal.II/lac/source/petsc_parallel_sparse_matrix.cc +++ b/deal.II/lac/source/petsc_parallel_sparse_matrix.cc @@ -310,8 +310,8 @@ namespace PETScWrappers //see below) const unsigned int - local_col_end = local_col_start + local_columns_per_process[this_process]; - + local_col_end = local_col_start + local_columns_per_process[this_process]; + // then count the elements in- and // out-of-window for the rows we own #ifdef PETSC_USE_64BIT_INDICES @@ -351,7 +351,7 @@ namespace PETScWrappers 0, &row_lengths_out_of_window[0], &matrix); AssertThrow (ierr == 0, ExcPETScError(ierr)); - + #else //PETSC_VERSION>=2.3.3 // new version to create the matrix. We // do not set row length but set the @@ -449,7 +449,7 @@ namespace PETScWrappers // now copy over the information // from the sparsity pattern. - { + { #ifdef PETSC_USE_64BIT_INDICES PetscInt #else @@ -465,9 +465,9 @@ namespace PETScWrappers std::copy(row_start, row_end, ptr); ptr += row_end - row_start; - } + } } - + // then call the petsc function // that summarily allocates these @@ -481,7 +481,7 @@ namespace PETScWrappers // this is only needed for old // PETSc versions: - // for some reason, it does not + // for some reason, it does not // seem to be possible to force // actual allocation of actual // entries by using the last @@ -539,7 +539,7 @@ namespace PETScWrappers #else ierr = MatSetOption (matrix, MAT_NO_NEW_NONZERO_LOCATIONS); AssertThrow (ierr == 0, ExcPETScError(ierr)); -#endif +#endif #else int ierr; #ifdef DEBUG diff --git a/deal.II/lac/source/petsc_parallel_vector.cc b/deal.II/lac/source/petsc_parallel_vector.cc index 78701fc1c3..3d62879ed7 100644 --- a/deal.II/lac/source/petsc_parallel_vector.cc +++ b/deal.II/lac/source/petsc_parallel_vector.cc @@ -63,7 +63,7 @@ namespace PETScWrappers VectorBase::operator = (v); } - + Vector::Vector (const MPI_Comm &communicator, const IndexSet & local, @@ -137,7 +137,7 @@ namespace PETScWrappers } - + void Vector::reinit (const MPI_Comm &comm, const IndexSet & local, @@ -271,11 +271,11 @@ namespace PETScWrappers #endif - } + } -} + } } diff --git a/deal.II/lac/source/petsc_vector_base.cc b/deal.II/lac/source/petsc_vector_base.cc index 336483ccbd..4121698551 100644 --- a/deal.II/lac/source/petsc_vector_base.cc +++ b/deal.II/lac/source/petsc_vector_base.cc @@ -16,6 +16,7 @@ #ifdef DEAL_II_USE_PETSC +# include # include # include # include @@ -43,7 +44,7 @@ namespace PETScWrappers if (dynamic_cast(&vector) != 0) { #if (PETSC_VERSION_MAJOR <= 2) && (PETSC_VERSION_MINOR < 3) - PetscScalar *ptr; + PetscScalar *ptr; int ierr = VecGetArray (vector.vector, &ptr); AssertThrow (ierr == 0, ExcPETScError(ierr)); @@ -1053,12 +1054,24 @@ namespace PETScWrappers } - unsigned int VectorBase::memory_consumption () const - { - AssertThrow(false, ExcNotImplemented() ); - return 0; + { + unsigned int mem = sizeof(Vec)+sizeof(LastAction::Values) + +MemoryConsumption::memory_consumption(ghosted) + +MemoryConsumption::memory_consumption(ghost_indices); + + // TH: I am relatively sure that PETSc is + // storing the local data in a contigious + // block without indices: + mem += local_size()*sizeof(PetscScalar); + // assume that PETSc is storing one index + // and one double per ghost element + if (ghosted) + mem += ghost_indices.n_elements()*(sizeof(PetscScalar)+sizeof(int)); + + //TODO[TH]: size of constant memory for PETSc? + return mem; } @@ -1123,8 +1136,8 @@ namespace PETScWrappers AssertThrow (ierr == 0, ExcPETScError(ierr)); ierr = VecGhostUpdateEnd(vector, INSERT_VALUES, SCATTER_FORWARD); AssertThrow (ierr == 0, ExcPETScError(ierr)); -} - + } + } diff --git a/deal.II/lac/source/sparsity_tools.cc b/deal.II/lac/source/sparsity_tools.cc index 7dc9f41f26..2aea7cb203 100644 --- a/deal.II/lac/source/sparsity_tools.cc +++ b/deal.II/lac/source/sparsity_tools.cc @@ -448,7 +448,7 @@ namespace SparsityTools { row_idx+=rows_per_cpu[myid]-1; continue; -} + } unsigned int rlen = csp.row_length(row); diff --git a/deal.II/lac/source/trilinos_vector_base.cc b/deal.II/lac/source/trilinos_vector_base.cc index 3ec70b5fe9..da2469e31b 100644 --- a/deal.II/lac/source/trilinos_vector_base.cc +++ b/deal.II/lac/source/trilinos_vector_base.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2008 by the deal.II authors +// Copyright (C) 2008, 2010 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -12,6 +12,7 @@ //--------------------------------------------------------------------------- +#include #include #ifdef DEAL_II_USE_TRILINOS @@ -397,8 +398,13 @@ namespace TrilinosWrappers unsigned int VectorBase::memory_consumption () const { - AssertThrow(false, ExcNotImplemented() ); - return 0; + //TODO[TH]: No accurate memory + //consumption for Trilinos vectors + //yet. This is a rough approximation with + //one index and the value per local + //entry. + return sizeof(*this) + + this->local_size()*( sizeof(double)+sizeof(int) ); } } /* end of namespace TrilinosWrappers */ -- 2.39.5