Constraints — g.constraints¶
Solver-agnostic kinematic-constraint engine. Constraints are
declared on geometry (part labels, optional entity scopes) and
resolved on the mesh by g.mesh.queries.get_fem_data.
Two-stage pipeline¶
Stage 1 — declare before meshing. The factory methods on
g.constraints (equal_dof, rigid_link, tie, …) store
ConstraintDef
dataclasses describing intent at the geometry level. These
definitions carry no node tags and survive remeshing.
Stage 2 — resolve after meshing.
ConstraintResolver
walks the def list and produces concrete
ConstraintRecord
objects (actual node tags, weights, offset vectors). Records land on
the FEM broker:
| Record family | Lives on |
|---|---|
NodePairRecord |
fem.nodes.constraints |
NodeGroupRecord |
fem.nodes.constraints |
NodeToSurfaceRecord |
fem.nodes.constraints |
InterpolationRecord |
fem.elements.constraints |
SurfaceCouplingRecord |
fem.elements.constraints |
Constraint taxonomy¶
Five tiers, ordered by topology:
| Tier | Methods | Record family |
|---|---|---|
| 1 — Pair | equal_dof, rigid_link, penalty |
NodePairRecord |
| 2 — Group | rigid_diaphragm, rigid_body, kinematic_coupling |
NodeGroupRecord |
| 2b — Mixed | node_to_surface, node_to_surface_spring |
NodeToSurfaceRecord |
| 3 — Surface | tie, distributing_coupling, embedded |
InterpolationRecord |
| 4 — Contact | tied_contact, mortar |
SurfaceCouplingRecord |
All constraints ultimately express the linear MPC equation
u_slave = C · u_master. Tiers differ in how C is built:
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 part label
(a key of g.parts._instances). _add_def validates both labels
against the registry and raises KeyError on a typo.
Optional master_entities / slave_entities arguments (lists of
(dim, tag)) narrow the search to a subset of the part's entities —
useful when a part has many surfaces and only one is the interface.
Exceptions to the part-label scheme:
node_to_surfaceandnode_to_surface_springtake bare tags instead — the master is a Gmsh point entity (dim=0) and the slave is one or more surface entities (dim=2).embeddeduseshost_label/embedded_labelto mirror Abaqus's vocabulary; the lookup logic otherwise matches the part-label scheme.
Worked example¶
from apeGmsh import apeGmsh
with apeGmsh(model_name="frame") as g:
# ... geometry + Parts already imported ...
# Tier 1 — co-located nodes share x/y/z
g.constraints.equal_dof("col", "beam", dofs=[1, 2, 3])
# Tier 2 — slab nodes follow a 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_floor", "solid_column",
master_entities=[(2, 17)],
slave_entities=[(2, 41)],
tolerance=5.0,
)
g.mesh.generation.generate(dim=3)
fem = g.mesh.queries.get_fem_data(dim=3)
# Grouped emission — accumulates rigid_beam / rigid_diaphragm /
# node_to_surface phantom links by master node.
for master, slaves in fem.nodes.constraints.rigid_link_groups():
for slave in slaves:
ops.rigidLink("beam", master, slave)
Composite¶
apeGmsh.core.ConstraintsComposite.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, :meth:mortar SurfaceCouplingRecord
============= ===================================================== =================================
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 part
label (a key of g.parts._instances). :meth:_add_def
validates both labels against the registry 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)])
Optional master_entities / slave_entities (list of
(dim, tag)) narrow the search to a subset of the part's
entities — useful when a part 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
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 :meth:g.loads.face_sp — 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.loads: 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 :meth:g.loads.face_sp. 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
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 label whose nodes drive the constraint.
slave_label : str
Part label 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
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 label 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 label 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
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 label that contains (or whose proximity will
select) the master node — typically a centre-of-mass
point.
slave_label : str
Part label 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
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 | |
rigid_body ¶
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 label that contains (or whose proximity selects) the master node. slave_label : str Part label whose nodes are gathered into the rigid body. master_point : (x, y, z), default (0, 0, 0) Coordinates of the master node. name : str, optional Friendly name.
Returns¶
RigidBodyDef
Raises¶
KeyError
If either label is not in g.parts.
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
kinematic_coupling ¶
kinematic_coupling(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), dofs=None, name=None) -> KinematicCouplingDef
Generalised one-master-many-slaves coupling on a chosen DOF subset.
The "parent" of :meth:rigid_diaphragm and :meth:rigid_body
— they are special cases with pre-set DOF lists. Use this
directly when you need a non-standard combination, e.g.::
* vertical-only follower: dofs=[3]
* 2-D in-plane rigid: dofs=[1, 2, 6]
* symmetry plane: dofs=[1, 4, 5]
Resolution emits a single
:class:~apeGmsh.solvers.Constraints.NodeGroupRecord.
Downstream this is typically expanded to one
ops.equalDOF per slave.
Parameters¶
master_label : str
Part label that owns the master node.
slave_label : str
Part label whose nodes are slaved.
master_point : (x, y, z), default (0, 0, 0)
Coordinates of the master node.
dofs : list[int], optional
1-based DOFs to couple. Default [1, 2, 3, 4, 5, 6]
(full 6-DOF, equivalent to :meth:rigid_body).
name : str, optional
Friendly name.
Returns¶
KinematicCouplingDef
Raises¶
KeyError
If either label is not in g.parts.
Source code in src/apeGmsh/core/ConstraintsComposite.py
tie ¶
tie(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, 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. Downstream the apeGmsh
OpenSees bridge emits these as ASDEmbeddedNodeElement
penalty elements (default K = 1e18; tunable on the bridge
ingest API).
Parameters¶
master_label : str
Part label of the master surface (the side whose mesh
will provide the shape functions).
slave_label : str
Part label 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 silently skipped — set generously if the two
meshes have a small geometric gap, but not so large
that the wrong face is selected. Unit-sensitive.
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 : Higher-accuracy variant via Lagrange multipliers.
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
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 | |
distributing_coupling ¶
distributing_coupling(master_label, slave_label, *, master_point=(0.0, 0.0, 0.0), dofs=None, weighting='uniform', name=None) -> DistributingCouplingDef
Not implemented — raises NotImplementedError.
A true distributing coupling (RBE3) distributes a master
force/moment to the slave nodes so that Σ Fᵢ = F and
Σ rᵢ × Fᵢ = M while leaving the surface free to deform —
a force relation, not a kinematic one.
The previous implementation did not do this: it emitted a
kinematic mean constraint (u_ref = Σ wᵢ u_surfᵢ) and its
weighting="area" option was inverse-distance-from-centroid
(physically meaningless — the opposite of tributary area),
with no moment-equilibrium term. It silently produced a
mechanically wrong model under a correct-looking API, so it is
refused rather than shipped.
Use instead, depending on intent:
- :meth:
kinematic_coupling— a DOF-selective rigid coupling of the surface to a reference node. - :meth:
tie— shape-function interpolation onto a master face (compatible, non-rigid). - a distributed nodal load (
g.loads) — to introduce a statically-equivalent load without any kinematic tie.
Raises¶
NotImplementedError Always. Parameters are accepted only so the message is actionable.
Source code in src/apeGmsh/core/ConstraintsComposite.py
embedded ¶
embedded(host_label, embedded_label, *, tolerance=1.0, host_entities=None, embedded_entities=None, 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.
Currently supports:
- 3-D host: tet4 (Gmsh element type 4) volumes.
- 2-D host: tri3 (Gmsh element type 2) surfaces.
Higher-order or hex/quad hosts are not yet supported and
will be silently skipped — fall back to :meth:tie if you
need that.
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 tet4/tri3 elements form the host
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
Reserved; not currently enforced (the resolver accepts
any located host record). Retained for API stability.
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.
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
)
Source code in src/apeGmsh/core/ConstraintsComposite.py
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
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 | |
tied_contact ¶
tied_contact(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, name=None) -> TiedContactDef
Bidirectional surface-to-surface tie.
Conceptually a :meth:tie applied in both directions —
slave nodes are projected onto the master surface, and
master nodes are also projected onto the slave surface, so
every node on either side is interpolated against the
opposite mesh. Useful when neither side can be picked as
clearly finer than the other and you want a symmetric
treatment.
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.
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 : Mathematically rigorous Lagrange-multiplier coupling.
Source code in src/apeGmsh/core/ConstraintsComposite.py
mortar ¶
mortar(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, integration_order=2, name=None) -> MortarDef
Not implemented — raises NotImplementedError.
A true mortar method introduces a Lagrange-multiplier space
ψᵢ on the slave side and integrates the coupling operator
Bᵢⱼ = ∫_Γ ψᵢ·Nⱼ dΓ over the overlapping surface segments,
satisfying the inf-sup (LBB) condition.
The previous implementation did none of that: no segment
intersection, no surface integral, no dual basis — it scattered
tied_contact collocation weights onto a block-diagonal
B with a hardcoded tolerance=10.0 (model-unit
dependent → mis-pairs on millimetre models), yet returned a
record labelled MORTAR that downstream could not
distinguish from a real one. It is refused rather than
shipped as a plausible-but-wrong operator.
Use :meth:tied_contact for a collocation-based non-matching
tie (the honest version of what the old code actually did).
Raises¶
NotImplementedError Always. Parameters are accepted only so the message is actionable.
Source code in src/apeGmsh/core/ConstraintsComposite.py
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
Base class¶
All Stage-1 definitions inherit from
ConstraintDef —
a thin dataclass carrying kind, master_label, slave_label, and
an optional friendly name. Subclasses add their kind-specific
parameters.
apeGmsh._kernel.defs.constraints.ConstraintDef
dataclass
¶
Base class for all constraint definitions.
Tier 1 — Node-to-Node¶
Pairwise constraints between co-located nodes. The resolver
matches master-side nodes against slave-side nodes within
tolerance and emits one NodePairRecord per match.
apeGmsh._kernel.defs.constraints.EqualDOFDef
dataclass
¶
EqualDOFDef(kind: str, master_label: str, slave_label: str, name: str | None = None, dofs: list[int] | None = None, tolerance: float = 1e-06, master_entities: list[tuple[int, int]] | None = None, slave_entities: list[tuple[int, int]] | None = None)
Bases: ConstraintDef
Co-located nodes share selected DOFs.
After meshing, the resolver finds node pairs within tolerance
on the interface between master and slave instances, and produces
one :class:NodePairRecord per pair.
Parameters¶
dofs : list[int] or None
DOF numbers to constrain (1-based: 1=ux, 2=uy, 3=uz,
4=rx, 5=ry, 6=rz). None = all DOFs.
tolerance : float
Spatial distance (in model units) within which two nodes
are considered co-located.
master_entities : list of (dim, tag), optional
Limit the master search to specific geometric entities.
slave_entities : list of (dim, tag), optional
Limit the slave search to specific geometric entities.
apeGmsh._kernel.defs.constraints.RigidLinkDef
dataclass
¶
RigidLinkDef(kind: str, master_label: str, slave_label: str, name: str | None = None, link_type: str = 'beam', master_point: tuple[float, float, float] | None = None, slave_entities: list[tuple[int, int]] | None = None, tolerance: float = 1e-06)
Bases: ConstraintDef
Rigid bar connecting master and slave nodes.
rigid_beam -> full 6-DOF coupling (translations + rotations)::
u_s = u_m + θ_m × r (translations)
θ_s = θ_m (rotations)
rigid_rod -> translations only, rotations independent::
u_s = u_m + θ_m × r
(θ_s free)
Parameters¶
link_type : "beam" or "rod"
master_point : (x,y,z) or None
If given, the master is the nearest node in the master set
to this point. If None, the master is the node nearest
the master set's centroid.
slave_entities : list of (dim, tag), optional
Geometric entities whose nodes become slaves.
tolerance : float
Reserved. Not currently enforced for master selection
(the nearest node is taken unconditionally); kept for API
stability and a future proximity-gated check.
apeGmsh._kernel.defs.constraints.PenaltyDef
dataclass
¶
PenaltyDef(kind: str, master_label: str, slave_label: str, name: str | None = None, stiffness: float = 10000000000.0, dofs: list[int] | None = None, tolerance: float = 1e-06, master_entities: list[tuple[int, int]] | None = None, slave_entities: list[tuple[int, int]] | None = None)
Bases: ConstraintDef
Soft spring between co-located node pairs.
Numerically approximates EqualDOF when K -> ∞. Useful when hard constraints cause ill-conditioning.
Parameters¶
stiffness : float Penalty spring stiffness (force/length units). dofs : list[int] or None DOFs to penalise. tolerance : float Node-matching tolerance.
Tier 2 — Node-to-Group¶
One master node drives many slave nodes through a kinematic transformation about a master point. Use these for floor diaphragms, lumped rigid bodies, or any cluster sharing a chosen DOF subset.
apeGmsh._kernel.defs.constraints.RigidDiaphragmDef
dataclass
¶
RigidDiaphragmDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] = (0.0, 0.0, 0.0), plane_normal: tuple[float, float, float] = (0.0, 0.0, 1.0), constrained_dofs: list[int] = (lambda: [1, 2, 6])(), plane_tolerance: float = 1.0)
Bases: ConstraintDef
In-plane rigid body constraint. All slave nodes at a given plane follow the master node for in-plane DOFs.
Classic use: floor slabs in multi-story buildings — all nodes at a floor elevation share in-plane translation + rotation about the out-of-plane axis.
Parameters¶
master_point : (x, y, z) Master node location (typically center of mass). plane_normal : (nx, ny, nz) Normal to the diaphragm plane. (0,0,1) = horizontal floor. constrained_dofs : list[int] DOFs constrained in-plane. For a horizontal floor with Z as vertical: [1, 2, 6] (ux, uy, rz). plane_tolerance : float Distance from the plane within which nodes are collected.
apeGmsh._kernel.defs.constraints.RigidBodyDef
dataclass
¶
RigidBodyDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] = (0.0, 0.0, 0.0), slave_entities: list[tuple[int, int]] | None = None)
Bases: ConstraintDef
Full rigid body constraint: all 6 DOFs of every slave node follow the master.
Parameters¶
master_point : (x, y, z) Master node location. slave_entities : list of (dim, tag), optional Geometric entities whose nodes become slaves.
apeGmsh._kernel.defs.constraints.KinematicCouplingDef
dataclass
¶
KinematicCouplingDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] = (0.0, 0.0, 0.0), slave_entities: list[tuple[int, int]] | None = None, dofs: list[int] = (lambda: [1, 2, 3, 4, 5, 6])())
Bases: ConstraintDef
Generalised master-slave: user picks which DOFs.
This is the parent of rigid_diaphragm and rigid_body — they are special cases with pre-set DOF lists.
Parameters¶
master_point : (x, y, z) Master node location. slave_entities : list of (dim, tag), optional Geometric entities whose nodes become slaves. dofs : list[int] DOFs to couple.
Tier 2b — Mixed-DOF¶
A 6-DOF master node coupled to 3-DOF slave nodes (typically a beam end framing into a solid face). The resolver duplicates each slave to a 6-DOF phantom node so that rotational kinematics can propagate through a rigid arm before being equal-DOF-coupled to the original 3-DOF slave.
Two variants:
NodeToSurfaceDefemits the master → phantom link as a kinematicrigidLink('beam', …)constraint. Cheap and exact.NodeToSurfaceSpringDefemits it as a stiffelasticBeamColumnelement. Use this when the master has free rotational DOFs that receive direct moment loading — the constraint variant can produce an ill-conditioned reduced stiffness matrix in that case.
apeGmsh._kernel.defs.constraints.NodeToSurfaceDef
dataclass
¶
NodeToSurfaceDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] | None = None, dofs: list[int] | None = None, tolerance: float = 1e-06)
Bases: ConstraintDef
6-DOF node to 3-DOF surface coupling via phantom (duplicate) nodes.
Connects a 6-DOF master node (beam, frame, or any reference point) to a group of 3-DOF slave nodes on a surface (solid elements) through an intermediate layer of phantom nodes that carry full 6-DOF kinematics.
The resolver:
- Duplicates each slave node -> creates phantom node tags at the same coordinates (6-DOF intermediaries).
-
Rigid links master -> each phantom node (
rigid_beam), propagating rotational effects through the offset arm::u_phantom = u_master + θ_master × r
-
EqualDOF phantom -> original slave, translations only
[1, 2, 3](rotations discarded since the solid has none).
This is the standard technique for mixed-dimensionality
coupling (Abaqus *COUPLING, KINEMATIC on solids; OpenSees
manual rigid-link + equalDOF pattern).
Unlike other constraint definitions that take string labels, this one accepts bare tags:
master_label: node tag (int, dim=0) — the 6-DOF node.slave_label: surface entity tag (int, dim=2) — the Gmsh surface whose nodes become the 3-DOF slaves.
Parameters¶
dofs : list[int] or None
Translational DOFs coupled to the solid. Default [1, 2, 3].
master_point : (x, y, z) or None
Ignored. The master is taken directly from the
master_label node tag (this def uses bare tags, see
above); there is no proximity master-detection. Retained
only for dataclass/API stability.
tolerance : float
Ignored for the same reason. Retained for API
stability.
apeGmsh._kernel.defs.constraints.NodeToSurfaceSpringDef
dataclass
¶
NodeToSurfaceSpringDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] | None = None, dofs: list[int] | None = None, tolerance: float = 1e-06)
Bases: NodeToSurfaceDef
Spring-based variant of :class:NodeToSurfaceDef.
Same topology as NodeToSurfaceDef — a 6-DOF master node is
coupled to the 3-DOF nodes of a surface through an intermediate
layer of phantom nodes — but the master → phantom link is emitted
downstream as a stiff elasticBeamColumn element instead of
a kinematic rigidLink('beam', …) constraint.
Why this variant exists¶
The standard NodeToSurfaceDef uses rigidLink + equalDOF. That
chain works perfectly for most cases — rigid load transfer,
prescribed translations at a master, fully-fixed masters — but
breaks down when all three of the following are true:
- The master has free rotational DOFs (fork support, free bending rotations at a simply-supported end).
- A moment is applied directly to those free rotation DOFs.
- The slave side is a solid element with
ndf=3(tet4, hex8, …), so the rigid-link constraint back-propagates stiffness to the master rotations only through kinematic coupling — no element attaches directly tomaster.ry/master.rz.
Under those conditions the reduced stiffness matrix becomes ill-conditioned and OpenSees's solver fails with "numeric analysis returns 1 -- UmfpackGenLinSolver::solve".
The spring variant fixes it by giving the master's rotation DOFs
direct element stiffness: each master → phantom link becomes
a stiff elasticBeamColumn element whose 6-DOF stiffness matrix
contributes terms on the master's rotation diagonal regardless of
any constraint handler gymnastics. Conditioning stays good.
Trade-offs¶
- Pro — robust for fork supports + moment loading.
- Pro — element-level stiffness is directly assembled into K, so no penalty factor to tune.
- Con — each master → phantom link is now an element, so
the element count grows by
n_slavesper coupling. For a typical face with ~30 slave nodes this is ~30 extraelasticBeamColumnelements pernode_to_surface_springcall. Negligible in solve time. - Con — approximate-rigid rather than truly rigid: the stiff beams have finite stiffness, so there is a tiny compliance in the coupling. Choose the section properties so they are orders of magnitude stiffer than the downstream elements.
Parameters¶
Inherited from :class:NodeToSurfaceDef.
See Also¶
NodeToSurfaceDef : constraint-based variant.
Tier 3 — Node-to-Surface¶
A slave node is constrained to the displacement field of a master surface or volume through shape-function interpolation. Handles non-matching meshes, distributed loads, and embedded reinforcement.
apeGmsh._kernel.defs.constraints.TieDef
dataclass
¶
TieDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_entities: list[tuple[int, int]] | None = None, slave_entities: list[tuple[int, int]] | None = None, dofs: list[int] | None = None, tolerance: float = 1.0)
Bases: ConstraintDef
Surface tie via shape function interpolation.
Each slave node is projected onto the closest master element face. Its DOFs are constrained to the master face via::
u_slave = Σ N_i(ξ,η) · u_master_i
where N_i are the shape functions of the master face element evaluated at the projected parametric coordinates.
This is what Abaqus *TIE does. It preserves displacement
continuity even with non-matching meshes.
Parameters¶
master_entities : list of (dim, tag) Master surface entities. slave_entities : list of (dim, tag) Slave surface entities (nodes on these are projected). dofs : list[int] or None DOFs to tie. None = all translational DOFs [1,2,3]. tolerance : float Maximum projection distance. Slave nodes farther than this from the master surface are skipped.
apeGmsh._kernel.defs.constraints.DistributingCouplingDef
dataclass
¶
DistributingCouplingDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_point: tuple[float, float, float] = (0.0, 0.0, 0.0), slave_entities: list[tuple[int, int]] | None = None, dofs: list[int] | None = None, weighting: str = 'uniform')
Bases: ConstraintDef
Distributing coupling (RBE3-style force distribution).
.. warning::
Not implemented. g.constraints.distributing_coupling
raises NotImplementedError. A correct RBE3 distributes a
master force/moment so that ΣF and Σr×F are preserved
while the surface deforms freely; the prior implementation was
a mislabelled kinematic mean (and its "area" weighting
was inverse-distance-from-centroid, not tributary area). This
dataclass is retained for a future correct implementation.
Parameters¶
master_point : (x, y, z)
Reference point where the load is applied.
slave_entities : list of (dim, tag)
Surface entities that receive the distributed load.
dofs : list[int] or None
DOFs to couple.
weighting : "uniform" or "area"
How to distribute: uniform gives equal weights; area
weights by tributary area (more physical).
apeGmsh._kernel.defs.constraints.EmbeddedDef
dataclass
¶
EmbeddedDef(kind: str, master_label: str, slave_label: str, name: str | None = None, host_entities: list[tuple[int, int]] | None = None, embedded_entities: list[tuple[int, int]] | None = None, tolerance: float = 1.0)
Bases: ConstraintDef
Embedded element constraint: nodes of a lower-dimensional element (beam, truss) are constrained to the displacement field of a higher-dimensional host element (solid).
Used for reinforcement in concrete, stiffeners in shells, etc.
Parameters¶
host_entities : list of (dim, tag), optional
Host volume/surface entities. Settable via
g.constraints.embedded(..., host_entities=...); when
omitted the whole host_label is used.
embedded_entities : list of (dim, tag), optional
Embedded line/surface entities. Settable via
embedded(..., embedded_entities=...); when omitted the
whole embedded_label is used.
tolerance : float
Reserved. Not currently enforced — the resolver accepts
any located host record (barycentric gating uses a fixed
internal tolerance). Kept for API stability.
Tier 4 — Surface-to-Surface¶
Bidirectional surface couplings. Use these when neither side can be clearly picked as finer than the other and you want a symmetric treatment.
apeGmsh._kernel.defs.constraints.TiedContactDef
dataclass
¶
TiedContactDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_entities: list[tuple[int, int]] | None = None, slave_entities: list[tuple[int, int]] | None = None, dofs: list[int] | None = None, tolerance: float = 1.0)
Bases: ConstraintDef
Full surface-to-surface tie. Every node on the slave surface is tied to the master surface via shape function interpolation. Bidirectional — also checks master nodes against slave faces.
Parameters¶
master_entities : list of (dim, tag) slave_entities : list of (dim, tag) dofs : list[int] or None tolerance : float
apeGmsh._kernel.defs.constraints.MortarDef
dataclass
¶
MortarDef(kind: str, master_label: str, slave_label: str, name: str | None = None, master_entities: list[tuple[int, int]] | None = None, slave_entities: list[tuple[int, int]] | None = None, dofs: list[int] | None = None, integration_order: int = 2)
Bases: ConstraintDef
Mortar coupling: Lagrange-multiplier space on the interface.
.. warning::
Not implemented. g.constraints.mortar raises
NotImplementedError. A correct mortar operator is
Bᵢⱼ = ∫_Γ ψᵢ·Nⱼ dΓ (segment integration, dual basis,
inf-sup/LBB). The prior implementation was a tied_contact
collocation tie with a hardcoded unit-dependent
tolerance=10.0 mislabelled MORTAR. This dataclass is
retained for a future correct implementation; use
tied_contact for a collocation-based non-matching tie.
Parameters¶
master_entities : list of (dim, tag) slave_entities : list of (dim, tag) dofs : list[int] or None integration_order : int Gauss quadrature order for the coupling integral.
Records¶
Resolved records — what the FEM broker exposes after meshing.
apeGmsh._kernel.records._constraints ¶
Stage 2 — Constraint Records (post-mesh, resolved).
These dataclasses carry the concrete mesh-level outputs of constraint resolution: node tags, shape-function weights, offset vectors, and phantom-node bookkeeping. Records are solver-agnostic — any adapter (OpenSees, Abaqus, Code_Aster, …) can consume them.
All records ultimately express the linear MPC equation::
u_slave = C · u_master
ConstraintRecord
dataclass
¶
Base for all resolved constraint records.
Every record expresses (or can be expanded to) the general linear MPC equation: u_slave = C · u_master.
NodePairRecord
dataclass
¶
NodePairRecord(kind: str, name: str | None = None, master_node: int = 0, slave_node: int = 0, dofs: list[int] = list(), offset: ndarray | None = None, penalty_stiffness: float | None = None)
Bases: ConstraintRecord
One master node ↔ one slave node.
Covers: equal_dof, rigid_beam, rigid_rod, penalty.
Attributes¶
master_node : int
Master node tag (from mesh).
slave_node : int
Slave node tag (from mesh).
dofs : list[int]
Constrained DOFs (1-based).
offset : ndarray or None
Rigid arm vector r = x_slave − x_master. Present for
rigid link types; None for equal_dof.
penalty_stiffness : float or None
For penalty type only.
constraint_matrix ¶
Build the constraint transformation matrix C such that u_slave[dofs] = C · u_master[all_dofs].
For equal_dof: C is a selection matrix (rows of identity). For rigid_beam: C includes the skew-symmetric offset matrix.
Parameters¶
ndof : int DOFs per node (default 6 for shell/beam).
Returns¶
ndarray of shape (len(dofs), ndof)
Source code in src/apeGmsh/_kernel/records/_constraints.py
NodeGroupRecord
dataclass
¶
NodeGroupRecord(kind: str, name: str | None = None, master_node: int = 0, slave_nodes: list[int] = list(), dofs: list[int] = list(), offsets: ndarray | None = None, plane_normal: ndarray | None = None)
Bases: ConstraintRecord
One master node ↔ multiple slave nodes.
Covers: rigid_diaphragm, rigid_body,
kinematic_coupling.
Attributes¶
master_node : int slave_nodes : list[int] dofs : list[int] DOFs constrained for all slaves. offsets : ndarray Array of shape (n_slaves, 3) — offset vector for each slave. plane_normal : ndarray or None For rigid_diaphragm: normal to the constraint plane.
expand_to_pairs ¶
Expand this group constraint into individual
:class:NodePairRecord objects — one per slave node.
This is the most common consumption path: most solvers
implement group constraints as loops of pair constraints
(e.g., OpenSees rigidDiaphragm or repeated equalDOF).
Source code in src/apeGmsh/_kernel/records/_constraints.py
InterpolationRecord
dataclass
¶
InterpolationRecord(kind: str, name: str | None = None, slave_node: int = 0, master_nodes: list[int] = list(), weights: ndarray | None = None, dofs: list[int] = list(), projected_point: ndarray | None = None, parametric_coords: ndarray | None = None)
Bases: ConstraintRecord
One slave node interpolated from a master element face.
Covers: tie, distributing, embedded.
The constraint equation is::
u_slave = Σ w_i · u_master_i
where w_i are the interpolation weights (shape function values at the projected parametric coordinates on the master face).
Attributes¶
slave_node : int
master_nodes : list[int]
Nodes of the master element face (ordered).
weights : ndarray
Shape function values N_i(ξ,η) — same length as
master_nodes. Sum to 1.0 for partition of unity.
dofs : list[int]
projected_point : ndarray or None
Physical coordinates of the projection onto the master face
(useful for verification / visualisation).
parametric_coords : ndarray or None
(ξ, η) on the master face.
constraint_matrix ¶
Build the constraint matrix C of shape (ndof, n_master_nodes * ndof).
u_slave[i] = Σ_j w_j · u_master_j[i] for each DOF i
Source code in src/apeGmsh/_kernel/records/_constraints.py
SurfaceCouplingRecord
dataclass
¶
SurfaceCouplingRecord(kind: str, name: str | None = None, slave_records: list[InterpolationRecord] = list(), mortar_operator: ndarray | None = None, master_nodes: list[int] = list(), slave_nodes: list[int] = list(), dofs: list[int] = list())
Bases: ConstraintRecord
Surface-to-surface coupling operator.
Covers: tied_contact, mortar.
The coupling is stored as a sparse set of interpolation records (one per slave node for tied_contact), or as the full mortar operator matrix B.
Attributes¶
slave_records : list[InterpolationRecord] Per-slave-node interpolation data (for tied_contact). mortar_operator : ndarray or None Dense coupling matrix B (for mortar method). Shape: (n_slave_dofs, n_master_dofs). master_nodes : list[int] All master nodes involved. slave_nodes : list[int] All slave nodes involved. dofs : list[int]
NodeToSurfaceRecord
dataclass
¶
NodeToSurfaceRecord(kind: str, name: str | None = None, master_node: int = 0, slave_nodes: list[int] = list(), phantom_nodes: list[int] = list(), phantom_coords: ndarray | None = None, rigid_link_records: list[NodePairRecord] = list(), equal_dof_records: list[NodePairRecord] = list(), dofs: list[int] = (lambda: [1, 2, 3])())
Bases: ConstraintRecord
Compound record for 6-DOF node to 3-DOF surface coupling via phantom nodes.
This record encapsulates the three-step coupling:
- Phantom nodes duplicated from the original slave positions.
- Rigid links from the 6-DOF master to each phantom node.
- EqualDOF from each phantom node to the original slave (translations only).
Solvers consume this by:
- Creating the phantom nodes (6-DOF, same coords as slaves).
- Emitting rigid_beam constraints master -> phantom.
- Emitting equal_dof constraints phantom -> slave for DOFs [1,2,3].
Attributes¶
master_node : int The 6-DOF master node tag. slave_nodes : list[int] Original 3-DOF slave node tags (from the surface mesh). phantom_nodes : list[int] Generated 6-DOF phantom node tags (one per slave, same coordinates). Tag generation is handled by the resolver using an offset above the maximum existing node tag. phantom_coords : ndarray Coordinates of phantom nodes, shape (n_slaves, 3). Identical to the slave coordinates. rigid_link_records : list[NodePairRecord] Master -> phantom rigid beam records (with offset vectors). equal_dof_records : list[NodePairRecord] Phantom -> slave equalDOF records (translations only). dofs : list[int] Translational DOFs coupled to the surface (default [1,2,3]).
expand ¶
Flatten into individual :class:NodePairRecord objects.
Returns the rigid link records followed by the equalDOF records — the natural emission order for solvers.
Source code in src/apeGmsh/_kernel/records/_constraints.py
Resolver¶
apeGmsh._kernel.resolvers._constraint_resolver._resolver.ConstraintResolver ¶
ConstraintResolver(node_tags: ndarray, node_coords: ndarray, elem_tags: ndarray | None = None, connectivity: ndarray | None = None)
Converts constraint definitions into resolved records.
The resolver works with raw numpy arrays of node coordinates and connectivity — it does NOT depend on Gmsh or any solver. This makes it fully portable.
Parameters¶
node_tags : ndarray, shape (n_nodes,)
Node tags (IDs) from the mesh.
node_coords : ndarray, shape (n_nodes, 3)
Nodal coordinates.
elem_tags : ndarray, shape (n_elems,)
Element tags.
connectivity : ndarray, shape (n_elems, n_nodes_per_elem)
Element connectivity (node tags).
face_connectivity : list of ndarray, optional
Element face connectivity for surface elements.
If None, the resolver extracts faces from the
volume connectivity.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_equal_dof ¶
resolve_equal_dof(defn: EqualDOFDef, master_nodes: set[int], slave_nodes: set[int]) -> list[NodePairRecord]
Resolve an EqualDOF definition into node pair records.
Parameters¶
defn : EqualDOFDef master_nodes : set[int] Node tags belonging to the master instance. slave_nodes : set[int] Node tags belonging to the slave instance.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_rigid_link ¶
resolve_rigid_link(defn: RigidLinkDef, master_nodes: set[int], slave_nodes: set[int]) -> list[NodePairRecord]
Resolve a rigid link definition.
If master_point is specified, find the closest master node.
Then link all slave nodes to that master via rigid offset.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_penalty ¶
resolve_penalty(defn: PenaltyDef, master_nodes: set[int], slave_nodes: set[int]) -> list[NodePairRecord]
Resolve a penalty definition into node pair records.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_rigid_diaphragm ¶
Resolve a rigid diaphragm.
Collects all nodes within plane_tolerance of the diaphragm
plane, then the closest to master_point becomes master.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_kinematic_coupling ¶
resolve_kinematic_coupling(defn: KinematicCouplingDef | RigidBodyDef, master_nodes: set[int], slave_nodes: set[int]) -> NodeGroupRecord
Resolve kinematic coupling or rigid body constraint.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_tie ¶
resolve_tie(defn: TieDef, master_face_conn: ndarray, slave_nodes: set[int]) -> list[InterpolationRecord]
Resolve a surface tie via closest-point projection.
For each slave node, find the closest master face, project onto it, and compute shape function weights.
Parameters¶
defn : TieDef master_face_conn : ndarray, shape (n_faces, n_nodes_per_face) Connectivity of master surface element faces (node tags). slave_nodes : set[int] Slave node tags to project.
Returns¶
list[InterpolationRecord]
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
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 | |
resolve_distributing ¶
resolve_distributing(defn: DistributingCouplingDef, master_nodes: set[int], slave_nodes: set[int]) -> InterpolationRecord
Not implemented — raises NotImplementedError.
Defence-in-depth: the distributing_coupling factory
already refuses (see ConstraintsComposite). This guards the
case where a DistributingCouplingDef is hand-constructed
and dispatched directly — it must not silently emit the old
mechanically-wrong kinematic-mean record.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_tied_contact ¶
resolve_tied_contact(defn: TiedContactDef, master_face_conn: ndarray, slave_face_conn: ndarray, master_nodes: set[int], slave_nodes: set[int]) -> SurfaceCouplingRecord
Resolve a surface-to-surface tie — one-directional.
Slave-surface nodes are interpolated onto the master faces
(the standard tied-contact / Abaqus *TIE convention: the
slave conforms to the master, which is the reference).
The previous implementation also projected master nodes onto
slave faces and concatenated both directions — a node could
then be a slave in one direction and a master-face node in
the other, producing cyclic / over-determined MPCs the
constraint handler cannot satisfy. slave_face_conn is
accepted for dispatch-signature stability but unused.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_mortar ¶
resolve_mortar(defn: MortarDef, master_face_conn: ndarray, slave_face_conn: ndarray, master_nodes: set[int], slave_nodes: set[int]) -> SurfaceCouplingRecord
Not implemented — raises NotImplementedError.
Defence-in-depth: the mortar factory already refuses (see
ConstraintsComposite). This guards a hand-constructed
MortarDef dispatched directly — it must not silently emit
the old collocation-tie operator mislabelled MORTAR with a
unit-dependent hardcoded tolerance.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_node_to_surface ¶
resolve_node_to_surface(defn: NodeToSurfaceDef, master_tag: int, slave_nodes: set[int]) -> NodeToSurfaceRecord
Resolve a 6-DOF node to 3-DOF surface coupling.
Steps:
- Use the master node tag directly (already resolved from
master_labelas bare node tag). - Generate phantom node tags — one per slave, starting at
max(all_existing_tags) + 1. - Build rigid-beam records: master -> each phantom.
- Build equalDOF records: each phantom -> original slave (translations only).
Parameters¶
defn : NodeToSurfaceDef master_tag : int The 6-DOF master node tag (dim=0). slave_nodes : set[int] Node tags belonging to the slave surface (dim=2, 3-DOF).
Returns¶
NodeToSurfaceRecord
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
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 | |
resolve_embedded ¶
resolve_embedded(defn, host_elems: ndarray, embedded_nodes: set[int] | list[int]) -> list[InterpolationRecord]
Resolve an embedded-element constraint.
Each embedded node is located inside a host element (tri3 in
2D or tet4 in 3D) via barycentric coordinates. The resulting
shape-function weights couple the embedded node to the host
element's corner nodes, matching the kinematics of
ASDEmbeddedNodeElement in OpenSees.
Parameters¶
defn : EmbeddedDef
Only defn.tolerance and defn.name are consulted.
host_elems : ndarray, shape (n_elems, 3 | 4)
Node-tag connectivity of the host elements. A row of 3
is treated as tri3; a row of 4 is treated as tet4.
embedded_nodes : iterable of int
Node tags to embed.
Returns¶
list[InterpolationRecord] One record per embedded node successfully located.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
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 | |
resolve_node_to_surface_spring ¶
resolve_node_to_surface_spring(defn: 'NodeToSurfaceSpringDef', master_tag: int, slave_nodes: set[int]) -> NodeToSurfaceRecord
Resolve a spring-variant 6-DOF → 3-DOF surface coupling.
Identical phantom-node generation and equalDOF records as
:meth:resolve_node_to_surface. The only difference is that
the master → phantom rigid-link records are tagged with
kind='rigid_beam_stiff' so they are routed through
stiff_beam_groups() at emission time (becoming stiff
elasticBeamColumn elements) instead of
rigid_link_groups() (which would emit rigidLink and
hit the ill-conditioning described in
:class:NodeToSurfaceSpringDef).
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
Module shim¶
The top-level apeGmsh.core.ConstraintsComposite module re-exports all
public names from the _constraint_* modules for backwards
compatibility. Module-level docstring contains the canonical
taxonomy.
apeGmsh.core.ConstraintsComposite ¶
ConstraintsComposite -- Define and resolve kinematic constraints.
Two-stage pipeline:
- Define (pre-mesh): factory methods store :class:
ConstraintDefobjects describing geometric intent. - Resolve (post-mesh): :meth:
resolvedelegates to :class:ConstraintResolver(insolvers/Constraints.py) with caller-provided node/face maps. Dependency-injected -- this module never imports PartsRegistry.
Usage::
g.constraints.equal_dof("beam", "slab", tolerance=1e-3)
g.constraints.tie("beam", "slab", master_entities=[(2, 5)])
fem = g.mesh.queries.get_fem_data(dim=2)
nm = g.parts.build_node_map(fem.nodes.ids, fem.nodes.coords)
fm = g.parts.build_face_map(nm)
recs = g.constraints.resolve(
fem.nodes.ids, fem.nodes.coords, node_map=nm, face_map=fm,
)