The longer you wait for something, the more you appreciate it, right? preCICE v3 is finally out and we are pretty happy about it.
v3.0.0 is a breaking release. This means that the API and the configuration of preCICE change. We understand that updating your codes and testcases to the new version requires some effort.
But updating should be …
- easy: We provide an explicit and complete porting guide.
- worth it: The new release simplifies many things and comes with important new features.
- sustainable: We promise to stick with the v3 series for at least two years (as documented in the release strategy), but presumably much longer. The v2 series, for example, was active for 4 years (v2.0.0 was released on February 7, 2020).
Still, for some users updating right away might not be doable. This is why we also backported a few fixes into a last v2 bugfix release: v2.5.1. If your department or organization requires further maintenance of version 2, please consider applying for a support license.
Not all official adapters are already ported to the new version, but for most of them and for the tutorials fully-ported develop versions exist. The next weeks will be a transition phase during which we will release most components and finally bundle them in a new preCICE distribution.
The website including the user documentation (except adapters and tutorials) is already updated to v3. But you can also still access older documentation.
In this blog post, we want to whetten your appetite by briefly highlighting a few major changes of v3.0.0. For all changes, see the changelog.
Most important renamings
Have you ever wondered why the preCICE API was called SolverInterface
? We too.
We now renamed it to Participant
to closely follow the language of the configuration.
Accordingly, the main C++ API header needed renaming. So, we grabbed the opportunity to introduce a central header for the API precice/precice.hpp
. And also the configuration reflects this change: Here, we got completely rid of the <solver-interface/>
layer.
The new configuration looks like:
<precice-configuration>
<data ... />
<mesh ...> </mesh>
<participant name="..."> ... </participant>
<m2n .../>
<coupling-scheme:...> ... </coupling-scheme:...>
</precice-configuration>
We also renamed some tags and attributes to make them easier to understand or use. Participants now <provide-mesh>
and <receive-mesh/>
instead of <use-mesh>
. m2n
now has an acceptor
and a connector
instead of from
and to
.
Data API
There are no longer all these different read and write functions (“read block vector scalar something”). Instead, there is one main read and one main write function:
void Participant::readData(...);
void Participant::writeData(...);
Also, there are no more mesh and data IDs. Instead, we directly work with names. This feels much more natural, especially in Python:
participant.write_data("Solid-Mesh", "Displacement", vertex_ids, displacements)
And in C++, we now work with spans, grouping a pointer and a size. This means that you can directly pass STL containers such as vectors and arrays, and we can watch your back when their sizes are inconsistent:
std::vector<double> coords(vertexSize*dim);
std::vector<int> vertexIDs(vertexSize);
std::vector<double> forces(vertexSize*dim);
//...
precice.setMeshVertices("Solid-Mesh", coords, vertexIDs);
//...
participant.writeData("Solid-Mesh", "Displacement", vertexIDs, forces);
Note that we provide an implementation of span
through the new header file precice.hpp
. This will be our strategy for continuing to develop preCICE using recent C++ practices, while maintaining a C++11 API that most codes and compilers can work with.
For details, see the reference of the C++ API.
Steering API
Have you always found it weird that initialize
and advance
return a time step size? Was it cumbersome to pass this around? This is gone now. Instead, you can explicitly get the maximum value for the next time step size with:
double getMaxTimeStepSize() const;
Writing coupling loops is now way cleaner as time step sizes do not have to be passed into and across coupling loops.
Another weird function that we removed is intializeData()
. The functionality is now fully integrated into initialize()
. Initial data is specified directly after defining the mesh, which feels more natural, too:
// define the mesh
participant.setMeshVertices("Mesh", ...);
// define initial data if needed
if (participant.requiresInitialData()) {
participant.writeData("Mesh", "Data", ...);
}
participant.initialize();
And instead of the awkward …
const std::string& cowic = precice::constants::actionWriteIterationCheckpoint();
if(interface.isActionRequired(cowic)){
// save checkpoint
interface.markActionFulfilled(cowic);
}
it is now simply …
if(participant.requiresWritingCheckpoint()){
// save checkpoint
}
We trust you will write that checkpoint anyway.
Multirate and higher-order time stepping
With the support of time interpolation, we have fundamentally revised and improved the treatment of time stepping and subcycling (multiple time steps per coupling time window, see the documentation of waveforms). We now support higher-order multirate time stepping and we are excited to see what you will do with this feature.
The readData
function now has the mandatory argument dt
. This allows you to sample coupling data from a time-continuous interpolant. dt
accepts values between dt = 0
(beginning of the current time step) and dt = getMaxTimeStepSize()
(end of the current time window). Nothing has changed with respect to writeData
– at least from your perspective. Behind the scenes, every time you call advance
, the data is stored inside preCICE for later use. This makes subcycling much more useful.
The attribute …
<data:... waveform-degree="1,2,3">
allows you to control the polynomial degree of the interpolation. Note that if you do not use subcycling, you are only able to use linear interpolation, which is already better than the constant value in preCICE v2. If you use subcycling, the additional data allows preCICE to create a higher-degree interpolation.
Storing additional data when subcycling, of course, comes with an overhead. By default the exchange of these substeps is deactivated. You can turn it on by setting substeps="true"
in the exchange
tag of the coupling scheme.
Currently, as acceleration, only constant underrelaxation fully supports subcycling and the additional substeps. This is also the reason, why we turned substeps off by default. Support for quasi-Newton based acceleration is on the way and we invite you to start experimenting with the new feature and constant underrelaxation already now. For some inspiration, what you can do with the new feature, refer to the talks and publications of Benjamin Rodenberg.
Tremendous improvements in RBF data mapping
In our continuous pursuit of optimizing our mapping methods, we are thrilled to announce a newly implemented Partition-of-Unity Radial-Basis-Function (RBF) data mapping method. It serves as a complement to the existing (global) RBF mapping techniques. The method is much faster, especially for large mapping problems (see as an example this presented case at last year’s workshop, where we show speed-ups of 1000).
To streamline the user experience and simplify the decision process for selecting RBF mapping variants, we overhauled the corresponding preCICE configuration: The previous RBF XML tag (e.g. mapping:rbf-compact-polynomial-c0
), has been replaced with an alias tag: mapping:rbf
. This alias tag automatically selects the appropriate method, eliminating the need for users to delve into intricate details during the configuration stage. For users seeking more granular control over all available methods: This is still possible and our revamped mapping configuration section provides you guidance.
But this is not all – we have extended our efforts to port our global RBF mappings to GPUs. This means that our methods are now empowered to leverage both NVIDIA and AMD devices, offering a significant performance boost for users. More details can be accessed at this ECCOMAS proceedings paper.
Experimental support for geometric multiscale data mapping
This release introduces experimental support for geometric multiscale data mapping, for now allowing you to couple 1D and 3D participants. Two types of consistent mapping are currently provided (axial and radial), tailored for fluid-fluid coupling and conjugate heat transfer scenarios involving 3D CFD and 1D system codes.
Plugging in your 1D pipe into a 3D pipe and you wish you had a fully-developed velocity profile? No problem! Instead of using a nearest-neighbor mapping, you can now directly use an axial geometric multiscale mapping, “spreading” information from the 1D to the 3D vertices, assuming that your 1D simulation is aligned with the X-axis of the 3D simulation:
<mapping:axial-geometric-multiscale direction="read" type="spread"
radius="1.0" axis="X" from="MyMesh2"
to="MyMesh1" constraint="consistent" />
See also the mapping:radial-geometric-multiscale
for a 3D domain encapsulating a 1D domain. Read more about all options in the documentation.
For now, preCICE meshes are still either 2D or 3D. 1D meshes are coming later in the v3.x cycle. For now, 1D meshes in the geometric multiscale mapping are still modeled as 3D with inactive components. However, in preparation for a full-scale implementation, we now moved the dimensions
configuration from the top level to each mesh:
<mesh name="MeshOne" dimensions="3">
In addition to that, you can ask preCICE for the dimensions using the new API methods getMeshDimensions(meshName)
and getDataDimensions(meshName, dataName)
. Note here the difference between the “mesh dimensions” (e.g. vertices in 3D space) and the “data dimensions” (e.g. data vectors with three components). These are not always the same.
With this foundation in place, we are looking forward to your feedback to make the implementation more flexible and cover more use cases.
Simpler mesh connectivity
Providing connectivity information of your meshes is now easier than ever. preCICE now only requires vertices, no more EdgeID
s and no need for hierarchical primitives. Additionally, every method now comes in a singular and plural form, giving more flexibility to adapter developers.
if (participant.requiresConnectivityFor("Mesh")) {
std::vector<VertexID> faceIDS = getTetrahedralFaceIDs();
participant.setMeshTriangles("Mesh", faceIDs);
}
Furthermore, preCICE now pre-processes meshes, removing redundant connectivity and making sure projection mappings work correctly.
For details, see the reference of the C++ API and the documentation page on mesh connectivity.
More useful profiling
preCICE now gathers basic profiling data by default and ships with a new tool precice-profiling
to analyze it. This gives you all you need for a quick overview of the overhead introduced by preCICE and lets you analyze load imbalances. You can also configure preCICE to give you the full story if you want to peek under the hood.
After the simulation, merge profiling data of the participants Fluid
and Solid
:
precice-profiling merge Fluid Solid
This gives you a profiling.json
, which you then can visualize or also simply analyze in the terminal:
precice-profiling analyze Fluid
Details and many more options in the performance analysis documentation.
Defaults for acceleration
You always found acceleration using IQN-ILS and IQN-IMVJ daunting to use as the configuration looked overly complicated? We added default values for many settings based on years of experience. You now specify the data to accelerate and you are good to go:
<acceleration:IQN-ILS>
<data name="Displacement" mesh="Solid-Mesh" />
<data name="Stress" mesh="Fluid-Mesh-Centers" />
</acceleration:IQN-ILS>
When should I update?
There is no reason not to start updating today. The API and the configuration are fixed. Official adapters will be updated soon. Register on the preCICE forum to receive announcements like this one.
Of course: If you run into problems, please let us know.
Happy coupling!