Microgen modules

Phase module

Phase 2.0 — implicit-first, CAD-optional, Piece-aware container.

A Phase is a region of space identified by an implicit scalar field (the canonical representation), with derived materialisations on demand:

  • grid() / surface_mesh() / volume_mesh() — PyVista views

  • cad — OCCT BREP via microgen.cad.shape_to_cad()

  • pieces — connected components of {field < iso} (the “Phase = collection of cut/split sub-solids” invariant)

  • center_of_mass / inertia_matrix — moments via grid quadrature (field-backed) or BRepGProp (CAD-backed)

Three construction paths:

  • Phase (field=..., bounds=..., iso=..., period=...) — field-first (no CAD required).

  • Phase.from_shape() — sugar over the field-first path; bridges from a Shape.

  • Phase.from_cad() — CAD-backed; required when loading a STEP file or wrapping a pre-built BREP.

The CAD seam is isolated in _materialise_cad(). Switching from microgen.cad to pyvista-cad later means swapping that one method; no other code in microgen needs to change.

A Phase is immutable: transforms (translated(), scaled(), rotated(), tiled()) return a new instance. This makes cache invalidation impossible by construction.

class microgen.phase.Phase(*, field: Field | None = None, bounds: BoundsType | None = None, iso: float = 0.0, period: PeriodType | None = None, name: str | None = None, resolution: int = 50)

Bases: object

Microstructure phase (implicit-first, CAD-optional).

A phase is one of the three:

  • field-backed: a callable field(x,y,z) -> array with negative values inside, plus an AABB bounds and an iso value (the solid is {p : field(p) < iso}).

  • mesh-backed: a triangulated surface pv.PolyData.

  • CAD-backed: a CAD shape (microgen.cad.CadShape today, any duck-typed CAD object — including future pyvista-cad shapes — tomorrow).

The other two materialisations are derived on demand.

Parameters:
  • field – SDF / level-set (x, y, z) -> array; negative inside. Required for field-backed construction.

  • bounds – axis-aligned bbox (xmin, xmax, ymin, ymax, zmin, zmax) spanning the field. Required if field is set.

  • iso – iso-value; the solid is {p : field(p) < iso}. Defaults to 0.0.

  • period(Lx, Ly, Lz) if the field is intrinsically periodic. Optional.

  • name – phase name (defaults to auto-generated Phase_N).

  • resolution – default sampling resolution used by lazy grid() / surface_mesh() / pieces().

Use from_shape(), from_cad() or from_mesh() to build from a Shape, a pre-built CAD object, or a triangulated mesh, respectively.

property bounds: BoundsType | None

AABB (xmin, xmax, ymin, ymax, zmin, zmax).

For field-backed phases this is set at construction. For CAD- or mesh-backed phases it’s derived from the underlying representation.

property cad: Any

The CAD representation (microgen.cad.CadShape today, lazy).

For CAD-backed phases this returns the stored shape. For field-backed or mesh-backed phases it’s lazily materialised via _materialise_cad().

Requires the [cad] extra (raises ImportError otherwise).

property center_of_mass: npt.NDArray[np.float64]

Volumetric center of mass.

For field-backed phases this is computed by quadrature on the sampled grid (no OCCT needed). For CAD-backed phases this uses BRepGProp.VolumeProperties_s.

property field: Field | None

The implicit scalar field, or None for non-field-backed phases.

classmethod from_cad(cad: Any, *, name: str | None = None) Phase

Construct a CAD-backed Phase.

cad may be a microgen.cad.CadShape or any duck-typed object with .solids(), .center(), .volume(), .bounding_box() methods (this is the seam that lets a future pyvista-cad backend drop in without touching Phase).

Parameters:
  • cad – a CAD shape (CadShape today)

  • name – phase name (auto-generated if omitted)

classmethod from_mesh(mesh: pv.PolyData, *, name: str | None = None) Phase

Construct a mesh-backed Phase from a closed surface mesh.

Parameters:
  • mesh – closed triangulated surface

  • name – phase name (auto-generated if omitted)

classmethod from_shape(shape: Shape, *, bounds: BoundsType | None = None, iso: float = 0.0, name: str | None = None, resolution: int = 50) Phase

Construct a field-backed Phase from an implicit Shape.

