pyfebiopt.mesh.mesh
===================

.. py:module:: pyfebiopt.mesh.mesh

.. autoapi-nested-parse::

   Mesh container for pyFEBiOpt.

   This module defines small data classes to store nodes, elements, and surfaces.
   It also provides helpers to export FEBio XML blocks and to build a mesh from
   Gmsh ``.msh`` files or from an already parsed ``xplt`` reader.



Classes
-------

.. autoapisummary::

   pyfebiopt.mesh.mesh.MeshProvider
   pyfebiopt.mesh.mesh.NodeArray
   pyfebiopt.mesh.mesh.ElementArray
   pyfebiopt.mesh.mesh.SurfaceArray
   pyfebiopt.mesh.mesh.FebNodeCache
   pyfebiopt.mesh.mesh.Mesh


Module Contents
---------------

.. py:class:: MeshProvider

   Bases: :py:obj:`Protocol`


   Minimal protocol for objects exposing a mesh attribute.


   .. py:attribute:: mesh
      :type:  Mesh


.. py:class:: NodeArray

   Dense node table with slice-friendly access.

   Attributes:
   -----------
   xyz
       Array of shape (N, 3). Stored as float64.


   .. py:attribute:: xyz
      :type:  numpy.ndarray


   .. py:method:: __post_init__() -> None


   .. py:method:: __len__() -> int

      Return the number of nodes.



   .. py:method:: __getitem__(idx: slice | int | numpy.ndarray | list[int]) -> NodeArray

      Return a sliced view.

      The returned object is a new ``NodeArray`` pointing to a view of
      the original array when possible.



   .. py:method:: take(ids: numpy.ndarray | list[int]) -> NodeArray

      Return a new node table with the given node ids.

      :param ids: 0-based node ids to select.



.. py:class:: ElementArray

   Mixed-element connectivity with padding.

   Elements live in a 2D integer array with ``-1`` padding on the right.
   The valid size of each row is stored in ``nper``. This keeps slicing
   and vectorized operations simple while still allowing mixed arity.

   Attributes:
   -----------
   conn
       Array of shape ``(E, Kmax)`` with 0-based node ids. Unused slots are ``-1``.
   nper
       Array of shape ``(E,)`` with the valid count per row.
   etype
       Array of shape ``(E,)`` with labels like ``'hex8'`` or ``'tet4'``.


   .. py:attribute:: conn
      :type:  numpy.ndarray


   .. py:attribute:: nper
      :type:  numpy.ndarray


   .. py:attribute:: etype
      :type:  numpy.ndarray


   .. py:method:: __post_init__() -> None


   .. py:method:: __len__() -> int

      Return the number of elements.



   .. py:method:: __getitem__(idx: slice | int | numpy.ndarray | list[int]) -> ElementArray

      Return a sliced view.



   .. py:method:: nodes_of(ei: int) -> numpy.ndarray

      Return node ids for a single element.

      :param ei: Element row index.

      :returns: Array of length ``nper[ei]`` with node ids.



   .. py:method:: unique_nodes() -> numpy.ndarray

      Return sorted unique node ids used by the selection.

      Empty selections return an empty array with ``int64`` dtype.



   .. py:method:: as_ragged() -> list[numpy.ndarray]

      Return a Python list with one 1D array of node ids per element.

      Useful when downstream code expects per-element lists.



.. py:class:: SurfaceArray

   Facet connectivity with padding.

   Facets are stored like elements: a padded 2D array plus an ``nper`` vector.

   Attributes:
   -----------
   faces
       Array of shape ``(F, Kmax)`` with 0-based node ids. Unused slots are ``-1``.
   nper
       Array of shape ``(F,)`` with valid count per facet.


   .. py:attribute:: faces
      :type:  numpy.ndarray


   .. py:attribute:: nper
      :type:  numpy.ndarray


   .. py:method:: __post_init__() -> None


   .. py:method:: __len__() -> int

      Return the number of facets.



   .. py:method:: __getitem__(idx: slice | int | numpy.ndarray | list[int]) -> SurfaceArray

      Return a sliced view.



   .. py:method:: nodes_of(fi: int) -> numpy.ndarray

      Return node ids for a single facet.

      :param fi: Facet row index.

      :returns: Array of length ``nper[fi]`` with node ids.



   .. py:method:: unique_nodes() -> numpy.ndarray

      Return sorted unique node ids used by the selection.

      Empty selections return an empty array with ``int64`` dtype.



.. py:class:: FebNodeCache

   Bases: :py:obj:`TypedDict`


   Cached node numbering for FEBio XML writers.

   Keys
   ----
   object_name
       Name used when no parts exist.
   part_nodes
       List of tuples ``(part_name, node_ids_sorted)``.
   part_map
       Mapping ``part_name -> {old_node_id: new_node_id}``.
   part_node_sets
       Mapping ``part_name -> set(old_node_id)`` for quick membership checks.
   global_map
       Mapping ``old_node_id -> new_node_id`` across all parts.
   max_node_id
       Highest assigned node id in the cache.

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: object_name
      :type:  str


   .. py:attribute:: part_nodes
      :type:  list[tuple[str, numpy.ndarray]]


   .. py:attribute:: part_map
      :type:  dict[str, dict[int, int]]


   .. py:attribute:: part_node_sets
      :type:  dict[str, set[int]]


   .. py:attribute:: global_map
      :type:  dict[int, int]


   .. py:attribute:: max_node_id
      :type:  int


