Skip to content

mask_base

mask_base

Post-assembly mesh query types used by femora.core.mask_manager.

Classes

MeshIndex dataclass

MeshIndex(node_ids: ndarray, node_coords: ndarray, node_ndf: ndarray, node_core_map: List[List[int]], element_ids: ndarray, element_connectivity: List[ndarray], element_centroids: ndarray, element_types: ndarray, material_tags: ndarray, section_tags: ndarray, region_tags: ndarray, core_ids: ndarray, element_id_to_index: Dict[int, int], node_tag_start: int = 1, element_tag_start: int = 1)

Immutable snapshot of assembled mesh data for efficient masking/filtering.

This structure is created after mesh assembly and stores the minimal arrays needed to perform fast vectorized spatial and attribute-based queries.

Attributes:

Name Type Description
node_ids ndarray

1D array of node IDs, zero-based indices derived from the assembled grid ordering. Shape (N,).

node_coords ndarray

Nodal coordinates. Shape (N, 3).

node_ndf ndarray

Degrees of freedom per node. Shape (N,).

node_core_map List[List[int]]

List where each item is a list of core IDs that the corresponding node belongs to (a node can be part of multiple cores). Shape (N,).

element_ids ndarray

1D array of element IDs, which can be cell indices or explicit identifiers. Shape (M,).

element_connectivity List[ndarray]

A list where each item is a 1D array of node indices (referencing node_ids) for that element.

element_centroids ndarray

Precomputed centroid coordinates for each element. Shape (M, 3).

element_types ndarray

Array of string or object types for each element. Shape (M,).

material_tags ndarray

Material tag for each element. Shape (M,).

section_tags ndarray

Section tag for each element. Shape (M,).

region_tags ndarray

Region tag for each element. Shape (M,).

core_ids ndarray

Partition/core ID for each element. Shape (M,).

element_id_to_index Dict[int, int]

A mapping from an explicit element ID to its 0-based row index in the element_ids array.

Example
import numpy as np
from femora.components.mask.mask_base import MeshIndex

# Assuming a simple mesh has been assembled
mesh_index = MeshIndex(
    node_ids=np.array([0, 1, 2]),
    node_coords=np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]),
    node_ndf=np.array([3, 3, 3]),
    node_core_map=[[0], [0], [0]],
    element_ids=np.array([0]),
    element_connectivity=[np.array([0, 1, 2])],
    element_centroids=np.array([[0.33, 0.33, 0.0]]),
    element_types=np.array(["TRIANGLE"]),
    material_tags=np.array([1]),
    section_tags=np.array([1]),
    region_tags=np.array([0]),
    core_ids=np.array([0]),
    element_id_to_index={0: 0}
)
print(mesh_index.node_ids.shape)
# Output: (3,)

_BaseMask

_BaseMask(mesh: MeshIndex, ids: ndarray)

Base class for typed masks over nodes or elements.

This mask holds a reference to the MeshIndex and a 1D array of selected entity IDs (node IDs for NodeMask, element IDs for ElementMask).

Attributes:

Name Type Description
_mesh MeshIndex

The MeshIndex instance this mask operates on.

_ids ndarray

1D array of unique, sorted integer IDs of the selected entities (nodes or elements).

Initializes a new _BaseMask instance.

Parameters:

Name Type Description Default
mesh MeshIndex

Immutable snapshot of the assembled mesh to query.

required
ids ndarray

A 1D array of selected entity IDs (node or element IDs depending on the concrete mask). The array will be normalized to sorted unique integers upon initialization.

required
Methods:
to_list
to_list() -> List[int]

Returns the selection as a Python list of IDs.

Returns:

Type Description
List[int]

List[int]: List of selected IDs in ascending order.

to_set
to_set() -> set

Returns the selection as a Python set of IDs.

Returns:

Name Type Description
set set

Set of selected IDs.

__len__
__len__() -> int

Returns the number of selected entities.

Returns:

Name Type Description
int int

The count of IDs in this mask.

is_empty
is_empty() -> bool

Checks if the selection is empty.

Returns:

Name Type Description
bool bool

True if no IDs are selected, False otherwise.

NodeMask

NodeMask(mesh: MeshIndex, ids: ndarray)

Bases: _BaseMask

A mask of node IDs with chainable spatial and predicate-based filters.

All methods return a new NodeMask instance, ensuring immutability at the API level.

Example
import numpy as np
from femora.components.mask.mask_base import MeshIndex, NodeMask

