- Potential breaking changes
- Simplified handling of runTime selection tables
- New List Methods
- Parallel gather/scatter routines
- Improved flexibilty of globalIndex
- Memory handling
Potential breaking changes
As part of the updates to the runTime selection mechanism, the
previous typedef has been changed to provide a better distinction
between content and storage type by appending Type
to the old
typedef name.
This seemingly cosmetic change was useful since it freed up the old
name for use as a singleton method. However, this will potentially
cause issues if derived code still uses the old typedef.
An example of broken code:
dictionaryConstructorTable::iterator cstrIter =
dictionaryConstructorTablePtr_->find(modelType);
The trivial "upgrade" is to simply replace the iterator type with
the c++11 auto
keyword. For example,
auto cstrIter = dictionaryConstructorTablePtr_->find(modelType);
NOTE: This change is valid for any OpenFOAM version/variant from the last 5+ years.
Simplified handling of runTime selection tables
To streamline handling of runTime selection (eg, the New
factory
method), the selection tables now provide a singleton accessor that
supports use of compatibility aliases and greatly simplifies handling.
Example of the new access method. Here we use the ctor
prefix
to denote constructor:
auto* ctorPtr = dictionaryConstructorTable(modelType);
if (!ctorPtr)
{
... error handling
}
return autoPtr<myModel>(ctorPtr(dict, ...));
This is less cluttered than the older style, which still works, but without any compatibility (alias) handling:
auto ctorIter = dictionaryConstructorTablePtr_->cfind(modelType);
if (!ctorIter.found())
{
... error handling
}
return autoPtr<myModel>(ctorIter()(dict, ...));
New List Methods
SubList Access
A SubList or SubField is frequently used to view or operate on a
sub-section of a List or Field. The new slice
method for List and
Field uses a syntax more similar to substr
and returns a
SubList/SubField. Here the offset is first, and the size is second.
For example,
auto data1 = allData.slice(0, 10); // The first 10 elements
auto data2 = allData.slice(10); // Remaining elements
An unspecified length is equivalent to "all remaining elements".
The sliced portion can also be selected with the usual labelRange
(which represents a start/size pair). For example,
labelRange range(...);
fullList.slice(range) = identity(range.size());
// And
fullList.slice(range) = UIndirectList<T>(other, addr);
No-copy resizing
The size of a List often requires adjustment prior to an operation,
but old values (if any) may not be of any interest and always be
overwritten with new values. For these cases the regular resize
or
setSize
methods are wasteful and the resize_nocopy
and
reserve_nocopy
versions can be used. These will avoid the overhead
of the intermediate list and the copy/move overhead of retaining the
old values (which would be subsequently discarded anyhow).
//OLD: field.resize(newLength);
field.resize_nocopy(newLength);
Contiguous data access methods
Especially when interfacing with external codes or for MPI
communications it is common to handle the contiguous data directly.
The new List methods data_bytes()
and cdata_bytes()
complement the
size_bytes()
methods for passing contiguous data pointers and sizes.
For example,
UOPstream::write
(
UPstream::commsTypes::nonBlocking,
proci,
sendBufs[proci].cdata_bytes(),
sendBufs[proci].size_bytes(),
tag,
comm
)
Parallel gather/scatter routines
Additional MPI gather/scatter routines, globalIndex gather methods
- UPstream::mpiGather (MPI_Gather) - used by Pstream::listGatherValues
- UPstream::mpiScatter (MPI_Scatter) - used by Pstream::listScatterValues
These are much simpler forms for gather/scatter of fixed-sized contiguous types data types (eg, primitives, simple pairs etc).
In the gather form, creates a list of gathered values on the master process. The subranks have a list size of zero.
Similarly, scatter will distribute a list of values to single values on each process.
Instead of
labelList sendSizes(Pstream::nProcs());
sendSizes[Pstream::myProcNo()] = sendData.size();
Pstream::gatherList(sendSizes);
Can write
const labelList sendSizes
(
UPstream::listGatherValues<label>(sendData.size())
);
// Less code, lower overhead and list can be const.
For scattering an individual value only. Instead of
labelList someValues;
if (Pstream::master()) someValues = ...;
Pstream::gatherList(sendSizes);
const label localValue
(
someValues[Pstream::myProcNo()]
);
Can write
labelList someValues;
if (Pstream::master()) someValues = ...;
Pstream::gatherList(sendSizes);
const label localValue
(
UPstream::listScatterValues<label>(someValues)
);
Can also mix listGatherValues to assemble a list on master and use Pstream::scatterList to distribute.
Improved flexibilty of globalIndex
-
When gathering data, the local size is properly respected, which allows support for gathering with 0 master elements.
-
Additional creation from local sizes. It is generally easier and safer to assemble sizes and let globalIndex determine the corresponding offsets, when working with raw values.
-
Add globalIndex const_iterator to iterate across the access ranges. This makes it simpler to use with the List slice() method to access or operate on a sub-section of List.
For example,
scalarField allValues = ...;
globalIndex procAccess = ...;
for (const labelRange& range : procAccess)
{
someOutput(allValues.slice(range));
}
Memory handling
Improved consistency in the refPtr
and tmp
classes, support for
shallow copying of references and improved checks. The shallow copying
enables building HashTables with shadowed variables (similar idea to
shared pointer semantics).
The methods good()
and valid()
as well as a direct bool test work
identically in the memory classes.
Explicit is_const()
checking methods for refPtr
and tmp
. These
can be used in combination with is_pointer()
or movable()
checks to
narrow down handling and memory reuse.
Added release()
method to refPtr
for consistency with autoPtr
and std::unique_ptr
.
The precision adaptors are modifiable, which allows reuse (similar to the refPtr container) when wrapping different contents. To support this, there are additional control for when contents are copied back, instead of waiting for the adaptor to go out of scope. For example,
if (adaptor.active())
{
adaptor.commit();
adaptor.clear();
}
Streams etc.
Added various methods to return object/stream names relative to the current case, which produces compacter, more readable output.
- IOobject : objectRelPath()
- IOstream : relativeName()
- dictionary : relativeName()
For the occasion functions that are isolated from the Time or other
case-specific reference the argList::envRelativePath()
static method
is available.