Peano
Loading...
Searching...
No Matches
Merging/coupling various applications

As several code types are built on top of the same Peano infrastructure, we can always combine them or alter the underlying Peano representation by adding additional mesh traversals for example.

Peano's Python API is a mere wrapper around the Peano C++ core, i.e. it provides a high-level abstraction of Peano's action sets, the data held by a Peano application, its Makefile, and so forth. Popular Peano extensions then create a layer on top of this API which eventually yields a Peano project. Obviously, once you have lowered the abstraction level into a Peano project, you can still alter this whole application model. Indeed, you can now add technical details that have previously been hidden, or you can combine different projects.

We distinguish various different terms in the context of coupling:

  • Multiplicative coupling means that two different solvers or applications take turns. If you have a solver A and a solver B, and if you write down their action as a function, you would write down the overall action of these solvers as \( ... \circ A \circ B \circ A \circ B \). This is where the name multiplicative comes from.
  • Additive coupling means that two solvers run in parallel for a while and eventually exchange data after that. The name additive results from the fact that you might want to write down such a solver as \( \circ (A + B) \circ (A+B)\).
  • Separate coupling sweeps are runs through the mesh which do not invoke any solver functionality but instead synchronise data and exchange all data.

Before we continue, we emphasise that we rely heavily on terms like observer and also make use of the notion of action sets. Each mesh sweep is tied to one observer, which in turn is represented by a peano4.solversteps.Step object.

Lowering two codes into a Peano project (multiplicatively)

Skip this part if you work with only one extension and want to add additional helper grid sweeps, e.g. If you have two extensions - such as ExaHyPE and MGHyPE or Swift - which both create Peano projects, you end up with a high-level code which resembles:

project_A = my_first_extension.Project(
namespace = ...
project_name = ...
directory = ...
subdirectory = ...
executable = ...
)
project_B = my_second_extension.Project(
namespace = ...
project_name = ...
directory = ...
subdirectory = ...
executable = ...
)

Solvers for each project need to be added (possibly, several):

project_A.add_solver(solver_A_for_my_first_extenstion) # solver_B_..., etc., if needed
project_B.add_solver(solver_A_for_my_second_extenstion) # solver_B_..., etc., if needed

An empty global Peano project is created, followed by subsequently adding the projects from the above two extensions into the global one:

peano4_project = peano4.Project(
namespace = [], # the namespaces from the above extension projects will be used subsequently
project_name = ..., # choose the name for the global Peano project
directory = ".", # the application directory (the current one in this example)
executable = ..., # choose the name for the resulting application
)
peano4_project.add_subproject(
my_first_extension.generate_Peano4_project(args.verbose),
master=True # sets this extension as the source of the template to generate the main source file from
)
peano4_project.add_subproject(
my_second_extension.generate_Peano4_project(args.verbose),
)
@ above
int main()
Definition main.cpp:321

Once merged (which was done by the new add_subproject method above), source files (such as repositories/StepRepository.h) are generated by calling peano4_project.generate() or peano4_project.build(make_clean_first=True, number_of_parallel_builds=args.j) methods. The files are written each correspondingly to their extension project in their own subdirectory which was passed as a parameter in their extension Project class constructor. Thus, there'll be two instances for every source file, such as (using the example of StepRepository.h):

project_A.subdirectory + "/repositories/StepRepository.h"
project_B.subdirectory + "/repositories/StepRepository.h"