.. py:class:: Mesh(nodes: NodeArray, elements: ElementArray, parts: dict[str, numpy.ndarray] | None = None, surfaces: dict[str, SurfaceArray] | None = None, nodesets: dict[str, numpy.ndarray] | None = None)

   Unified mesh container.

   Holds node, element, surface, and set tables. Keeps each concern in its own
   class. Provides name-first access and FEBio XML builders.

   Attributes:
   -----------
   nodes
       Node table.
   elements
       Element table for the full mesh.
   parts
       Mapping ``name -> element indices`` selecting rows of ``elements``.
   surfaces
       Mapping ``name -> SurfaceArray``.
   nodesets
       Mapping ``name -> node ids``.

   Initialize the mesh.

   :param nodes: Node table.
   :param elements: Element table.
   :param parts: Optional mapping ``name -> element indices``.
   :param surfaces: Optional mapping ``name -> SurfaceArray``.
   :param nodesets: Optional mapping ``name -> node ids``.


   .. py:attribute:: nodes
      :type:  NodeArray


   .. py:attribute:: elements
      :type:  ElementArray


   .. py:attribute:: parts
      :type:  dict[str, numpy.ndarray]


   .. py:attribute:: surfaces
      :type:  dict[str, SurfaceArray]


   .. py:attribute:: nodesets
      :type:  dict[str, numpy.ndarray]


   .. py:property:: nelems
      :type: int


      Return the number of elements in the mesh.


   .. py:property:: nnodes
      :type: int


      Return the number of nodes in the mesh.


   .. py:method:: bounds() -> tuple[numpy.ndarray, numpy.ndarray]

      Return the axis-aligned bounding box.

      :returns: A tuple ``(min_xyz, max_xyz)`` with two arrays of shape ``(3,)``.



   .. py:method:: getDomain(name: str) -> ElementArray

      Return a slice of ``elements`` for a named part.

      :param name: Part name.

      :returns: Slice view of ``elements`` for the requested domain.



   .. py:method:: getSurface(name: str) -> SurfaceArray

      Return a surface by name.

      :param name: Surface name.

      Returns:
      Surface table.



   .. py:method:: getNodeset(name: str) -> NodeArray

      Return a nodeset by name as a node slice.

      :param name: Nodeset name.

      :returns: Node slice view.



   .. py:method:: to_feb_nodes_xml(object_name: str = 'Object1') -> list[xml.etree.ElementTree.Element]

      Build FEBio ``<Nodes>`` elements grouped by part.

      Node ids are re-numbered per part in ascending order of original ids.
      If no parts exist, a single group named ``object_name`` is created.

      :param object_name: Name used when no parts are present.

      :returns: List of ``<Nodes>`` elements. Order follows the internal part order.

      Notes:
      ------
      Node ids in XML are 1-based. Coordinates are written as comma-separated
      values ``x,y,z``.



   .. py:method:: to_feb_elements_xml() -> list[xml.etree.ElementTree.Element]

      Build one FEBio ``<Elements>`` element per part.

      :returns: List of ``<Elements>`` elements.

      Notes:
      ------
      Element ids in XML are 1-based and follow the original element order.
      Element type is taken as the first unique ``etype`` in the part.
      Mixed types in one part are not expanded.



   .. py:method:: to_feb_surfaces_xml() -> list[xml.etree.ElementTree.Element]

      Build one FEBio ``<Surface>`` per named surface.

      :returns: List of ``<Surface>`` elements.

      Notes:
      ------
      Facet tags are chosen by node count: ``line2``, ``tri3``, or ``quad4``.
      Fallback tag is ``facet`` when the count is unknown.



   .. py:method:: to_feb_nodesets_xml() -> list[xml.etree.ElementTree.Element]

      Build FEBio ``<NodeSet>`` elements.

      :returns: List of ``<NodeSet>`` elements.

      Notes:
      ------
      Node ids are re-numbered using the same rules as for nodes and surfaces.



   .. py:method:: from_gmsh_msh(path: str, include: collections.abc.Sequence[str] | None = None, scale: collections.abc.Sequence[float] | numpy.ndarray = (1.0, 1.0, 1.0), quiet: bool = False) -> Mesh
      :classmethod:


      Create a mesh from a Gmsh 2.0/2.2 ASCII ``.msh`` file.

      The reader:
      - Supports physical groups for volumes, surfaces, and lines.
      - Builds parts from volume groups. Optionally filters by ``include``.
      - Builds surfaces and nodesets from surface/line groups.
      - Reindexes node ids to 0-based and preserves original ordering.
      - Pads connectivity to a common width for simple slicing.

      :param path: Path to the ``.msh`` file.
      :param include: Physical names to include as parts. When ``None``, include all volumes.
      :param scale: Per-axis scale for node coordinates. Applied as ``(sx, sy, sz)``.
      :param quiet: When ``True``, suppress warnings about unsupported types.

      :returns: New ``Mesh`` instance.

      Notes:
      ------
      Only Gmsh ASCII formats 2.0 and 2.2 are supported. Quadratic types are
      kept as-is but not expanded. Unknown element types are skipped.



   .. py:method:: from_xplt(xplt_reader: MeshProvider) -> Mesh
      :classmethod:


      Create a mesh from an ``xplt`` reader that already parsed a mesh.

      :param xplt_reader: Reader whose ``mesh`` attribute is an instance compatible with ``Mesh``.

      :returns: Detached copy of the reader mesh.

      Raises:
      -------
      TypeError
          When ``xplt_reader.mesh`` is not compatible with this class.




