Alex started a good discussion on the mailing list (May 3, 2019) about best practices when writing a new adapter:
I wanted to ask if there are some additional guidelines on how to write good adapters for preCICE. I have the feeling that I might think too much about it, but I wanted to see what you guys are thinking about that.
Adapter advice
I am in the process of writing some adapter for the projects here. It is mainly for coupling DuMuX [1]. DuMuX itself is not a big solver that you compile once and then use it via configuration files (as OpenFOAM for example), but more a toolbox of functions (similar to FEniCS) where you can easily set up a solver tailored to your problem. One usually defines a main.cc
that drives the solving process (time loop etc) and problem.hh
which described the problem in detail (equations used, boundary conditions etc.).
Currently, I have two use cases:
- Bidirectional coupling: Coupling DuMuX with DuMuX. Exchange temperature and heat-flux. The problem is very similar to the CHT test case from the tutorials.
- Unidirectional “coupling”: Coupling DuMuX with some other solver SolverB that receives data from the DuMuX solver. No data is transferred back to DuMuX. It is not clear what data should be transferred within that project. I want to be able to transfer several quantities that might live on the same mesh, but could also live on several different meshes.
At the moment, I have several adapters for my two use cases that I would like to merge.
Case 1:
- For the first use case I wrote an adapter that has function calls tailored to the use case like
writeHeatFlux
andreadTemperature
and it has some convenience function for users likegetTemperatureOnFace( DumuxFaceId )
. There is no configuration file for the adapter since the use case is fixed at the moment. I do not see a clear benefit from adding a configuration file.
Case 2:
2.1 The Dumux adapter here is more general. It has rather general functions like announceQuantity(name)
and getQuantityOnFace(name)
as the transferred quantities might vary/change a lot. This adapter has no configuration file either. We have to touch the main.cc
for DuMuX anyway to set up the test case and the interface mesh so I do not see the clear benefit of having an additional configuration file.
2.2 For SolverB I have a very simple adapter that is tailored to the current use case where a scalar quantity is used. The solver already uses a JSON configuration file. The Solver is rather general and should read an arbitrary number of dataset from preCICE on an arbitraty number of meshes. Therefore, I see a clear benefit for having a configuration file. I would prefer JSON as file format since a parser is part of the solver.
Question
How would you merge something like that into one code base?
A possible approach would be to write a very general base adapter and derive a class from it that would be tailored to the actual solver and use case. However, that means I might end up with plenty of very special classes.
General questions/remarks regarding adapters
- What are indications of a good adapter?
- How specific should functions be?
-
ReadScalarQuantity
(quantityId) vsReadTemperature()
-
- What is the preferred format for the adapter configuration? There seems to be no clear rule what file format to use. preCICE uses XML, but the adapters usually bring additional dependencies (even though a XML parser must be present due to preCICE).
- preCICE -> XML
- OpenFOAM -> YAML (additional dependency)
- Calculix -> YAML (additional dependency)
- FEniCS -> JSON
- SU2 -> Uses SU2-configuration file?!
- Why do the C/C++ adapters not use XML as it is a dependency for preCICE already?
- Can I use the preCICE logging features for my adapter do I have to set up my own logging?
- I usually try to keep the number of dependencies low so it feels like a natural thing to reuse libraries that I need for preCICE anyway (boost and XML). Why is this not done in the adapters? Is XML or the parser too complicated/annoying?
The adapter example on the homepage [2] does not answer all the questions. I also checked the available adapters, but they follow different approaches due to different authors, programming languages and solvers they are written for.
TL;DR (short version)
- What functionality should a wrapper have to be called “adapter”? Even with an adapter I might have to change my source code, but in a more convenient way tailored to my solver. How much easier does it have to be to count as an “adapter”.
- Are there any common agreements on nomenclature, minimum required feature set etc?
- How verbose should functions of that wrapper be? If my adapter reads
temperatures, should the adapter provide a function calledreadTemperature
or ratherreadQuantity( "temperature" )
. I have the impression that more descriptive function names make a configuration file less necessary and easier for users to understand that have to add it to their code. At the same time it restricts the use cases I can use my adapter for. - When do you think a configuration file for an adapter is needed?