Wildmeshing Toolkit
Loading...
Searching...
No Matches
Functions | Variables
simwild.polyfem_ops.constraints Namespace Reference

Functions

None _warn (str msg)
 
None _error (str msg)
 
list[tuple[int, int]] _orient_edge_loops_2d (np.ndarray coords, list[tuple[int, int]] oriented_edges)
 
 _selected_interfaces (TaggedMesh mesh, list selections)
 
 _auto_interfaces (TaggedMesh mesh)
 
 load_mesh (str msh_path, list|None selections=None)
 
None write_fitting_constraint_hdf5 (str path, np.ndarray node_ids, int dim=2, np.ndarray coords=None, list interface_edges=None, bool graph=False, bool normalize=False, list interface_faces=None)
 
None write_pin_constraint_hdf5 (str path, np.ndarray node_ids, int dim, axes=None)
 
 parse_axes (spec, int dim)
 
np.ndarray get_mass_matrix (np.ndarray coords, list interface_edges, np.ndarray node_ids, bool graph, list interface_faces=None)
 
np.ndarray get_stiffness_matrix (np.ndarray coords, list interface_edges, np.ndarray node_ids, bool graph, list interface_faces=None)
 
np.ndarray get_laplacian_matrix (np.ndarray coords, list interface_edges, np.ndarray node_ids, bool graph, list interface_faces=None)
 
None write_laplacian_constraint_hdf5 (str path, np.ndarray node_ids, np.ndarray coords, list interface_edges, bool graph=False, float scale=1.0, bool normalize=False, list interface_faces=None, bool smooth_positions=False)
 
None write_collision_mesh_obj (str path, np.ndarray coords, np.ndarray node_ids, list interface_edges, list|None interface_faces=None, list[int]|None collision_node_ids=None, list[tuple[int, int]]|None collision_edges_local=None)
 
None write_linear_map_hdf5 (str path, np.ndarray node_ids, int total_n_nodes)
 
None write_collision_body_ids_txt (str path, list[list[int]] face_tags)
 
None make_interface_constraint (str mesh_path, list|None selections=None, str|None out_dir=None, str|None constraint_path=None, str|None collision_path=None, str|None linear_map_path=None, str|None body_ids_path=None, bool use_graph=False, bool normalize=False, float scale=0.001, bool smooth_positions=False, int|None dim=None, bool skip_collision_artifacts=False)
 
 main ()
 

Variables

str _RED = "\033[31m"
 
str _YELLOW = "\033[33m"
 
str _RESET = "\033[0m"
 

Detailed Description

Export the polyfem interface artifacts from a gmsh .msh:

  1. interface_constraint.hdf5           — fitting constraint anchoring the
                                            interface nodes near rest pose.
  2. interface_constraint_laplacian.hdf5 — Laplacian smoothness constraint
                                            over the interface patch.
  3. interface_collision.obj             — collision proxy mesh (vertices +
                                            edges, plus faces in 3D).
  4. interface_linear_map.hdf5           — displacement map W, shape
                                            [n_proxy_verts, total_n_fe_nodes],
                                            with proxy_disp = W @ fem_disp.
  5. collision_body_ids.txt              — per-proxy-face collision body ids.

Interfaces are picked automatically (every material interface; the CLI path)
or via `selections` — {"region": expr, "filter": expr?, "id": int?} specs
(bare string = region): the boundary of `region`, kept where the outside
satisfies `filter`. Shared with minimum_separation's collision_pairs and
laplacian smoothing's interfaces.

Constraint HDF5 format (polyfem SolveData.cpp):
  local2global        : (n,)       int32  — 0-indexed FE vertex IDs
  A_triplets/rows     : (n,)       int32  — identity row indices
  A_triplets/cols     : (n,)       int32  — identity col indices
  A_triplets/values   : (n,)       float64
  A_triplets/shape    : (2,)       int64  — [n, n]
  b                   : (n, dim)   float64 — zeros

Linear map HDF5 format (polyfem CollisionProxy.cpp):
  weight_triplets          (group, attribute shape=[n_proxy, n_fe_total])
  weight_triplets/rows     : (n,)  int32
  weight_triplets/cols     : (n,)  int32   — input mesh vertex IDs (polyfem remaps internally)
  weight_triplets/values   : (n,)  float64

