Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model direct access and just-in-time mapping in schema #31

Open
uekerman opened this issue Jan 29, 2025 · 9 comments
Open

Model direct access and just-in-time mapping in schema #31

uekerman opened this issue Jan 29, 2025 · 9 comments
Assignees
Labels
schema Related to the adapter config schema

Comments

@uekerman
Copy link
Member

uekerman commented Jan 29, 2025

There are (now) two distinct ways how to interact with meshes and data in preCICE: provide coordinates or directly access received meshes (potentially via just-in-time mapping).

<participant name="P">
  <receive-mesh name="Received-Mesh" from="OtherP" direct-access="true" />
  <write-data name="Write-Data" mesh="Received-Mesh" />
  <provide-mesh name="Provided-Mesh" />
  <read-data name="Read-Data" mesh="Provided-Mesh" />
</participant>

We are currently discussing to rename direct-access to api-access.

We also need a way to distinguish both options in adapter code.

Either:

vertex_ids = participant.set_mesh_vertices("Provided-Mesh", locations)
data = participant.read_data("Provided-Mesh", "Read-Data", vertex_ids, dt)
participant.write_data("Provided-Mesh", "Write-Data", vertex_ids, data)

Or:

precice.set_mesh_access_region("Received-Mesh", bounding_box);
precice.get_mesh_vertex_ids_and_coordinates("Received-Mesh", vertex_ids, coordinates);
data = participant.read_data("Received-Mesh", "Read-Data", vertex_ids, dt)
participant.map_and_write_data("Received-Mesh", "Write-Data", vertex_ids, data, location)

There are different ways we could do this:

1. With an is_received option in the adapter config defaulting to "false":

  "interfaces": [
    {
      "mesh_name": "Received-Mesh",
      "is_received": true,  
      "read_data_names": [ ... ],
      "write_data_names": [ ... ]      
    },
    {
      "mesh_name": "Provided-Mesh",
      "read_data_names": [ ... ],
      "write_data_names": [ ... ]      
    },
  ]

Instead of is_received, we could, obviously, do is_provided.

Or some enum: mesh_locations = {received or provided}. We use location currently to distinguish between nodes/centers or surface/volume. Is there a better name?

2. With a new API function:

  • participant.is_mesh_received("Mesh") or
  • participant.is_mesh_access_region_required("Mesh") or
  • participant.requiresCoordinates("Mesh")
@MakisH
Copy link
Member

MakisH commented Jan 30, 2025

Since preCICE has this information already, I don't see why we should ask the user to also specify this in the adapter config. -> do we really need to give this in the config?

An API function would be great!

* `participant.is_mesh_received("Mesh")` or 
* `participant.is_mesh_access_region_required("Mesh")` or
* `participant.requiresCoordinates("Mesh")`

Of the three, I like the participant.is_mesh_access_region_required("Mesh") more because it is difficult to assume that it is needed in a normal situation. Both other options could cause confusion with the terminology used in simpler contexts.

However, the check in the "normal case" would be:

if !participant.is_mesh_access_region_required("Mesh"):
    # set mesh vertices

Given this, the participant.requiresCoordinates("Mesh") sounds more natural:

if participant.requiresCoordinates("Mesh"):
    # set mesh vertices
else:
    # set mesh access region

@uekerman
Copy link
Member Author

A potential difficulty with is_mesh_access_region_required I did not mention above is that we currently don't plan to require the call for received mesh if run in serial.

@davidscn
Copy link
Member

To also enable a mixture of both scenarios, we should distinguish

    1. whether an api-access is allowed or not
    1. whether an access region is required or not

then we would have something like

std::string mesh("wormhole");

if (!accessAllowed){
  participant.set_mesh_vertices(mesh, locations, vertex_ids);
} else {

  if(requiresAccessRegion){
    precice.set_mesh_access_region(mesh, bounding_box);
  }
  precice.get_mesh_vertex_ids_and_coordinates(mesh, vertex_ids, coordinates);
}

data = participant.read_data(mesh, "Read-Data", vertex_ids, dt)
participant.write_data(mesh, "Write-Data", vertex_ids, data)

These would be all information we have in preCICE and can clearly forward to the user via the API. In principle, I would also vote to encode this information in the API rather than any adapter configuration file. For the ID-based reading and writing, the above approach should be sufficient in my opinion.

For just-in-time mapping, however, we would need to have another API function to distinguish reading and writing via IDs vs via coordinates on top.