Inherits field, bounds, and period from the shape. bounds may be overridden (e.g., to clip a periodic field to a sub-region).

Parameters:
  • shape – source implicit shape; must have func is not None.

  • bounds – override the shape’s bounds; defaults to shape.bounds.

  • iso – iso-value (default 0.0).

  • name – phase name (auto-generated if omitted).

  • resolution – default sampling resolution.

grid(resolution: int | None = None) pv.StructuredGrid

Return a structured grid sampling of the field.

Only meaningful for field-backed phases.

property inertia_matrix: npt.NDArray[np.float64]

Inertia tensor (about the origin) of the phase.

Field-backed: grid quadrature. CAD-backed: BRepGProp.

property is_empty: bool

True if the phase has no backing representation.

property iso: float

Iso-value for the implicit field (the solid is {field < iso}).

property period: PeriodType | None

Intrinsic period (Lx, Ly, Lz) if the field is periodic.

property pieces: list[Piece]

Connected components of the phase (the “sub-pieces” invariant).

  • CAD-backed phase: one Piece per TopoDS_Solid.

  • Field-backed phase: scipy.ndimage.label on grid_field < iso; one piece per connected component.

  • Mesh-backed phase: polydata.connectivity().split_bodies().

property resolution: int

Default sampling resolution for lazy grid/mesh/pieces materialisation.

scaled(factor: float | tuple[float, float, float]) Phase

Return a new Phase scaled by factor about its center of mass.

CAD-backed phases use OCCT BRepBuilderAPI_GTransform about the BRep center. Field-backed phases compose the field via f'(p) = f((p - c) / s + c) * s_min (uniform scale only; a per-axis scale is not exact for an SDF, so non-uniform factors rescale the bbox only and leave the field’s iso-distance approximate — caller’s responsibility).

surface_mesh(resolution: int | None = None) pv.PolyData

Return a triangulated surface mesh of the solid boundary.

  • Mesh-backed phase: returns the stored mesh.

  • Field-backed phase: marching cubes on the sampled grid.

  • CAD-backed phase: tessellates via OCCT incremental mesh.

tiled(rve: Rve, grid: tuple[int, int, int]) Phase

Return a new Phase periodically tiled on the RVE.

Builds grid translated copies of the current phase and fuses them. Only implemented for CAD-backed phases today (the field-backed equivalent — domain folding via mod — lands in a follow-up; the periodic-shape work in microgen.shape.implicit_ops already covers it for Shape directly).

translated(offset: Sequence[float]) Phase

Return a new Phase translated by offset.

volume_mesh(resolution: int | None = None) pv.UnstructuredGrid

Return the volumetric cells where field < iso.

Only meaningful for field-backed phases.

class microgen.phase.Piece(com: tuple[float, float, float], volume: float, bounds: BoundsType, cad: Any | None = None, mesh: pv.PolyData | None = None, voxel_mask: npt.NDArray[np.bool_] | None = None)

Bases: object

One connected sub-region of a Phase.

Pieces are what survives “split this phase under periodicity” or “raster this phase into a per-cell grid” — they expose the per-piece geometric moments without forcing every Phase to be a list of solids.

Payload fields are populated lazily and may be None depending on how the parent Phase was constructed: a CAD-backed phase populates cad; a field-backed phase populates voxel_mask; a mesh-backed phase populates mesh.

bounds: BoundsType
cad: Any | None = None
com: tuple[float, float, float]
mesh: pv.PolyData | None = None
volume: float
voxel_mask: npt.NDArray[np.bool_] | None = None

Operations

Boolean operations.

All OCP imports are deferred so the module loads cleanly in a mesh-only install. Functions that use OCCT raise a clear ImportError (via microgen.cad.require_cad()) if the [cad] extra isn’t installed.

microgen.operations.cut_phase_by_shape_list(phase_to_cut: Phase, shapes: list[CadShape]) Phase

Cut a phase by a list of shapes.

Parameters:
  • phase_to_cut – phase to cut (must be CAD-backed)

  • shapes – list of cutting shapes

Return resultCut:

cut phase

microgen.operations.cut_phases(phases: list[Phase], *, reverse_order: bool = True) list[Phase]

