Session — apeGmsh¶
The top-level session object. Owns a single Gmsh kernel and wires
all composites (model, mesh, parts, constraints, loads,
masses, …). The OpenSees bridge is not a session composite —
import it explicitly via from apeGmsh.opensees import apeSees.
Native persistence¶
The session can persist the neutral zone (the solver-agnostic
FEMData snapshot — nodes, elements, physical groups, labels,
loads, masses, constraints) to a native model.h5. Two write
paths are exposed on the session:
# Autosave: write the neutral zone on context-manager exit
with apeGmsh(model_name="Tower", save_to="model.h5") as g:
g.model.geometry.add_box(0, 0, 0, 1, 1, 1, label="body")
g.physical.add_volume("body", name="body")
g.mesh.generation.generate(3)
# model.h5 now exists
# Manual: write at any point inside the session
with apeGmsh(model_name="Tower") as g:
...
g.save("model.h5") # explicit path
apeGmsh(save_to=..., overwrite=True) configures autosave at
construction; the file is written on end() / context exit.
overwrite=False makes a pre-existing target fail-loud on save.
g.save(path=None) writes immediately and returns the resolved
Path; with no argument it reuses save_to, and raises
RuntimeError if neither a path nor save_to was supplied.
Both paths write the neutral zone only. The OpenSees zone
(typed primitives, recorders, analysis chain) is written
separately by the bridge via apeSees(fem).h5(path) — see the
OpenSees bridge page.
Chain-phase reassembly¶
apeGmsh.from_h5(path, *, model_name=None, verbose=False) rebuilds
a session directly from a model.h5, skipping the Gmsh build
entirely. The returned session is a chain-phase session: it has
no live kernel, so geometry/meshing verbs are unavailable, but it
can still compose, save, and feed the bridge.
g = apeGmsh.from_h5("model.h5") # no gmsh; loads the neutral zone
ops = apeSees(g.mesh.queries.get_fem_data(dim=3))
Composition¶
g.compose(source, *, label, ...) merges another saved module's
model.h5 into the current session under a namespaced label,
applying an optional rigid placement (translate, rotate,
anchor) and reserving a disjoint tag span so child tags never
collide (ADR 0038).
It returns a ComposedModule handle.
with apeGmsh.from_h5("frame.h5") as g:
g.compose("panel.h5", label="Panel_A", translate=(0, 0, 3.0))
g.compose("panel.h5", label="Panel_B", translate=(0, 0, 6.0))
g.compose_list() # -> (ComposedModule, ...)
g.compose_tree() # nested-compose hierarchy
Inspect a candidate file without composing via
g.compose_inspect(path) (returns a dict: fem_hash,
neutral_schema_version, tag_span_max, pg_inventory,
label_inventory, record_counts, compose_tree, …).
g.compose_list() enumerates the modules already composed into
this session; g.compose_tree() returns the nested-compose
hierarchy. In the viewer, composed parts are colourable by the
string-keyed Module modes ('Module', 'Module: Root',
'Module: Leaf').
Declarative assembly — Assembly + couple¶
To spatially couple several saved model.h5 modules without
hand-wiring compose + constraints, use the declarative builder
(shipped in v2.0.0, ADR 0043
slice 1.4). It is imported from a sub-path — apeGmsh.Assembly
is intentionally not exported, so the top-level "the session is the
assembly" model is unchanged.
from apeGmsh.assembly import Assembly
g = (
Assembly("frame")
.add("col", "col.h5") # first add = host (bare PGs)
.add("beam", "beam.h5", translate=(0.0, 3.0, 0.0)) # composed under label "beam"
.couple("col", "beam", kind="equal_dof",
ports=("top", "end"), dofs=[1, 2, 3])
.materialize() # -> composed apeGmsh session
)
g.save("frame.h5")
materialize() is a thin wrapper over apeGmsh.from_h5 (host) +
g.compose (each later part) + g.constraints.<kind>. Couple kind
is equal_dof or tied_contact; ports are bare per-part
physical-group names. A couple that resolves to zero constraints, an
unknown part, or no parts raises AssemblyError.
Package¶
apeGmsh ¶
apeGmsh — Gmsh wrapper for structural FEM workflows.¶
Composition-based API with sub-composites for focused surfaces:
-
Standalone (single-model, quick prototyping)::
from apeGmsh import apeGmsh
g = apeGmsh(model_name="plate", verbose=True) g.begin() p = g.model.geometry.add_point(0, 0, 0) ... g.end()
-
Multi-part (assembly workflow via
g.parts)::from apeGmsh import apeGmsh, Part
web = Part("web") web.begin() web.model.geometry.add_box(0, 0, 0, 1, 0.5, 10) web.save("web.step") web.end()
g = apeGmsh(model_name="bridge") g.begin() g.parts.add(web, label="web") g.parts.fragment_all() g.constraints.equal_dof("web", "slab", tolerance=1e-3) with g.loads.case("dead"): g.loads.gravity("web", g=(0, 0, -9.81), density=7850) g.masses.volume("web", density=7850) g.mesh.generation.generate(dim=3) fem = g.mesh.queries.get_fem_data(dim=3) g.end()
-
Persisted session (autosave + resume across scripts)::
from apeGmsh import apeGmsh, FEMData
# Build once and autosave on context-manager exit. with apeGmsh(model_name="plate", save_to="plate.h5") as g: g.model.geometry.add_box(0, 0, 0, 1, 1, 0.1, label="body") g.physical.add_volume("body", name="body") g.mesh.generation.generate(dim=3)
# Resume in a later script — symmetric load. fem = FEMData.from_h5("plate.h5")
apeGmsh ¶
apeGmsh(*, model_name: str = 'ModelName', verbose: bool = False, save_to: str | Path | None = None, overwrite: bool = True)
Bases: _SessionBase
Standalone single-model Gmsh session with all composites.
Parameters¶
model_name : str
Name passed to gmsh.model.add().
verbose : bool
If True, composites print diagnostic messages.
Source code in src/apeGmsh/_core.py
from_h5
classmethod
¶
Construct a session in chain phase directly from a saved FEMData.
Skips the gmsh build phase entirely: the loaded FEMData becomes
the session's chain head and there is no gmsh kernel behind
this session at all. model.h5 persists the FEMData
snapshot (nodes, elements, physical groups, labels) — not the
geometry kernel — so anything that would read or mutate BRep /
mesh state raises :class:~.core._compose_errors.ChainPhaseError
naming the H5-safe alternative.
Useful for cross-session composition workflows::
# Day 1
with apeGmsh(model_name="host", save_to="host.h5") as g:
...
# Day 2
g = apeGmsh.from_h5("host.h5")
g.compose("module_a.h5", label="A")
g.compose("module_b.h5", label="B")
g.save("final.h5")
What works¶
g.mesh.queries.get_fem_data()— the chain head, and the surface every refusal below points back at.g.compose(...)/compose_inspect(...)/compose_list()and :meth:save.- The chain-phase authoring shims, routed through
FEMData.with_*:g.constraints.bc/tie/embedded/tied_contact/equalDOF/rigid_link/rigid_diaphragm, plus pointg.loads.X/g.masses.X. - Kernel-free helpers:
g.model.queries.plane/registry,g.view.list_views/count,g.plot.show/savefig/clear/figsize/use_axes. repr()of any composite. The two kernel-backed reprs (g.physical,g.labels) report"no live gmsh kernel — from_h5 session"rather than raising, so debuggers and logging stay usable.
Refused — no live kernel to read¶
These need the gmsh model and raise on a from_h5 session
specifically (a live session still has a kernel, so they stay
legal there). Each message names the broker counterpart.
========================= ==================================
Surface Guarded members
========================= ==================================
g.inspect get_geometry_info,
get_mesh_info, print_summary
g.physical get_all, get_entities,
entities,
get_groups_for_entity,
get_name, get_tag,
summary, get_nodes
g.labels entities, get_all,
summary, has,
reverse_map,
labels_for_entity
g.mesh.queries get_nodes, get_elements,
get_element_properties,
get_element_qualities,
quality_report
g.model.queries bounding_box,
center_of_mass, mass,
boundary, boundary_curves,
boundary_points,
adjacencies,
entities_in_bounding_box
g.mesh.partitioning n_partitions, summary,
entity_table, save
g.model.io save_step, save_iges,
save_dxf, save_msh — the
exporters only; the importers are
frozen instead (below)
g.model.<geometry> find_stale_metadata, and
validate_pre_mesh through it
g.mesh.recipe check
g.parts build_face_map
g.rebar resolve
g.sections plot_faces
g.view add_element_scalar /
add_element_vector /
add_node_scalar /
add_node_vector
g.plot geometry, mesh, quality,
label_entities, label_nodes,
label_elements,
physical_groups,
physical_groups_mesh
========================= ==================================
Counterparts: fem.inspect for summaries, fem.physical
(:class:~.mesh._group_set.PhysicalGroupSet) for physical
groups, fem.nodes.labels / fem.elements.labels
(:class:~.mesh._group_set.LabelSet) for labels,
fem.nodes / fem.elements / fem.info for mesh data,
and results.inspect for post-processing — where
fem = g.mesh.queries.get_fem_data(). BRep geometry has no
counterpart: derive it from mesh coordinates or rebuild the
geometry in a live session.
Refused — model frozen¶
Mutations are refused on any chain-phase session, not just this one: once a FEMData snapshot exists the broker is canonical, and mutating gmsh would silently desync the two. Listed by composite — each guards its mutating operations at a shared chokepoint, so the coverage is per-composite rather than the per-method enumeration given for the reads above.
- Geometry —
g.model.<geometry>(viaModel._register, plusadd_wire, which creates OCC geometry but is deliberately not registered),g.model.boolean,g.model.transforms,g.model.io.heal_shapes/load_msh/load_geo, andg.model.queries.remove/remove_duplicates/make_conformal(mutations despite the composite name). - Mesh —
g.mesh.generation,g.mesh.editing,g.mesh.sizing,g.mesh.structured,g.mesh.recipe, andg.mesh.partitioning(its mutating opspartition/partition_explicit/unpartition/renumber; the composite's four readers take the kernel guard instead, and are listed in the read table above). - Naming —
g.physical.add/set_name/remove/remove_name/remove_all, andg.labels.add/remove/rename/promote_to_physical. - Assembly —
g.partsinstance registration,g.sectionsbuilds,g.rebar.place.
Refused — resolves from live geometry¶
g.constraints.contact / contact_plane / interface,
g.embed, g.reinforce and g.decouple_node record
definitions that are resolved against live gmsh at extraction.
A from_h5 session never re-extracts, so the definition
would be stored and silently never applied — declare these in
the source part session before saving; the resolved records
round-trip through model.h5 and survive g.compose.
Parameters¶
path : str or Path
Path to a model.h5 written by :meth:save /
:meth:FEMData.to_h5.
model_name : str or None
Session name (used by :meth:save for /meta/model_name).
Defaults to the source file's stem.
verbose : bool, default False
Verbose-mode flag forwarded to the constructor.
Raises¶
~.core._compose_errors.ChainPhaseError From any surface listed above. The message names the offending call and the alternative that answers it.
Source code in src/apeGmsh/_core.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | |
decouple_node ¶
decouple_node(*, coords: 'tuple[float, float, float] | None' = None, point: 'str | None' = None, label: 'str | None' = None) -> Any
Declare a decoupled node — an auxiliary node that is not
a Gmsh mesh vertex (spring/dashpot ground, rigidDiaphragm
master, control node, load/mass anchor).
Exactly one of coords=(x, y, z) or point="label" locates
it; point= is snapshotted to coordinates at mesh-extraction
time. label is an optional friendly name.
The node is appended to fem.nodes at extraction with a
deterministic tag above every mesh node (dedup-immune by
construction) and provenance == "decoupled". It carries
no ndf — DOF count is a bridge concern (ops.ndf).
Returns the :class:~apeGmsh._kernel.defs.decoupled.DecoupledNodeDef
handle; its tag is populated after
g.mesh.queries.get_fem_data(...).
Source code in src/apeGmsh/_core.py
save ¶
Write the neutral-zone model.h5 for this session.
Persists what the session knows about the model: nodes,
elements, physical groups, labels, constraints, loads, masses.
Downstream solver enrichment (e.g. apeSees(fem).h5(p)) is
a separate user-driven action and not invoked here.
Parameters¶
path : str, Path, or None
Destination file. None (default) uses the save_to
given to the constructor. Raises if neither is set.
Returns the resolved path.
Source code in src/apeGmsh/_core.py
compose ¶
Merge a previously-saved apeGmsh model into this session.
See :meth:apeGmsh.mesh._compose.Compose.compose for the full
signature, validation contract, and exception types. Phase
3B.1 scaffolds the facade — the merge engine itself lands in
Phase 3B.2.
Source code in src/apeGmsh/_core.py
compose_inspect ¶
Read a module's H5 header without composing it.
See :meth:apeGmsh.mesh._compose.Compose.compose_inspect for
the returned dict shape.
Source code in src/apeGmsh/_core.py
compose_list ¶
Composed modules currently on this session.
See :meth:apeGmsh.mesh._compose.Compose.compose_list.
compose_tree ¶
Derived nested-compose tree view of this session's modules.
See :meth:apeGmsh.mesh._compose.Compose.compose_tree.
Part ¶
Bases: _SessionBase
An isolated geometry unit — no meshing, no solver state.
Carries geometry plus Tier-1 naming (labels + auto-created
physical groups from label= kwargs, persisted via the STEP
sidecar). For independently-meshed parts use a full session per
part + g.compose instead — see the module docstring.
Parameters¶
name : str
Descriptive name (also used as the Gmsh model name).
auto_persist : bool, default True
When True, the Part writes its geometry to an OS tempfile
on end() if save() was not called explicitly. The
tempfile is reclaimed via weakref.finalize when the
Part is garbage-collected, or eagerly via cleanup().
Set to False to opt out — in that case parts.add(part)
will raise FileNotFoundError unless you called save()
by hand.
Source code in src/apeGmsh/core/Part.py
begin ¶
Open the Part's Gmsh session.
If the Part is being reused — a previous with part: block
auto-persisted a tempfile and this call re-enters — the stale
tempfile is cleaned up before the new session starts so the
next end() can auto-persist fresh geometry.
Source code in src/apeGmsh/core/Part.py
end ¶
Close the Part's Gmsh session.
When auto_persist=True and the user did not call
save() inside the session, the geometry is written to
an OS tempfile before Gmsh is finalised so the Part can
flow straight into assembly.parts.add(part).
Exceptions raised by auto-persist itself are caught and emitted as a warning rather than masking any exception the user's build code may have raised. Gmsh finalisation always runs.
Source code in src/apeGmsh/core/Part.py
cleanup ¶
Delete any auto-persisted tempfile now, without waiting for garbage collection.
Safe to call multiple times. Safe to call on a Part whose
file_path was set by explicit save() — the
_owns_file guard means the user's file is never
touched. After cleanup(), has_file returns False
and the Part can be re-built via a new with block.
Source code in src/apeGmsh/core/Part.py
save ¶
save(file_path: str | Path | None = None, *, fmt: str | None = None, write_anchors: bool = True, _internal_autopersist: bool = False) -> Path
Export the Part geometry to a CAD file.
Calling save() with a user-supplied path transfers
ownership of the output file to the caller — any
tempfile previously created by auto-persist is cleaned up
immediately, and the library will never delete the new
output.
Parameters¶
file_path : str, Path, or None
Destination path. If None, defaults to
"{name}.step". The extension determines the format
unless fmt overrides it.
fmt : str, optional
Force format: "step" or "iges".
write_anchors : bool, default True
Write a JSON sidecar ({file_path}.apegmsh.json)
carrying the label -> center-of-mass map for every
user-named entity in the Part. This is what lets
assembly.parts.add(part) expose the instance's
labels via inst.by_label('name'). The sidecar is
silently omitted when the Part has no user-named
entities, so there is no cost for small throwaway
Parts. Pass write_anchors=False to suppress
unconditionally — useful when publishing a CAD file
to third-party tools that shouldn't see apeGmsh
metadata.
Returns¶
Path Resolved path of the written file.
Source code in src/apeGmsh/core/Part.py
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | |
PartsRegistry ¶
Bases: _PartsFragmentationMixin
Instance management composite — registered as g.parts.
Source code in src/apeGmsh/core/_parts_registry.py
part ¶
Track entities created inside the block as a named part.
Yields the label string. After the block, any entities that exist now but didn't before are stored as an Instance.
Example::
with g.parts.part("beam"):
g.model.geometry.add_box(0, 0, 0, 1, 0.5, 10)
Source code in src/apeGmsh/core/_parts_registry.py
register ¶
register(name: str, dimtags: list[DimTag] | None = None, *, label: str | None = None, pg: str | None = None, dim: int | None = None) -> Instance
Tag existing entities under a part name.
Exactly one of dimtags, label, or pg must be given.
Parameters¶
name : str
Unique part name.
dimtags : list of (dim, tag), optional
Entities to assign directly. Also accepted positionally
as the second argument.
label : str, optional
Name of an apeGmsh label (g.labels) whose entities
should be adopted.
pg : str, optional
Name of a physical group (g.physical) whose entities
should be adopted.
dim : int, optional
Forwarded to g.labels.entities(label, dim=dim) when
using label= and the label spans multiple dimensions.
Returns¶
Instance
Source code in src/apeGmsh/core/_parts_registry.py
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
from_model ¶
Adopt entities already in the Gmsh session as a named part.
Useful after g.model.io.load_step() or g.model.io.load_iges()
when you want the imported geometry tracked for constraints
and fragmentation.
Parameters¶
label : str Part name. dim : int, optional Dimension to adopt. If None, adopts all dimensions. tags : list[int], optional Specific entity tags to adopt. If None, adopts all untracked entities (not already assigned to a part).
Returns¶
Instance
Examples¶
::
# Load geometry, then adopt it
g.model.io.load_step("bracket.step")
g.parts.from_model("bracket")
# Adopt only specific volumes
g.parts.from_model("slab", dim=3, tags=[1, 2])
Source code in src/apeGmsh/core/_parts_registry.py
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | |
add ¶
add(part: 'Part', *, label: str | None = None, translate: tuple[float, float, float] = (0.0, 0.0, 0.0), rotate: tuple[float, ...] | None = None, highest_dim_only: bool = True) -> Instance
Import a saved Part into the session.
Parameters¶
part : Part
Must have been save()-d to disk.
label : str, optional
Auto-generated as "{part.name}_1" if omitted.
translate, rotate : placement transforms.
highest_dim_only : keep only highest-dim entities from the CAD.
Source code in src/apeGmsh/core/_parts_registry.py
add_plane_wave_box ¶
add_plane_wave_box(*, x: tuple[float, int], y: tuple[float, int], z, skin_thickness=None, center: tuple[float, float, float] = (0.0, 0.0, 0.0), rotation_z_deg: float = 0.0, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True)
Build a structured soil box wrapped by an ASDAbsorbingBoundary skin.
A plane-wave box is an axis-aligned structured soil box plus a
one-element-thick absorbing offset shell on its five truncation
faces (the local +Z top is the free surface and is never shelled).
Soil + shell form one rectangular block; the shell is decomposed into
face / vertical-edge / bottom-edge / bottom-corner regions, each tagged
with its OpenSees btype. The companion bridge element
(ASDAbsorbingBoundary3D) fans out one element per skin-region hex.
Built directly in the live session (no Part/STEP round-trip); pairs with
— but does not use — :meth:add_DRM_box. See ADR 0054.
Parameters¶
x, y : (size, n_elements)
Lateral soil extent (symmetric, centred) and element count.
z : (depth, n_elements) | list[(depth, n_elements)]
Vertical soil extent (downward, free surface at the top) and element
count. Pass a top → bottom list of layers for a stratified column
(e.g. z=[(15, 3), (25, 5)]); each layer gets its own soil + lateral
skin PGs, so it can take its own absorbing material via
ops.element.absorbing_boundary(materials=[m0, m1, …]) (ADR 0054 AB-1c).
skin_thickness : float | (tx, ty, tz) | None
Absorbing-skin thickness. None (default) matches the adjacent
soil element size per face. A skin much thicker than the adjacent
soil element warns (WarnAbsorbingSkinAspect) — it absorbs poorly.
center : (cx, cy, cz)
World location of the soil top-face centre (free surface).
rotation_z_deg : float
Must be 0 — the ASDAbsorbingBoundary3D element requires
boundary-face normals along global X or Y, so a rotated absorbing
box is rejected by the solver.
name, names, apply_transfinite :
PG-name prefix, per-PG override dict, and transfinite toggle —
mirroring :meth:add_DRM_box.
Returns¶
AbsorbingSkinResult
PG names (soil_pg, skin_pgs by btype, skin_all_pg,
bottom_pgs, free_surface_pg), axes, and placement.
Example¶
::
res = g.parts.add_plane_wave_box(
x=(605, 22), y=(605, 20), z=(420, 16),
)
g.mesh.generation.generate(dim=3)
# res.skin_pgs["L"], res.skin_all_pg, res.bottom_pgs ...
Source code in src/apeGmsh/core/_parts_registry.py
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | |
add_DRM_box_from_h5drm ¶
add_DRM_box_from_h5drm(*, h5drm: str, crd_scale: float = 1000.0, buffer: int = 0, absorbing: bool = False, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True)
Build a structured soil box matched to an .h5drm station grid.
Reads a ShakerMaker-style .h5drm DRM dataset and builds, in the live
session, a single transfinite hex box whose nodes land EXACTLY on the
dataset stations (so OpenSees' H5DRM node-matching is trivial), tags the
soil volume + the six outer boundary faces (the dataset "b" shell) as
physical groups, and returns the frame contract the matching
ops.pattern.H5DRM(...) consumes — so the user never re-derives the
km→m / centred / z-down handshake. See ADR 0066.
The dataset-keyed sibling of the parametric :meth:add_DRM_box (an SSI
inner/transition/outer layout, NOT keyed to a dataset). Geometry + PGs
only — assign the soil material and elements via the bridge
(ops.nDMaterial + ops.element.stdBrick(pg=result.soil_pg)).
Parameters¶
h5drm : str
Path to the .h5drm dataset (DRM_Data/{xyz,internal} +
DRM_Metadata/drmbox_x0). The station grid must be a complete,
uniform, isotropic regular grid.
crd_scale : float
Station-units → model-units scale. ShakerMaker stations are in km,
FE models in m ⇒ default 1000.0.
buffer : int
Number of exterior soil layers to add OUTWARD on the four sides + the
bottom (never the free surface), at the same grid spacing. 0
(default) builds just the inner DRM box. A free DRM box diverges
(rigid-body null-space excited by the residual), so a real run needs a
buffer + a far boundary: the buffer hexes carry only NON-dataset
nodes, so H5DRM excludes them from the effective-force set
(H5DRMLoadPattern.cpp:580). Apply the boundary on
result.exterior_pgs via the bridge (ops.fix for the validated
fixed far field).
absorbing : bool
When True (requires buffer >= 1), wrap the buffered box in a
one-element ASD absorbing skin (btype-tagged ghost layer) on the
sides + bottom — the production-SSI boundary (ADR 0054). The skin
sits on the buffer's outer (NON-dataset) faces, so it never lands on
the DRM b shell. result.skin is then an AbsorbingSkinResult
ready for ops.element.absorbing_boundary(skin=result.skin, ...) +
the staged s.activate_absorbing() flip.
name, names, apply_transfinite :
PG-name prefix, per-PG override dict, and transfinite toggle —
mirroring :meth:add_DRM_box.
Returns¶
DRMBoxFromH5Result
soil_pg, boundary_pgs (by face key), boundary_all_pg,
free_surface_pg, exterior_pgs (sides+bottom), the frame
contract (crd_scale / transform / x0 / center), and
the grid descriptor (origin / spacing / counts).
Example¶
::
drm = g.parts.add_DRM_box_from_h5drm("motions.h5drm")
g.mesh.generation.generate(dim=3)
fem = g.mesh.queries.get_fem_data(dim=3)
ops = apeSees(fem)
soil = ops.nDMaterial.ElasticIsotropic(E=E, nu=nu, rho=rho)
ops.element.stdBrick(pg=drm.soil_pg, material=soil)
with ops.pattern.H5DRM(h5drm="motions.h5drm"): # defaults match drm
pass
Source code in src/apeGmsh/core/_parts_registry.py
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | |
add_absorbing_shell ¶
add_absorbing_shell(*, box, element_size, skin_thickness=None, faces: tuple[str, ...] | None = None, layers: list[tuple[float, int]] | None = None, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True)
Weld a one-element ASDAbsorbingBoundary skin onto your own soil box.
The bring-your-own-box counterpart to :meth:add_plane_wave_box: you
build the soil box (its placement, PGs, the material/structure you later
put on it), and this wraps a one-element-thick absorbing skin onto its
five truncation faces — the local +Z top is the free surface and is
never shelled. Returns the same :class:AbsorbingSkinResult as
:meth:add_plane_wave_box, so the bridge element
(ops.element.absorbing_boundary) and the staged flip
(s.activate_absorbing) consume it identically. See ADR 0054 (AB-1b).
The skin discretization is size-based and (re)applied to box + skin
together after the weld: gmsh cannot report transfinite counts back and
the boolean fragment renumbers entities, so the box's prior mesh state
is irrelevant — this call makes box + skin one structured hex region.
Parameters¶
box :
The soil box — a PG / label name or a volume handle. Must resolve to
exactly one axis-aligned rectangular volume (fail-loud otherwise;
rotated / curved / multi-volume boxes are out of scope for this slice).
element_size : float | (sx, sy, sz)
Target soil element size; sets the structured node counts on box+skin.
skin_thickness : float | (tx, ty, tz) | None
Absorbing-skin thickness. None (default) matches element_size
per axis (one element thick).
faces : tuple[str, ...] | None
Restrict the skin to a subset of ("L","R","F","K","B") (e.g. omit a
symmetry plane). None (default) shells all five truncation faces.
layers : list[(depth, n_elements)] | None
Stratify the box top → bottom (depths must sum to the box's z-extent).
Slices the box into per-layer soil volumes and splits the lateral skin
per layer, so each layer can take its own absorbing material via
ops.element.absorbing_boundary(materials=[m0, m1, …]) (ADR 0054
AB-1c). None (default) = homogeneous.
name, names, apply_transfinite :
PG-name prefix, per-PG override dict, and transfinite toggle — mirroring
:meth:add_plane_wave_box. When box is a name, soil_pg is
reported as that name (no duplicate PG is created).
Returns¶
AbsorbingSkinResult
PG names (soil_pg, skin_pgs by btype, skin_all_pg,
bottom_pgs, free_surface_pg), axes, and placement.
Example¶
::
g.model.geometry.add_box(0, 0, -40, 20, 20, 40, label="soil")
res = g.parts.add_absorbing_shell(box="soil", element_size=2.5)
g.mesh.generation.generate(dim=3)
# res.skin_all_pg, res.bottom_pgs, res.free_surface_pg ...
Source code in src/apeGmsh/core/_parts_registry.py
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 | |
add_plane_wave_box_2d ¶
add_plane_wave_box_2d(*, x: tuple[float, int], y, skin_thickness=None, center: tuple[float, float] = (0.0, 0.0), rotation_z_deg: float = 0.0, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True)
Build a 2D plane-strain soil box wrapped by an absorbing skin.
The 2D sibling of :meth:add_plane_wave_box (ADR 0054, AB-5): a
structured soil rectangle in the global X–Y plane at z = 0 (X
lateral, Y vertical, free surface at the local top y = 0) plus a
one-element-thick absorbing skin on its three truncation faces.
Skin regions carry the 2D btypes — L = min-X, R = max-X,
B = min-Y, corners BL/BR — and fan out to
ASDAbsorbingBoundary2D quads via
ops.element.absorbing_boundary(skin=…, thickness=…) (the 2D
element needs the out-of-plane slab thickness).
Parameters¶
x : (size, n_elements)
Lateral soil extent (symmetric, centred) and element count.
y : (depth, n_elements) | list[(depth, n_elements)]
Vertical soil extent (downward, free surface at the top). Pass a
top → bottom list of layers for a stratified column; each
layer gets its own soil + lateral skin PGs for per-layer
absorbing materials (materials=[…]).
skin_thickness : float | (tx, ty) | None
Absorbing-skin thickness. None (default) matches the
adjacent soil element size per face.
center : (cx, cy)
World location of the soil top-face centre (free surface).
rotation_z_deg : float
Must be 0 — the ASDAbsorbingBoundary2D element has no
distortion handling (it sizes itself from sorted nodal x/y
coordinates), so a rotated skin runs with silently wrong terms.
name, names, apply_transfinite :
PG-name prefix, per-PG override dict, and transfinite toggle.
Returns¶
AbsorbingSkinResult
Same shape as the 3D result (ndm == 2; free_surface_pg
is a dim-1 edge PG).
Example¶
::
res = g.parts.add_plane_wave_box_2d(x=(100, 20), y=(50, 10))
g.mesh.generation.generate(dim=2)
# res.skin_pgs -> {"B": ..., "L": ..., "R": ..., "BL": ..., "BR": ...}
Source code in src/apeGmsh/core/_parts_registry.py
add_absorbing_shell_2d ¶
add_absorbing_shell_2d(*, box, element_size, skin_thickness=None, faces: tuple[str, ...] | None = None, layers: list[tuple[float, int]] | None = None, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True)
Weld a one-element absorbing skin onto your own 2D soil rectangle.
The bring-your-own-box 2D entry (ADR 0054, AB-5), mirroring
:meth:add_absorbing_shell: box must resolve to exactly one
axis-aligned rectangular surface lying flat in a z = const
plane. The skin goes on the L/R/B truncation edges (the
top is the free surface); faces= restricts it (subset of
("L", "R", "B"), e.g. drop a symmetry edge). layers
stratifies box + lateral skin top → bottom (depths must sum to the
box's y-extent). Discretization is size-based and (re)applied to
box + skin together after the weld, as in 3D.
Returns¶
AbsorbingSkinResult
Same shape as the 3D result (ndm == 2).
Example¶
::
g.model.geometry.add_rectangle(0, -50, 0, 100, 50, label="soil")
res = g.parts.add_absorbing_shell_2d(box="soil", element_size=5.0)
g.mesh.generation.generate(dim=2)
Source code in src/apeGmsh/core/_parts_registry.py
add_DRM_box ¶
add_DRM_box(*, x_inner: tuple[float, int], x_layer: tuple[float, int], x_outer: tuple[float, int], y_inner: tuple[float, int], y_layer: tuple[float, int], y_outer: tuple[float, int], z_top: tuple[float, int], z_mid: tuple[float, int], z_bottom: tuple[float, int], center: tuple[float, float, float] = (0.0, 0.0, 0.0), rotation_z_deg: float = 0.0, name: str | None = None, names: dict[str, str] | None = None, apply_transfinite: bool = True, tag_line_pgs: bool = True)
Build, place, and tag a Domain-Reduction-Method soil box.
A DRM box is a layered solid with three concentric regions
per lateral axis (inner core | transition layer | outer
absorbing layer) and a downward Z stack (top | mid | bottom).
The classic symmetric case has 5 * 5 * 3 = 75 axis-aligned
hex sub-volumes, each meshed structured-hex with per-region
element counts.
center=(0, 0, 0) puts the top-face centre of the inner
box at the origin (free-surface convention). Rotation is
applied CCW about +Z at center; the rotated frame
survives every step (volume PGs, line PGs, transfinite
cascade) because we classify by world-coords transformed
back to the local frame.
Parameters¶
x_inner, x_layer, x_outer, y_inner, y_layer, y_outer :
(size, n_elements) tuples — symmetric layered lateral
axes. Each segment's element count drives the
transfinite cascade.
z_top, z_mid, z_bottom :
(size, n_elements) tuples — downward Z stack with the
free surface at z = 0 (inner-box top).
center :
World-coordinate location for the top-face centre of the
inner box.
rotation_z_deg :
CCW rotation about +Z applied at center, in
degrees.
name :
Instance label and default PG prefix. When None,
uses "drm_box". PGs default to inner_box /
transition_box / outer_box (and the matching
lines_* curves); when name is given they become
{name}_inner_box etc.
names :
Per-PG override dict. Keys: inner_pg, transition_pg,
outer_pg, line_pg_<region>_<axis> (e.g.
line_pg_inner_x, line_pg_top_z). Each override
replaces the entire PG name (the name prefix is
ignored for that key).
apply_transfinite :
When True (default), apply the structured-hex transfinite
cascade to every sub-volume using the per-region element
counts in axis_x / axis_y / axis_z.
tag_line_pgs :
When True (default), tag axis-parallel edges by region
into curve PGs lines_{region}_{axis}. When False,
result.line_pgs is empty.
Returns¶
DRMBoxResult
Frozen summary with PG names, Axis1D descriptors, the
applied center and rotation_z (in radians).
Example¶
::
res = g.parts.add_DRM_box(
x_inner=(605, 10), x_layer=(10, 1), x_outer=(20, 2),
y_inner=(605, 10), y_layer=(10, 1), y_outer=(20, 2),
z_top=(50, 5), z_mid=(50, 5), z_bottom=(200, 20),
center=(0, 0, 0),
)
g.mesh.generation.generate(dim=3)
# res.inner_pg == "inner_box", res.transition_pg == "transition_box",
# res.outer_pg == "outer_box"
Source code in src/apeGmsh/core/_parts_registry.py
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | |
import_step ¶
import_step(file_path: str | Path, *, label: str | None = None, translate: tuple[float, float, float] = (0.0, 0.0, 0.0), rotate: tuple[float, ...] | None = None, highest_dim_only: bool = True, heal: bool | float | str = False, dedupe: bool | float = False, properties: dict[str, Any] | None = None) -> Instance
Import a STEP or IGES file as a named instance.
Parameters¶
file_path : path
STEP (.step, .stp) or IGES (.iges, .igs) file.
label : str, optional
Auto-generated from file stem if omitted.
translate, rotate : placement transforms.
heal : bool, float, or "auto"
Heal the imported CAD immediately after import — same
semantics as :meth:g.model.io.load_step <_IO.load_step>:
True / "auto" use a scale-aware tolerance, a float
overrides, False (default) imports raw and emits a
:class:WarnGeomImportHealth advisory if slivers are found.
Best-effort for sidecar-carrying parts (healing renumbers,
so anchors rebind against the healed geometry).
dedupe : bool or float
Merge coincident entities after import (and after heal).
properties : arbitrary metadata.
Source code in src/apeGmsh/core/_parts_registry.py
build_node_map ¶
Partition mesh nodes by instance bounding box.
Returns {label: {node_tag, ...}}.
Source code in src/apeGmsh/core/_parts_registry.py
build_face_map ¶
Partition surface elements by instance node ownership.
Returns {label: face_connectivity_array}.
Source code in src/apeGmsh/core/_parts_registry.py
get ¶
Return the Instance registered under label.
Useful when you didn't store the return value of
:meth:add / :meth:import_step and want to access an
Instance later — e.g. to apply inst.edit.* transforms::
g.parts.add(beam, label="b1")
g.parts.get("b1").edit.translate(0, 0, 50)
Raises¶
KeyError
If no instance is registered under label. The error
message lists the available labels so you can spot a typo.
Source code in src/apeGmsh/core/_parts_registry.py
labels ¶
rename ¶
Rename an instance.
Raises¶
KeyError if old_label does not exist. ValueError if new_label already exists.
Source code in src/apeGmsh/core/_parts_registry.py
delete ¶
Remove an instance from the registry.
The entities remain in the Gmsh session — they become "untracked" and will appear under the Untracked group in the viewer's Parts tab.
Raises¶
KeyError if label does not exist.
Source code in src/apeGmsh/core/_parts_registry.py
Instance
dataclass
¶
Instance(label: str, part_name: str, file_path: Path | None = None, entities: dict[int, list[int]] = dict(), translate: tuple[float, float, float] = (0.0, 0.0, 0.0), rotate: tuple[float, ...] | None = None, properties: dict[str, Any] = dict(), bbox: tuple[float, float, float, float, float, float] | None = None, label_names: list[str] = list())
Bookkeeping record for one part placement.
Attributes¶
label : unique name inside the session
part_name : name of the source Part or file stem
file_path : CAD file that was imported (None for inline parts)
entities : {dim: [tag, ...]} — updated in-place by fragment
translate : applied translation (dx, dy, dz)
rotate : applied rotation (angle_rad, ax, ay, az[, cx, cy, cz])
properties : arbitrary user metadata
bbox : axis-aligned bounding box (xmin, ymin, zmin, xmax, ymax, zmax)
label_names : label names created for this instance (Tier 1
naming, e.g. ["col_A.shaft", "col_A.top"]).
Populated by _import_cad when the Part's CAD
file has a .apegmsh.json sidecar carrying
label definitions. These are NOT solver-facing
physical groups — use g.labels.entities(name)
to resolve entity tags, and
g.labels.promote_to_physical(name) to create
a solver PG when ready.
Axis1D
dataclass
¶
A 1-D axis split into named, contiguous segments.
Parameters¶
name : str
Axis name — only used for error messages.
segments : tuple of (region, lo, hi, count)
Ordered contiguous segments. lo of each segment must
match hi of the previous one; hi > lo; count >= 1.
breaks
property
¶
Ordered list of all break coordinates, length n_segments + 1.
The first entry is the axis lo, the last is hi, and the
interior entries are the boundaries between segments.
slice_offsets ¶
Interior break coordinates (everything in :attr:breaks except endpoints).
These are the offsets the geometry builder slices the box at.
witnesses ¶
One (region, midpoint) per segment, in order.
Useful as test fixtures and as sanity-check probe points.
Source code in src/apeGmsh/parts/_axis1d.py
region_of ¶
Return the region whose segment contains value.
Points exactly on a segment boundary are assigned to the
segment ending at that boundary (value == hi) when on the
very last break, otherwise to the segment beginning there
(value == lo). tol widens both endpoint comparisons.
Raises ValueError when value is outside [lo, hi].
Source code in src/apeGmsh/parts/_axis1d.py
count_for ¶
Return the element count of the segment containing value.
Source code in src/apeGmsh/parts/_axis1d.py
symmetric_layered
classmethod
¶
symmetric_layered(name: str, *, inner: tuple[float, int], layer: tuple[float, int], outer: tuple[float, int]) -> 'Axis1D'
5-segment symmetric layout — outer | layer | inner | layer | outer.
The inner segment is centred on zero; the layer mirrors on
both sides; the outer mirrors beyond the layer. Each tuple
is (size, n_elements).
Source code in src/apeGmsh/parts/_axis1d.py
downward_layered
classmethod
¶
downward_layered(name: str, *, top: tuple[float, int], mid: tuple[float, int], bottom: tuple[float, int]) -> 'Axis1D'
3-segment downward layout — bottom | mid | top, with hi = 0.
Convention for the DRM box: the free surface sits at z = 0
(top of the inner box), and the stack descends downward.
Each tuple is (size, n_elements).
Source code in src/apeGmsh/parts/_axis1d.py
DRMBox ¶
DRMBox(*, x_inner: tuple[float, int], x_layer: tuple[float, int], x_outer: tuple[float, int], y_inner: tuple[float, int], y_layer: tuple[float, int], y_outer: tuple[float, int], z_top: tuple[float, int], z_mid: tuple[float, int], z_bottom: tuple[float, int], name: str = 'drm_box')
Bases: Part
Layered DRM-box geometry, built in its own Gmsh session.
The box is centred laterally on (0, 0) and descends from
z = 0 (top of the inner box, free-surface convention). No
labels or physical groups are attached — the assembly-side helper
re-classifies sub-volumes by centroid + Axis1D lookup after
import, which is robust to STEP renumbering and to the
placement transform.
Parameters¶
x_inner, x_layer, x_outer, y_inner, y_layer, y_outer :
(size, n_elements) tuples — symmetric layered axes
(outer | layer | inner | layer | outer) along X and Y.
z_top, z_mid, z_bottom :
(size, n_elements) tuples — downward Z stack
(bottom | mid | top, hi = 0).
name :
Gmsh model name and default Part / instance name.
Source code in src/apeGmsh/parts/drm_box.py
build ¶
Build the 75-volume sliced box inside the Part's session.
Must be called inside with drm_box:. Returns self so
the caller can chain with DRMBox(...) as d: d.build() if
desired. Idempotent within a session — repeated calls slice
nothing on the already-fully-sliced model.
Source code in src/apeGmsh/parts/drm_box.py
DRMBoxResult
dataclass
¶
DRMBoxResult(inner_pg: str, transition_pg: str, outer_pg: str, line_pgs: dict[str, str] = dict(), axes: dict[str, Axis1D] = dict(), center: tuple[float, float, float] = (0.0, 0.0, 0.0), rotation_z: float = 0.0)
Summary of a DRM-box placement.
Returned by :func:PartsRegistry.add_DRM_box. The user keeps it
for downstream references — PG names to feed into recorders or
constraints, Axis1D descriptors to drive auxiliary mesh sizing.
ConstraintsComposite ¶
Solver-agnostic kinematic-constraint composite — declare on geometry, resolve to nodes after meshing.
Two-stage pipeline¶
- Declare (pre-mesh): the factory methods on this composite
(
equal_dof,rigid_link,rigid_diaphragm,tie, …) store :class:~apeGmsh.solvers.Constraints.ConstraintDefdataclasses describing intent at the geometry level. Defs carry no node tags and survive remeshing. - Resolve (post-mesh): :meth:
resolve(called automatically by :meth:Mesh.queries.get_fem_data) walks the def list and hands each one to :class:~apeGmsh.solvers.Constraints.ConstraintResolver, which produces concrete :class:~apeGmsh.solvers.Constraints.ConstraintRecordobjects — actual node tags, weights, and offset vectors.
The resolved records land on the FEM broker:
- node-pair / node-group / node_to_surface records →
fem.nodes.constraints - surface-coupling / interpolation records →
fem.elements.constraints
Constraint taxonomy¶
Five tiers, ordered by topology and the role each plays in a structural model:
============= ===================================================== =================================
Tier Methods Record family
============= ===================================================== =================================
1 — Pair :meth:equal_dof, :meth:rigid_link, NodePairRecord
:meth:penalty
2 — Group :meth:rigid_diaphragm, :meth:rigid_body, NodeGroupRecord
:meth:kinematic_coupling
2b — Mixed :meth:node_to_surface, NodeToSurfaceRecord
:meth:node_to_surface_spring (+ phantom nodes)
3 — Surface :meth:tie, :meth:distributing_coupling, InterpolationRecord
:meth:embedded
4 — Contact :meth:tied_contact SurfaceCouplingRecord
5 — Fork :meth:contact, :meth:mortar (deprecated alias) ContactRecord
============= ===================================================== =================================
All constraints ultimately express the linear MPC equation
u_slave = C · u_master. Tiers differ in how C is
built — by node co-location (Tier 1), kinematic transformation
around a master point (Tier 2), shape-function interpolation
(Tier 3), or numerical integration on the interface (Tier 4).
Target identification¶
Most methods identify their master and slave sides by name — a
part label (a key of g.parts._instances), a physical
group (g.physical / .to_physical), or a label
(g.labels). :meth:_add_def validates both names and raises
KeyError on a typo::
g.constraints.tie(master_label="column",
slave_label="slab",
master_entities=[(2, 13)], # optional scope
slave_entities=[(2, 17)])
A physical-group model therefore constrains without building
Parts (tie("A_top", "B_bot") just works), matching how
g.loads / g.masses already resolve names. Precedence:
a Part registered under the name wins (the part node/face map is
consulted first); otherwise the name resolves through the shared
label→PG→part geometry resolver. A name that is simultaneously a
Part and a physical group binds the Part's node set.
Optional master_entities / slave_entities (list of
(dim, tag)) narrow the search to a subset of the target's
entities — useful when a target has many surfaces and only one is
the interface.
Exceptions to the part-label scheme ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :meth:
node_to_surfaceand :meth:node_to_surface_springtake bare tags instead. Themasteris a Gmsh point entity (dim=0) andslaveis one or more surface entities (dim=2). Both arguments acceptint,str, or(dim, tag); label validation is skipped. - :meth:
embeddeduseshost_label/embedded_labelto mirror the host/embedded vocabulary, but the lookup logic otherwise matches the part-label scheme.
Resolution semantics¶
:meth:resolve is dependency-injected — it never imports
PartsRegistry. The caller (typically
Mesh.queries.get_fem_data) supplies:
node_map:{part_label → set[int]}of mesh node tagsface_map:{part_label → ndarray(F, n_per_face)}built only when surface constraints (Tier 3 / 4) are present.
See Also¶
apeGmsh.solvers.Constraints :
Module-level taxonomy and theory.
apeGmsh.solvers._constraint_defs :
Stage-1 dataclasses with full per-method theory.
apeGmsh.solvers._constraint_resolver.ConstraintResolver :
Stage-2 implementation.
apeGmsh.mesh._record_set.NodeConstraintSet :
Iteration helpers (rigid_link_groups, equal_dofs,
rigid_diaphragms, pairs).
Examples¶
Declare a mix of constraints, mesh, and read out grouped rigid-link masters for OpenSees emission::
with apeGmsh(model_name="frame") as g:
# Tier 1 — co-located nodes share x/y/z
g.constraints.equal_dof("col", "beam", dofs=[1, 2, 3])
# Tier 2 — slab nodes follow the centre-of-mass node
g.constraints.rigid_diaphragm(
"slab", "slab_master",
master_point=(2.5, 2.5, 3.0),
plane_normal=(0, 0, 1),
)
# Tier 3 — non-matching shell-to-solid interface
g.constraints.tie(
"shell", "solid",
master_entities=[(2, 17)],
slave_entities=[(2, 41)],
)
g.mesh.generation.generate(dim=3)
fem = g.mesh.queries.get_fem_data(dim=3)
for master, slaves in fem.nodes.constraints.rigid_link_groups():
for slave in slaves:
ops.rigidLink("beam", master, slave)
Source code in src/apeGmsh/core/ConstraintsComposite.py
contact ¶
contact(master, slave, *, formulation='nts', kn=None, kt=None, mu=None, eps_n=None, eps_t=None, cohesion=None, tau_max=None, aug_tol=None, max_aug=None, ngp=None, tie=False, thickness=None, outward=None, soft=None, visc=None, consistent_tan=False, geom_tan=False, cell=None, edge_edge=False, edge_kn=None, edge_band=None, edge_mu=None, edge_kt=None, edge_cohesion=None, edge_tau_max=None, edge_consistent_tan=False, edge_soft=None, edge_alm=False, edge_aug_tol=None, master_entities=None, slave_entities=None, name=None) -> ContactDef
Declare a face-to-face contact between two meshed surfaces
(fork contactSurface + contact + LadrunoContact handler).
Parameters¶
master, slave : str
The two surface PG / part labels in contact. The master is
faceted (-master); the slave is a node set (NTS, -slave) or
faceted (mortar, -slave-segments).
formulation : {"nts", "mortar"}
"nts" = node-to-segment penalty; "mortar" =
segment-to-segment ALM (the non-matching-mesh accuracy lane).
kn, kt, mu : float, optional
NTS normal/tangential penalty + Coulomb friction (kn may be
"auto"). Rejected for mortar.
eps_n, eps_t : float | "auto", optional
Mortar ALM normal/tangential penalty. Rejected for NTS.
cohesion, tau_max : float, optional
Mortar friction-cone adhesion + Tresca cap.
aug_tol, max_aug, ngp : optional
Mortar Uzawa tolerance / max augmentations / slave-facet Gauss order.
tie : bool
Permanent mesh-tie bond (mortar only; excludes friction).
thickness : float, optional
2D mortar only — the plane-model out-of-plane thickness h
(-thickness; fork default 1.0). The mortar lane's interval
integrals produce force per unit thickness, so the fork applies
h once, at its 2D injection site, to eps_n/eps_t/
visc/cohesion/tau_max and the tie stiffness. Keep the
three thickness conventions apart: the ELEMENT thickness
(ops.element.FourNodeQuad(thickness=…)) is baked into element
stiffness and contact never re-reads it; this h scales the
EXPLICIT penalties above; and eps_n="auto" is deliberately NOT
h-scaled (it already absorbs the element thickness through
getInitialStiff(), so re-scaling would be an h² error). An
eps_t="auto" (or an eps_t defaulted from eps_n under
friction) inherits eps_n's provenance, so it h-scales only
when eps_n is explicit. The NTS lane has no -thickness
at all, and a 3D model is refused by name here.
soft : float | bool, optional
Explicit-only Courant-stable SOFT penalty (-soft): True ⇒
the fork default SOFSCL (0.10); a float ⇒ an explicit SOFSCL. Needs
a base penalty (kn/eps_n); excludes tie. NTS=SOFT=1,
mortar=SOFT=2. See :class:ContactDef.
visc : float, optional
Viscous normal-stabilisation coefficient μ_c (-visc); excludes
tie.
consistent_tan : bool
Non-symmetric consistent friction tangent (-consistanttan) —
needs an unsymmetric solver (FullGeneral / UmfPack / BandGeneral).
geom_tan : bool
NTS ∂n/∂u geometric normal tangent (-geomtan) for curved /
large-sliding interfaces. NTS-only.
cell : float, optional
Broad-phase cell-size scale (-cell): the spatial-hash bucket size
as a fraction of the median segment diagonal (must be > 0). A
performance knob — omit for the fork default. Both formulations.
edge_edge : bool
Enable the perpendicular edge-edge contact fallback (-edgeedge,
ADR-57 E2). Mortar-only. See :class:ContactDef.
edge_kn : float | "auto", optional
Edge-edge normal penalty (-edgeKn); None ⇒ the mortar penalty.
edge_band : float, optional
Edge-edge gap activation band (-edgeBand).
edge_mu, edge_kt, edge_cohesion, edge_tau_max : float, optional
Edge-edge Coulomb/Tresca friction (-edgeMu/-edgeKt/
-edgeCohesion/-edgeTauMax).
edge_consistent_tan : bool
Edge-edge non-symmetric Csl friction tangent (-edgeConsistentTan).
edge_soft : float | bool, optional
Edge-edge explicit Courant-stable SOFT penalty (-edgeSoft).
edge_alm : bool
Edge-edge commit-cycle augmented Lagrangian (-edgeAlm).
edge_aug_tol : float, optional
Edge-edge ALM tolerance (-edgeAugTol).
outward : (float, float, float) | (float, float) | "winding", optional
None (default) → no -outward is emitted; the fork derives a
correct per-facet normal (right for separated bodies and curved /
closed / solid masters). Set an explicit direction ONLY for an
initially-coincident (zero-gap) FLAT contact, where the fork's
per-pair sign reference is in-plane and ambiguous. A single global
outward is wrong on a non-flat master. See :class:ContactDef.
**In a 2D model** this is a 2-vector ``(ox, oy)``, and a flush
interface REQUIRES one (or ``"winding"``) — the fork's 2D lanes
orient from an interface-level centroid vote that is ambiguous
there and aborts. ``outward="winding"`` (2D NTS only) declares the
side through the master chain's own winding instead of a
direction, so it also orients curved and closed masters; it needs
a fork build carrying ``-outward winding``.
master_entities, slave_entities : list of (dim, tag), optional Restrict each side to specific Gmsh entities. name : str, optional Friendly name (round-trips into the emitted deck comment).
Returns¶
ContactDef
Source code in src/apeGmsh/core/ConstraintsComposite.py
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | |
resolve_contacts ¶
Resolve every :meth:contact def to a :class:ContactRecord.
Pulls the master faceted surface (+ slave node set / faceted surface)
from the live Gmsh session, dropping higher-order facets to corners,
mirroring the additive g.reinforce / g.embed resolve. The outward
normal is carried through only when the user set it explicitly — the
fork kernel derives a correct per-facet normal otherwise (see the
outward note below). node_tags / node_coords are accepted for
signature parity with the sibling resolvers. Serial-only (the fork
contact subsystem is not parallel).
The model dimension is read once, here, and threaded — it is the
single branch point of the 2D lane, mirroring
:meth:resolve_interfaces. In a 2D model the master is a dim-1
curve, collected as line segments and CHAINED head-to-tail into the
fork's stride-2 pair list; in a 3D model nothing below changes.
Source code in src/apeGmsh/core/ConstraintsComposite.py
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | |
contact_plane ¶
contact_plane(slave, *, normal, point, kn, visc=None, soft=None, slave_entities=None, name=None) -> ContactPlaneDef
Declare a rigid analytical-plane contact (fork contactPlane).
The meshed slave surface contacts a fixed infinite rigid plane
(normal + point) with normal penalty kn — frictionless, no
master mesh. Use it for a rigid floor / wall / foundation where the
counter-body needn't be meshed. Optional visc (viscous normal
stabilisation) and soft (explicit Courant-stable SOFT penalty;
True ⇒ the fork default SOFSCL 0.10, or a float SOFSCL). Fork-only
at run time.
Parameters¶
slave : str
The meshed surface PG / part label whose nodes contact the plane.
In a 2D model it is the meshed boundary CURVE (a dim-1 PG) or
a point set — naming the dim-2 plane collects every interior node
of the body and is refused by name.
normal : (float, float, float) | (float, float)
The plane's outward unit normal (toward the slave / open side).
In a 2D model it is the 2-vector (nx, ny); it is z-padded
internally and emitted as the fork's permanently-valid zero-padded
9-argument form, so there is only ever one grammar to read.
point : (float, float, float) | (float, float)
Any point on the plane; (px, py) in a 2D model.
kn : float
Normal penalty stiffness (required — there is no "auto" on
contactPlane).
visc : float, optional
Viscous normal-stabilisation coefficient μ_c (-visc).
soft : float | bool, optional
Explicit-only Courant-stable SOFT penalty (-soft).
slave_entities : list of (dim, tag), optional
Restrict the slave to specific Gmsh entities.
name : str, optional
Friendly name (round-trips into the emitted deck comment).
Returns¶
ContactPlaneDef
Source code in src/apeGmsh/core/ConstraintsComposite.py
resolve_contact_planes ¶
Resolve every :meth:contact_plane def to a
:class:ContactPlaneRecord — the slave node set is pulled from the live
Gmsh session (mirroring the NTS slave of :meth:resolve_contacts).
node_tags / node_coords are accepted for signature parity with
the sibling resolvers. Serial-only (the fork contact subsystem is not
parallel).
The model dimension is read once, here, and threaded — the
:meth:resolve_contacts idiom. This lane has no master mesh and no
facets, so the whole 2D difference is the slave gate below plus the
refusal of an out-of-plane normal / point: the rigid-plane lane keeps
the fork's ndf >= ndm (its adapter couples the first ndm DOFs
by construction, which is what lets a 3D ndf-6 shell sit on a plane),
so unlike the NTS/mortar lanes there is nothing else to branch on.
Source code in src/apeGmsh/core/ConstraintsComposite.py
interface ¶
interface(master, slave, *, normal, tangential, thickness, tolerance=1e-06, slave_ndf=None, master_entities=None, slave_entities=None, name=None) -> InterfaceDef
Declare an oriented coincident-pair zeroLength interface
(ADR 0093).
One zeroLength spring per coincident (master, slave) node
pair, with the local axes taken per pair from the master
face geometry — so a curved master's normal follows the face
from wall to crown instead of collapsing to one average frame —
and the normal / tangential laws scaled by each pair's
tributary area. The point of the verb is a unilateral
(compression-only, separation allowed) and strength-capped
interface: with a bilateral bond a converging ground drives the
liner's demand without bound.
2D line masters only in v1; a 3D model or a surface master
raises :class:NotImplementedError (ADR 0093 D2).
Parameters¶
master, slave : str
The master curve PG / part label — a free boundary of
the meshed 2D continuum — and the node-for-node coincident
slave label. The two node sets must be disjoint.
normal : NormalLaw
Per-area normal law: NormalLaw(kind="ent"|"epp_gap"|
"elastic", k_per_area=..., ...). Declarative kernel data,
translated to a typed uniaxial material (scaled by
A_trib) only at emit.
tangential : TangentialLaw
Per-area tangential law: TangentialLaw(kind="epp"|
"elastic", k_per_area=..., tau_b=...).
thickness : float
Out-of-plane thickness (required, > 0) —
A_trib = ell_trib * thickness.
tolerance : float
Coincidence radius for the node pairing. A slave with no
master inside it is an error, never a silent skip.
slave_ndf : {None, 2, 3}
The ndf the slave wire will be declared with. None /
2 ⇒ the slave matches the 2D continuum and the pair
connects directly; 3 ⇒ a beam slave, so each pair gets
the phantom bridge of ADR 0093 D4 (the fork refuses a
mixed-ndf zeroLength). Explicit by design — see
:class:~apeGmsh._kernel.defs.constraints.InterfaceDef.
master_entities, slave_entities : list of (dim, tag), optional
Restrict each side to specific Gmsh entities.
name : str, optional
Friendly name (carried onto every resolved record).
Returns¶
InterfaceDef
Source code in src/apeGmsh/core/ConstraintsComposite.py
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | |
resolve_interfaces ¶
Resolve every :meth:interface def to :class:InterfaceRecord\ s.
Gathers the live-Gmsh inputs — both node sets, the master's
boundary line elements, and the model's 2D domain elements —
and hands the geometry math to
:func:~apeGmsh._kernel.resolvers._interface_resolver.resolve_interface_records
(pure kernel, no Gmsh), mirroring how :meth:resolve_contacts
gathers and delegates.
Must run after :meth:resolve so the interface phantom tags
start above the MP lane's phantom high-water mark; the factory
orders them that way.
Source code in src/apeGmsh/core/ConstraintsComposite.py
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | |
bc ¶
Homogeneous single-point constraint — fix a pattern to ground.
The natural (essential / Dirichlet) boundary condition: every
mesh node in the resolved pattern gets ops.fix(node, *mask)
downstream. There is no master and no slave — unlike every
other method on this composite, this is a constraint to
ground, not between two parts. It resolves into
fem.nodes.sp (homogeneous :class:SPRecord\ s) — the same
broker channel as g.displacements.surface — not
fem.nodes.constraints.
Because it is a permanent constraint (not a pattern-scoped
quantity), it lives here on g.constraints rather than on
g.displacements: there is no load-pattern context to accidentally
scope it into, and the downstream emitter places it in the
model → bcs → patterns deck order via ops.fix.
Parameters¶
target : str or list[(dim, tag)]
Pattern to fix. Resolved label → physical group → raw
tags (or a mesh selection) — the same flexible target
model as g.displacements.surface. Pass pg= / label=
/ tag= instead to force a specific resolution path.
dofs : list[int], optional
Restraint mask (1 = constrained, 0 = free), in
DOF order [ux, uy, uz, rx, ry, rz]. Default
[1, 1, 1] (pin all translations). This is the
OpenSees ops.fix / face_sp convention — not
the index-list convention used by
:meth:equal_dof (dofs=[1,2,3]).
name : str, optional
Friendly name shown in summaries / the viewer.
Returns¶
BCDef
The stored definition; the same object is appended to
self._bc_defs.
Warnings¶
Resolution is dimension-agnostic — a point, edge, surface, or volume pattern all just contribute their mesh nodes. Pointing a BC at a volume physical group therefore fixes every interior node of the solid, which is almost never intended; target a boundary surface/edge instead.
Examples¶
::
g.constraints.bc("base_face") # pin x,y,z
g.constraints.bc(pg="Supports", dofs=[1, 1, 0])
g.constraints.bc(label="col.base",
dofs=[1, 1, 1, 1, 1, 1]) # full fixity
Source code in src/apeGmsh/core/ConstraintsComposite.py
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 | |
resolve_bcs ¶
Resolve every :meth:bc def to homogeneous SPRecord\ s.
Mirrors the load/SP resolution path: each BCDef target is
run through the loads composite's dimension-agnostic
_target_nodes (label → PG → tag → mesh-selection, any
dim), then one SPRecord(value=0.0, is_homogeneous=True) is
emitted per restrained DOF per node.
Fails loud — consistent with :meth:_resolve_nodes and the
resolver contract — if a pattern resolves to zero mesh nodes:
a BC that silently binds nothing is worse than one that errors.
Source code in src/apeGmsh/core/ConstraintsComposite.py
equal_dof ¶
equal_dof(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1e-06, name=None) -> EqualDOFDef
Tie matching DOFs between co-located node pairs.
At resolution time the resolver finds every master node
whose coordinates match a slave node within tolerance
and emits one
:class:~apeGmsh.solvers.Constraints.NodePairRecord per
match. Each pair becomes ops.equalDOF(master, slave, *dofs)
downstream — i.e. u_slave[i] = u_master[i] for every
i in dofs.
Use this for conformal interfaces only — meshes that share
nodes at the boundary. For non-matching meshes use :meth:tie.
Parameters¶
master_label : str
Part, physical-group, or label name whose nodes drive the
constraint.
slave_label : str
Part, physical-group, or label name whose matching nodes
are slaved.
master_entities, slave_entities : list of (dim, tag), optional
Restrict the node search to specific Gmsh entities of
each side. Useful when only one face of a multi-face
part is the interface.
dofs : list[int], optional
1-based DOF indices to constrain (1=ux, 2=uy, 3=uz,
4=rx, 5=ry, 6=rz). None (default) means all DOFs
available — the actual count depends on the model's
ndf.
tolerance : float, default 1e-6
Maximum distance (in model units) between two nodes for
them to be treated as co-located. Unit-sensitive:
1e-3 for millimetre models, 1e-6 for metre
models.
name : str, optional
Friendly name shown in :meth:summary and the viewer.
Returns¶
EqualDOFDef
The stored definition; the same object is appended to
self.constraint_defs.
Raises¶
KeyError
If master_label or slave_label is not in
g.parts.
See Also¶
tie : Non-matching mesh equivalent (shape-function projection). rigid_link : Add a kinematic offset on top of co-location.
Examples¶
Translational continuity between a column and a beam at a joint::
g.constraints.equal_dof(
"column", "beam",
dofs=[1, 2, 3],
tolerance=1e-3, # mm model
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 | |
equal_dof_mixed ¶
equal_dof_mixed(master_label, slave_label, *, dof_pairs, master_entities=None, slave_entities=None, tolerance=1e-06, name=None) -> EqualDOFMixedDef
Tie differently-numbered DOFs between co-located node pairs.
The mixed analog of :meth:equal_dof: rather than tying DOF
i to DOF i, each (retained_dof, constrained_dof)
couple in dof_pairs is tied explicitly — so master ux
can drive slave rz, etc. Resolves like equal_dof (one
record per co-located pair) and emits
ops.equalDOF_Mixed(R, C, numDOF, RDOF1, CDOF1, ...) per pair.
Use this for conformal interfaces where the two sides expose
the coupled quantity under different DOF indices (e.g. tying a
solid's translation to a shell's drilling rotation). For matching
DOFs use :meth:equal_dof; for non-matching meshes use :meth:tie.
Parameters¶
master_label : str
Part label whose nodes are retained (the R node).
slave_label : str
Part label whose matching nodes are constrained (C).
dof_pairs : list of (int, int)
(retained_dof, constrained_dof) couples, 1-based
(1=ux, 2=uy, 3=uz, 4=rx, 5=ry, 6=rz). Required and
non-empty; the two members of a couple may differ.
master_entities, slave_entities : list of (dim, tag), optional
Restrict the node search to specific Gmsh entities of each side.
tolerance : float, default 1e-6
Co-location distance (model units). Unit-sensitive — see
:meth:equal_dof.
name : str, optional
Friendly name shown in :meth:summary and the viewer.
Returns¶
EqualDOFMixedDef
The stored definition; also appended to self.constraint_defs.
See Also¶
equal_dof : Same-DOF co-located tie (the common case).
Examples¶
Tie a solid face's z-translation to a shell edge's drilling DOF::
g.constraints.equal_dof_mixed(
"solid", "shell",
dof_pairs=[(3, 6)], # master uz → slave rz
tolerance=1e-3, # mm model
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
rigid_link ¶
rigid_link(master_label, slave_label, *, link_type='beam', master_point=None, slave_entities=None, tolerance=1e-06, name=None) -> RigidLinkDef
Rigid bar between a master node and one or more slave nodes.
Each slave node is constrained to follow the master through
a rigid offset arm r = x_slave − x_master::
link_type="beam": u_s = u_m + θ_m × r, θ_s = θ_m
link_type="rod": u_s = u_m + θ_m × r, θ_s free
Use "beam" for fully rigid kinematic offsets (eccentric
connections, lumped-mass arms, fictitious rigid extensions).
Use "rod" when you want to transmit translation but leave
the slave free to rotate — e.g. pinned eccentric supports.
Parameters¶
master_label : str
Part, physical-group, or label name that owns the master
node. The master is
identified inside this part either by master_point
(proximity match) or by being the unique node when the
part collapses to a single point.
slave_label : str
Part, physical-group, or label name whose nodes become
slaves.
link_type : "beam" or "rod", default "beam"
"beam" couples 6 DOFs with rotational offset;
"rod" couples translations only.
master_point : (x, y, z), optional
Explicit master coordinates. If None, the resolver
picks the master node by proximity within tolerance.
slave_entities : list of (dim, tag), optional
Restrict the slave node search to specific entities.
tolerance : float, default 1e-6
Proximity tolerance for master-node detection.
name : str, optional
Friendly name.
Returns¶
RigidLinkDef
Raises¶
KeyError
If either label is not in g.parts.
See Also¶
kinematic_coupling : Same idea, but lets you pick which DOFs to couple instead of the fixed beam/rod sets. node_to_surface : When the slave side has only translational DOFs (3-DOF solid nodes).
Examples¶
Lumped-mass arm at the top of a tower::
g.constraints.rigid_link(
"tower_top", "lumped_mass",
link_type="beam",
master_point=(0, 0, 30.0),
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 | |
penalty ¶
penalty(master_label, slave_label, *, stiffness=10000000000.0, dofs=None, tolerance=1e-06, name=None) -> PenaltyDef
Soft-spring (penalty) coupling between co-located node pairs.
Numerically approximates :meth:equal_dof as
stiffness → ∞. The resolver still requires master and
slave nodes to be co-located within tolerance, but
downstream the constraint is enforced by inserting a stiff
spring element between each pair instead of a hard MPC.
Use this when:
- The hard
equal_dofconstraint causes the constraint-handler to ill-condition the reduced stiffness matrix (typical with mismatched DOF spaces). - You want a tunable interface compliance — e.g. a soft contact at a bearing pad.
Parameters¶
master_label : str
Part label of the master side.
slave_label : str
Part label of the slave side.
stiffness : float, default 1e10
Penalty spring stiffness in force/length units. Pick
~3–6 orders of magnitude above the stiffest neighbouring
element diagonal — overshoot causes ill-conditioning,
undershoot leaks displacement.
dofs : list[int], optional
1-based DOFs to penalise. None = all available.
tolerance : float, default 1e-6
Spatial co-location tolerance.
name : str, optional
Friendly name.
Returns¶
PenaltyDef
Raises¶
KeyError
If either label is not in g.parts.
See Also¶
equal_dof : Hard MPC equivalent (no tunable stiffness).
Source code in src/apeGmsh/core/ConstraintsComposite.py
rigid_diaphragm ¶
rigid_diaphragm(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), plane_normal=(0.0, 0.0, 1.0), constrained_dofs=None, plane_tolerance=1.0, name=None) -> RigidDiaphragmDef
In-plane rigid floor — slaves follow master in the diaphragm plane.
Classic use: each floor of a multi-storey building. All
slab nodes within plane_tolerance of the diaphragm
plane share in-plane translation and rotation about the
out-of-plane axis with the master node, while remaining
free in the out-of-plane direction.
Resolution emits a single
:class:~apeGmsh.solvers.Constraints.NodeGroupRecord
with one master and many slaves. Downstream this becomes
ops.rigidDiaphragm(perpDirn, master, *slaves).
Parameters¶
master_label : str
Part, physical-group, or label name that contains (or whose
proximity will
select) the master node — typically a centre-of-mass
point.
slave_label : str
Part, physical-group, or label name whose nodes are
gathered into the diaphragm.
master_point : (x, y, z), default (0, 0, 0)
Coordinates of the master node. Used to disambiguate
when the master part has more than one node.
plane_normal : (nx, ny, nz), default (0, 0, 1)
Unit normal to the diaphragm plane. (0, 0, 1) is a
horizontal floor; (0, 1, 0) is a vertical wall, etc.
constrained_dofs : list[int], optional
DOFs slaved to the master. Default for a horizontal
floor (Z up) is [1, 2, 6] — ux, uy, rz. For a
vertical wall use [1, 3, 5].
plane_tolerance : float, default 1.0
Perpendicular distance (in model units) from the
diaphragm plane within which a slave node is
collected. Unit-sensitive — set this to a fraction
of slab thickness.
name : str, optional
Friendly name.
Returns¶
RigidDiaphragmDef
Raises¶
KeyError
If either label is not in g.parts.
See Also¶
kinematic_coupling : When you need a different DOF subset
than [1, 2, 6] and don't need plane filtering.
rigid_body : When all 6 DOFs must follow the master.
Examples¶
A horizontal slab at z = 3.0 m::
g.constraints.rigid_diaphragm(
"slab", "slab_master",
master_point=(2.5, 2.5, 3.0),
plane_normal=(0, 0, 1),
constrained_dofs=[1, 2, 6],
plane_tolerance=0.05,
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 | |
rigid_body ¶
rigid_body(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), as_element=False, mass=None, omega=None, name=None) -> RigidBodyDef
Fully rigid cluster — every slave DOF follows the master.
All six DOFs (ux, uy, uz, rx, ry, rz) of every node in
the slave part follow the master node through a rigid
transformation::
u_s = u_m + θ_m × (x_s − x_m)
θ_s = θ_m
Use this for genuinely rigid pieces (bearing blocks, lumped rigid masses) where the slave region must not deform.
Parameters¶
master_label : str
Part, physical-group, or label name that contains (or whose
proximity selects)
the master node.
slave_label : str
Part, physical-group, or label name whose nodes are
gathered into the rigid
body.
master_point : (x, y, z), default (0, 0, 0)
Coordinates of the master node.
as_element : bool, default False
Emit the fork element LadrunoRigidBody over the whole node
set {master, *slaves} (class tag 33015, 3D only)
instead of the default rigidLink chain. The element gives
a private centre-of-mass node, condensed body mass, and
explicit-dynamics support that the rigidLink chain cannot.
Fork-only: deck emission works on any build; running needs the
Ladruno fork.
mass : float or None
Total body mass for as_element (-mass); None
condenses it from the slaves' nodal mass. Only valid with
as_element=True.
omega : (wx, wy, wz) or None
Initial body-frame angular velocity for as_element
(-omega) — an explicit-dynamics initial condition (the body
spins from t=0). Only valid with as_element=True.
name : str, optional
Friendly name.
Returns¶
RigidBodyDef
Raises¶
KeyError
If either label is not in g.parts.
ValueError
If mass/omega is set without as_element=True, or
mass < 0.
See Also¶
kinematic_coupling : Same topology but with a user-selectable DOF subset. rigid_diaphragm : In-plane variant with plane filtering.
Source code in src/apeGmsh/core/ConstraintsComposite.py
2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 | |
kinematic_coupling ¶
kinematic_coupling(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), dofs=None, k=None, k_alpha=None, host=None, kr=None, enforce='penalty', bipenalty_dtcr=None, bipenalty_wcap=None, absolute=False, name=None) -> KinematicCouplingDef
RBE2 / kinematic coupling — a reference node rigidly drives a node set.
Emits the Ladruno-fork element LadrunoKinematicCoupling (class
tag 33012): a penalty rigid-body driver with the correct moment-arm
transport u_i = u_R + θ_R × d_i, so an offset reference is
coupled rigidly. This replaces the previous equalDOF-per-slave
expansion, which ignored the lever arm (correct only for coincident
nodes). Fork-only: the deck emits on any build, but running it
needs the Ladruno fork — stock OpenSees fails loud at the element
line (it does not know class tag 33012).
Reach for this when the region must move as a rigid body (a loading platen, a rigid offset / connection block, a rigid diaphragm over an arbitrary node set). To introduce a load at a point while the region stays flexible, use a distributing coupling (RBE3) instead.
Parameters¶
master_label : str | DecoupledNodeDef
Part, physical-group, or label name that owns the reference
(master) node — or a g.decouple_node handle / its
label= (ADR 0049 OQ2). The reference node must carry the
rotational DOFs (ndf 6 in 3D / 3 in 2D); the fork refuses a
too-small reference at setDomain.
slave_label : str | DecoupledNodeDef
Part, physical-group, or label name whose nodes are slaved
(may mix 3- and 6-DOF nodes). A g.decouple_node handle
is accepted the same way as master_label.
master_point : (x, y, z), default (0, 0, 0)
Coordinates of the reference node when the master role
resolves to a multi-node set (nearest-in-set). Ignored
when the role is a single decoupled node — that node's own
coordinates are used.
dofs : list[int], optional
1-based dependent components tied on each slave (-dof).
None (default) ties every DOF the slave has — the right
choice for a mixed 3/6-DOF slave set; pass an explicit list to
restrict, e.g. [1, 2, 3] for translations only or
[3] for a vertical-only follower.
k : float | "auto", optional
Translational penalty stiffness (-k). None ⇒ the fork
default (1e12). "auto" scales it off a representative
host element's stiffness diagonal
(K_t = k_alpha · max|K_host(i,i)|) — requires host.
k_alpha : float, optional
Multiplier for k="auto" (-kAlpha; fork default 1e3).
Only valid together with k="auto".
host : int, optional
Representative host element for k="auto" / bipenalty_wcap
(-host) as a FEM element id — the bridge translates it to
the emitted OpenSees tag at emit time. Pick a typical element of
the coupled part (e.g. one touching the slave surface).
kr : float, optional
Rotational penalty stiffness (-kr). None ⇒ fork-derived
K_t·ℓ² (keeps the translation/rotation conditioning matched).
enforce : "penalty" | "al", default "penalty"
"al" = augmented Lagrangian (near-exact rigidity at moderate
k; implicit only — cannot combine with the bipenalty
knobs).
bipenalty_dtcr : float, optional
Explicit-dynamics critical-time-step target (-bipenalty
-dtcr); lumps a penalty mass on any massless tied DOF so the
stiff tie doesn't collapse the explicit step. None ⇒ off
(the master is usually a massed node).
bipenalty_wcap : float, optional
Bipenalty via the host frequency (-bipenalty -wcap):
m_p = K_t/(β·ω_host)² with β = this value — sets the
penalty-mode frequency at β·ω_host instead of a hard dt
budget. Requires host; mutually exclusive with
bipenalty_dtcr.
absolute : bool, default False
Keep the absolute tie (-absolute) — skip the default
g0 stress-free birth (a coupling added to a deformed model
is otherwise born force-free).
name : str, optional
Friendly name (also the stage-claim key for s.kinematic_coupling).
Returns¶
KinematicCouplingDef
Raises¶
KeyError
If either label is not in g.parts / PGs / labels and is not
a labelled g.decouple_node.
ValueError
On an invalid knob (enforce not in {penalty, al};
non-positive k/kr/bipenalty_dtcr/bipenalty_wcap;
al + a bipenalty knob; k="auto" or bipenalty_wcap
without host; k_alpha without k="auto"; a dangling
host no knob consumes; bipenalty_dtcr + bipenalty_wcap);
a g.decouple_node handle without label=; a label that
names both a decoupled node and a Part/PG; an ambiguous
duplicate decoupled label.
Source code in src/apeGmsh/core/ConstraintsComposite.py
2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 | |
tie ¶
tie(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, stiffness='auto', stiffness_p=None, rotational=False, pressure=False, enforce='penalty', control=None, method='collocation', outward=None, name=None) -> TieDef
Non-matching mesh tie via shape-function interpolation.
For each slave node, the resolver finds the closest master element face, projects the node onto it, and constrains its DOFs to the master corner DOFs through that face's shape functions::
u_slave = Σ N_i(ξ, η) · u_master_i
where (ξ, η) are the projected parametric coordinates
and N_i are the master face's shape functions (tri3,
quad4, tri6, quad8 supported). This is what Abaqus
*TIE does — it preserves displacement continuity across
non-matching meshes.
Resolution emits one
:class:~apeGmsh.mesh.records.InterpolationRecord per
successfully projected slave node. The emitted OpenSees coupling
depends on enforce= (see below): a penalty
ASDEmbeddedNodeElement (default), the fork
LadrunoEmbeddedNode, or an exact equationConstraint
(EQ_Constraint).
Parameters¶
master_label : str
Part, physical-group, or label name of the master surface
(the side whose mesh
will provide the shape functions).
slave_label : str
Part, physical-group, or label name of the slave surface
(whose nodes are
projected).
master_entities : list of (dim, tag), optional
Restrict the master surface to specific Gmsh
entities. Strongly recommended when the master
part has more than one face.
slave_entities : list of (dim, tag), optional
Restrict the slave surface to specific entities.
dofs : list[int], optional
DOFs to tie. None (default) ties all translational
DOFs available — typically [1, 2, 3].
tolerance : float, default 1.0
Maximum allowed projection distance from a slave node
to the master surface. Slave nodes farther than this
are skipped with a warning (an all-skipped tie raises) —
set generously if the two meshes have a small geometric
gap, but not so large that the wrong face is selected.
Unit-sensitive.
stiffness : float or "auto", default "auto"
Penalty stiffness K of the emitted
ASDEmbeddedNodeElement (penalty routes only; ignored by
"equation"). "auto" resolves at emit from the host
material: K = α·E_host·L_char (α = 1e3, E from the
element's material, L from the master-face size) — a few
orders above the host element stiffness, which is all the
penalty needs. A numeric value is unit-dependent and must
be calibrated against a known solution: the pre-slice-B
default 1e18 (the OpenSees C++ default) destroys the
conditioning in N/mm/MPa (E ≈ 2e5) and Newton stalls, while
1e10–1e12 converge with sub-percent stiffness
error; emit still warns when a record carries 1e18.
stiffness_p : float, optional
Separate rotational/pressure penalty (-KP); None ⇒
the element falls back to K. Same unit caveat.
rotational, pressure : bool, default False
Extend the coupling to rotational / pressure DOFs
(-rot / -p); penalty routes only.
enforce : {"penalty", "penalty_al", "equation"}, default "penalty"
Coupling route (ADR 0068). "penalty" →
ASDEmbeddedNodeElement penalty element (tunable K,
handler-independent). "equation" → exact
equationConstraint (EQ_Constraint), translations only,
enforced by the Lagrange (implicit) / LadrunoProjection
(explicit, Δt-neutral) handler — auto-selected at emit, and
penalty-only knobs (rotational/pressure/stiffness_p)
are rejected. "penalty_al" → fork LadrunoEmbeddedNode
(penalty + augmented-Lagrange + bipenalty, translations only),
configured via control= (see below).
control : CouplingControl, optional
LadrunoEmbeddedNode penalty/AL/bipenalty knobs — only valid with
enforce="penalty_al" (reuses the RBE2/RBE3
:class:CouplingControl: -k/-kAlpha/-host/
-enforce al/-bipenalty/-absolute). None ⇒ the
fork element's own defaults.
method : {"collocation", "mortar"}, default "collocation"
Weight-computation method (ADR 0086). "collocation" is
the classic node-to-face projection above. "mortar"
integrates the interface over the slave/master facet
overlaps with a dual (biorthogonal) slave basis, so
neither side's interpolation order is imposed on the
other — the fix for order-mismatched interfaces (e.g.
hex20 faces tied onto hex8 faces, where collocation
over-constrains the quadratic side). Requires
enforce="equation" (v1), works on composed assemblies
(chain phase), and is fail-loud end to end: a flat,
coincident, convex interface is required and every
degenerate case raises MortarTieError — a mortar tie
never silently resolves to nothing. tolerance becomes
the out-of-plane coincidence tolerance. Interface edges must
be straight (every midside node at its edge midpoint) and no
master facet may overlap another — both are hard errors,
because the kernel integrates on the corner polygon and its
coverage check counts multiplicity. tri6 SLAVE facets are
refused (dual-basis degeneracy) — swap the sides or use
collocation.
outward : (ox, oy, oz), optional
method="mortar" only: interface-plane normal override.
Normally derived from master facet winding; needed only
when the winding sum cancels (the kernel raises naming
this knob — there is no silent zero-force path).
name : str, optional
Friendly name.
Returns¶
TieDef
Raises¶
KeyError
If either label is not in g.parts.
See Also¶
equal_dof : Conformal-mesh equivalent (no interpolation).
tied_contact : Bidirectional surface-to-surface tie.
mortar : Deprecated alias for a fork mortar mesh-tie
(contact(formulation="mortar", tie=True)).
Notes¶
Master/slave choice matters for accuracy. As a rule:
- The master should have the finer mesh (more shape functions to project onto).
- The slave should have the coarser mesh (fewer projection operations).
Examples¶
Shell-to-solid tie at a column-top interface::
g.constraints.tie(
"shell_floor", "solid_column",
master_entities=[(2, 17)], # column top face
slave_entities=[(2, 41)], # shell bottom face
tolerance=5.0, # mm gap
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 | |
distributing_coupling ¶
distributing_coupling(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), weighting='uniform', k=None, k_alpha=None, host=None, kr=None, enforce='penalty', bipenalty_dtcr=None, bipenalty_wcap=None, absolute=False, name=None) -> DistributingCouplingDef
RBE3 / distributing coupling — distribute a load at a reference point over a node set while the set stays flexible.
Emits the Ladruno-fork element LadrunoDistributingCoupling
(class tag 33011): the reference (dependent) node R is the
weighted-average rigid-body fit of the independent set, and a
force/moment at R is distributed to the set as a
statically-equivalent pattern (Σ Fᵢ = F, Σ rᵢ × Fᵢ = M)
adding no stiffness to the independents. This is the proper
RBE3 — it replaces the prior NotImplementedError stub (whose
predecessor emitted a mechanically-wrong kinematic mean). It is
the flexible counterpart of :meth:kinematic_coupling (RBE2,
which holds the set rigid). Fork-only: the deck emits on any
build, but running it needs the Ladruno fork — stock OpenSees
fails loud at the element line (it does not know class tag 33011).
Reach for this to introduce/transmit a load or BC at a point
while the region stays flexible (a column base on a footing, an
actuator head on a face, a beam/shell moment into a solid face);
use :meth:kinematic_coupling (RBE2) when the region must move as
a rigid body, or :meth:tie for a compatible face interpolation.
Parameters¶
master_label : str | DecoupledNodeDef
Part, physical-group, or label name owning the reference
(dependent) node R — or a g.decouple_node handle / its
label= (ADR 0049 OQ2). R must carry the rotational DOFs
(ndf 6 in 3D / 3 in 2D) to transmit a moment; the fork refuses
a too-small reference at setDomain.
slave_label : str | DecoupledNodeDef
Part, physical-group, or label name whose nodes form the
independent set (translations-only is fine — no rotational
stiffness is injected). A g.decouple_node handle is
accepted the same way as master_label.
master_point : (x, y, z), default (0, 0, 0)
Coordinates of the reference node R when the master role
resolves to a multi-node set (nearest-in-set). Ignored
when the role is a single decoupled node — that node's own
coordinates are used.
weighting : "uniform" | "area", default "uniform"
"uniform" ⇒ equal weights (-w omitted, the fork
element's default). "area" ⇒ apeGmsh computes each
independent node's tributary area over the slave
surface (each face's area split equally among its nodes —
the same lumping model as g.loads surface-tributary
resolution) and emits -w w1..wN, so a force at R
distributes like a uniform traction on the surface.
Requires the slave label (or slave_entities) to resolve
to meshed surface faces; an independent node on no slave
face fails loud.
k : float | "auto", optional
Translational penalty stiffness (-k). None ⇒ the fork
default (1e12). "auto" scales it off a representative
host element's stiffness diagonal
(K_t = k_alpha · max|K_host(i,i)|) — requires host.
Note the force distribution is exact for any penalty (the
RBE3 property); k only relaxes the kinematic fit of the
reference node.
k_alpha : float, optional
Multiplier for k="auto" (-kAlpha; fork default 1e3).
Only valid together with k="auto".
host : int, optional
Representative host element for k="auto" / bipenalty_wcap
(-host) as a FEM element id — the bridge translates it to
the emitted OpenSees tag at emit time. RBE3 has no single host
by construction; name ONE typical element among the independents'
parents — it is read ONLY to scale the penalties.
kr : float, optional
Rotational penalty stiffness (-kr). None ⇒ fork-derived.
enforce : "penalty" | "al", default "penalty"
"al" = augmented Lagrangian — recovers a near-exact weighted
fit of R at moderate k (implicit only; cannot combine
with the bipenalty knobs).
bipenalty_dtcr : float, optional
Explicit-dynamics critical-time-step target (-bipenalty
-dtcr). The reference node is massless by construction, so
an explicit run needs this (or it has a zero stable step).
None ⇒ off.
bipenalty_wcap : float, optional
Bipenalty via the host frequency (-bipenalty -wcap):
m_p = K_t/(β·ω_host)² with β = this value. Requires
host; mutually exclusive with bipenalty_dtcr.
absolute : bool, default False
Keep the absolute tie (-absolute) — skip the default
g0 stress-free birth.
name : str, optional
Friendly name (also the stage-claim key for s.distributing).
Returns¶
DistributingCouplingDef
Raises¶
ValueError
On an invalid knob (enforce not in {penalty, al};
non-positive k/kr/bipenalty_dtcr/bipenalty_wcap;
al + a bipenalty knob; k="auto" or bipenalty_wcap
without host; k_alpha without k="auto"; a dangling
host no knob consumes; bipenalty_dtcr + bipenalty_wcap;
weighting not in {uniform, area}); a g.decouple_node
handle without label=; a label that names both a
decoupled node and a Part/PG; an ambiguous duplicate
decoupled label.
Source code in src/apeGmsh/core/ConstraintsComposite.py
2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 | |
embedded ¶
embedded(host_label, embedded_label, *, tolerance=1.0, host_entities=None, embedded_entities=None, stiffness='auto', stiffness_p=None, rotational=False, pressure=False, host_coupling='linear', name=None) -> EmbeddedDef
Embed lower-dimensional elements inside a host volume or surface.
Each node of the embedded part is constrained to the displacement field of the host element it falls inside via host shape functions. Used for rebar in concrete, stiffeners in shells, fibres in composite hosts, etc.
Supported host element types:
- 3-D host: tet4 (etype 4), tet10 (11), hex8 (5), hex20 (17), prism6 (6), prism15 (18), pyramid5 (7), pyramid13 (14).
- 2-D host: tri3 / CST (etype 2), tri6 / LST (9), quad4 (3), quad8 (16), quad9 (10).
Non-simplex and higher-order hosts are decomposed to
linear sub-tris / sub-tets using corner nodes only (hex8
→ 6 Kuhn tets; prism6 → 3 tets; pyramid5 → 2 tets;
quad4 → 2 tris on the (0,2) diagonal; tri6 / tet10 /
hex20 / quad8 / quad9 / prism15 / pyramid13 →
corner-only). The embedded coupling is therefore
linear regardless of the host's native interpolation
order — see host_coupling and :class:EmbeddedDef
for the full contract. A UserWarning fires once per
(host type, entity) the first time a midside-bearing host
is decomposed.
The resolver automatically drops embedded nodes that coincide with host element corners, since those are already rigidly attached through shared connectivity.
Parameters¶
host_label : str
Part label whose host elements form the embedding
field. Stored internally as master_label.
embedded_label : str
Part label whose nodes are embedded. Stored as
slave_label. (Label validation is bypassed for
EmbeddedDef — these labels may also be physical
group names if no part registry is in use.)
tolerance : float, default 1.0
Maximum dimensionless barycentric excess allowed when
locating an embedded node inside a host sub-element.
0.0 means strictly inside; the default 1.0
preserves pre-Phase-2 permissive behaviour. See
:class:EmbeddedDef for the fail-loud gate.
stiffness : float or "auto", default "auto"
Penalty stiffness K of the emitted
ASDEmbeddedNodeElement. "auto" resolves at emit
from the host material (K = α·E_host·L_char, α = 1e3).
A numeric value is unit-dependent and must be calibrated
against a known solution — the pre-slice-B default
1e18 (the OpenSees C++ default) stalls Newton in
N/mm/MPa models while 1e10–1e12 converge; emit
still warns when a record carries 1e18. (Unlike
:meth:tie, embedded has no enforce="equation"
escape hatch — the fork g.embed is the conditioned
alternative.)
stiffness_p : float, optional
Separate rotational/pressure penalty (-KP); None ⇒
falls back to K. Same unit caveat.
host_entities, embedded_entities : list of (dim, tag), optional
Restrict the host / embedded sides to specific Gmsh
entities. When omitted the whole label is used.
host_coupling : {"linear"}, default "linear"
Reserved keyword pinning the coupling kinematics. Only
"linear" is currently accepted (coupling to 3 or 4
corner nodes via barycentric shape functions, matching
ASDEmbeddedNodeElement). Reserved so that future
higher-order options ("trilinear", "biquadratic")
can be added without breaking old models.
name : str, optional
Friendly name.
Returns¶
EmbeddedDef
Notes¶
Emitted downstream as ASDEmbeddedNodeElement. The
host_label / embedded_label argument names mirror
Abaqus's *EMBEDDED ELEMENT vocabulary; internally the
composite still stores them as master/slave for
consistency with the rest of the constraint records.
Examples¶
Rebar curve embedded inside a concrete tet mesh::
g.constraints.embedded(
host_label="concrete_block",
embedded_label="rebar_curve",
tolerance=2.0, # mm
)
Same rebar embedded into a hex8 mesh (each rebar node is located inside one of the 6 Kuhn sub-tets of the enclosing hex and coupled to that sub-tet's 4 corners)::
g.constraints.embedded(
host_label="concrete_block_hex",
embedded_label="rebar_curve",
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 | |
node_to_surface ¶
6-DOF node to 3-DOF surface coupling via phantom nodes.
Creates a single constraint that aggregates all surface entities in slave. Shared-edge mesh nodes are deduplicated so each original slave node gets exactly one phantom.
Parameters¶
master : int, str, or (dim, tag) The 6-DOF reference node. slave : int, str, or (dim, tag) The surface(s) to couple. If it resolves to multiple surface entities, they are combined into a single constraint and slave nodes are deduplicated.
Returns¶
NodeToSurfaceDef A single def covering all resolved surface entities.
Source code in src/apeGmsh/core/ConstraintsComposite.py
node_to_surface_spring ¶
Spring-based variant of :meth:node_to_surface.
Identical topology and call signature, but the master → phantom
links are tagged for downstream emission as stiff
elasticBeamColumn elements instead of kinematic
rigidLink('beam', ...) constraints. Use this variant when
the master carries free rotational DOFs (fork support on a
solid end face) that receive direct moment loading — the
constraint-based variant of node_to_surface can produce an
ill-conditioned reduced stiffness matrix in that case because
the master rotation DOFs get stiffness only through the
kinematic constraint back-propagation, with nothing attaching
directly to them.
See :class:~apeGmsh.solvers.Constraints.NodeToSurfaceSpringDef
for the full rationale.
Emission in OpenSees::
# Each master → phantom link becomes a stiff beam element
next_eid = max_tet_eid + 1
for master, slaves in fem.nodes.constraints.stiff_beam_groups():
for phantom in slaves:
ops.element(
'elasticBeamColumn', next_eid,
master, phantom,
A_big, E, I_big, I_big, J_big, transf_tag,
)
next_eid += 1
# equalDOFs are unchanged from the normal variant
for pair in fem.nodes.constraints.equal_dofs():
ops.equalDOF(
pair.master_node, pair.slave_node, *pair.dofs)
Parameters¶
Same as :meth:node_to_surface.
Returns¶
NodeToSurfaceSpringDef
Source code in src/apeGmsh/core/ConstraintsComposite.py
2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 | |
tied_contact ¶
tied_contact(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, stiffness='auto', stiffness_p=None, rotational=False, pressure=False, enforce='penalty', control=None, name=None) -> TiedContactDef
Full surface-to-surface tie (slave conforms to master).
Every slave-surface node is tied to the master surface via shape-function interpolation. One-directional: an earlier bidirectional variant (also projecting master nodes onto slave faces) was removed because it produced cyclic / over-determined MPCs the constraint handler cannot satisfy. Pick the finer mesh as the master.
enforce= selects the coupling route exactly as in :meth:tie
("penalty" default → ASDEmbeddedNodeElement; "equation"
→ exact equationConstraint; "penalty_al" →
LadrunoEmbeddedNode).
Resolution emits
:class:~apeGmsh.solvers.Constraints.SurfaceCouplingRecord
objects on fem.elements.constraints.
Parameters¶
master_label : str
Part label of the first surface.
slave_label : str
Part label of the second surface.
master_entities, slave_entities : list of (dim, tag), optional
Restrict each side to specific Gmsh entities.
dofs : list[int], optional
DOFs to tie. None = all translational.
tolerance : float, default 1.0
Maximum projection distance. Unit-sensitive.
stiffness : float or "auto", default "auto"
Penalty stiffness K of each emitted
ASDEmbeddedNodeElement (penalty routes only).
"auto" resolves at emit from the host material — see
:meth:tie for the formula and the numeric-value caveat
(a fixed number is unit-dependent; the old 1e18 default
stalls Newton in N/mm/MPa and still warns at emit).
stiffness_p : float, optional
Separate rotational/pressure penalty (-KP); None ⇒
falls back to K.
enforce : {"penalty", "penalty_al", "equation"}, default "penalty"
Coupling route (ADR 0068) — same semantics as :meth:tie;
"equation" emits exact equationConstraint rows and
rejects the penalty-only knobs.
name : str, optional
Friendly name.
Returns¶
TiedContactDef
Raises¶
KeyError
If either label is not in g.parts.
See Also¶
tie : One-directional tie (slave-projected only).
mortar : Deprecated alias for a fork mortar mesh-tie
(contact(formulation="mortar", tie=True)).
Source code in src/apeGmsh/core/ConstraintsComposite.py
2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 | |
mortar ¶
mortar(master_label, slave_label, *, eps_n='auto', outward, master_entities=None, slave_entities=None, name=None) -> ContactDef
Deprecated alias for a fork segment-to-segment mortar mesh-tie.
.. deprecated::
mortar() is a thin convenience alias for
:meth:contact with formulation="mortar", tie=True; call
that directly. It emits a :class:DeprecationWarning.
Delegates to the fork's ALM-penalty mortar mesh-tie (ADR 0073):
a permanent segment-to-segment bond (the zero-gap limit — the full
3-vector residual driven to zero, no friction). Returns a
:class:ContactDef resolving to fem.elements.contacts (the fork
contactSurface + contact -mortar -tie pair + the
LadrunoContact handler), not the old MortarDef /
Lagrange-multiplier path. Fork-only at run time; deck emission works on
any build.
This is a breaking change from the prior stub (which raised
NotImplementedError): the return type is now ContactDef, the
semantics are an ALM penalty contact-tie (not a Lagrange-multiplier
operator), and the never-functional dofs / integration_order
parameters are removed (a permanent penalty tie bonds the full
3-vector with a single penalty and has no DOF-subset or quadrature-order
knob — passing them now raises TypeError).
Parameters¶
master_label, slave_label : str
The two surface PG / part labels to bond. Both resolve to faceted
surfaces (master -master, slave -slave-segments); pick the
finer mesh as whichever side you trust more — the fork integrates
the overlap either way.
eps_n : float | "auto", default "auto"
ALM normal penalty for the tie ("auto" sizes it from the solid).
outward : (float, float, float)
Required. The master surface normal toward the slave. A tie
interface is coincident-flat, so without an explicit sign the fork's
per-pair reference is in-plane and gate H2 silently drops every pair
to zero force (the tie would bond nothing). See :class:ContactDef.
master_entities, slave_entities : list of (dim, tag), optional
Restrict each side to specific Gmsh entities.
name : str, optional
Friendly name (round-trips into the emitted deck comment).
Returns¶
ContactDef
See Also¶
contact : The canonical fork contact / mortar-tie generator. tied_contact : Collocation-based non-matching tie (no fork required).
Source code in src/apeGmsh/core/ConstraintsComposite.py
2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 | |
validate_pre_mesh ¶
No-op: constraints validate targets eagerly at _add_def.
Present so :meth:Mesh.generate can invoke validate_pre_mesh
on all three composites uniformly.
Source code in src/apeGmsh/core/ConstraintsComposite.py
summary ¶
DataFrame of the declared constraint intent — one row per def.
Columns: kind, name, master, slave, params. params is a
short stringified view of the kind-specific fields (dofs,
tolerance, etc.).
Source code in src/apeGmsh/core/ConstraintsComposite.py
NormalLaw
dataclass
¶
Declarative per-area normal-direction law (ADR 0093 D1).
A flat-scalar description of the interface's normal constitutive
response — stored on :class:InterfaceRecord (h5-serializable),
translated to a typed uniaxial material only at emit time in
build.py, scaled per pair by A_trib. Kernel data — imports
nothing from apeGmsh.opensees (INV-4). The sign convention is
owned by the emit-time translation, never the caller (INV-1): the
fields here are positive-magnitude physical quantities.
Attributes¶
kind
"ent" — unilateral, compression-only
(ENT(E = k_per_area * A_trib)).
"epp_gap" — elastic-perfectly-plastic with a gap
(ElasticPPGap(E = k_per_area * A_trib,
Fy = -tau_b_n * A_trib, gap)); requires tau_b_n and gap.
"elastic" — bilateral elastic (Elastic(E = k_per_area *
A_trib)); the acceptance battery's bonded-limit law (ADR 0093
"Alternatives rejected").
k_per_area
Stiffness per unit tributary area ([F/L**3]); required for
every kind.
tau_b_n
Normal bond strength, a positive magnitude (epp_gap only);
None for ent / elastic.
gap
Initial gap, <= 0 (epp_gap only); None for ent /
elastic.
TangentialLaw
dataclass
¶
Declarative per-area tangential-direction law (ADR 0093 D1).
The tangential sibling of :class:NormalLaw — see its docstring for
the storage/translation/layering contract.
Attributes¶
kind
"epp" — elastic-perfectly-plastic slip cap
(ElasticPP(E = k_per_area * A_trib, epsyP = tau_b /
k_per_area)); requires tau_b. A_trib cancels in the
strain — the physical yield force tau_b * A_trib is the
emergent product E * epsyP.
"elastic" — bilateral elastic (Elastic(E = k_per_area *
A_trib)); the acceptance battery's bonded-limit law.
k_per_area
Stiffness per unit tributary area ([F/L**3]); required for
every kind.
tau_b
Tangential bond strength, a positive magnitude (epp only);
None for elastic.
FEMData ¶
FEMData(nodes: NodeComposite, elements: ElementComposite, info: MeshInfo, mesh_selection: 'MeshSelectionStore | None' = None, composed_from: 'ComposeSet | tuple[ComposeRecord, ...] | None' = None)
Solver-ready FEM mesh broker.
Organized by what the user needs::
fem.nodes → NodeComposite
fem.elements → ElementComposite
fem.info → MeshInfo
fem.inspect → InspectComposite
Source code in src/apeGmsh/mesh/FEMData.py
1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 | |
snapshot_id
property
¶
Deterministic content hash identifying this FEMData snapshot.
Computed once and cached. Used by the Results module to bind
result files to their producing geometry — see
internal_docs/Results_architecture.md § "FEMData embedding
& binding".
from_gmsh
classmethod
¶
Extract FEMData from a live Gmsh session.
Parameters¶
dim : int or None
Element dimension to extract. None = all dims.
session : apeGmsh session, optional
When provided, auto-resolves constraints, loads, masses.
ndf : int
DOFs per node for load/mass vector padding.
remove_orphans : bool
If True, remove mesh nodes not connected to any element.
Source code in src/apeGmsh/mesh/FEMData.py
from_msh
classmethod
¶
Load FEMData from an external .msh file.
Source code in src/apeGmsh/mesh/FEMData.py
from_h5
classmethod
¶
Load a :class:FEMData snapshot from a root-layout model.h5.
Inverse of :meth:to_h5. Reads the seven neutral-zone groups
plus /meta and rebuilds nodes, elements (per type),
physical groups, labels, mesh selections, constraints, loads,
and masses — everything the writer round-trips.
Parameters¶
path : str
Path to a model.h5 written by :meth:to_h5, g.save(),
or apeSees(fem).h5(path).
root : str, default "/"
Sub-group root inside path to read from. Default
rehydrates from the file root (standalone model.h5
shape). Per ADR 0020 (Phase 4 cleanup), composed
results.h5 files carry the same rich layout under
/model/; pass root="/model" to rehydrate from a
composed file. Backcompat: root="/" produces
byte-identical behaviour to the pre-refactor reader.
Use this to resume a session-saved model in a later script::
# script 1 — build & save
with apeGmsh(model_name="m", save_to="m.h5") as g:
...
# script 2 — analyse
fem = FEMData.from_h5("m.h5")
apeSees(fem).h5("m.h5") # enrich with /opensees/...
Source code in src/apeGmsh/mesh/FEMData.py
to_native_h5 ¶
Embed this FEMData into an open HDF5 group (/model/).
Used by NativeWriter to snapshot the geometry alongside
results. The reconstructed FEMData (via from_native_h5)
will produce the same snapshot_id — this is the linking
contract for Results.bind().
Phase 4 cleanup (ADR 0020): writes the rich neutral-zone
layout :func:write_fem_h5 produces at the file root, but
under group instead. The composed results.h5 thus
carries /model/meta, /model/nodes, /model/elements,
etc. — the same layout :func:read_fem_h5(path, root="/model")
rehydrates from. This eliminates the /opensees_archive/
zone that the previous lean embedding required to round-trip
the full :class:OpenSeesModel.
Source code in src/apeGmsh/mesh/FEMData.py
to_h5 ¶
Write a fresh model.h5 containing the neutral zone.
Phase 8.5 entry point: dumps everything the broker knows about
the model (nodes, elements per type, physical groups, labels,
constraints, loads, masses) into a root-level
model.h5. No /opensees/ content is emitted — absent
enrichment is the right "no solver loaded" signal.
Use apeSees(fem).h5(path) instead to get a fully enriched
file (neutral zone + /opensees/...).
Source code in src/apeGmsh/mesh/FEMData.py
from_native_h5
classmethod
¶
Reconstruct a FEMData from its embedded /model/ group.
Phase 4 cleanup (ADR 0020): production writers (:meth:to_native_h5
via :class:NativeWriter) embed the rich neutral zone — full
constraints, loads, masses, mesh selections and partitions
round-trip alongside nodes/elements/PGs. snapshot_id of
the rebuilt FEM matches the source's /meta/snapshot_id
attribute (the linking contract :class:Results.bind relies
on).
Source code in src/apeGmsh/mesh/FEMData.py
from_mpco_model
classmethod
¶
Synthesize a partial FEMData from an MPCO MODEL/ group.
Carries: nodes, elements (per OpenSees class tag), physical
groups derived from MPCO Regions (MODEL/SETS).
Missing vs. native:
- apeGmsh-specific labels
- Pre-mesh declarations (loads / masses / constraints)
- STKO named selection sets (those live in .cdata sidecars)
- Gmsh-style element type codes (uses negated class_tag instead)
snapshot_id will not match a native FEMData of the same
mesh — that's expected. Results.bind() will refuse such
mismatches.
Source code in src/apeGmsh/mesh/FEMData.py
from_ladruno_model
classmethod
¶
Synthesize a partial FEMData from a .ladruno MODEL/ group.
Sibling of :meth:from_mpco_model for the self-describing
.ladruno layout (element groups carry a CONNECTIVITY
dataset + BASIS attrs). Carries nodes, elements (per OpenSees
class tag), and physical groups from MODEL/SETS. Missing vs.
native: apeGmsh labels, pre-mesh declarations, selection-set
names. snapshot_id will not match a native FEMData of the
same mesh — expected.
Source code in src/apeGmsh/mesh/FEMData.py
with_constraint ¶
Return a new :class:FEMData with record appended.
Pure transform. self is unchanged. Dispatch is by record
type:
===================================== =================================
Record subclass Appended to
===================================== =================================
NodePairRecord nodes.constraints
NodeGroupRecord nodes.constraints
NodeToSurfaceRecord nodes.constraints
InterpolationRecord elements.constraints
SurfaceCouplingRecord elements.constraints
SPRecord nodes.sp
===================================== =================================
Routing an unknown record subclass raises TypeError — this
is a fail-loud contract because the compose engine needs every
record to land in a known broker bucket.
Source code in src/apeGmsh/mesh/FEMData.py
with_load ¶
Return a new :class:FEMData with record appended.
Pure transform. self is unchanged. Dispatch is by record
type:
===================================== =================================
Record subclass Appended to
===================================== =================================
NodalLoadRecord nodes.loads
ElementLoadRecord elements.loads
SPRecord nodes.sp
===================================== =================================
Source code in src/apeGmsh/mesh/FEMData.py
compose ¶
compose(source: 'str | Path', *, label: str, translate: tuple[float, float, float] = (0.0, 0.0, 0.0), rotate: tuple[float, float, float, float] | None = None, anchor: str | None = None, partition_rank: int | None = None, properties: 'dict | None' = None, compose_size_per_module: int | None = None, max_compose_depth: int | None = None) -> 'FEMData'
Return a new :class:FEMData extending this chain with a
composed module.
Pure transformation. self is unchanged. The returned
FEMData's composed_from chain is
self.composed_from + (new_record,) and every IMPORT-verdict
record from the source H5 surfaces on the result, namespaced
with label and offset into a non-overlapping tag window
per ADR 0038 §"Tag-offset scheme".
Geometry / mesh build-phase operations are not part of this
primitive — the merge runs entirely against the FEMData
broker; no live Gmsh state is touched. The compose API is
the canonical entry point for cross-session composition:
FEMData.from_h5(path).compose("module.h5", label="A").
Drift hazard: the returned FEMData is decoupled from any
live gmsh state on the producing session. Mutating the
session's mesh/PG/label/parts AFTER calling compose without
also re-extracting + re-applying the bundle drops the
composed module's records on the floor. The
:meth:apeGmsh.compose shim handles this via session-level
bundle-replay; if you call this primitive directly, replay
is your responsibility.
See :func:apeGmsh.mesh._compose.Compose.compose for the full
parameter contract; max_compose_depth is the only
Compose.compose parameter intentionally absent here
(depth checks come in Phase 3E.1).
Source code in src/apeGmsh/mesh/FEMData.py
2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 | |
compose_tree ¶
Derived nested-compose tree view of self.composed_from.
Reconstructs the nested-compose hierarchy from this FEMData's
flat composed_from chain (per PR #369's flat-graft
storage). Returns a tuple of root
:class:~apeGmsh.mesh._compose.ComposeTreeNode instances;
each root carries its :class:ComposeRecord plus any direct
children parsed from joined labels via the separator-
alternation rule (depth-1 ., depth-2 /, depth-3
., ...).
Empty tuple when self.composed_from is empty (an
uncomposed FEMData).
Source code in src/apeGmsh/mesh/FEMData.py
with_mass ¶
Return a new :class:FEMData with record appended to
nodes.masses.
Pure transform. self is unchanged. Only
:class:~apeGmsh._kernel.records._masses.MassRecord is
accepted; anything else raises TypeError.
Source code in src/apeGmsh/mesh/FEMData.py
assess ¶
Compile a v1 :class:~apeGmsh.assess.AssessmentReport (ADR 0094).
figures=True writes one undeformed mesh still via
:meth:render. Default is False.
Source code in src/apeGmsh/mesh/FEMData.py
render ¶
render(path: 'str | Path', *, camera: 'str | None' = None, window_size: tuple[int, int] = (1280, 720)) -> 'Path | None'
Write one undeformed mesh still (ADR 0094 S1).
VTK offscreen — no Qt window, no event loop. Returns the
written :class:~pathlib.Path, or None (and prints the
[skip viewer] notice) under APEGMSH_SKIP_VIEWER=1 or
with no GL.
camera= defaults to xy for a planar model, iso
otherwise (ADR 0094 Amendment 3); pass it explicitly to
override.
Source code in src/apeGmsh/mesh/FEMData.py
viewer ¶
Open a non-interactive mesh viewer from this snapshot.
Currently disabled — the legacy Results.from_fem(...).viewer()
path was removed when the Results module was rebuilt. For a
headless mesh still use :meth:render. For an interactive
mesh window, use g.mesh.viewer().
Source code in src/apeGmsh/mesh/FEMData.py
MeshInfo ¶
Read-only summary of mesh statistics.
Accessed via fem.info.
Attributes¶
n_nodes : int n_elems : int bandwidth : int types : list[ElementTypeInfo] Element types present in the mesh.
Source code in src/apeGmsh/mesh/FEMData.py
summary ¶
One-line summary string.
Source code in src/apeGmsh/mesh/FEMData.py
PhysicalGroupSet ¶
Bases: NamedGroupSet
Snapshot of solver-facing physical groups.
Accessed via fem.nodes.physical / fem.elements.physical
(shared reference) and indirectly via
fem.nodes.select(pg="Base").
Source code in src/apeGmsh/mesh/_group_set.py
LabelSet ¶
Bases: NamedGroupSet
Snapshot of geometry-time labels (Tier 1).
Accessed via fem.nodes.labels / fem.elements.labels
(shared reference) and indirectly via
fem.nodes.select(label="col.web").
Source code in src/apeGmsh/mesh/_group_set.py
Algorithm2D ¶
Bases: IntEnum
2-D meshing algorithm selector (legacy IntEnum form).
Prefer passing a string name to
:meth:apeGmsh.mesh.Mesh._Generation.set_algorithm — see
:data:ALGORITHM_2D and :class:MeshAlgorithm2D for the canonical
names and the accepted aliases.
Algorithm3D ¶
Bases: IntEnum
3-D meshing algorithm selector (legacy IntEnum form).
MeshAlgorithm2D ¶
Canonical 2-D algorithm names as string constants (IDE autocomplete).
MeshAlgorithm3D ¶
Canonical 3-D algorithm names as string constants (IDE autocomplete).
OptimizeMethod ¶
Mesh optimisation method names — use with g.mesh.generation.optimize.
MshLoader ¶
Bases: _HasLogging
Load .msh files and produce solver-ready :class:FEMData.
Can be used standalone via the :meth:load classmethod, or as a
composite on a apeGmsh / Assembly session via g.loader.
Parameters¶
parent : _SessionBase or None
The owning session when used as a composite. None when
used standalone.
Source code in src/apeGmsh/mesh/MshLoader.py
load
classmethod
¶
Load a .msh file and return a :class:FEMData.
Manages its own Gmsh session internally — no apeGmsh
instance, no begin()/end() needed. Supports MSH2
and MSH4 formats.
Parameters¶
path : str or Path
Path to the .msh file.
dim : int
Element dimension to extract (1 = lines, 2 = tri/quad,
3 = tet/hex). Default is 2.
verbose : bool
Print a summary of what was loaded.
Returns¶
FEMData Self-contained solver-ready mesh data with physical groups, mesh statistics, and connectivity.
Example¶
::
from apeGmsh import MshLoader, Numberer
fem = MshLoader.load("bridge.msh", dim=2)
print(fem.info)
print(fem.physical.summary())
numb = Numberer(fem)
data = numb.renumber(method="rcm")
Source code in src/apeGmsh/mesh/MshLoader.py
from_msh ¶
Load a .msh file into the active Gmsh session.
The mesh is merged via gmsh.merge(), so all composites
(g.physical, g.plot, g.inspect, etc.) remain
usable afterwards.
Parameters¶
path : str or Path
Path to the .msh file.
dim : int
Element dimension to extract. Default is 2.
Returns¶
FEMData Self-contained solver-ready mesh data.
Raises¶
FileNotFoundError
If path does not exist.
RuntimeError
If no Gmsh session is active (call g.begin() first).
Example¶
::
g = apeGmsh(model_name="imported")
g.begin()
fem = g.loader.from_msh("model.msh", dim=2)
print(fem.physical.summary())
g.end()
Source code in src/apeGmsh/mesh/MshLoader.py
Results ¶
Results(reader: ResultsReader, *, fem: 'Optional[FEMData]' = None, stage_id: Optional[str] = None, path: Optional[Path] = None, model: 'OpenSeesModel', model_path: Optional[Path] = None)
Top-level results object. Returned by Results.from_* constructors.
Stage scoping¶
Instances may be unscoped (top-level — accesses any stage) or
scoped to one stage (returned by .stage(name),
.modes[i]). Scoped instances expose stage metadata as
properties (.kind, .time, .n_steps); mode-scoped
instances additionally expose .eigenvalue, .frequency_hz,
.period_s, .mode_index.
Source code in src/apeGmsh/results/Results.py
model
property
¶
The bound :class:OpenSeesModel broker.
Phase 8 (ADR 0020 INV-1) — always non-None on a constructed
:class:Results. The chain-forward handle from which the
OpenSeesModel and its embedded FEMData can be reached.
lineage
property
¶
Phase-6 lineage chain — git-style fem → model → results.
ADR 0021 defines a three-link hash chain fem_hash →
model_hash → results_hash where each layer's hash includes
its parent's hash (one-directional, tamper-evident).
Mismatches between stored and recomputed hashes surface as
[lineage] ... warnings in :attr:Lineage.warnings; they
never raise from this property (INV-2).
Phase-8 derivation order:
- Inherit
fem_hash+model_hash+ accumulated warnings from :attr:model.lineage(the broker recomputes against the same file). - Read the stored
/meta/lineage/results_hashvia the reader'sresults_lineage_attrshelper and recompute from/stages/...viarecompute_results_hash; append a drift warning on mismatch.
Readers that don't implement the Phase-6 result-layer
protocol methods are tolerated via getattr cushions:
their lineage stays at the model layer, no warning emitted.
modes
property
¶
Stages with kind='mode' as a list of mode-scoped Results.
Order is the order the modes were written (typically by
ascending mode_index). For a stable lookup by index, sort:
sorted(results.modes, key=lambda m: m.mode_index).
eigen_modes
property
¶
Mode-kind stages as lightweight :class:EigenMode snapshots.
Each :class:EigenMode carries only the four scalar fields
(mode_index, eigenvalue, frequency_hz, period_s)
— no file handle, no mode-shape arrays. Use this when you
need the eigenvalue spectrum but not the per-node shapes
(e.g. an LTB Mcr probe, a pickle-able report, or a return
value from a function whose Results context is about to be
closed).
For the per-node mode shape arrays, use the mode-scoped
:class:Results from :attr:modes instead and query via
mode.nodes.get(component="displacement_x", ...).
Order matches :attr:modes. For a stable lookup by index,
sort: sorted(results.eigen_modes, key=lambda m: m.mode_index).
plot
property
¶
results.plot — static matplotlib renderer.
Mirrors the interactive viewer's diagram catalog as headless, publication-ready matplotlib figures::
results.plot.contour("displacement_z", step=-1)
results.plot.deformed(step=-1, scale=50, component="stress_xx")
results.plot.history(node=412, component="displacement_x")
Requires the [plot] extra (matplotlib).
from_native
classmethod
¶
from_native(path: str | Path, *, fem: 'Optional[FEMData]' = None, model: 'Optional[OpenSeesModel]' = None, model_path: 'Optional[str | Path]' = None) -> 'Results'
Open an apeGmsh native HDF5 results file.
Phase 8 (ADR 0020 INV-1) — model= is required. Missing
supply raises :class:TypeError. Pass
model=OpenSeesModel.from_h5(path_to_model_h5) (often the
same path as path when the file is a Composed-file
per ADR 0020).
If fem is omitted, the embedded /model/ snapshot is
used as the bound FEMData.
model_path records the on-disk archive the model was read
from, for when it is not path itself — e.g. results whose
embedded /model zone is not independently readable. The
non-blocking subprocess viewer forwards it as --model-h5 so the
child re-reads the model from there instead of from path.
Source code in src/apeGmsh/results/Results.py
from_recorders
classmethod
¶
from_recorders(spec, output_dir: str | Path, *, fem: 'FEMData', cache_root: str | Path | None = None, stage_name: str = 'analysis', stage_kind: str = 'transient', file_format: str = 'out', stage_id: str | None = None, model: 'Optional[OpenSeesModel]' = None) -> 'Results'
Open the result of an OpenSees run driven by Tcl/Py recorders.
Phase 8 (ADR 0020 INV-1) — model= is required. Missing
supply raises :class:TypeError. The model's /opensees/
zone is embedded into the transcoded native h5 (the
Composed-file pattern); downstream
:meth:Results.from_native then auto-resolves the broker
from the same file.
Parses the .out / .xml files emitted at
output_dir (matching what spec.emit_recorders(...) or
the apeGmsh OpenSees bridge's Tcl/Py emit produced) into an
apeGmsh native HDF5, caches the result at
cache_root, and opens it through NativeReader.
Caching: subsequent calls with unchanged input files return
the cached HDF5 directly (file mtime + size + spec
snapshot_id form the cache key). See
writers/_cache.py.
stage_id matches the per-stage filename prefix used by
:meth:ResolvedRecorderSpec.emit_recorders together with
begin_stage(stage_id, ...). When set, only files prefixed
with <stage_id>__ are read; stage_name defaults to
stage_id if not overridden. None (default) keeps the
legacy flat-naming used by Tcl/Py exports.
Phase 6 v1 supports nodal records only; element-level records in the spec are skipped with a note. The capture flow (Phase 7) handles modal recorders.
Source code in src/apeGmsh/results/Results.py
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | |
from_mpco
classmethod
¶
from_mpco(path: 'str | Path | list[str | Path]', *, fem: 'Optional[FEMData]' = None, merge_partitions: bool = True, model_h5: 'Optional[str | Path]' = None) -> 'Results'
Open a STKO .mpco HDF5 results file.
Phase 8 (ADR 0020 INV-1) — model_h5= is required. Missing
supply raises :class:TypeError. The broker is loaded via
:meth:OpenSeesModel.from_h5 and attached to the resulting
:class:Results; INV-3 — the broker is held in memory only
(no derived results.h5 is written copying the
/opensees/ zone in).
Single-file mode (default for non-partitioned analyses): pass
the path of one .mpco file. Synthesizes a partial FEMData
from the MPCO MODEL/ group if fem is omitted.
Multi-partition mode (parallel OpenSees runs): pass either
- a single
<stem>.part-<N>.mpcopath — siblings are discovered automatically by globbing<stem>.part-*.mpcoin the same directory and merged into one virtual reader; - an explicit list of partition paths.
Boundary nodes deduplicate by ID (first-occurrence wins); elements concatenate (disjoint by partition); slabs stitch across partitions transparently. Stage and time vectors must match across partitions or construction raises.
Pass merge_partitions=False to opt out of auto-discovery
and read only the file at path even if it follows the
.part-N naming convention.
Source code in src/apeGmsh/results/Results.py
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | |
from_ladruno
classmethod
¶
from_ladruno(path: 'str | Path | list[str | Path]', *, fem: 'Optional[FEMData]' = None, merge_partitions: bool = True, model_h5: 'Optional[str | Path]' = None) -> 'Results'
Open a Ladruno .ladruno HDF5 results file.
The Ladruno recorder is the fork's canonical recorder. Unlike
.mpco (and unlike :meth:from_mpco, which requires
model_h5=), a .ladruno is self-sufficient — it carries
its own geometry, regions and beam local axes (schema Principle 0:
"this is the native path; no sibling file"). So model_h5= is
optional:
- omitted → the broker is built in-memory from the file's own
MODELgroup (geometry + inferredndm/ndf; bridge record zones empty). This is read-time interpretation, not a transcode. - supplied → the richer broker is loaded via
:meth:
OpenSeesModel.from_h5(full bridge records + lineage), and — whenever the model records an element_meta pairing — the fem_eid↔ops-tag translator is attached (ADR 0043; required for composed models AND for any sparsely-renumbered mesh, e.g. a gmsh solid whose 2-D boundary elements consumed the low ids).
Keys on INFO/GENERATOR="Ladruno" + a supported
FORMAT_VERSION (the reader rejects a .mpco / foreign file
or an out-of-window version loudly).
Multi-partition merge: a parallel run writes one
<stem>.part-<N>.ladruno per rank. Passing one partition path
auto-discovers its siblings (<stem>.part-*.ladruno) and merges
them into one virtual reader (node-union + element-concat);
passing a list merges exactly those paths. merge_partitions=False
opts out of sibling auto-discovery.
Source code in src/apeGmsh/results/Results.py
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | |
from_fem
classmethod
¶
from_fem(fem: 'FEMData', path: 'str | Path | list[str | Path]', *, kind: str = 'auto', merge_partitions: bool = True, cache_root: 'str | Path | None' = None) -> 'Results'
Open a results file against a bare :class:FEMData snapshot.
The one-call route to :class:Results / :meth:viewer for a
model that did not go through the apeSees bridge — e.g. a
physical-group model where fem = g.mesh.queries.get_fem_data()
drove a hand-written OpenSees deck. Without this the only routes
were the bridge-bound constructors (which need a bridge-emitted
model.h5 / OpenSeesModel) or the self-describing
:meth:from_ladruno.
from_fem materialises a neutral-only model.h5 from fem
(cached, keyed by fem.snapshot_id) and binds it to the reader
for path. Materialising a file — rather than an in-memory
model — is deliberate: it sets model_path so the non-blocking
/ web viewers work (they forward --model-h5), not just data
access.
Parameters¶
fem
The bound snapshot (from g.mesh.queries.get_fem_data() or
FEMData.from_h5). A composed fem is refused — see
below.
path
The results file (or a partition list).
kind
"mpco" / "ladruno" / "native", or "auto"
(default) to detect from the suffix (.mpco / .ladruno;
a native .h5 must pass kind="native").
merge_partitions
Forwarded to :meth:from_mpco / :meth:from_ladruno for
.part-N auto-discovery.
cache_root
Where the materialised model.h5 is written — under
<cache_root>/from_fem/ (default <cwd>/results/from_fem/
or $APEGMSH_RESULTS_DIR).
Raises¶
ValueError
When fem is composed (g.compose / from_h5
assembly). Element / Gauss results are relabelled through the
fem_eid ↔ ops-tag map that only a real bridge run records;
a neutral-only model.h5 carries none, so a composed model
would silently mislabel every element result. Build the model
through apeSees and pass its model.h5 (e.g.
apeSees(fem).h5(...) / g.save →
from_mpco(path, model_h5=...)).
Notes¶
A bare fem carries no envelope ndf (MeshInfo has none), so
the cached model's ndf is 0. This is harmless for reading
results and for the viewer; it only matters for deck re-emit
(model.build(...)), which is not this path's purpose.
Source code in src/apeGmsh/results/Results.py
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 | |
energy ¶
Energy-balance time history — Ladruno-recorder feature.
Returns a :class:pandas.DataFrame of the closure components
KE / IE / DW / ULW / RES / ERR indexed by
simulation time, written by the recorder's -G energy verb.
region=None→ whole-domain balance (ON_DOMAIN).region=<tag>→ the per-region balance (ON_REGIONS) for the OpenSees region tag.
ERR (the normalized energy-balance error %) is the headline
solution-quality diagnostic for explicit runs. Raises
:class:TypeError on a non-Ladruno results object (MPCO / native
carry no energy balance) and ValueError if energy was not
recorded / the region is unknown.
results.plot.energy(...) renders this as a matplotlib
time-history figure.
Source code in src/apeGmsh/results/Results.py
energy_regions ¶
OpenSees region tags with a recorded per-region energy balance.
The bridge auto-allocates an integer region tag when a Ladruno
recorder is given energy_pg= (or a value filter + energy);
that tag is opaque to the author. This lists the tags actually
present in ON_REGIONS/energyBalance so you can pick one to pass
to :meth:energy — e.g. r.energy(region=r.energy_regions()[0]).
Returns [] when only the whole-model balance was recorded
(read it with energy(), no region=). Ladruno-recorder
feature; raises :class:TypeError on MPCO / native results.
Source code in src/apeGmsh/results/Results.py
node_envelope ¶
Per-node time-reduced extremes — Ladruno -envelope feature.
When a .ladruno is recorded with the recorder's -envelope
flag, each node channel stores componentwise running extremes
(MIN/MAX/ABSMAX and the step at which the abs-extreme
occurred) instead of a time series — the cheap way to capture peak
response over a long run without keeping every step.
Returns a :class:pandas.DataFrame indexed by node id with columns
min / max / absmax / arg_step for component (a
neutral name like "displacement_x"). Raises :class:TypeError
on a non-Ladruno results object, and :class:ValueError if the file
was not recorded with -envelope or the component is absent.
results.plot.node_envelope(...) paints a chosen measure on
the mesh as a matplotlib figure.
Source code in src/apeGmsh/results/Results.py
bind ¶
Re-bind to fem.
Useful when you've re-built the same mesh in a fresh session and want labels / Parts that the embedded snapshot doesn't carry. No hash validation is performed — pairing the FEMData with a results file from the same run is the user's responsibility.
Source code in src/apeGmsh/results/Results.py
stage ¶
Return a Results scoped to a stage (matched by id or name).
close ¶
demo
classmethod
¶
Return a ready-to-view demo :class:Results (cantilever pushover).
Zero-setup sample data so Results.demo().show_web() (or
.viewer()) renders without supplying an .mpco /
model.h5 pair — handy for docs, smoke tests, and trying the
viewer. A real apeSees-emitted model with a synthetic, ramped
cantilever deflection (no OpenSees solve). See
:func:apeGmsh.results.make_demo_results for the keyword options
(length / n_elements / n_steps / tip_drift /
path).
Source code in src/apeGmsh/results/Results.py
assess ¶
Compile a v1 :class:~apeGmsh.assess.AssessmentReport.
figures=True calls :meth:render_pack. Default is False.
Source code in src/apeGmsh/results/Results.py
session ¶
The presentation session for these results (ADR 0098 §1).
Presentation with no window: a ResultsSession (from
apeGmsh.results.session) bound to this broker, booted with
the default picture — ONE empty mesh view (grey analysis mesh,
no slots, no legends). Configure it (slots, deform, time), then
s.render("a.png") for a still; the Qt client (s.show())
arrives at S2 and viewer() flips onto it at S6.
Persisted section cuts boot as view clips (ADR 0098 S6b).
The retired section_cut diagram kind took its auto-load
contract with it, but not the contract itself: cuts persisted
under /opensees/cuts/ come back on the booted view as
clips. Only the ones that translate honestly do — a cut that
named a strict subset of the model's elements, or that carries
a bounding polygon, cuts LESS than a view clip does, so it is
skipped with one [session] line rather than silently
widening what disappears from the screen. Reading the cuts can
never fail this call: a bad zone is a line, not a traceback.
Source code in src/apeGmsh/results/Results.py
viewer ¶
viewer(*, blocking: 'Optional[bool]' = None, title: Optional[str] = None, restore_session: 'bool | str' = 'prompt', save_session: bool = True)
Open the post-solve results window on a ResultsSession.
Sugar for :meth:session + show() (ADR 0098 §1, flipped at
S6a). The one-liner is unchanged; what it opens is not. A
:class:~apeGmsh.results.session.ResultsSession is the
document — tiled mesh and plot panes, each with the closed §4
slot catalog — and the window is a client that projects it. The
retired Geometry / Composition / Diagram window is gone from
this door. Everything the window does, a script can do to the
same object::
s = results.session() # the document, no window
s.render("a.png") # a still, no Qt
results.viewer() # the human one-liner
Parameters¶
blocking
None (default) — auto: True in scripts and the
plain CLI, False inside a Jupyter / IPython ZMQ kernel,
where the blocking Qt loop would freeze (often kill) the
kernel. An in-memory Results in a notebook cannot spawn a
subprocess and falls back to :meth:show_web. Either
notebook path announces itself with one line.
True — open the window in-process and block the calling
thread until it closes. Matches the signature of
:meth:g.mesh.viewer and :meth:g.model.viewer.
False — spawn a subprocess via
python -m apeGmsh.viewers <path> so the notebook /
kernel can keep running. Requires that the Results was
opened from disk (self._path is set); raises
:class:RuntimeError for in-memory Results.
title
Optional window title; defaults to "Results — <filename>".
restore_session
What to do with a session snapshot saved beside the results
file. True restores silently, False ignores it,
"prompt" (default) asks. No effect for in-memory
Results, which have no file to sit beside.
save_session
If True (default), the session — panes, slots, pose,
time link, selection — is written to
<results>.viewer-session.json when the window closes.
False disables auto-save. Auto-save also disarms itself
for a window that could not read an existing file there, so
the unreadable file survives (INV-SESSION-OPEN, see
apeGmsh.results.session._boot).
Returns¶
ResultsSession
The session the window projected, after the window
closes (blocking). Still live: query it, render stills off
it, snapshot it.
subprocess.Popen
The spawned process handle (non-blocking). Deliberately not
unified with the blocking return — a session in this
process is not what the child window is showing.
WebViewer
The :meth:show_web handle (auto mode, in-memory Results
in a notebook). The web client is a later client of the
same session; until it lands this hatch keeps today's path.
None
If APEGMSH_SKIP_VIEWER is set in the environment. This
lets the same cell run under jupyter nbconvert --execute
or in CI without spawning a GUI window.
Notes¶
The v13 <results>.viewer-session.json written by the retired
window is not restorable (ADR 0098 Consequences). The first
flipped open says so in one line and renames it aside to
.legacy — never overwriting it, and never overwriting an
aside that is already there.
cuts= is retired with the diagram ontology (§1): a cut plane
is clip state on a view. Build cuts with :mod:apeGmsh.cuts and
add them as clips — results.session() then view.add_clip.
Source code in src/apeGmsh/results/Results.py
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 | |
render ¶
render(path: 'str | Path', *, view: str = 'contour', component: Optional[str] = None, step: int = -1, deform: 'Optional[Any]' = None, camera: 'Optional[str]' = None, window_size: tuple[int, int] = (1280, 720)) -> 'Optional[Path]'
Write one offscreen still (ADR 0094 S1).
VTK offscreen from the viewer scene / diagram pipeline — no
Qt window, no event loop, no setup(plotter, director).
view is a closed set: mesh / contour / deformed
/ reactions.
camera= defaults to xy for a planar model, iso
otherwise (ADR 0094 Amendment 3); pass it explicitly to
override.
Returns the written :class:~pathlib.Path, or None (and
prints the [skip viewer] notice) under
APEGMSH_SKIP_VIEWER=1 or with no GL.
Source code in src/apeGmsh/results/Results.py
render_pack ¶
render_pack(out_dir: 'str | Path', *, camera: 'Optional[str]' = None, window_size: tuple[int, int] = (1280, 720)) -> tuple[Path, ...]
Write the canned report pack (ADR 0094 S3).
Returns the tuple of written paths, or () under
APEGMSH_SKIP_VIEWER=1 / no GL (and prints the
[skip viewer] notice). Closed view= set only; no
setup(). There is no fem.render_pack.
camera= defaults to xy for a planar model, iso
otherwise (ADR 0094 Amendment 3); pass it explicitly to
override.
Source code in src/apeGmsh/results/Results.py
export_animation ¶
export_animation(path: 'str | Any', *, fps: int = 30, step_stride: int = 1, stage: 'Optional[str]' = None, deform: 'Optional[Any]' = None, camera: 'Optional[Any]' = None, window_size: 'Optional[tuple[int, int]]' = (1280, 720), setup: 'Optional[Any]' = None)
Render the time history to a video / GIF without a GUI session.
Builds the full results viewer off-screen (so deformation,
contours, camera, and theming are pixel-identical to the
interactive viewer), walks every step capturing a frame, and
encodes to the format chosen by path's suffix — .mp4
(H.264, needs the apegmsh[animation] extra) or .gif
(Pillow, no extra). The viewer window is shown briefly while
rendering (the OpenGL context requires a realized surface) but
no blocking event loop is entered.
Parameters¶
path
Output file. Suffix selects the format (.mp4 / .gif).
fps
Frames per second of the output.
step_stride
Capture every N-th step (plus always the last). Useful to
keep long histories short.
stage
Stage id/name to animate. Defaults to the active stage.
deform
Deformed-shape scaling. A number applies that scale to the
"displacement" field; a (field, scale) pair selects
another field. None (default) renders the undeformed
mesh.
camera
Optional value assigned to plotter.camera_position (e.g.
"iso", "xy", or an explicit position triple) before
rendering. None keeps the auto-framed camera.
window_size
(width, height) of the rendered frames. None keeps
the viewer's default size.
setup
Optional callback(plotter, director) invoked after the
scene is built and before capture — the escape hatch for
adding contours / section cuts / custom camera work via the
same APIs the interactive viewer uses.
Returns¶
pathlib.Path
The resolved output path, or None when
APEGMSH_SKIP_VIEWER is set in the environment.
Source code in src/apeGmsh/results/Results.py
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 | |
show_web ¶
show_web(*, stage: 'Optional[str]' = None, show: bool = True, controls: bool = True, render_mode: str = 'client')
Open the view-only web / Jupyter results viewer (ADR 0042 R-C).
Renders the FEM substrate plus any diagrams the director holds
through a pyvista.trame backend — the kernel-safe path that
replaces the blocking Qt :meth:viewer in a notebook. View-only
(picking is deferred to R-D), but with a step slider + per-layer
visibility checkboxes when ipywidgets is available.
No results file handy? Results.demo().show_web() renders a
zero-setup cantilever-pushover sample.
Parameters¶
stage
Stage id or name to activate; defaults to the first stage.
show
When True (default), display inline immediately. When
False, return the :class:~apeGmsh.viewers.web_viewer.WebViewer
unshown so diagrams can be added via viewer.director first.
controls
When True (default), stack an ipywidgets control panel
(step slider + layer toggles) above the view. Degrades to a
bare view if ipywidgets is absent.
render_mode
"client" (default) renders in the browser via WebGL — fast
camera interaction. "server" renders on the kernel and
streams images (laggy, most VTK-feature-complete; for very
large models). "hybrid" is pyvista's trame backend with
a local/remote toggle in the toolbar.
Returns¶
WebViewer
The viewer handle (.director / .set_step / .show).
Source code in src/apeGmsh/results/Results.py
serve_web ¶
serve_web(*, stage: 'Optional[str]' = None, render_mode: str = 'client', port: 'Optional[int]' = None, open_browser: bool = True, title: str = 'apeGmsh', **start_kwargs)
Serve the results as a standalone trame web app (ADR 0042 R-C).
The non-Jupyter counterpart of :meth:show_web: builds a vuetify3
single-page app (the FEM view plus a step slider and per-layer
switches) and serves it at a local URL, opening a browser tab and
blocking until stopped (Ctrl-C). In a notebook use :meth:show_web
instead.
Parameters¶
stage
Stage id or name to activate; defaults to the first stage.
render_mode
"client" (default), "server", or "hybrid" — see
:meth:show_web.
port
Port to serve on; None lets trame pick one.
open_browser
Open a browser tab at the served URL.
title
App title shown in the toolbar.
**start_kwargs
Passed through to the trame server.start (e.g.
exec_mode).
Returns¶
WebViewer The viewer handle.
Source code in src/apeGmsh/results/Results.py
save_definitions ¶
Persist this Results' custom scalar definitions to a JSON
sidecar (default <results>.defs.json).
Reloaded automatically by :meth:from_native / :meth:from_mpco
/ :meth:from_ladruno, and carried to the subprocess viewer.
Raises for an in-memory Results with no path= given.
Source code in src/apeGmsh/results/Results.py
load_definitions ¶
Load and register custom scalar definitions from a JSON sidecar
(default <results>.defs.json). Returns the count applied; a
missing file is a no-op returning 0. Idempotent / best-effort —
see :meth:_apply_definitions_payload.
Source code in src/apeGmsh/results/Results.py
Numberer ¶
Renumbers a FEM mesh for solver consumption.
Parameters¶
fem_data : dict
Output of Mesh.get_fem_data(). Must contain:
node_tags, node_coords, elem_tags,
connectivity, used_tags.
Source code in src/apeGmsh/mesh/_numberer.py
renumber ¶
Produce a solver-ready mesh with contiguous IDs.
Parameters¶
method : "simple" or "rcm"
"simple" — preserves relative order, just makes IDs
contiguous. Fast, no optimisation.
``"rcm"`` — Reverse Cuthill-McKee bandwidth minimisation.
Reorders nodes so that the assembled stiffness matrix has
minimal bandwidth. Recommended for direct solvers.
int
Starting ID (default 1 = Fortran/OpenSees convention; use 0 for C/Python convention).
bool
If True (default), only include nodes that appear in at least one element (skip orphan nodes). Set False to include all nodes from the mesh.
Returns¶
NumberedMesh
Source code in src/apeGmsh/mesh/_numberer.py
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | |
NumberedMesh
dataclass
¶
NumberedMesh(node_ids: ndarray, node_coords: ndarray, elem_ids: ndarray, connectivity: ndarray, n_nodes: int = 0, n_elems: int = 0, bandwidth: int = 0, method: str = 'simple', gmsh_to_solver_node: dict[int, int] = dict(), solver_to_gmsh_node: dict[int, int] = dict(), gmsh_to_solver_elem: dict[int, int] = dict(), solver_to_gmsh_elem: dict[int, int] = dict())
Solver-ready mesh with contiguous IDs and bidirectional maps.
All IDs are 1-based (the standard in structural FEM solvers
like OpenSees, Abaqus, SAP2000). Set base=0 in
:meth:Numberer.renumber for 0-based if your solver needs it.
Attributes¶
node_ids : ndarray(N,)
New contiguous node IDs.
node_coords : ndarray(N, 3)
Nodal coordinates, same order as node_ids.
elem_ids : ndarray(E,)
New contiguous element IDs.
connectivity : ndarray(E, npe)
Element connectivity in terms of new node IDs.
n_nodes : int
n_elems : int
bandwidth : int
Semi-bandwidth of the resulting adjacency.
method : str
Numbering method used ("simple" or "rcm").
Maps ~~~~ gmsh_to_solver_node : dict[int, int] Gmsh node tag -> solver node ID. solver_to_gmsh_node : dict[int, int] Solver node ID -> Gmsh node tag. gmsh_to_solver_elem : dict[int, int] Gmsh element tag -> solver element ID. solver_to_gmsh_elem : dict[int, int] Solver element ID -> Gmsh element tag.
summary ¶
RenumberResult ¶
RenumberResult(method: str, n_nodes: int, n_elements: int, bandwidth_before: int, bandwidth_after: int)
Result of a mesh renumbering operation.
Attributes¶
method : str
Algorithm used ("simple", "rcm", "hilbert", "metis").
n_nodes : int
Number of nodes renumbered.
n_elements : int
Number of elements renumbered.
bandwidth_before : int
Semi-bandwidth before renumbering.
bandwidth_after : int
Semi-bandwidth after renumbering.
Source code in src/apeGmsh/mesh/_mesh_partitioning.py
PartitionInfo ¶
PartitionInfo(n_parts: int, elements_per_partition: dict[int, int], weights_per_partition: dict[int, float] | None = None)
Result of a mesh partitioning operation.
Attributes¶
n_parts : int
Number of partitions created.
elements_per_partition : dict[int, int]
{partition_id: element_count}.
weights_per_partition : dict[int, float] | None
{partition_id: total_weight} when partition() was called
with weights=, otherwise None. Populated by
_gather_partition_info() from the per-element weight vector
cached on _Partitioning during the weighted call.
Source code in src/apeGmsh/mesh/_mesh_partitioning.py
MeshViewer ¶
MeshViewer(parent: '_SessionBase', *, dims: list[int] | None = None, point_size: float | None = None, line_width: float | None = None, surface_opacity: float | None = None, show_surface_edges: bool | None = None, origin_markers: list[tuple[float, float, float]] | None = None, origin_marker_show_coords: bool | None = None, view: 'ViewerData | None' = None, fast: bool = True, on_selection_changed: Callable[['SelectionState'], None] | None = None, **kwargs: Any)
Interactive mesh viewer with element/node picking.
Displays mesh elements and nodes with optional load, constraint,
and mass overlays. Overlay data comes from a resolved
:class:apeGmsh.viewers.data.ViewerData snapshot — either passed
explicitly or auto-resolved from the session at show time.
Parameters¶
parent : _SessionBase
The apeGmsh session.
dims : list[int], optional
Which mesh dimensions to show (default: [1, 2, 3]).
point_size, line_width, surface_opacity, show_surface_edges
Visual properties.
view : ViewerData, optional
Pre-resolved structural snapshot. If not provided, the
viewer calls get_fem_data() automatically when the
window opens and wraps the resulting FEMData. Phase 8.7
commit 6 renamed this kwarg from fem to view.
fast : bool
Ignored (always fast). Kept for backward compatibility.
on_selection_changed : callable, optional
callback(SelectionState) fired on every BREP pick change
(ADR 0095 studio host). Dispatcher-legal owner mutator.
Source code in src/apeGmsh/viewers/mesh_viewer.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
show ¶
Open the viewer window, block until closed.
Source code in src/apeGmsh/viewers/mesh_viewer.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | |
ModelViewer ¶
ModelViewer(parent: '_SessionBase', model: 'Model', *, physical_group: str | None = None, dims: list[int] | None = None, point_size: float | None = None, line_width: float | None = None, surface_opacity: float | None = None, show_surface_edges: bool | None = None, origin_markers: list[tuple[float, float, float]] | None = None, origin_marker_show_coords: bool | None = None, on_selection_changed: Callable[['SelectionState'], None] | None = None, annotate: bool = False)
Interactive BRep model viewer with physical group management.
Displays BRep geometry, parts, physical groups, and labels.
This is a geometry-only viewer — loads, constraints, and masses
are mesh-resolved concepts and live on g.mesh.viewer() instead.
Parameters¶
parent : _SessionBase
The apeGmsh session (provides name, _verbose).
model : Model
The apeGmsh model (provides sync()).
physical_group : str, optional
Auto-activate this physical group on open.
dims : list[int], optional
Which entity dimensions to show (default: [0, 1, 2, 3]).
point_size, line_width, surface_opacity, show_surface_edges
Visual properties forwarded to the scene builder.
on_selection_changed : callable, optional
callback(SelectionState) fired on every pick change
(ADR 0095 studio host). Dispatcher-legal owner mutator.
annotate : bool
If True, turn on part + entity name labels with overall
sizes (cotas) at open. Studio host sets this; the View tab
still toggles them.
Source code in src/apeGmsh/viewers/model_viewer.py
active_group
property
¶
The name of the physical group currently receiving picks.
show ¶
Open the viewer window, block until closed.
Source code in src/apeGmsh/viewers/model_viewer.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 | |
to_physical ¶
Write the current picks as a Gmsh physical group.
Source code in src/apeGmsh/viewers/model_viewer.py
SectionProperties ¶
SectionProperties(fem: 'FEMData', *, materials: Mapping[str, SectionMaterial] | None = None, name: str | None = None, disconnected: Literal['raise', 'sum'] = 'raise')
Analyzer + declaration for one meshed cross-section.
Parameters¶
fem
A FEMData whose 2-D elements mesh the section face in the
global XY plane (g.mesh.queries.get_fem_data(dim=2)).
materials
Physical-group name → :class:SectionMaterial. Every 2-D
element must belong to exactly one named PG. Omit entirely for
geometric-only mode (unit moduli — classic geometric numbers).
name
Handle used in fail-loud messages and displays.
disconnected
Multi-part policy (ADR 0078): "raise" (default) makes the
S2 warping solve fail loud on a disconnected mesh — usually the
forgot-to-fragment authoring bug; "sum" opts into per-part
Saint-Venant solves. Geometric and plastic analyses are
connectivity-blind in either mode.
Notes¶
The analyzer is a declaration: frozen inputs, memoized frozen
results. ops.section.ComputedSection(analysis=sec) (S5) binds
it to the OpenSees bridge and resolves lazily at emit.
Source code in src/apeGmsh/sections/_analysis.py
materials
property
¶
Read-only PG-name → material view (empty-ish placeholder map in geometric-only mode).
geometric ¶
Area-based (modulus-weighted) properties. Pure quadrature — connectivity-blind, valid for disconnected sections.
Source code in src/apeGmsh/sections/_analysis.py
warping ¶
Saint-Venant warping / shear analysis: GJ, shear centre
(elasticity + Trefftz), warping rigidity EGamma, shear
rigidities GAs_*, monosymmetry constants.
Requires a connected mesh under the default
disconnected="raise"; "sum" solves per part (ADR 0078).
Warns :class:SectionAccuracyWarning on linear elements.
Source code in src/apeGmsh/sections/_analysis.py
plastic ¶
Rigid-plastic analysis: plastic centroids, fy-weighted plastic
moments Mp_*, first-yield shape factors. Requires fy on
every material; connectivity-blind (valid for disconnected
sections). Invalid for strain-softening materials.
Source code in src/apeGmsh/sections/_analysis.py
stress ¶
stress(*, N: float = 0.0, Vx: float = 0.0, Vy: float = 0.0, Mxx: float = 0.0, Myy: float = 0.0, M11: float = 0.0, M22: float = 0.0, Mzz: float = 0.0) -> SectionStress
Linear-elastic stress recovery for one load vector.
A weighted blend of unit-load fields computed once from the
cached geometric + warping solutions — calling with a new load
vector never re-solves anything. Sign conventions: N
tension-positive; Mxx tension at +y; Myy tension at
+x; M11/M22 likewise in the principal frame;
Mzz counter-clockwise. See :class:SectionStress for the
component list and the per-region access contract.
Under disconnected="sum" the actions distribute per the
ADR 0078 policy: N/Mxx/Myy use the global
plane-sections composite state unchanged; Mzz goes to parts
∝ GJᵢ/ΣGJ; Vx/Vy ∝ the part flexural-rigidity
shares (scalar per axis — exact for parts whose principal axes
align with x/y; approximate for in-plane-rotated parts).
Consistent with the no-inter-part-shear-transfer lower bound
the warping results already carry.
Source code in src/apeGmsh/sections/_analysis.py
analyze ¶
Run every available analysis (S1–S3: geometric + warping + plastic when fy is available). Returns self.
Source code in src/apeGmsh/sections/_analysis.py
to_elastic_section ¶
Eagerly lower this analyzer into a plain populated
:class:~apeGmsh.opensees.section.ElasticSection.
Runs the same shared lowering as
ops.section.ComputedSection(analysis=...) — authoring
Ixx_c → Iz, Iyy_c → Iy, J → J, As_y/A → alphaY,
As_x/A → alphaZ — but resolves now and returns an
inspectable, analyzer-decoupled primitive.
E / G default from the single material on a homogeneous
analyzer; for a composite they are required reference
moduli (transformed-section EA/E, EI/E, GJ/G) and
for a geometric-only analyzer they are required deck
moduli. ndm=3 (default) emits the 3-D form; ndm=2 the
2-D shear-flexible form.
Source code in src/apeGmsh/sections/_analysis.py
summary ¶
Plain-text properties report.
Source code in src/apeGmsh/sections/_analysis.py
plot_mesh ¶
Matplotlib wireframe of the section mesh, colored by material region.
Source code in src/apeGmsh/sections/_analysis.py
plot_section ¶
plot_section(*, centroid: bool = True, shear_centre: bool = True, principal_axes: bool = True, ax=None)
Section outline + glyph overlay: elastic centroid, shear
centre (triggers :meth:warping — pass shear_centre=False
for disconnected sections under the default policy), principal
axes at phi.
Source code in src/apeGmsh/sections/_analysis.py
plot_warping ¶
plot_warping(*, shear_flow: bool = False, ax=None, cmap: str = 'viridis', levels: int = 15, max_arrows: int = 800)
Filled contour of the Saint-Venant warping function ω.
Triggers the (memoized) :meth:warping solve. Works for
connected sections and per-part under disconnected="sum"
(each part carries its own ∫ω dA = 0 reference).
shear_flow=True overlays a quiver of the unit-torsion
shear stress τ per Mzz = 1 (direction = the shear-flow
pattern). It rides the stress unit fields; under
disconnected="sum" each part shows its own flow for its
GJᵢ/ΣGJ share of the torque.
Source code in src/apeGmsh/sections/_analysis.py
plot ¶
One-call overview figure: the glyphed section view (left)
beside the :meth:summary report (right). Returns the
matplotlib Figure.
Triggers :meth:geometric + :meth:warping (memoized); for a
disconnected section under the default policy this fails loud
like :meth:warping does.
Source code in src/apeGmsh/sections/_analysis.py
viewer ¶
Open the Qt section inspector (ADR 0078 S6).
Left: the meshed section with glyph overlays, switching to
stress contours when a component is picked. Right: tabbed
property tables (Geometric / Warping / Plastic as available;
composite sections gain an e_ref input driving a
transformed column) and six live load inputs that re-blend the
precomputed unit stress fields — no solve ever runs on the UI
thread.
Notebooks must pass blocking=False (a blocking Qt loop
kills the kernel; enable %gui qt so the window stays
responsive). Qt absent raises ImportError with install
guidance; every capability is equally reachable headless via
:meth:summary, :meth:plot_section, and
stress(...).plot().
Source code in src/apeGmsh/sections/_analysis.py
SectionMaterial
dataclass
¶
SectionMaterial(*, E: float, nu: float, G: float | None = None, fy: float | None = None, density: float | None = None, name: str | None = None)
Material assigned to one physical-group region of a cross-section.
Parameters¶
E
Young's modulus (> 0). Weights the geometric integrals.
nu
Poisson's ratio (−1 < nu < 0.5).
G
Shear-modulus override; default is the isotropic
E / (2 (1 + nu)). An independent G exists for
equivalent shear media — smeared battens / lacing, corrugated
webs: a strip with near-zero E and a calibrated G
transfers shear between parts without adding parasitic flexural
area. The solver assembles the E-field (geometric) and G-field
(warping) separately, so the override is exact, not a fudge.
fy
Yield stress (> 0). Required by plastic().
density
Mass density; when every material carries one, the analyzer
reports mass per unit length.
name
Display-only label (tables, plots). Falls back to the physical
group name where one is needed.
shear_modulus
property
¶
Effective shear modulus: the G override when given, else
the isotropic E / (2 (1 + nu)).
settings ¶
Open the global preferences editor (modal dialog).
Persists changes to the JSON file at
PreferencesManager.path (platform-appropriate config dir).
Spins up a QApplication if none exists.
Returns the dialog result code (QDialog.Accepted / Rejected).
Source code in src/apeGmsh/viewers/__init__.py
theme_editor ¶
Open the theme editor (modal dialog with live preview).
Custom themes are persisted under ThemeManager.themes_dir()
(platform-appropriate config dir). Spins up a QApplication if
none exists.
Returns the dialog result code (QDialog.Accepted / Rejected).
Source code in src/apeGmsh/viewers/__init__.py
preview ¶
preview(session: Any = None, *, mode: str = 'mesh', dims: list[int] | None = None, show_nodes: bool = True, browser: bool = False, return_fig: bool = False) -> Any
Unified entry point — routes to preview_model or preview_mesh.
Parameters¶
mode : {"model", "mesh"}
Which scene to render. Default "mesh".
show_nodes : bool
Mesh mode only — render the full mesh-node cloud as a
separate trace. Ignored in model mode.
browser : bool
Open in a new browser tab instead of rendering inline.
return_fig : bool
Skip display and return the raw plotly Figure.
Source code in src/apeGmsh/viz/NotebookPreview.py
workdir ¶
Return Path(name) after ensuring it exists.
Convention for example notebooks: every script puts its
artifacts (capture.h5, recorders/, exports, etc.) under
a sibling outputs/ folder so the example directory stays
self-contained. Typical use::
from apeGmsh import workdir
OUT = workdir() # ./outputs/
cap_path = OUT / 'capture.h5'
Pass an explicit name for nested or non-default layouts
(workdir('outputs/run_42')).
Source code in src/apeGmsh/_workdir.py
Session class¶
apeGmsh._core.apeGmsh ¶
apeGmsh(*, model_name: str = 'ModelName', verbose: bool = False, save_to: str | Path | None = None, overwrite: bool = True)
Bases: _SessionBase
Standalone single-model Gmsh session with all composites.
Parameters¶
model_name : str
Name passed to gmsh.model.add().
verbose : bool
If True, composites print diagnostic messages.
Source code in src/apeGmsh/_core.py
from_h5
classmethod
¶
Construct a session in chain phase directly from a saved FEMData.
Skips the gmsh build phase entirely: the loaded FEMData becomes
the session's chain head and there is no gmsh kernel behind
this session at all. model.h5 persists the FEMData
snapshot (nodes, elements, physical groups, labels) — not the
geometry kernel — so anything that would read or mutate BRep /
mesh state raises :class:~.core._compose_errors.ChainPhaseError
naming the H5-safe alternative.
Useful for cross-session composition workflows::
# Day 1
with apeGmsh(model_name="host", save_to="host.h5") as g:
...
# Day 2
g = apeGmsh.from_h5("host.h5")
g.compose("module_a.h5", label="A")
g.compose("module_b.h5", label="B")
g.save("final.h5")
What works¶
g.mesh.queries.get_fem_data()— the chain head, and the surface every refusal below points back at.g.compose(...)/compose_inspect(...)/compose_list()and :meth:save.- The chain-phase authoring shims, routed through
FEMData.with_*:g.constraints.bc/tie/embedded/tied_contact/equalDOF/rigid_link/rigid_diaphragm, plus pointg.loads.X/g.masses.X. - Kernel-free helpers:
g.model.queries.plane/registry,g.view.list_views/count,g.plot.show/savefig/clear/figsize/use_axes. repr()of any composite. The two kernel-backed reprs (g.physical,g.labels) report"no live gmsh kernel — from_h5 session"rather than raising, so debuggers and logging stay usable.
Refused — no live kernel to read¶
These need the gmsh model and raise on a from_h5 session
specifically (a live session still has a kernel, so they stay
legal there). Each message names the broker counterpart.
========================= ==================================
Surface Guarded members
========================= ==================================
g.inspect get_geometry_info,
get_mesh_info, print_summary
g.physical get_all, get_entities,
entities,
get_groups_for_entity,
get_name, get_tag,
summary, get_nodes
g.labels entities, get_all,
summary, has,
reverse_map,
labels_for_entity
g.mesh.queries get_nodes, get_elements,
get_element_properties,
get_element_qualities,
quality_report
g.model.queries bounding_box,
center_of_mass, mass,
boundary, boundary_curves,
boundary_points,
adjacencies,
entities_in_bounding_box
g.mesh.partitioning n_partitions, summary,
entity_table, save
g.model.io save_step, save_iges,
save_dxf, save_msh — the
exporters only; the importers are
frozen instead (below)
g.model.<geometry> find_stale_metadata, and
validate_pre_mesh through it
g.mesh.recipe check
g.parts build_face_map
g.rebar resolve
g.sections plot_faces
g.view add_element_scalar /
add_element_vector /
add_node_scalar /
add_node_vector
g.plot geometry, mesh, quality,
label_entities, label_nodes,
label_elements,
physical_groups,
physical_groups_mesh
========================= ==================================
Counterparts: fem.inspect for summaries, fem.physical
(:class:~.mesh._group_set.PhysicalGroupSet) for physical
groups, fem.nodes.labels / fem.elements.labels
(:class:~.mesh._group_set.LabelSet) for labels,
fem.nodes / fem.elements / fem.info for mesh data,
and results.inspect for post-processing — where
fem = g.mesh.queries.get_fem_data(). BRep geometry has no
counterpart: derive it from mesh coordinates or rebuild the
geometry in a live session.
Refused — model frozen¶
Mutations are refused on any chain-phase session, not just this one: once a FEMData snapshot exists the broker is canonical, and mutating gmsh would silently desync the two. Listed by composite — each guards its mutating operations at a shared chokepoint, so the coverage is per-composite rather than the per-method enumeration given for the reads above.
- Geometry —
g.model.<geometry>(viaModel._register, plusadd_wire, which creates OCC geometry but is deliberately not registered),g.model.boolean,g.model.transforms,g.model.io.heal_shapes/load_msh/load_geo, andg.model.queries.remove/remove_duplicates/make_conformal(mutations despite the composite name). - Mesh —
g.mesh.generation,g.mesh.editing,g.mesh.sizing,g.mesh.structured,g.mesh.recipe, andg.mesh.partitioning(its mutating opspartition/partition_explicit/unpartition/renumber; the composite's four readers take the kernel guard instead, and are listed in the read table above). - Naming —
g.physical.add/set_name/remove/remove_name/remove_all, andg.labels.add/remove/rename/promote_to_physical. - Assembly —
g.partsinstance registration,g.sectionsbuilds,g.rebar.place.
Refused — resolves from live geometry¶
g.constraints.contact / contact_plane / interface,
g.embed, g.reinforce and g.decouple_node record
definitions that are resolved against live gmsh at extraction.
A from_h5 session never re-extracts, so the definition
would be stored and silently never applied — declare these in
the source part session before saving; the resolved records
round-trip through model.h5 and survive g.compose.
Parameters¶
path : str or Path
Path to a model.h5 written by :meth:save /
:meth:FEMData.to_h5.
model_name : str or None
Session name (used by :meth:save for /meta/model_name).
Defaults to the source file's stem.
verbose : bool, default False
Verbose-mode flag forwarded to the constructor.
Raises¶
~.core._compose_errors.ChainPhaseError From any surface listed above. The message names the offending call and the alternative that answers it.
Source code in src/apeGmsh/_core.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | |
decouple_node ¶
decouple_node(*, coords: 'tuple[float, float, float] | None' = None, point: 'str | None' = None, label: 'str | None' = None) -> Any
Declare a decoupled node — an auxiliary node that is not
a Gmsh mesh vertex (spring/dashpot ground, rigidDiaphragm
master, control node, load/mass anchor).
Exactly one of coords=(x, y, z) or point="label" locates
it; point= is snapshotted to coordinates at mesh-extraction
time. label is an optional friendly name.
The node is appended to fem.nodes at extraction with a
deterministic tag above every mesh node (dedup-immune by
construction) and provenance == "decoupled". It carries
no ndf — DOF count is a bridge concern (ops.ndf).
Returns the :class:~apeGmsh._kernel.defs.decoupled.DecoupledNodeDef
handle; its tag is populated after
g.mesh.queries.get_fem_data(...).
Source code in src/apeGmsh/_core.py
save ¶
Write the neutral-zone model.h5 for this session.
Persists what the session knows about the model: nodes,
elements, physical groups, labels, constraints, loads, masses.
Downstream solver enrichment (e.g. apeSees(fem).h5(p)) is
a separate user-driven action and not invoked here.
Parameters¶
path : str, Path, or None
Destination file. None (default) uses the save_to
given to the constructor. Raises if neither is set.
Returns the resolved path.
Source code in src/apeGmsh/_core.py
compose ¶
Merge a previously-saved apeGmsh model into this session.
See :meth:apeGmsh.mesh._compose.Compose.compose for the full
signature, validation contract, and exception types. Phase
3B.1 scaffolds the facade — the merge engine itself lands in
Phase 3B.2.
Source code in src/apeGmsh/_core.py
compose_inspect ¶
Read a module's H5 header without composing it.
See :meth:apeGmsh.mesh._compose.Compose.compose_inspect for
the returned dict shape.
Source code in src/apeGmsh/_core.py
compose_list ¶
Composed modules currently on this session.
See :meth:apeGmsh.mesh._compose.Compose.compose_list.
compose_tree ¶
Derived nested-compose tree view of this session's modules.
See :meth:apeGmsh.mesh._compose.Compose.compose_tree.
Base¶
apeGmsh._session._SessionBase ¶
Base class for objects that own a Gmsh session and parent composites.
Source code in src/apeGmsh/_session.py
begin ¶
Open a Gmsh session, create composites.
Parameters¶
verbose : bool or None
Override the verbosity set in __init__. None keeps
the current value.
Returns self for chaining.
Source code in src/apeGmsh/_session.py
end ¶
Close the Gmsh session.
If the subclass set a _save_to path (autosave configured at
construction), the broker snapshot is written before
gmsh.finalize(). Save failures are logged and swallowed —
the gmsh process must still finalize.