Skip to content

Connectivity Management

Connectivity management handles element-to-element relationships and topological information for unstructured meshes, providing the foundation for mesh traversal and numerical computations.

Purpose

Connectivity information enables:

  • Mesh traversal algorithms
  • Finite element assembly
  • Boundary condition application
  • Mesh adaptation operations
  • Parallel data dependencies

Key Components

Connectivity Storage (GPU)

// Connectivity stored as tuple of device vectors (SFC keys)
template<typename ElementTag, typename AcceleratorTag, typename T>
struct ConnectivityTupleHelper<TetTag, AcceleratorTag, T> {
    using type = std::tuple<
        typename VectorSelector<T, AcceleratorTag>::type,  // indices0
        typename VectorSelector<T, AcceleratorTag>::type,  // indices1
        typename VectorSelector<T, AcceleratorTag>::type,  // indices2
        typename VectorSelector<T, AcceleratorTag>::type   // indices3
    >;
};

// Usage in ElementDomain
using ConnTuple = typename ConnectivityTupleHelper<ElementTag, AcceleratorTag, KeyType>::type;
ConnTuple d_conn_keys_;  // SFC keys, not integer indices

Topological Relationships

  • Element-to-node connectivity (primary)
  • Node-to-element relationships (derived)
  • Face-based element adjacency
  • Boundary face identification

Usage Example

// Build connectivity information
domain.build_connectivity();

// Access connectivity data
auto connectivity = domain.get_connectivity_manager();

// Get nodes of an element
auto elem_nodes = connectivity.element_to_nodes[element_id];
std::cout << "Element " << element_id << " has nodes: ";
for (auto node : elem_nodes) {
    std::cout << node << " ";
}

// Get elements sharing a node
auto node_elements = connectivity.get_elements_at_node(node_id);
std::cout << "Node " << node_id << " belongs to elements: ";
for (auto elem : node_elements) {
    std::cout << elem << " ";
}

// Find adjacent elements
auto adjacent = connectivity.get_adjacent_elements(element_id);
std::cout << "Adjacent elements: ";
for (auto adj : adjacent) {
    std::cout << adj << " ";
}

Building Connectivity

Element-to-Node Mapping

void build_element_to_nodes(std::vector<std::vector<size_t>>& elem_to_nodes,
                          const std::vector<Element>& elements) {
    elem_to_nodes.resize(elements.size());
    for (size_t i = 0; i < elements.size(); ++i) {
        elem_to_nodes[i] = elements[i].nodes;
    }
}

Node-to-Element Mapping

void build_node_to_elements(std::vector<std::vector<size_t>>& node_to_elem,
                          const std::vector<Element>& elements,
                          size_t num_nodes) {
    node_to_elem.resize(num_nodes);
    for (size_t elem_id = 0; elem_id < elements.size(); ++elem_id) {
        for (size_t node : elements[elem_id].nodes) {
            node_to_elem[node].push_back(elem_id);
        }
    }
}

Face-Based Adjacency

struct Face {
    std::vector<size_t> nodes;
    size_t element_id;
    int local_face_index;
};

std::vector<std::vector<size_t>> build_face_adjacency(
    const std::vector<Element>& elements) {

    std::map<std::set<size_t>, std::vector<size_t>> face_to_elements;

    // Extract faces from all elements
    for (size_t elem_id = 0; elem_id < elements.size(); ++elem_id) {
        auto faces = extract_faces(elements[elem_id]);
        for (const auto& face : faces) {
            std::set<size_t> face_key(face.nodes.begin(), face.nodes.end());
            face_to_elements[face_key].push_back(elem_id);
        }
    }

    // Build element-to-element adjacency
    std::vector<std::vector<size_t>> adjacency(elements.size());
    for (const auto& [face_key, elem_list] : face_to_elements) {
        if (elem_list.size() == 2) {
            // Interior face
            adjacency[elem_list[0]].push_back(elem_list[1]);
            adjacency[elem_list[1]].push_back(elem_list[0]);
        }
        // Boundary faces have only one element
    }

    return adjacency;
}

Applications

Finite Element Assembly

// Assemble global matrix using connectivity
void assemble_matrix(SparseMatrix& global_matrix,
                    const ConnectivityManager& connectivity,
                    const std::vector<Element>& elements) {

    for (size_t elem_id = 0; elem_id < elements.size(); ++elem_id) {
        // Get local element matrix
        auto local_matrix = compute_element_matrix(elements[elem_id]);

        // Get global node indices
        auto global_nodes = connectivity.element_to_nodes[elem_id];

        // Add to global matrix
        for (size_t i = 0; i < global_nodes.size(); ++i) {
            for (size_t j = 0; j < global_nodes.size(); ++j) {
                global_matrix.add(global_nodes[i], global_nodes[j], 
                                local_matrix(i,j));
            }
        }
    }
}

Mesh Traversal

// Breadth-first traversal
void traverse_mesh(size_t start_element,
                  const ConnectivityManager& connectivity) {

    std::queue<size_t> queue;
    std::vector<bool> visited(connectivity.element_to_nodes.size(), false);

    queue.push(start_element);
    visited[start_element] = true;

    while (!queue.empty()) {
        size_t current = queue.front(); queue.pop();

        // Process current element
        process_element(current);

        // Add unvisited neighbors
        for (size_t neighbor : connectivity.get_adjacent_elements(current)) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                queue.push(neighbor);
            }
        }
    }
}

Boundary Detection

std::vector<size_t> find_boundary_faces(
    const ConnectivityManager& connectivity) {

    std::vector<size_t> boundary_faces;

    for (size_t face_id = 0; face_id < connectivity.face_to_elements.size(); ++face_id) {
        if (connectivity.face_to_elements[face_id].size() == 1) {
            // Face belongs to only one element (boundary)
            boundary_faces.push_back(face_id);
        }
    }

    return boundary_faces;
}

Performance Optimizations

Memory Layout

  • Compressed storage for sparse connectivity
  • Cache-aligned data structures
  • Minimal memory overhead

Query Optimization

  • Pre-computed adjacency lists
  • Fast lookup tables
  • Lazy evaluation for derived relationships

Parallel Considerations

  • Local connectivity within partitions
  • Halo connectivity for inter-partition relationships
  • Consistent global indexing

Integration with Other Components

  • Adjacency Structures: Builds upon basic connectivity
  • Halo Management: Uses connectivity for ghost element identification
  • Characteristic Sizes: Leverages connectivity for quality metrics

Error Handling

  • Validate connectivity consistency
  • Check for orphaned nodes/elements
  • Detect topological errors
  • Provide debugging utilities

See Also