Cut list of phases in the given order (or reverse) and fuse them.

Parameters:
  • phases – list of phases to cut (each must be CAD-backed)

  • reverse_order – order for cutting shapes; when True, the last shape of the list is not cut

Returns:

list of phases

microgen.operations.cut_phases_by_shape(phases: list[Phase], cut_obj: CadShape) list[Phase]

Cut list of phases by a given shape.

Parameters:
  • phases – list of phases (must be CAD-backed)

  • cut_obj – cutting object

Return phase_cut:

final result

microgen.operations.cut_shapes(shapes: list[CadShape], *, reverse_order: bool = True) list[CadShape]

Cut list of shapes in the given order (or reverse) and fuse them.

Parameters:
  • shapes – list of shapes to cut

  • reverse_order – bool, order for cutting shapes, when True: the last shape of the list is not cut

Return cutted_shapes:

list of CadShape

microgen.operations.fuse_shapes(shapes: list[CadShape], *, retain_edges: bool) CadShape

Fuse all shapes in the list.

Parameters:
  • shapes – list of shapes to fuse

  • retain_edges – retain intersecting edges

Returns:

fused shape

microgen.operations.raster_phase(phase: Phase, rve: Rve, grid: list[int], *, phase_per_raster: bool = True) Phase | list[Phase]

Raster a phase according to the RVE divided by the given grid.

Each solid in the phase is split by the (grid-1) interior planes per axis (CAD-backed phases only). When phase_per_raster is true, each non-empty grid cell becomes one Phase; otherwise the split sub-solids are fused into one new Phase.

Parameters:
  • phase – CAD-backed phase to raster

  • rve – RVE divided by the given grid

  • grid – number of divisions in each direction [x, y, z]

  • phase_per_raster – if True, returns list of phases

Returns:

Phase or list of Phases

microgen.operations.repeat_polydata(mesh: pv.PolyData, rve: Rve, grid: tuple[int, int, int]) pv.PolyData

Repeat mesh in each direction according to the given grid.

Parameters:
  • mesh – pv.PolyData to repeat

  • rve – RVE of the geometry to repeat

  • grid – list of number of geometry repetitions in each direction

Returns:

pv.PolyData of the repeated geometry

microgen.operations.repeat_shape(unit_geom: CadShape, rve: Rve, grid: tuple[int, int, int]) CadShape

Repeat unit geometry in each direction according to the given grid.

Parameters:
  • unit_geom – Shape to repeat

  • rve – RVE of the geometry to repeat

  • grid – list of number of geometry repetitions in each direction

Returns:

CadShape of the repeated geometry

microgen.operations.rescale(shape: CadShape, scale: float | tuple[float, float, float]) CadShape

Rescale shape by scale = (sx, sy, sz) (or a scalar) about its centroid.

Preserves the shape’s center of mass — scaling is performed about it.

microgen.operations.rotate(obj: CadShape, center: npt.NDArray[np.float64] | Sequence[float], rotation: Rotation) CadShape
microgen.operations.rotate(obj: PolyData, center: npt.NDArray[np.float64] | Sequence[float], rotation: Rotation) PolyData
microgen.operations.rotate(obj: UnstructuredGrid, center: npt.NDArray[np.float64] | Sequence[float], rotation: Rotation) UnstructuredGrid

Rotate object according to given rotation.

Supports microgen.cad.CadShape and any pyvista.DataSet (PolyData, UnstructuredGrid, StructuredGrid, …) — i.e. any pyvista mesh exposing rotate_vector.

Parameters:
  • obj – Object to rotate

  • center – numpy array (x, y, z)

  • rotation – scipy Rotation object

Returns:

Rotated object (same type as input)

Raises:

ValueError – if object type is not supported

microgen.operations.rotate_euler(obj: CadShape, center: npt.NDArray[np.float64] | Sequence[float], angles_or_rotation: Sequence[float] | Rotation) CadShape
microgen.operations.rotate_euler(obj: PolyData, center: npt.NDArray[np.float64] | Sequence[float], angles_or_rotation: Sequence[float] | Rotation) PolyData

Rotate object according to ZXZ Euler angle convention.

Accepts CadShape or pyvista.PolyData.