The generated main header and source files will be located in the common application folder (corresponding to peano4_project.directory, above the subprojects' subdirectories) and have the names peano4_project.project_name + "-main.h" and peano4_project.project_name + "-main.cpp" respectively. Initially, it is created using the template from extension set as "master" (in our example, this is the first extension indicated by the master=True parameter). A template from any extension contains definitions for the selectNextAlgorithmicStep() and step() functions. The generated instances of these functions in the source file have to be modified to include the steps from the second extension (non-master extension). The choice of master and non-master is somewhat arbitrary; the goal is to manually insert all the steps from the non-master extension into the master extension, so perhaps it makes sense to choose the more complex one as master which has more steps. Thus, your task becomes copying the steps from the template of the non-master extension and inserting them one by one in the appropriate positions between the steps of the master extension (in the generated source file). Keep in mind that the template file may contain template expressions which are translated in C++ expression in the generated source file, so it is suggested to switch temporarily the master extension to generate a source file from the other extension to copy the steps from there instead from the template. Now, the main source file is adapted and includes all the steps from both extension projects in the right order.

Once lowered, you can also add new algorithmic steps as they are required for separate coupling sweeps, e.g. From hereon, the (merged) Peano 4 project is the work data set, as any change in the original project (such as an exahype2.Project instance) does not propagate through anymore.

Once merged, it is reasonably to doublecheck the generated file

_A.subdirectory + "repositories/StepRepository.h

in each extension subdirectory. Each of them defines observers from their corresponding extension project, but StepRepository notably hosts an enum called Steps which accommodates identifiers for the steps from their corresponding project.

Todo
We need to figure out how to toggle and decide whether we want to run a step from either project. How to switch forth and back in arbitrary order.

Peano usually expects that the high-level extension ensures that no two algorithmic steps use the same name for two different observers. Our approach in merging two projects guarantees unique names by using namespaces passed as parameters when creating extension projects. Therefore, steps with the same name from solvers of different extension projects are differentiated as simple as

.namespace + "::Step1

and projectB.namespace + "::Step1</tt>, where <tt>"Step1"</tt> is the step name in both solvers. @section autotoc_md1253 Add additional mesh sweeps manually Once you have the low-level Peano project, you can add an arbitrary number of additional algorithmic steps. Each step models a new mesh sweep and gives you a brand new observer object: @icode additional_step = peano4.solversteps.Step( name = "AdditionalStep", add_user_defined_actions=False, ) peano4_project.solversteps.add_step( additional_step ) @endicode Passing in True gives you a C++ template which you can befill with semantics. Consult the @ref page_architecture_home "architecture" and @ref peano_action_sets "action set" descriptions. Per step added, you should find a new class in observers.

Unify the data usage

Though the repository now contains both solver's data structures and observers, the observers are not yet compatible with each other: Each observer needs to be told via use_face(), use_cell() and use_vertex() which data entities are out there. If we construct two separate solvers, solver A knows nothing of the data declared in solver B and vice versa. If we introduce additional mesh sweeps, these mesh sweeps do not know anything of each other either.

The merge() operation for the projects automatically ensures that all data are correct: It runs, for example, through all use_vertex() data of the first observer of solver A and adds those guys to all steps in solver B. There are some assumptions here to make this work:

  • The attributes all have different names.
  • All observers use all vertices, faces and cells of a project. There are not a few observers which only use a subset. Peano allows this to happen, but this is not allowed if we merge projects.

Todo
Dmitry, if we find that attributes' names clash, we have to rename them too

Additional external mesh sweeps are slightly more complicated. Here, we have to add all the use statements manually. Fortunately, some extensions such as ExaHyPE offer routines to register external, additional mesh sweeps, and you should use those.

Create a merged main file

Once you have created your fused project with additional mesh traversals, you can alter your main routine and invoke the steps from the new, joint project.

[...]
) {
repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::AdditionalStep )
);
[...]
}
[...]
) {
repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::SolverA_Sweep1 )
);
[...]
}
[...]
) {
repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::SolverB_Sweep1 )
);
[...]
}
void setNextProgramStep(int number)
The user tells the set which program step to use, i.e.
Definition Node.cpp:441
static Node & getInstance()
This operation returns the singleton instance.
Definition Node.cpp:108

The toggling is straightforward and simple. However, you might have objects with states in your code, such as global solver objects. Some of these toggle their state after and before each mesh sweep, and then they make their behaviour depend upon the actual state. Some extensions therefore define routines such as

repositories::suspendSolversForOneGridSweep();

which you can use in the code to ensure that solvers do not misbehave.

Lowering two codes into a Peano project (additively)