mesh_index = MeshIndex(
    node_ids=np.array([0, 1, 2, 3]),
    node_coords=np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [2.0, 2.0, 2.0]]),
    node_ndf=np.array([3, 3, 3, 3]),
    node_core_map=[[0], [0], [0], [0]],
    element_ids=np.array([0]),
    element_connectivity=[np.array([0, 1, 2])],
    element_centroids=np.array([[0.33, 0.33, 0.0]]),
    element_types=np.array(["TRIANGLE"]),
    material_tags=np.array([1]),
    section_tags=np.array([1]),
    region_tags=np.array([0]),
    core_ids=np.array([0]),
    element_id_to_index={0: 0}
)
all_nodes = NodeMask(mesh_index, mesh_index.node_ids)
filtered_nodes = all_nodes.by_bbox(0.5, 1.5, -0.5, 0.5, -0.5, 0.5)
print(filtered_nodes.to_list())
# Output: [1]
Methods:
by_ids
by_ids(ids: Sequence[int]) -> 'NodeMask'

Intersects the current node mask with specific node IDs.

Parameters:

Name Type Description Default
ids Sequence[int]

A sequence of node IDs to retain.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing the intersection of the current mask's IDs and the provided ids.

Example
# assuming `mask` is a NodeMask holding [0, 1, 2]
filtered_mask = mask.by_ids([1, 3])
print(filtered_mask.to_list())
# Output: [1]
by_bbox
by_bbox(xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float) -> 'NodeMask'

Filters nodes inside an axis-aligned bounding box.

Parameters:

Name Type Description Default
xmin float

The minimum x-coordinate of the bounding box.

required
xmax float

The maximum x-coordinate of the bounding box.

required
ymin float

The minimum y-coordinate of the bounding box.

required
ymax float

The maximum y-coordinate of the bounding box.

required
zmin float

The minimum z-coordinate of the bounding box.

required
zmax float

The maximum z-coordinate of the bounding box.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing only nodes whose coordinates are within the specified bounding box.

Example
# assuming `all_nodes` is a NodeMask
filtered_nodes = all_nodes.by_bbox(0.5, 1.5, -0.5, 0.5, -0.5, 0.5)
print(filtered_nodes.to_list())
# Output: [1]
near_point
near_point(point: Tuple[float, float, float], radius: float) -> 'NodeMask'

Filters nodes within a specified distance from a given point.

Parameters:

Name Type Description Default
point Tuple[float, float, float]

The reference point (x, y, z) from which to measure the distance.

required
radius float

The radial distance threshold. Nodes within this radius will be included.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing only nodes that are within the specified radial distance from the point.

Example
# Get nodes within radial distance of 0.5 from origin
filtered_nodes = all_nodes.near_point((0.0, 0.0, 0.0), 0.5)
print(filtered_nodes.to_list())
# Output: [0]
along_line
along_line(point1: Tuple[float, float, float], point2: Tuple[float, float, float], radius: float) -> 'NodeMask'

Filters nodes within a specified distance from a line segment.

The line segment is defined by two endpoints. Nodes within a cylindrical region around this segment will be included.

Parameters:

Name Type Description Default
point1 Tuple[float, float, float]

The first endpoint of the line segment (x, y, z).

required
point2 Tuple[float, float, float]

The second endpoint of the line segment (x, y, z).

required
radius float

The radial distance threshold from the line segment.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing nodes that are within the cylindrical region around the line segment.

Example
# Get nodes along line segment from (0,0,0) to (1,0,0) within radius 0.2
filtered_nodes = all_nodes.along_line((0.0, 0.0, 0.0), (1.0, 0.0, 0.0), 0.2)
print(filtered_nodes.to_list())
# Output: [0, 1, 2]
along_axis
along_axis(axis: str, vmin: float, vmax: float) -> 'NodeMask'

Filters nodes whose coordinate along a specified axis lies within a range.

Parameters:

Name Type Description Default
axis str

The name of the axis ('x', 'y', or 'z') to filter along. Case-insensitive.

required
vmin float

The minimum coordinate value (inclusive) for the specified axis.

required
vmax float

The maximum coordinate value (inclusive) for the specified axis.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing only nodes whose coordinate along the given axis falls within the [vmin, vmax] range.

Example
# Get nodes with x-coordinate in [0.5, 1.5]
filtered_nodes = all_nodes.along_axis('x', 0.5, 1.5)
print(filtered_nodes.to_list())
# Output: [1]
by_predicate
by_predicate(fn: Callable[[int, ndarray], bool]) -> 'NodeMask'

Filters nodes using a custom predicate function.

The predicate function will be called for each node, and only nodes for which the function returns True will be included in the new mask.

Parameters:

Name Type Description Default
fn Callable[[int, ndarray], bool]

A callable that accepts two arguments: - node_id (int): The ID of the current node. - coord (np.ndarray): The 3D coordinates of the current node. The function should return True to keep the node, False otherwise.

required

Returns:

Name Type Description
NodeMask 'NodeMask'

A new mask containing only nodes that pass the predicate.

Example
# Keep nodes with x-coordinate > 0.5 using a custom predicate
def filter_x_gt_0_5(node_id: int, coord: np.ndarray) -> bool:
    return coord[0] > 0.5

filtered_nodes = all_nodes.by_predicate(filter_x_gt_0_5)
print(filtered_nodes.to_list())
# Output: [1, 3]
touching_elements
touching_elements() -> 'ElementMask'

Converts the current node mask to an element mask.

The resulting mask will contain all elements that are connected to any of the nodes currently selected in this NodeMask.

Returns:

Name Type Description
ElementMask 'ElementMask'

A new ElementMask containing elements that share connectivity with any node in this mask.

Example
# Get elements connected to any of the selected nodes
touching_elements = selected_nodes.touching_elements()
print(touching_elements.to_list())
# Output: [10, 11]
to_tags
to_tags(start_tag: int | None = None) -> List[int]

Converts selected node IDs to OpenSees node tags.

This method applies an offset to the internal zero-based node IDs to generate application-specific node tags, typically for OpenSees.

Parameters:

Name Type Description Default
start_tag int | None

Optional. The starting node tag. If None, it attempts to retrieve the starting node tag from a Model instance; otherwise, it defaults to 1.

None

Returns:

Type Description
List[int]

List[int]: A list of OpenSees-compatible node tags for the selected nodes.

Example
# Convert node IDs to tag integers
print(selected_nodes.to_tags(start_tag=100))
# Output: [100, 101]

ElementMask

ElementMask(mesh: MeshIndex, ids: ndarray)

Bases: _BaseMask

A mask of element IDs with chainable attribute and spatial filters.

All methods return a new ElementMask instance, ensuring immutability at the API level.

Example
import numpy as np
from femora.components.mask.mask_base import MeshIndex, ElementMask

mesh_index = MeshIndex(
    node_ids=np.array([0, 1, 2, 3]),
    node_coords=np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [2.0, 2.0, 2.0]]),
    node_ndf=np.array([3, 3, 3, 3]),
    node_core_map=[[0], [0], [0], [0]],
    element_ids=np.array([10, 11, 12]),
    element_connectivity=[np.array([0, 1]), np.array([1, 2]), np.array([2, 3])],
    element_centroids=np.array([[0.5, 0.0, 0.0], [0.5, 0.5, 0.0], [1.0, 1.5, 1.0]]),
    element_types=np.array(["BEAM", "BEAM", "TRUSS"]),
    material_tags=np.array([1, 2, 1]),
    section_tags=np.array([1, 1, 2]),
    region_tags=np.array([0, 1, 0]),
    core_ids=np.array([0, 0, 1]),
    element_id_to_index={10: 0, 11: 1, 12: 2}
)
all_elements = ElementMask(mesh_index, mesh_index.element_ids)
filtered_elements = all_elements.by_material(1)
print(filtered_elements.to_list())
# Output: [10, 12]
Methods:
by_ids
by_ids(ids: Sequence[int]) -> 'ElementMask'

Intersects the current element mask with specific element IDs.

Parameters:

Name Type Description Default
ids Sequence[int]

A sequence of element IDs to retain.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing the intersection of the current mask's IDs and the provided ids.

Example
# assuming `mask` is an ElementMask holding [10, 11, 12]
filtered_mask = mask.by_ids([11, 13])
print(filtered_mask.to_list())
# Output: [11]
by_type
by_type(name: str) -> 'ElementMask'

Filters elements by their type name (case-insensitive).

Parameters:

Name Type Description Default
name str

The element type name to match.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements whose type name matches the provided name.

Example
# Filter elements by type string (case-insensitive)
filtered_elements = all_elements.by_type("beam")
print(filtered_elements.to_list())
# Output: [10, 11]
by_material
by_material(tag: int) -> 'ElementMask'

Filters elements by their associated material tag.

Parameters:

Name Type Description Default
tag int

The integer material tag value to match.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements that have the specified material tag.

Example
# Filter elements by material tag
filtered_elements = all_elements.by_material(1)
print(filtered_elements.to_list())
# Output: [10, 12]
by_section
by_section(tag: int) -> 'ElementMask'

Filters elements by their associated section tag.

Parameters:

Name Type Description Default
tag int