Parameters:
  • obj – Object to rotate

  • center – numpy array (x, y, z)

  • angles_or_rotation – list of Euler angles (psi, theta, phi) in degrees, or a scipy Rotation object

Returns:

Rotated object

Rve

Representative Volume Element (RVE).

Frozen, immutable container for the RVE bounding box and its periodicity flags.

Rve.box is a CadShape wrapping an OCCT box; it is built lazily on first access and requires the [cad] extra. Rve.grid returns a pyvista.StructuredGrid aligned with the cell — used by implicit shapes to sample SDF/level-set fields.

class microgen.rve.Rve(center: Vector3DType = (0, 0, 0), dim: float | Vector3DType = 1, pbc: bool | tuple[bool, bool, bool] | Sequence[bool] = (True, True, True))

Bases: object

Representative Volume Element (RVE) — frozen.

Parameters:
  • center – center of the RVE

  • dim – dimensions of the RVE (scalar → cube)

  • pbc – periodic-boundary-condition flags per axis (x, y, z); defaults to fully periodic. A single bool is broadcast to all axes.

property box: CadShape

Return a CadShape box of the RVE (cached).

Requires the [cad] extra.

center: ndarray
dim: ndarray
classmethod from_min_max(min_point: Vector3DType = (-0.5, -0.5, -0.5), max_point: Vector3DType = (0.5, 0.5, 0.5), pbc: bool | tuple[bool, bool, bool] | Sequence[bool] = (True, True, True)) Rve

Generate a Rve from min and max corner points.

Parameters:
  • min_point(x_min, y_min, z_min) corner of the RVE

  • max_point(x_max, y_max, z_max) corner of the RVE

  • pbc – periodic-boundary-condition flags (see __init__)

grid(resolution: ResolutionType) pv.StructuredGrid

Return a structured grid aligned with the RVE.

Parameters:

resolution – points per axis — int (broadcast to all 3 axes) or length-3 sequence. The grid spans min_pointmax_point with endpoints included on every axis.

property max_point: ndarray

Max corner center + 0.5 * dim.

property min_point: ndarray

Min corner center - 0.5 * dim.

pbc: tuple[bool, bool, bool]

Periodic

Periodic cut — rearrange a shape so it wraps around an RVE.

Pure OCP implementation (cadquery-ocp-novtk); no cadquery dependency. Requires the [cad] extra.

microgen.periodic.periodic_split_and_translate(phase: Phase, rve: Rve) Phase

Rearrange a phase periodically so it wraps the RVE.

Pure OCP implementation — no cadquery dependency.

Parameters:
  • phase – Phase to cut periodically

  • rve – RVE for periodicity

Returns:

resulting Phase

Mesh

Mesh using gmsh.

exception microgen.mesh.OutputMeshNotPeriodicError

Bases: Exception

Raised when output mesh from mesh_periodic is not periodic.

microgen.mesh.is_periodic(nodes_coords: ndarray[tuple[Any, ...], dtype[float64]], tol: float = 1e-08) bool

Check whether a mesh is periodic, given its nodes’ coordinates.

Parameters:
  • nodes_coords – list of nodes coordinates of the analyzed mesh

  • tol – tolerance

microgen.mesh.mesh(mesh_file: str, list_phases: list[Phase], size: float, order: int, output_file: str = 'Mesh.msh', msh_file_version: int = 4) None

Mesh step file with gmsh with list of phases management.

Parameters:
microgen.mesh.mesh_periodic(mesh_file: str, rve: Rve, list_phases: list[Phase], size: float, order: int, output_file: str = 'MeshPeriodic.msh', msh_file_version: int = 4, tol: float = 1e-08) None

Mesh periodic geometries with gmsh.

Parameters:

Remesh

Remesh a mesh using mmg while keeping boundaries untouched.

exception microgen.remesh.InputMeshNotPeriodicError

Bases: Exception

Raised when the input mesh is not periodic.

Triggered when periodic=True is passed to remesh_keeping_boundaries_for_fem() but the supplied input mesh fails the periodicity check.

exception microgen.remesh.OutputMeshNotPeriodicError

Bases: Exception

Raised when the remeshed output is not periodic.

Triggered when periodic=True is passed to remesh_keeping_boundaries_for_fem() but mmg’s output fails the periodicity check.