Usage: python -m simwild.polyfem_ops.constraints sim_input_2.msh
The CLI prints the JSON snippet to add under contact.collision_mesh and
constraints.soft.

Function Documentation

◆ _auto_interfaces()

simwild.polyfem_ops.constraints._auto_interfaces ( TaggedMesh  mesh)
protected
Legacy auto-detect: every face whose incident phys-tag multiset is not
a same-tag interior pair — material interfaces plus one-sided boundary
skins. Orientation: hi phys tag = inside (3D normals outward from it).

◆ _orient_edge_loops_2d()

list[tuple[int, int]] simwild.polyfem_ops.constraints._orient_edge_loops_2d ( np.ndarray  coords,
list[tuple[int, int]]  oriented_edges 
)
protected
Chain directed 2D edges into loops. Each simple closed loop keeps the
direction its input edges agree on — the explicit region/filter
orientation from _selected_interfaces, which is the only hole-safe source
of truth (a cavity loop is wound CW, an outer loop CCW; signed area can't
tell the material side). A loop whose inputs contradict each other (the
same interface selected from both sides, so no single direction can be
correct for both bodies) raises. Non-loop components (open chains, branch
points) pass through with their original order and orientation.

◆ _selected_interfaces()

simwild.polyfem_ops.constraints._selected_interfaces ( TaggedMesh  mesh,
list  selections 
)
protected
Selection-scoped interfaces: returns (oriented_faces_3d,
oriented_edges_2d, tags_rows). Orientation: 3D normals point out of the
region; 2D left-normals point toward the region (legacy GCP convention);
the downstream loop pass preserves this orientation and only dedupes.

◆ get_laplacian_matrix()

np.ndarray simwild.polyfem_ops.constraints.get_laplacian_matrix ( np.ndarray  coords,
list  interface_edges,
np.ndarray  node_ids,
bool  graph,
list   interface_faces = None 
)
Laplacian L = M^{-1} S over the interface as COO triplets: the
get_stiffness_matrix triplets scaled by the inverse lumped mass diagonal,
indexed locally like get_mass_matrix.

◆ get_mass_matrix()

np.ndarray simwild.polyfem_ops.constraints.get_mass_matrix ( np.ndarray  coords,
list  interface_edges,
np.ndarray  node_ids,
bool  graph,
list   interface_faces = None 
)
Lumped mass matrix over the interface as diagonal COO triplets, indexed
locally (local index = position in node_ids). 3D: igl barycentric mass
(area-weighted); 2D: half-edge-length lumping; graph: identity.

◆ get_stiffness_matrix()

np.ndarray simwild.polyfem_ops.constraints.get_stiffness_matrix ( np.ndarray  coords,
list  interface_edges,
np.ndarray  node_ids,
bool  graph,
list   interface_faces = None 
)
Stiffness matrix S over the interface as COO triplets (zero row sums,
off-diagonal <= 0, diagonal >= 0), indexed locally like get_mass_matrix.
3D: negated igl.cotmatrix; 2D: 1/edge-length weights; graph: unit weights.

◆ load_mesh()

simwild.polyfem_ops.constraints.load_mesh ( str  msh_path,
list | None   selections = None 
)
Load a multi-tag .msh and extract its material-interface surfaces —
every interface when `selections` is omitted, else those selected by the
given {"region", "filter", "id"} specs (see mesh_core). Returns the 9-tuple
(node_tag_to_idx, coords (n, mesh_dim) float64 in mesh units,
interface_edges, total_n_nodes, mesh_dim, interface_faces (3D, else []),
collision_node_ids in collision-OBJ vertex order, collision_edges_local
over those local IDs, collision_face_tags — per-face sorted id lists
(interface ids when selected, phys tags in auto mode)).

◆ make_interface_constraint()