@uekerman
Copy link
Member Author

I agree that option 2 above might be easier seen from the configuration. I am bit afraid, however, that the necessary API functions will get complicated.

@davidscn IIUC you are suggesting 3 functions? I don't yet understand why 2 would not be enough. Could you explain your current suggestion, potential names and signatures of the functions and what they do?

@MakisH
Copy link
Member

MakisH commented Feb 20, 2025

Do we still want the set_mesh_vertices to be the default? If yes, then the story of the example sounds complicated to me with a first look:

if no access is allowed,
    do the default / encouraged
else
   do the complicated
endif

I would expect the default case to not need an if around it, or to be formulated positively (if default possible, do the default).

Or do I overlook a larger paradigm shift?

@davidscn
Copy link
Member

davidscn commented Feb 26, 2025

IIUC you are suggesting 3 functions? I don't yet understand why 2 would not be enough. Could you explain your current suggestion, potential names and signatures of the functions and what they do?

/// Whether the API access was enabled on a received mesh or not. (don't like the name though)
bool allowsAPIAccess(std::string_view meshName);

/// Whether the API access requires defining an access region using \p setMeshAccessRegion or whether this may be omitted
/// (currently not entirely straightforward to implement, we raise an error only later in the partitioning, if it was required, i.e., our check in the ParticipantImpl is not complete at the moment as the partitioning will complain otherwise) 
bool requiresAccessRegion(std::string_view meshName);

These API functions are sufficient for calling the getMeshVertexIDsAndCoordinates and performing reading and writing on the mesh. In fact, one could always call getMeshVertexIDsAndCoordinates in case the above API functions are used properly.

The point I was trying to make was that (if you want a fully configurable adapter), you have to use different API functions for reading and writing using just-in-time mapping as opposed to the direct access. The adapter itself doesn't know about it. The third one would then be

/// Whether we can use the just-in-time mapping interface or not.
bool hasJustInTimeMapping(std::string_view meshName, std::string_view dataName);

I am not sure if there are use cases for this. I could imagine that a could uses either the one or the other API for data access.

@uekerman
Copy link
Member Author

The difficulty of

bool allowsAPIAccess(std::string_view meshName);

is that it should also return false on provided meshes. It would need to be isReceivedAndAllowsAPIAccess.

The more I think about it, the less I like option 2 above. Most adapters will anyway only support one variant: either the old one (setMeshVertices) or one of the others for specific cases (e.g. particle code). With is_received defaulting to false, we keep the standard case as easy as possible and only make the special cases more complicated. A code that only supports direct access, could either ignore is_received or assert it. Only a code that really supports both possibilities needs to read it. And then, we leave it to the user to also enable api-access in the preCICE configuration. Otherwise, we will anyway throw an error.

Admittedly, all this is a bit motivated from the perspective of a user who does not treat the adapter as a black box. Maybe, we could assume this for these advanced features?

@MakisH
Copy link
Member

MakisH commented Feb 27, 2025

With is_received defaulting to false, we keep the standard case as easy as possible and only make the special cases more complicated. A code that only supports direct access, could either ignore is_received or assert it. Only a code that really supports both possibilities needs to read it. And then, we leave it to the user to also enable api-access in the preCICE configuration. Otherwise, we will anyway throw an error.

This approach sounds intuitive to me. 👍

Admittedly, all this is a bit motivated from the perspective of a user who does not treat the adapter as a black box. Maybe, we could assume this for these advanced features?

I am not sure I understand what you mean here. Could you please elaborate?

@uekerman
Copy link
Member Author

uekerman commented Mar 3, 2025

There are two typical preCICE users: Those that use adapters off-the-shelve and those that develop their own (and sometimes both use cases are combined).

For an adapter dev, such new API functions as discussed above could be intricate as they are a few and their combination needs careful thought.

For a (black-box) adapter user, it's indeed annoying to changes things in two places (preCICE config and adapter config). But this only comes into play if an adapter really support two variants here. For example, I could imagine this for the FEniCS adapter at some point: (1) the classical provided mesh cases, (2) direct access to sample higher-order shape functions. Then to change from (1) to (2), indeed one needs to change things in two places. However, even with new API functions this is the case as one always needs to change the mesh name (from "MyMesh" to "TheirMesh").

@uekerman uekerman added the schema Related to the adapter config schema label Mar 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
schema Related to the adapter config schema
Projects
None yet
Development

No branches or pull requests

3 participants