microgen.remesh.remesh_keeping_boundaries_for_fem(input_mesh: BoxMesh, *, periodic: bool = True, mesh_version: int = 2, dimension: int = 3, tol: float = 1e-08, hausd: float | None = None, hgrad: float | None = None, hmax: float | None = None, hmin: float | None = None, hsiz: float | None = None) BoxMesh
microgen.remesh.remesh_keeping_boundaries_for_fem(input_mesh: UnstructuredGrid, *, periodic: bool = True, mesh_version: int = 2, dimension: int = 3, tol: float = 1e-08, hausd: float | None = None, hgrad: float | None = None, hmax: float | None = None, hmin: float | None = None, hsiz: float | None = None) UnstructuredGrid

Remesh a mesh using mmg while keeping boundary elements untouched.

See https://www.mmgtools.org/mmg-remesher-try-mmg/mmg-remesher-options for the full reference on each h* parameter.

Parameters:
  • input_mesh – BoxMesh or pv.UnstructuredGrid mesh to be remeshed

  • periodic – whether the mesh is periodic and must stay periodic

  • mesh_version – mesh file version

  • dimension – mesh dimension

  • tol – tolerance for the periodicity check

  • hausd – maximal Hausdorff distance for the boundaries approximation

  • hgrad – gradation value, i.e. ratio between lengths of adjacent mesh edges

  • hmax – maximal edge size

  • hmin – minimal edge size

  • hsiz – build a constant size map of size hsiz

External

Wrappers around external command-line tools.

class microgen.external.Mmg

Bases: object

Wrapper around the mmg2d_O3 / mmgs_O3 / mmg3d_O3 binaries.

Each method exposes the underlying mmg CLI flags as keyword arguments using the same short names as the binaries themselves (input, output, ls, nr, A, ar, …). See the mmg documentation for the full reference.

A larger redesign of this interface is planned; for now it intentionally mirrors the original flat-kwarg shape.

static mmg2d(d=None, h=None, m=None, v=None, val=None, default=None, input=None, output=None, solution=None, metric=None, A=None, ar=None, hausd=None, hgrad=None, hmax=None, hmin=None, hsiz=None, lag=None, ls=None, _3dMedit=None, noinsert=None, nomove=None, nosurf=None, noswap=None, nr=None, nreg=None, nsd=None, optim=None, opnbdy=None, rmc=None)

Run mmg2d_O3 from the command line with given arguments.

static mmg3d(d=None, h=None, m=None, v=None, val=None, default=None, input=None, output=None, solution=None, metric=None, A=None, ar=None, octree=None, hausd=None, hgrad=None, hmax=None, hmin=None, hsiz=None, lag=None, ls=None, nofem=None, noinsert=None, nomove=None, nosurf=None, noswap=None, nr=None, nreg=None, nsd=None, optim=None, optimLES=None, opnbdy=None, rmc=None, rn=None)

Run mmg3d_O3 from the command line with given arguments.

static mmgs(d=None, h=None, m=None, v=None, val=None, default=None, input=None, output=None, solution=None, metric=None, A=None, ar=None, hausd=None, hgrad=None, hmax=None, hmin=None, hsiz=None, ls=None, noinsert=None, nomove=None, nosurf=None, noswap=None, nr=None, nreg=None, nsd=None, optim=None, rn=None)

Run mmgs_O3 from the command line with given arguments.

exception microgen.external.MmgError

Bases: Exception

Raised when an mmg command fails.

class microgen.external.Neper

Bases: object

Wrapper around the neper command-line tool.

static parse_tess(filename: str) dict[str, dict]

Parse a tessellation (.tess) file generated by neper.

Returns a dictionary with keys cells, vertices, edges, faces, polyhedra. Follows the .tess structure from `neper's documentation`_.

static run(filename: str, n_cells: int, cube_dim: tuple[float, float, float]) None

Run a neper tessellation from the command line.

Equivalent to:

neper -T -n n_cells -id 1 -dim 3 \
      -domain 'cube(cube_dim[0], cube_dim[1], cube_dim[2])' \
      -morpho gg -o filename
Parameters:
static voronoi_from_tess_file(filename: str) list[Polyhedron]

Build Polyhedron shapes from a neper-generated .tess file.