The DoF indices for a vertex are stored in an array where we just
collate the indices for each level. There is currently another
array that stors the offset within this array where the DoFs
for a given level start. This array is dynamically allocated,
but it is altogether unnecessary because the offsets are computable:
they are simply the number of the multigrid level times
dofs_per_vertex.
Consequently, get rid of the array and replace it by storing dofs_per_vertex.
We can then easily compute the starting offset wherever necessary, rather than
having to look it up.
The hp DoF renumbering function for the <3,3> case was unnecessarily
convoluted because it tried to look just like the functions that
deal with faces of higher dimensional cells. But we know that in <3,3>,
a hex is a cell, and so there can only be one finite element associated
with the cell. This allows simplifying a fair share of code.
Currently, the code is duplicated between the DoFHandler and the policy
class. This makes no sense, and indeed the policy class code is more
evolved, so let the former call the latter following exposing the
interface in the previous patch.
The existing code in DoFHandler looks like it did not actually support
parallel triangulations (either shared or distributed). This was
probably a bug, and consequently the implementation of the functionality
in the ParallelDistributed policy class now just throws an error.
This relates to the discussion in #4559.
The previous refactoring allows parallelizing renumbering for vertices,
cells, and faces, since these all work on mutually independent data
structures.
The 1d, 2d, and 3d implementations of these functions had most of their code
duplicated. This can be done more elegantly by instead splitting the code into
vertices, cells, and faces. The main function then becomes dimension independent,
as are the functions dealing with vertices and cells, and only the face function
requires dimensional specialization.
Make the DoFHandler a template type of the Sequential policy.
Also adjust all of the places where the class is used.
This change by itself is not useful, but will become useful when also
using the policy class from hp::DoFHandler. Similar changes will at a
later time be made for the ParallelShared and ParallelDistributed
policies.
When distributing MG DoF indices, we accidentally used 'unsigned int'
instead of 'types::global_dof_index'. That's a bug. We didn't notice
this because we never have more than 4B unknowns on one processor,
and the function was only called in the parallel context to enumerate
the DoFs on the *local* portion of the mesh, before indices were
shifted after communication with other processors. Regardless, it's
worth fixing.
We had a function with a funny comment suggesting that a compiler did not
correctly understand the code. But the comment is wrong: when the only
argument to a function that references a 'dim' or 'spacedim' template
argument is of the form
onst typename DoFHandler<dim,spacedim>::level_cell_iterator &
then 'dim' and 'spacedim' are simply not deducible. That's how C++ works.
The function therefore had a dummy argument of type DoFHandler<dim,spacedim>
that isn't used. That's awkward, but works. But we have a way to deal
with the lack of deducibility that's used elsewhere in the library
namely internal::int2type. Use this approach here as well, and remove
the misleading comment.
Do not derive the ParallelShared policy from the Sequential one.
The derivation was slightly contrived, but is not in fact useful any more since
the code has been refactored in ways that allow breaking the derivation and
just calling in ParallelShared what is also called in Sequential. In fact, the
code may have even become a bit shorter that way.
Remove an argument to a function that was no longer used.
Specifically, it allowed to start enumerating DoFs at something other than zero.
There was a time when we allowed that, but that possibility was removed
several years ago.
The corresponding argument to distribute_mg_dofs() was removed a few days ago,
but it was accidently left on distribute_dofs().
The compiler warns about the line
header_ += gzip::magic::id2; // ID2.
deep in BOOST when writing the header for a gzip-compressed block of data. This
is because (i) gzip::magic::id2 is declared as an int, and (ii) has a value
greater than what a *signed char* can store, i.e., greater than 127. Furthermore,
std::string::operator+ takes a 'char', which may or may not be unsigned but
apparently on my system is signed. So we get a warning about overflow.
The only reasonable way to deal with this is to do the casting explicitly.
David Wells [Sat, 24 Jun 2017 14:35:27 +0000 (10:35 -0400)]
Grammar: clarify obtain vs. attain.
Attain and obtain are nearly, but not quite, synonyms: 'attain' implies
achievement or accomplishment while 'obtain' just implies
ownership. This patch fixes our errant usage of 'attain' (one comment
also had a logic error, which I fixed).
Remove an argument to a function that was no longer used.
Specifically, it allowed to start enumerating DoFs at something other than zero.
There was a time when we allowed that, but that possibility was removed
several years ago.
Let DoFHandler policy classes return the NumberCache also in distribute_mg_dofs().
This follows the same pattern as a previous commit. However, it involves less
drama, with the exception of an innocent assertion that had to be removed
(see the last commit). Other than that, everything seems to work.
The assertion gets in the way of the following commit because it tests
a condition that at the time when we want to call this function is not
yet satisfied. However, I've convinced myself that if the DoFHandler
is truly not set up at all (as the assertion wants to check), that
the calls further down in the function will fail to work as well. In
other words, one will get *some* kind of error.
Let the DoFHandler policy classes' distribute_dofs() return the NumberCache.
In times of move operators, there is really no reason why these functions should
return the NumberCache object by reference, rather than by value. We make this
slightly more efficient by providing (defaulted) move operations to
NumberCache.
However, this change uncovered a subtle problem that sooner or later would
certainly have tripped someone up: If any of the policy classes call
any function that relies on the correctness of the number cache of the
DoFHandler object being worked on, then this is likely going to fail.
Indeed, the implementation of distribute_dofs() for shared triangulations
has this problem by calling some of the DoFTools functions that query
properties of the DoFHandler that aren't all completely consistent
yet internally.
This happened to work -- more or less by design -- because the
NumberCache object that the DoFHandler passes to distribute_dofs()
by reference is the one that belongs to the DoFHandler, and
distribute_dofs() makes sure that it sets the one member that is
necessary to the correct value before calling the DoFTools function.
This can only properly be described as 'brittle': small changes to
the code will make this fail, and it is not obvious why this works
or doesn't.
The only proper solution, obviously, is to not call external functions
from inside distribute_dofs() until all data structures have been set
up completely and are internally consistent. I work around this
by duplicating a small amount of code (specifically, the
DoFTools::get_subdomain_association() and DoFRenumbering::subdomain_wise()
functions), but this effort is made more palatable because these functions
are in fact more general than we need here, and we can throw away a good
amount of code that deals with either sequential or completely
distributed DoFHandlers; we also know that a couple of assertions
in these functions must be satisfied here, so we can eliminate them
as well.
The consequence of this code is slightly more code that is more robust
and contains fewer surprises.
Let the DoFHandler policy classes store references to the DoFHandler.
This is rather than passing this reference to each and every call to the policy functions.
The primary goal of this is so that we can use the Policy base class not only for the
'normal' DoFHandler, but also for the hp::DoFHandler by removing the reference to the
actual class type from the interface of the base class.
Also replace all uses by numbers::invalid_dof_index. I can't see a reason to
have both of these numbers, in particular because the only logical choice is
to have them have the same value.
The functions in question take an IndexSet argument that can either mean
something, or if it is empty, is ignored. In the places where an empty
IndexSet is passed, it actually has size zero, not just zero elements,
but in the existing code, we test whether 'indices.n_elements()==0'.
It turns out that 'n_elements()' is a pretty expensive function to call.
What we really wanted, of course, was 'indices.size()==0', and this
patch makes that change.
I ran all multigrid tests (the functions in question are multigrid
related), and they all succeed.