None simwild.polyfem_ops.constraints.make_interface_constraint ( str  mesh_path,
list | None   selections = None,
str | None   out_dir = None,
str | None   constraint_path = None,
str | None   collision_path = None,
str | None   linear_map_path = None,
str | None   body_ids_path = None,
bool   use_graph = False,
bool   normalize = False,
float   scale = 0.001,
bool   smooth_positions = False,
int | None   dim = None,
bool   skip_collision_artifacts = False 
)
Extract interface surfaces from a multi-tag .msh (`selections` as in
load_mesh; omitted = every material interface) and write the polyfem
artifacts, by default next to the mesh or under out_dir:
  interface_constraint.hdf5 / interface_constraint_laplacian.hdf5,
  interface_collision.obj (collision proxy / selection viz), and — unless
  skip_collision_artifacts (smoothing mode; polyfem doesn't read them) —
  interface_linear_map.hdf5 and collision_body_ids.txt.

◆ parse_axes()

simwild.polyfem_ops.constraints.parse_axes (   spec,
int  dim 
)
'z' | 'xy' | [2] | None -> a tuple of component indices (None = all).

◆ write_collision_body_ids_txt()

None simwild.polyfem_ops.constraints.write_collision_body_ids_txt ( str  path,
list[list[int]]  face_tags 
)
Write one space-separated line of collision body IDs per face/edge,
row order matching the OBJ.

◆ write_collision_mesh_obj()

None simwild.polyfem_ops.constraints.write_collision_mesh_obj ( str  path,
np.ndarray  coords,
np.ndarray  node_ids,
list  interface_edges,
list | None   interface_faces = None,
list[int] | None   collision_node_ids = None,
list[tuple[int, int]] | None   collision_edges_local = None 
)
Write the collision proxy OBJ: vertices plus 'f' faces (3D) or 'l'
edges (2D), falling back to node_ids/interface_edges when the collision_*
args are empty. Vertices are in mesh units (un-scaled); polyfem applies
the geometry transformation from the JSON.

◆ write_fitting_constraint_hdf5()

None simwild.polyfem_ops.constraints.write_fitting_constraint_hdf5 ( str  path,
np.ndarray  node_ids,
int   dim = 2,
np.ndarray   coords = None,
list   interface_edges = None,
bool   graph = False,
bool   normalize = False,
list   interface_faces = None 
)
Write the fitting constraint HDF5 (local2global, A_triplets, b = 0)
with A = sqrt(M), so (1/2)||A u||^2 is a proper interface L2 norm;
`normalize` also divides A by sqrt(L_total) so a uniform displacement
u_ref costs w * u_ref^2.

◆ write_laplacian_constraint_hdf5()

None simwild.polyfem_ops.constraints.write_laplacian_constraint_hdf5 ( str  path,
np.ndarray  node_ids,
np.ndarray  coords,
list  interface_edges,
bool   graph = False,
float   scale = 1.0,
bool   normalize = False,
list   interface_faces = None,
bool   smooth_positions = False 
)
Write the Laplacian smoothness constraint HDF5 with A = L. Default
b = 0 enforces harmonic displacements (L·u = 0; zero energy and gradient
at rest, preserving equilibrium); smooth_positions instead sets
b = -L·(scale * rest_coords) so absolute positions are harmonic (scale =
solver units per mesh unit). `normalize` divides A by ||L||_F for a
mesh-independent, O(1) energy on smooth u.

◆ write_linear_map_hdf5()

None simwild.polyfem_ops.constraints.write_linear_map_hdf5 ( str  path,
np.ndarray  node_ids,
int  total_n_nodes 
)
Write the identity displacement map proxy_vert[i] ← fe_node[node_ids[i]]
as a weight_triplets HDF5 group with shape attribute
[n_proxy, total_n_nodes], per polyfem CollisionProxy.cpp.

◆ write_pin_constraint_hdf5()

None simwild.polyfem_ops.constraints.write_pin_constraint_hdf5 ( str  path,
np.ndarray  node_ids,
int  dim,
  axes = None 
)
Write a pin constraint (A u = 0 on node_ids): as a polyfem HARD
constraint (constraints/hard) it holds those nodes at rest via the
augmented Lagrangian — used for min-sep protected_regions.

axes=None pins every component. Otherwise `axes` is a subset of the
component indices (0=x, 1=y, 2=z) and only those are held, leaving the
node free to slide along the rest.

The two cases use polyfem's two scatter_matrix modes: with b of `dim`
columns each triplet is replicated across all components (node-wise);
with b of ONE column the triplet's column index is a flattened DOF
(node * dim + component), which is what lets a single axis be pinned.