|
Wildmeshing Toolkit
|
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" |
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.
|
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).
|
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.
|
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.
| 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.
| 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.
| 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.
| 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)).
| 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.
| simwild.polyfem_ops.constraints.parse_axes | ( | spec, | |
| int | dim | ||
| ) |
'z' | 'xy' | [2] | None -> a tuple of component indices (None = all).
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.