The integer section tag value to match.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements that have the specified section tag.

Example
# Filter elements by section tag
filtered_elements = all_elements.by_section(2)
print(filtered_elements.to_list())
# Output: [12]
by_region
by_region(tag: int) -> 'ElementMask'

Filters elements by their associated region tag.

Parameters:

Name Type Description Default
tag int

The integer region tag value to match.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements that are associated with the specified region tag.

Example
# Filter elements by region tag
filtered_elements = all_elements.by_region(1)
print(filtered_elements.to_list())
# Output: [11]
by_core
by_core(core: int) -> 'ElementMask'

Filters elements by their partition or core ID.

Parameters:

Name Type Description Default
core int

The integer core/partition identifier to match.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements that belong to the specified core.

Example
# Filter elements by partition/core ID
filtered_elements = all_elements.by_core(0)
print(filtered_elements.to_list())
# Output: [10, 11]
by_bbox
by_bbox(xmin: float, xmax: float, ymin: float, ymax: float, zmin: float, zmax: float, *, use_centroid: bool = True) -> 'ElementMask'

Filters elements inside an axis-aligned bounding box.

Parameters:

Name Type Description Default
xmin float

The minimum x-coordinate of the bounding box.

required
xmax float

The maximum x-coordinate of the bounding box.

required
ymin float

The minimum y-coordinate of the bounding box.

required
ymax float

The maximum y-coordinate of the bounding box.

required
zmin float

The minimum z-coordinate of the bounding box.

required
zmax float

The maximum z-coordinate of the bounding box.

required
use_centroid bool

If True (default), elements are filtered based on whether their centroid lies within the bounding box. If False, an element is included if any of its nodes lies within the bounding box.

True

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing elements that are located within the specified bounding box.

Example
# Filter elements by centroid or nodal coordinates in bounding box
filtered_by_centroid = all_elements.by_bbox(0.5, 1.0, 0.5, 1.0, -0.1, 0.1, use_centroid=True)
print(filtered_by_centroid.to_list())
# Output: [11]
by_predicate
by_predicate(fn: Callable[[int, ndarray, str, int, int, int], bool]) -> 'ElementMask'

Filters elements using a custom predicate function.

The predicate function will be called for each element, and only elements for which the function returns True will be included in the new mask.

Parameters:

Name Type Description Default
fn Callable[[int, ndarray, str, int, int, int], bool]

A callable that accepts six arguments: - elem_id (int): The ID of the current element. - centroid (np.ndarray): The 3D centroid coordinates of the element. - type_name (str): The type name of the element (e.g., "BEAM", "TRIANGLE"). - material_tag (int): The material tag associated with the element. - section_tag (int): The section tag associated with the element. - region_tag (int): The region tag associated with the element. The function should return True to keep the element, False otherwise.

required

Returns:

Name Type Description
ElementMask 'ElementMask'

A new mask containing only elements that pass the predicate.

Example
# Custom predicate filtering
def filter_elements(eid, centroid, etype, mtag, stag, rtag):
    return mtag == 1 and centroid[0] > 0.2

filtered_elements = all_elements.by_predicate(filter_elements)
print(filtered_elements.to_list())
# Output: [10]
to_nodes
to_nodes() -> 'NodeMask'

Converts to a node mask containing all nodes in the mesh.

This method currently returns all nodes found in the underlying

mesh, regardless of which elements are selected in the current ElementMask. This behavior is due to an internal implementation detail where it iterates over all mesh elements' connectivity.

Returns:

Name Type Description
NodeMask 'NodeMask'

A new NodeMask containing all nodes incident to any element in the mesh.

Example
# Convert ElementMask to NodeMask containing elements' nodes
incident_nodes = selected_elements.to_nodes()
print(incident_nodes.to_list())
# Output: [0, 1, 2, 3]
to_tags
to_tags(start_tag: int | None = None) -> List[int]

Converts selected element IDs to OpenSees element tags.

This method offsets the existing explicit element IDs (which are already considered tags within Femora) by a start_tag value, primarily for compatibility with external tools like OpenSees that might use a different tagging scheme or require a specific offset.

Parameters:

Name Type Description Default
start_tag int | None

Optional. The value to add to each selected element ID. If None, it attempts to retrieve the starting element tag from a Model instance; otherwise, it defaults to 1.

None

Returns:

Type Description
List[int]

List[int]: A list of OpenSees-compatible element tags for the selected elements. These are the original element IDs plus the start_tag offset.

Example
# Convert ElementMask to integer tag list
print(selected_elements.to_tags(start_tag=500))
# Output: [510, 512]