| ... | @@ -4,23 +4,369 @@ |
... | @@ -4,23 +4,369 @@ |
|
|
[][upgrade-guide]
|
|
[][upgrade-guide]
|
|
|
[][code-patterns]
|
|
[][code-patterns]
|
|
|
|
|
|
|
|
***Preview information, subject to change at any time !!!***
|
|
|
|
|
|
|
|
|
|
[[_TOC_]]
|
|
[[_TOC_]]
|
|
|
|
|
|
|
|
## Deprecation and Removal
|
|
## Changes in definition
|
|
|
|
|
|
|
|
### Deprecated Methods
|
|
### Field construction from dictionary entry
|
|
|
|
|
|
|
|
### Removed Methods
|
|
Lookup and construct a Field from a dictionary entry now always uses a
|
|
|
|
`literal` lookup to avoid the suprise of accidental matches.
|
|
|
|
|
|
|
|
### Removed Items
|
|
|
|
|
|
|
|
|
|
## Changes in definition
|
|
### List/HashTable emplacement
|
|
|
|
|
|
|
|
Existing `emplace_back()` and `emplace_set()` methods (primarily
|
|
|
|
DynamicList, DyPtrList, HashTable, HashPtrTable) return a reference to
|
|
|
|
the newly added element. This makes for convenient use and aligns with
|
|
|
|
the C++17 definition for `std::vector`.
|
|
|
|
|
|
|
|
|
|
|
|
### Processor topology - procNeighbours()
|
|
|
|
|
|
|
|
The definition for processorTopology `procNeighbours()` has changed.
|
|
|
|
This was previously the entire adjacency table but is now only the
|
|
|
|
processor-local neighbours. This change avoids an unnecessary global
|
|
|
|
communication at startup. If required, the new method `procAdjacency()`
|
|
|
|
can be used to create or recover the entire adjacency table.
|
|
|
|
|
|
|
|
```
|
|
|
|
// Old:
|
|
|
|
const labelList& neighbourProcs =
|
|
|
|
mesh.globalData().topology().procNeighbours()[Pstream::myProcNo()];
|
|
|
|
|
|
|
|
// New:
|
|
|
|
const labelList& neighbourProcs =
|
|
|
|
mesh.globalData().topology().procNeighbours();
|
|
|
|
|
|
|
|
// If really needed, the old definition (with communication!)
|
|
|
|
const labelListList& connectivity =
|
|
|
|
mesh.globalData().topology().procAdjacency();
|
|
|
|
```
|
|
|
|
|
|
|
|
## Changes in behaviour
|
|
## Changes in behaviour
|
|
|
|
|
|
|
|
## Namespace changes
|
|
### IOobject
|
|
|
|
|
|
|
|
The `headerClassName()` member is now always reset by the
|
|
|
|
`typeHeaderOk()` call, which ensures that read failures can be
|
|
|
|
properly detected. Since the initialised state also has an empty
|
|
|
|
`headerClassName()` member, special handling of _"sticky"_ object
|
|
|
|
states is no longer necessary.
|
|
|
|
|
|
|
|
For further convenience, the new `IOobject::resetHeader()` method can
|
|
|
|
be used for repeated read operations. It also it enforces resetting of
|
|
|
|
headerClassName, scalar/label sizes etc prior to reading. For example,
|
|
|
|
```
|
|
|
|
IOobject rio("none", ..., IOobject::LAZY_READ);
|
|
|
|
|
|
|
|
rio.resetHeader("U")
|
|
|
|
if (returnReduceOr(rio.typeHeaderOk<volVectorField>(false)))
|
|
|
|
...
|
|
|
|
|
|
|
|
io.resetHeader("p")
|
|
|
|
if (returnReduceOr(rio.typeHeaderOk<volScalarField>(false)))
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### The default fileHandler storage
|
|
|
|
|
|
|
|
The file-handler (`fileOperation`) bookkeeping has changed from an
|
|
|
|
`autoPtr` to a `refPtr` to enable reuse of file handlers for some
|
|
|
|
specialised cases. It is not expected that this change will affect any
|
|
|
|
user coding.
|
|
|
|
|
|
|
|
|
|
|
|
## Name adjustments
|
|
|
|
|
|
|
|
### IOobjectOption
|
|
|
|
|
|
|
|
The IOobjectOption (introduced in v2212) now supports shorter names
|
|
|
|
as a convenience for less typing (and have nicer code alignment).
|
|
|
|
|
|
|
|
| Shorter name | Longer (legacy) name |
|
|
|
|
|-------------------|-----------------------|
|
|
|
|
| NO_READ | NO_READ |
|
|
|
|
| MUST_READ | MUST_READ |
|
|
|
|
| READ_MODIFIED | MUST_READ_IF_MODIFIED |
|
|
|
|
| LAZY_READ | READ_IF_PRESENT |
|
|
|
|
|
|
|
|
Accompanying these style changes are some additional handling methods.
|
|
|
|
For example,
|
|
|
|
```
|
|
|
|
// Relax the read requirements
|
|
|
|
// (downgrade MUST_READ/READ_MODIFIED -> LAZY_READ)
|
|
|
|
readOrigin = IOobjectOption::lazierRead(readOrigin);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Base classes/container improvements
|
|
|
|
|
|
|
|
The new `stdFoam::span` container holds a pointer and size,
|
|
|
|
with basic functionality similar to `std::span` (C++20). This allows
|
|
|
|
for lightweight handling of address ranges when dealing with binary
|
|
|
|
data.
|
|
|
|
|
|
|
|
The `UList::begin()` method is now also available with an integer
|
|
|
|
argument. It returns the offset from begin (with access range clamping)
|
|
|
|
to help simplify addressing within List sub-ranges.
|
|
|
|
|
|
|
|
|
|
|
|
## Field improvements
|
|
|
|
|
|
|
|
### Simpler, more consistent construct or assign Field from dictionary
|
|
|
|
|
|
|
|
When contructing with a field entry, it is now possible to use the
|
|
|
|
`IOobjectOption::readOption` as a boolean-like selector with essentially
|
|
|
|
yes/no/maybe states. For example,
|
|
|
|
```
|
|
|
|
mass_("mass", dict, p.size(), IOobjectOption::LAZY_READ)
|
|
|
|
```
|
|
|
|
This will read the "mass" entry if it exists, or else initialize with
|
|
|
|
zero values.
|
|
|
|
|
|
|
|
As well as being able to construct from a dictionary entry,
|
|
|
|
the `Field` class also supports assignment from a dictionary entry.
|
|
|
|
This also respects the provided `IOobjectOption::readOption` to allow
|
|
|
|
conditional or unconditional reading, etc.
|
|
|
|
|
|
|
|
|
|
|
|
### Field clamping
|
|
|
|
|
|
|
|
Regular fields and geometric fields support various types of inplace
|
|
|
|
clamping: `clamp_min()`, `clamp_max()`, `clamp_range()`.
|
|
|
|
Range clamping can generally also be used with the `zero_one` definition
|
|
|
|
for this type of code:
|
|
|
|
```
|
|
|
|
lambda.clamp_range(zero_one{});
|
|
|
|
```
|
|
|
|
|
|
|
|
The methods `clamp_min()` and `clamp_max()` are the currently preferred
|
|
|
|
means of imposing value clamping on fields since they allow for less
|
|
|
|
confusing code. For example,
|
|
|
|
```
|
|
|
|
// New: clamps the min value to the given value
|
|
|
|
geofield.clamp_min(lower);
|
|
|
|
|
|
|
|
// Old: retains the max of field value and the given value
|
|
|
|
// (ie, sets the lower bound)
|
|
|
|
geofield.max(lower);
|
|
|
|
```
|
|
|
|
In some case, the updated naming helps avoid coding errors.
|
|
|
|
For example,
|
|
|
|
```
|
|
|
|
// Wrong (2212 and earlier)
|
|
|
|
Re.min(1200.0);
|
|
|
|
Re.max(18800.0);
|
|
|
|
|
|
|
|
// Corrected
|
|
|
|
Re.clamp_range(1200.0, 18800.0);
|
|
|
|
```
|
|
|
|
|
|
|
|
### Field linear interpolation
|
|
|
|
|
|
|
|
A `lerp()` function (naming as per C++20)
|
|
|
|
is now available for the linear interpolation between two fields,
|
|
|
|
either with a constant or a field of interpolation factors.
|
|
|
|
It adds some convenience, but packing multiple data into a single
|
|
|
|
instruction does not seem to have any measurable code performance
|
|
|
|
improvement.
|
|
|
|
|
|
|
|
|
|
|
|
### GeometricField construction
|
|
|
|
|
|
|
|
It is now possible to construct a GeometricField from separate
|
|
|
|
value/dimensions, which can be easier that copying into a dimensioned
|
|
|
|
type (especially when using derived field types). For example,
|
|
|
|
|
|
|
|
```
|
|
|
|
// New code
|
|
|
|
|
|
|
|
fluxFieldType fld2(io, mesh, Zero, Uf.dimensions()*dimArea/dimTime);
|
|
|
|
|
|
|
|
// Old code
|
|
|
|
fluxFieldType fld2
|
|
|
|
(
|
|
|
|
io,
|
|
|
|
mesh,
|
|
|
|
dimensioned<typename flux<Type>::type>
|
|
|
|
(
|
|
|
|
Uf.dimensions()*dimArea/dimTime, Zero
|
|
|
|
)
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
|
|
|
## Coding for Boundary Conditions
|
|
|
|
|
|
|
|
Some of the implementation details of patch fields (eg,
|
|
|
|
`fvPatchField`) that form the basis for implementing boundary
|
|
|
|
conditions have been adjusted, cleaned up, extended.
|
|
|
|
|
|
|
|
None of the changes affect any existing user code, but can make it
|
|
|
|
*less messy* to write boundary conditions.
|
|
|
|
At the top-level, the template-independent parts have been moved into
|
|
|
|
a base class, which also allows convenient method calls for commonly
|
|
|
|
used boundary types.
|
|
|
|
For example,
|
|
|
|
```
|
|
|
|
fvPatchFieldBase::zeroGradientType();
|
|
|
|
```
|
|
|
|
instead of previously:
|
|
|
|
```
|
|
|
|
fvPatchField<scalar>::zeroGradientType();
|
|
|
|
"zeroGradient"
|
|
|
|
```
|
|
|
|
|
|
|
|
The commonly used `value` entry has dedicated read and write methods,
|
|
|
|
to simplify logic within boundary conditions. For example, with this
|
|
|
|
type of code:
|
|
|
|
```
|
|
|
|
if (!this->readValueEntry(dict))
|
|
|
|
{
|
|
|
|
// Fallback: set to the internal field
|
|
|
|
this->extrapolateInternal();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
If the value is required for visualization or restart, that can be
|
|
|
|
expressed within the `write()` method as follows:
|
|
|
|
```
|
|
|
|
this->writeValueEntry(os);
|
|
|
|
```
|
|
|
|
|
|
|
|
Boundary conditions also provide an `extrapolateInternal()` protected
|
|
|
|
method, which provides a more succinct and memory efficient means of
|
|
|
|
sampling the internal field and storing the values on the patch.
|
|
|
|
|
|
|
|
|
|
|
|
## Changes in parallel (MPI) handling
|
|
|
|
|
|
|
|
As part of [exaFOAM](https://exafoam.eu/exafoam) activities, a number
|
|
|
|
of low-level adjustments have been made behind the scenes to enable
|
|
|
|
different parallel algorithms. These can be activated by [parallel tuning
|
|
|
|
switches](/tuning#parallel-tuning).
|
|
|
|
|
|
|
|
|
|
|
|
### New algorithms, MPI primitives
|
|
|
|
|
|
|
|
A non-blocking consensus exchange (NBX) is a promising means of
|
|
|
|
exchange data between processors with very sparse data patterns.
|
|
|
|
However, in general, the current approach in OpenFOAM will be to
|
|
|
|
primarily use it to replace expensive AllToAll communication for
|
|
|
|
exchanging buffer sizes, whilst retaining point-to-point communication
|
|
|
|
for the actual buffer transfers (since these can subsequently be
|
|
|
|
queued to execute in non-blocking mode too). The new
|
|
|
|
`Pstream::allToAllConsensus()` method can be used directly as a
|
|
|
|
drop-in replacement for `Pstream::allToAll()` size exchange.
|
|
|
|
|
|
|
|
|
|
|
|
#### allGatherValues
|
|
|
|
|
|
|
|
The new `Pstream::allGatherValues()` method maps directly to calling
|
|
|
|
`MPI_Allgather`, which simplifies that caller and enables MPI to
|
|
|
|
decide on its own best strategy. As a comparison for when values are
|
|
|
|
required everywhere:
|
|
|
|
|
|
|
|
```
|
|
|
|
// New code: as single MPI_Allgather()
|
|
|
|
const labelList nPerProc
|
|
|
|
(
|
|
|
|
UPstream::allGatherValues<label>(patch_.size(), myComm)
|
|
|
|
);
|
|
|
|
// vs older code: MPI_Gather() + MPI_Bcast()
|
|
|
|
labelList nPerProc
|
|
|
|
(
|
|
|
|
UPstream::listGatherValues<label>(patch_.size(), myComm)
|
|
|
|
);
|
|
|
|
Pstream::broadcast(nPerProc, myComm);
|
|
|
|
```
|
|
|
|
This new mapping to `MPI_Allgather()` is now also harnessed by
|
|
|
|
`Pstream::allGatherList()` when handling contiguous values, instead of
|
|
|
|
the hand-rolled tree walking involved with gatherList/scatterList.
|
|
|
|
|
|
|
|
|
|
|
|
#### Barrier, sync-send
|
|
|
|
|
|
|
|
The new `UPstream::barrier()` call adds barrier support and is
|
|
|
|
available in blocking or non-blocking versions. The `UPstream::write`
|
|
|
|
methods now also support MPI sync-send variants.
|
|
|
|
|
|
|
|
|
|
|
|
### General
|
|
|
|
|
|
|
|
Additional parallel guards are available that also check
|
|
|
|
partially populated communicators, which can occur if sparse communicators
|
|
|
|
(eg, inter-host) are being used. The guards are as follows:
|
|
|
|
- `UPstream::is_rank(comm)` : <br>
|
|
|
|
True if process corresponds to a rank in the communicators.
|
|
|
|
Can be a master rank or a sub-rank.
|
|
|
|
- `UPstream::is_parallel(comm)` : <br>
|
|
|
|
True if parallel algorithm or exchange is used on the process.
|
|
|
|
This is the same as the following:
|
|
|
|
```
|
|
|
|
(parRun() && (nProcs(comm) > 1) && is_rank(comm))
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Improved communicator wrapping
|
|
|
|
|
|
|
|
Many algorithms will use the MPI_COMM_WORLD communicator (for OpenFOAM
|
|
|
|
this is the `UPstream::worldComm` handle), but it may also be
|
|
|
|
necessary within particular algorithms to swap out what is considered
|
|
|
|
the _world_ communicator with a different communicator. The setting
|
|
|
|
and restoring are now handled with setter/getter methods. Since the
|
|
|
|
setter returns the previous value, it can be used to obtain the value
|
|
|
|
to restore as well:
|
|
|
|
```
|
|
|
|
const label oldWorldComm = UPstream::commWorld(myComm);
|
|
|
|
...
|
|
|
|
|
|
|
|
UPstream::commWorld(oldWorldComm); // Restore
|
|
|
|
```
|
|
|
|
This was previously only possible with a few more code lines (and
|
|
|
|
potentially coding errors).
|
|
|
|
|
|
|
|
In addition to the standard handles for MPI_COMM_WORLD and
|
|
|
|
MPI_COMM_SELF, this version also provides routines for creating
|
|
|
|
intra-host and inter-host communicators
|
|
|
|
|
|
|
|
|
|
|
|
### Improved request handling
|
|
|
|
|
|
|
|
OpenFOAM generally uses a globally managed list of MPI requests, which
|
|
|
|
are typically accessed like this:
|
|
|
|
```
|
|
|
|
const label startOfRequests = UPstream::nRequests()
|
|
|
|
|
|
|
|
// ... do something
|
|
|
|
|
|
|
|
// MPI_Waitall()
|
|
|
|
UPstream::waitRequests(startOfRequests);
|
|
|
|
```
|
|
|
|
This conveniently wraps handling of MPI requests without needing to
|
|
|
|
send through additional calling parameters.
|
|
|
|
However, this approach also makes it difficult to write algorithms
|
|
|
|
with local bookkeeping of the MPI requests.
|
|
|
|
|
|
|
|
OpenFOAM-v2306 now contains a `UPstream::Request` wrapper class, which
|
|
|
|
is a very simple, lightweight opaque wrapper around the MPI_Request handles
|
|
|
|
which allows them to be copied, moved, stored in Lists etc within
|
|
|
|
OpenFOAM code.
|
|
|
|
|
|
|
|
|
|
|
|
### Added request wait/test/cancel
|
|
|
|
|
|
|
|
This version adds wrapping for a large variety of request handling:
|
|
|
|
- MPI_Waitall
|
|
|
|
- MPI_Waitany
|
|
|
|
- MPI_Waitsome
|
|
|
|
- MPI_Testsome
|
|
|
|
- MPI_Cancel() + MPI_Request_free()
|
|
|
|
|
|
|
|
The new MPI_Testsome and MPI_Waitsome checks can be used, for example,
|
|
|
|
when writing algorithms that dispatch data when a receive is
|
|
|
|
completed.
|
|
|
|
|
|
|
|
|
|
|
|
----
|
|
----
|
|
|
|
|
|
| ... | | ... | |