This function is thread parallel over each link in the given link_data that falls within the given link_subset_selector. Notice that for_each_link does not require this synchronization, meaning that dynamic linker creation and destruction can be done in parallel within a for_each_link loop AND you can continue to perform for_each_link loops while the link data is out-of_sync.
- for_each_linked_entity_run(link_data, linked_entity_selector, linker_subset_selector, functor) The functor must either have an operator(const stk::mesh::BulkData&, const stk::mesh::Entity&linked_entity, const stk::mesh::Entity&linker) or an operator(const LinkData&, const stk::mesh::Entity& linked_entity, const stk::mesh::Entity& linker). This functions is thread parallel over each linked entity in the given linked_entity_selector that falls within a bucket that a linker in the given linker_subset_selector connects to. This means that the functor will be called in serial for each link that connects to a linked entity. Importantly, this function requires infrastructure that requires the link data to be "up-to-date" (i.e., you must call link_data.update_crs_from_coo() before using it if you have modified the link data).
You might think, why not just provide a get_connected_links(entity) function? The reason is that the links themselves are heterogeneous and bucketized. As such, there is no practical way to provide contiguous access to the set of links that an entity connects to while supporting subset selection of links and without dynamic memory allocation.
- Note
- To Devs: Hi! Welcome. If you want to better understand the LinkData or our links in general, I recommend looking at it as maintaining two connectivity structures: a COO-like structure providing access from a linker to its linked entities and a CSR-like structure providing access from a linked entity to its linkers. The COO is the dynamic "driver" that is trivial to modify (even in parallel) and the CSR is a more heavy-weight sparse data structure that is non-trivial to modify in parallel since it often requires memory allocation. The update_crs_from_coo function is responsible for mapping all of the modifications to the COO structure to the CSR structure. There are some operations that fundamentally require a CSR-like structure such as maintaining parallel consistency as entities are removed from the mesh or performing operations that require a serial loop over each linker that connects to a given linked entity.
Links vs. Connectivity
Unlike STK's connectivity, links
- do not induce changes to part membership, ownership, or sharing
- have no restrictions on which entities they can connect (may link same rank entities)
- allow data to be stored on the links themselves in a bucket-aware manner
- allow either looping over the links or the entities to which they are connected
- enforce a weaker aura condition whereby every locally-owned-or-shared link and every locally-owned-or-shared linked entity that it connects have at least ghosted access to one another
- allow for the creation and destruction of link to linked entity relations outside of a modification cycle and in parallel
Links are not meant to be a replacement for connectivity, but rather a more flexible and dynamic alternative. They are great for encoding neighbor relationships that require data to be stored on the link itself, such as reused invariants. They also work well for composite relationships whereby an entity needs to "know about" the state of another entity in a way that cannot be expressed topologically. For example, a quad face storing a set of nodes that are "attached" to it at locations other than the corners.
From a user's perspective, links are Entities with a connected entity ids and connected entity rank field. You are free to use links like regular entities by adding them to parts, using subset relations/selectors, and by adding additional fields to them. This makes them fully compatible with our Aggregates. Importantly, they may even be written out to the EXO file using standard STK_IO functionality with care to avoid accidentally loadbalancing them if they don't have spacial information.
LinkData
The LinkData class is the main interface for interacting with the link data on the mesh. It is meant to mirror BulkData's connectivity interface while allowing multiple LinkData objects to be used on the same mesh, each with separately managed data. Use LinkData to connect links to linked entities and to get the linked entities for a given link. And similar to STK's for_each_entity_run, use non-member functions acting on the LinkData to loop over links and linked entities.
Declaring/Connecting Links
Declaring links can be done via the standard declare_entity interface, however, connecting these links to their linked entities must be mediated via the link data. For this reason, we provide only const access to the linked entities field. Use it to ~view~ the connections, not to ~modify~ them. Instead, use the link data's declare_relation(linker, linked_entity, link_ordinal) and destroy_relation(linker, link_ordinal) to modify the relationship between a link and its linked entities. These functions are thread-safe and may be called in parallel so long as you do not call declare_relation(linker, *, link_ordinal) for the same linker and ordinal on two different threads (something that would be weird to do anyway).
Some comments on requirements:
- Both declare_relation and destroy_relation require that the linker be valid, but not necessarily the linked entity.
- To maintain parallel consistency, we require that declare/destroy_relation be performed consistently for each process that locally owns or shares the given linker or linked entity.
- Note
- Once a relationship between a link and a linked entity is declared or destroyed, the link data is marked as "needs updated", as the changes to the linked data are only reflected by the get_linked_entity function and not some of the other infrastructure needed to maintain parallel consistency. As such, you must call update_crs_from_coo() before entering a modification cycle or before using the for_each_linked_entity_run function.
Getting Linked Entities
In contrast to STK's connectivity, links are designed to be declared dynamically and to be created and destroyed at any time. This must be done outside of a mesh modification cycle. Once a relation between a link and a linked entity is declared, you may call get_linked_entity(linker, link_ordinal) to get the linked entity at the given ordinal. If no relation exists, this will return an invalid entity and is valid so long as link_ordinal is less than the linker dimensionality, otherwise, it will throw an exception (in debug). This function is thread-safe and immediately reflects any changes made to the link data.
Note, we do not offer a get_linked_entities function, as this would require either dynamic memory allocation or compile-time link dimensionality. Similarly, we do not offer a get_connected_links(entity) function. Instead, we offer two for_each_entity_run-esk functions for looping over links and linked entities.
- for_each_link_run(link_data, link_subset_selector, functor) works the same as for_each_entity_run, but for links. The functor must either have an operator(const stk::mesh::BulkData& bulk_data, const stk::mesh::Entity& linker) or an