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¶
Seven 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 |
SurfaceCouplingRecord |
| 5 — Fork | contact, contact_plane, mortar (deprecated alias for contact(formulation="mortar", tie=True)) |
ContactRecord, ContactPlaneRecord |
| 6 — Interface | interface |
InterfaceRecord |
Tiers 1 to 4 ultimately express the linear MPC equation
u_slave = C · u_master, and 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). A labelled g.decouple_node (or
its handle) is a valid RBE2/RBE3 master_label so BCs land on an
ndf=6 work point. Tiers 5 and 6 are not
equations at all — they resolve onto their own additive side-lists
(fem.elements.contacts, fem.elements.interfaces) and emit
elements, which is how they can carry a force that drops to zero.
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 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
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, as_element: bool = False, mass: float | None = None, omega: tuple[float, float, float] | None = None)
Bases: ConstraintDef
Full rigid body constraint: all 6 DOFs of every slave node follow the master.
By default the body is emitted as a chain of rigidLink "beam"
constraints (master → each slave). Set as_element=True to emit the
fork element LadrunoRigidBody instead (class tag 33015, 3D
only): the whole node set {master, *slaves} becomes one 6-DOF
rigid body with a private internal centre-of-mass node and condensed
mass — which the rigidLink chain cannot represent (no body mass, no
CoM, no explicit-dynamics support). Fork-only: the element line
emits on any build but needs the Ladruno fork to run.
Parameters¶
master_point : (x, y, z)
Master node location.
slave_entities : list of (dim, tag), optional
Geometric entities whose nodes become slaves.
as_element : bool, default False
Emit element LadrunoRigidBody over {master, *slaves} (3D
only) instead of the rigidLink chain.
mass : float or None
Total body mass for the as_element form (-mass); None
condenses the mass from the slaves' own nodal mass. Ignored by the
rigidLink form (raises if set without as_element).
omega : (wx, wy, wz) or None
Initial body-frame angular velocity for the as_element form
(-omega, an explicit-dynamics initial condition — the body
spins from t=0). None ⇒ no initial spin. Only valid with
as_element=True.
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] | None = None, control: CouplingControl = CouplingControl())
Bases: ConstraintDef
RBE2 / kinematic coupling — a reference (master) node rigidly drives a set of slave nodes.
Emitted as 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 handled rigidly — unlike the old equalDOF expansion,
which ignored the lever arm). Fork-only: the deck emits on any
build, but running it needs the Ladruno fork; stock OpenSees fails
loud at the element line.
Parameters¶
master_point : (x, y, z)
Reference (master) node location — must carry the rotational DOFs
(ndf 6 in 3D / 3 in 2D); the fork refuses a too-small reference.
slave_entities : list of (dim, tag), optional
Geometric entities whose nodes become slaves (may mix 3- and
6-DOF nodes — the element resolves the ragged layout).
dofs : list[int] or None
1-based dependent components to tie on each slave (-dof).
None (default) ties every DOF the slave has (the element's
own default), which is the right behaviour for a mixed 3/6-DOF
slave set; pass an explicit list to restrict (e.g. [1, 2, 3]
for translations only).
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.
Embedded — host mesh compatibility¶
g.constraints.embedded(host_label, embedded_label, ...) accepts any
standard structural mesh on the host side. The collector decomposes
non-simplex and higher-order hosts into linear sub-tris / sub-tets
using corner nodes only, then dispatches to the existing C++
ASDEmbeddedNodeElement (which accepts 3- or 4-node retained sets).
| Gmsh etype | Code | Host-side decomposition |
|---|---|---|
| tri3 (CST) | 2 | identity (1 tri per host) |
| tet4 | 4 | identity (1 tet per host) |
| quad4 | 3 | 2 tris via (0,2) diagonal split |
| hex8 | 5 | 6 right-handed Kuhn tets (shared main diagonal) |
| prism6 | 6 | 3 tets |
| pyramid5 | 7 | 2 tets |
| tri6 (LST) | 9 | corners only → 1 tri (midsides discarded) |
| tet10 | 11 | corners only → 1 tet |
| pyramid13 | 14 | corners only → 2 tets |
| quad8 / quad9 | 16/10 | corners only → 2 tris |
| hex20 | 17 | corners only → 6 Kuhn tets |
| prism15 | 18 | corners only → 3 tets |
Sub-element rows are virtual — they do not correspond to elements
in the gmsh mesh. They exist purely as a coupling-layer fabrication
so the linear-shape-function coupling of ASDEmbeddedNodeElement
works against any supported host topology.
The linear-coupling contract (host_coupling="linear")¶
The embedded coupling is always linear over 3 or 4 corner nodes, regardless of the host's native interpolation order. An LST plate's quadratic curvature, a hex8's bilinear twist mode, a quad9's biquadratic field — none are seen by the embedded node. The embed sees only the linear corner-to-corner stretch of whichever sub-tri / sub-tet contains it.
EmbeddedDef.host_coupling is a reserved keyword that pins this
behaviour. Only "linear" is currently accepted. The keyword is
reserved (not just documented) so a future "trilinear" /
"biquadratic" option — which would require a new OpenSees element
class supporting N-node retained sets — can land without breaking
existing models.
Warning on midside-bearing hosts¶
The first time the collector decomposes a host that carries midside
nodes (tri6, tet10, quad8, quad9, hex20, prism15, pyramid13), one
UserWarning fires per (etype, entity) pointing at the
linear-coupling consequence. Acknowledge by setting
host_coupling="linear" explicitly on the embedded(...) call.
If you chose LST / quad8 / hex20 specifically for curvature fidelity,
the embed will not give it to you — either accept the linear coupling
or wait for the HostProjector work (deferred; see ADR 0036).
Per-hex coupling asymmetry¶
Two embedded nodes inside the same hex8 may couple to different 4-corner subsets depending on which of the 6 Kuhn sub-tets contains each one. This is geometrically correct under linear coupling but can surprise readers of the resolved records. The Kuhn decomposition is symmetric (orientation-independent across adjacent hexes), so there is no neighbour-hex-dependence in the choice.
Mixed-dim host fail-loud¶
A host part / physical group that combines 2D entities (shell, quad
plate) and 3D entities (brick, tet volume) raises at collection
time. The linear coupling cannot pick between sub-tris and sub-tets
deterministically (kNN centroid search would dispatch based on
opaque proximity, which is opaque physics). Split the host into two
separate g.constraints.embedded(...) calls — one for the 2D part,
one for the 3D part.
Off-host fail-loud¶
An embedded node that falls outside every host sub-element by more
than EmbeddedDef.tolerance (default 1.0 from the factory; the
class default is 0.0 for strictly-inside) raises naming the
offending slave node and its barycentric excess. Either fix the
geometry / mesh so the embed lies inside the host, or widen
tolerance= explicitly if extrapolation is intentional.
See ADR 0036
for the full decision record (Kuhn-table orientation invariants,
alternatives rejected, HostProjector RFC deferral).
Example — rebar in hex-meshed concrete¶
from apeGmsh import apeGmsh
with apeGmsh(model_name="rc_block") as g:
# ... CAD import, parts, etc. ...
# Hex-meshed concrete host, line-meshed rebar curve
g.constraints.embedded(
host_label="concrete_block_hex",
embedded_label="rebar_curve",
stiffness=1.0e8, # STKO-parity penalty (ADR 0035)
# host_coupling="linear" is the default; setting it
# explicitly acknowledges the linear-coupling contract
# if your host carries midside nodes.
)
g.mesh.generation.generate(dim=3)
fem = g.mesh.queries.get_fem_data(dim=3)
# Embedded records land on fem.elements.constraints as
# InterpolationRecord; each rebar node couples to 4 of the
# 8 corners of the hex that contains it (one of 6 Kuhn
# sub-tets — see the per-hex asymmetry note above).
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, stiffness: 'float | str' = 'auto', stiffness_p: float | None = None, rotational: bool = False, pressure: bool = False, enforce: str = 'penalty', control: CouplingControl | None = None, method: str = 'collocation', outward: tuple[float, float, float] | None = None)
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, weighting: str = 'uniform', control: CouplingControl = CouplingControl())
Bases: ConstraintDef
RBE3 / distributing coupling — a reference (dependent) node is the weighted-average rigid-body fit of a set of independent nodes, and a load applied at the reference is distributed to the set as a statically-equivalent force pattern, adding no stiffness to the independents (the set stays free to deform).
Emitted as the Ladruno-fork element LadrunoDistributingCoupling
(class tag 33011). It is the inverse-role sibling of
:class:KinematicCouplingDef (RBE2): there the single node is the
rigid master; here it is the flexible dependent. Fork-only:
the deck emits on any build, but running it needs the Ladruno fork.
Parameters¶
master_point : (x, y, z)
Location of the reference (dependent) node R — must carry the
rotational DOFs (ndf 6 in 3D / 3 in 2D) so a moment transmits.
slave_entities : list of (dim, tag), optional
Geometric entities whose nodes become the independent set
(translations-only is fine — the fit injects no rotational
stiffness into them).
weighting : "uniform" | "area"
"uniform" ⇒ equal weights (-w omitted ⇒ the fork
element's equal-weight default). "area" ⇒ the resolver
computes per-independent tributary areas over the slave
surface faces (each face's area split equally among its nodes —
the g.loads surface-tributary lumping model) and stores
them on the record's weights ⇒ -w w1..wN emitted in the
sorted independent order.
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 = 0.0, stiffness: 'float | str' = 'auto', stiffness_p: float | None = None, rotational: bool = False, pressure: bool = False, host_coupling: str = 'linear')
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
Maximum dimensionless barycentric excess allowed when
locating an embedded node inside a host element. 0.0
(the default) means strictly inside; 0.05 allows ~5%
extrapolation; inf accepts everything (the pre-Phase-2
behaviour). An embedded node whose excess exceeds this
threshold raises ValueError from the resolver naming the
offending slave node and its excess — fail-loud, since
accepting an extrapolated node silently produces an
ASDEmbeddedNodeElement with negative shape-function
weights and the wrong physics.
host_coupling : {"linear"}
Reserved keyword that pins the coupling kinematics for this
embed. Only "linear" is currently accepted: the embedded
node is coupled to 3 or 4 corner nodes of a host tri/tet
sub-element via linear barycentric shape functions, matching
the kinematics of OpenSees ASDEmbeddedNodeElement.
For non-simplex / higher-order hosts (tri6, tet10, quad4,
quad8, quad9, hex8, hex20, prism6, prism15, pyramid5,
pyramid13) the
``ConstraintsComposite._collect_host_subelements`` collector
decomposes the host into linear sub-tris / sub-tets using
corner nodes only and ignores midside nodes. Consequence:
the embedded coupling does NOT see the host's native
bilinear / trilinear / quadratic displacement field — only
a linear projection over the corner subset that brackets
the embedded point.
Per-hex asymmetry: two embedded nodes inside the same hex8
may couple to *different* 4-corner subsets depending on
which of the 6 Kuhn sub-tets contains each one. This is
geometrically correct under linear coupling but can surprise
readers of the resolved records.
The keyword is reserved (not just documented) so that a
future ``"trilinear"`` / ``"biquadratic"`` option can be
added without changing the public API; pre-existing models
will keep producing identical numerical results because
``"linear"`` stays the default.
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, stiffness: 'float | str' = 'auto', stiffness_p: float | None = None, rotational: bool = False, pressure: bool = False, enforce: str = 'penalty', control: CouplingControl | None = None)
Bases: ConstraintDef
Full surface-to-surface tie. Every node on the slave surface is tied to the master surface via shape function interpolation.
One-directional (slave conforms to master): an earlier bidirectional variant was removed because projecting master nodes onto slave faces as well produced cyclic / over-determined MPCs the constraint handler cannot satisfy.
Parameters¶
master_entities : list of (dim, tag) slave_entities : list of (dim, tag) dofs : list[int] or None tolerance : float
Tier 5 — Fork contact¶
Every tier above is a permanent bond, active from the first step.
g.constraints.contact(master, slave, ...) is the one that can open,
close, slide and carry friction — a real contact interaction rather
than a kinematic constraint, emitted through the Ladruno fork's
contact subsystem. "nts" is node-to-segment penalty; "mortar" is
segment-to-segment augmented Lagrange, the accuracy lane for
non-matching interfaces.
g.constraints.contact_plane(slave, ...) is the same idea with no
master mesh at all: the slave meets a fixed infinite rigid plane.
Both resolve additively onto fem.elements.contacts /
fem.elements.contact_planes rather than the MP-constraint channels,
both are serial-only, and both need a live gmsh session — declaring one
on a from_h5 or composed session raises. The deck emits on any build
but runs only on the fork. Contact has no recorder channel, so results
come back through the live queries described in
the constraints concept page.
apeGmsh._kernel.defs.constraints.ContactDef
dataclass
¶
ContactDef(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, formulation: str = 'nts', kn: float | str | None = None, kt: float | None = None, mu: float | None = None, eps_n: float | str | None = None, eps_t: float | str | None = None, cohesion: float | None = None, tau_max: float | None = None, aug_tol: float | None = None, max_aug: int | None = None, ngp: int | None = None, tie: bool = False, thickness: float | None = None, outward: tuple | None = None, soft: float | bool | None = None, visc: float | None = None, consistent_tan: bool = False, geom_tan: bool = False, cell: float | None = None, edge_edge: bool = False, edge_kn: float | str | None = None, edge_band: float | None = None, edge_mu: float | None = None, edge_kt: float | None = None, edge_cohesion: float | None = None, edge_tau_max: float | None = None, edge_consistent_tan: bool = False, edge_soft: float | bool | None = None, edge_alm: bool = False, edge_aug_tol: float | None = None)
Bases: ConstraintDef
Face-to-face contact between two meshed surfaces, emitted as the fork's
contactSurface + contact pair (node-to-segment or mortar/ALM).
The geometry-side def captured by g.constraints.contact(...). The
master is a faceted surface; the slave is a node set (NTS) or a faceted
surface (mortar). apeGmsh resolves the face connectivity + (optionally) an
outward normal from the CAD geometry, then emits contactSurface defs +
the contact verb + constraints('LadrunoContact').
Parameters¶
master_label, slave_label
The two surface PG / part labels in contact.
formulation
"nts" (node-to-segment penalty, slave = node set) or "mortar"
(segment-to-segment ALM, slave = faceted surface).
kn, kt, mu
NTS normal/tangential penalty + Coulomb friction. kn may be
"auto" (sized from the solid). Mortar rejects kn/kt.
eps_n, eps_t
Mortar ALM normal/tangential penalty ("auto" allowed). NTS rejects.
cohesion, tau_max
Mortar friction-cone adhesion intercept + Tresca shear cap.
aug_tol, max_aug, ngp
Mortar Uzawa augmentation tolerance / max augmentations / slave-facet
Gauss order.
tie
Permanent mesh-tie bond (mortar only; mutually exclusive with friction).
thickness
2D mortar only — the plane-model out-of-plane thickness h
(fork -thickness, 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 / the friction clamps (cohesion / tau_max) and the
tie stiffness. Three conventions live here and must not be
conflated:
1. The **element** thickness (``ops.element.FourNodeQuad(thickness=…)``)
is baked into the element's own stiffness; contact never re-reads
or re-derives it.
2. ``thickness=h`` scales the **explicit** per-unit-thickness
penalties listed above — apeGmsh only emits ``-thickness h``; the
scaling itself is entirely fork-side.
3. ``eps_n="auto"`` is **NOT** h-scaled: it resolves from the owning
element's ``getInitialStiff()``, which already absorbs the
element's thickness, so re-scaling it by ``h`` would be an h²
error (regression-gated fork-side). ``eps_t`` INHERITS that
provenance when it is ``"auto"`` (or when friction defaults it
from ``eps_n``), so the pair moves together: both h-scale under
an explicit ``eps_n``, neither h-scales under an auto one.
The NTS lane has no ``-thickness`` at all (``kn`` is force per unit
length of gap, never a pressure) and the fork's parser refuses the
flag there; a 3D mortar deck's thickness lives in its elements and
the fork FATALs on ``-thickness`` at ``handle()``. Both are refused
here instead — the second at resolve time, where the model dimension
is known.
outward
Optional single outward direction. None (default) → emit no
-outward; the fork derives a correct PER-FACET normal from
connectivity (the right choice for separated bodies and for curved /
closed / solid-part masters). Pass an explicit (ox, oy, oz) ONLY
for an initially-COINCIDENT (zero-gap) contact, where the fork's
per-pair sign reference (slave − segment-centroid) is in-plane and
ambiguous — there the explicit direction pins the sign (matches the
fork's "use -outward for just-penetrated starts"). A single global
outward is wrong on a non-flat master (it skips perpendicular facets
and inverts opposed ones), so only set it for an effectively flat
interface.
**2D (``ndm=2``) models** take a 2-vector ``(ox, oy)`` — it is
z-padded to ``(ox, oy, 0.0)`` on the record and the emitter drops the
third component back off, because the fork REJECTS the 3-component
form on a 2D surface. And 2D is where an explicit direction is
usually needed at all: the fork's 2D lanes orient from an
interface-level centroid vote that is genuinely ambiguous on a FLUSH
interface (the masonry joint, the footing on soil) and aborts there.
``outward="winding"`` (2D NTS only) declares the side through the
master chain's own winding instead of a vector — the fork's
``-outward winding``. Exact per segment, so it orients curved and
closed masters that no single direction can, and no direction is
auto-derived by apeGmsh. It needs a fork build carrying the mode;
older builds refuse the keyword at parse.
master_entities, slave_entities
Restrict each side to specific Gmsh entities (default = whole label).
soft
Explicit-only Courant-stable SOFT penalty (-soft, ADR 0073).
True ⇒ the fork default SOFSCL (0.10); a float ⇒ an explicit SOFSCL.
Sizes the contact stiffness from the nodal mass + timestep under
explicit dynamics so contact never throttles dt_cr (impact /
pounding / recontact runs at the structural dt); inert under
implicit (which uses the base penalty). NTS = SOFT=1, mortar = SOFT=2.
Requires a base penalty (kn/eps_n); mutually exclusive with
tie. A SOFSCL above the coupled-stability bound (mortar > 0.25,
NTS > 1) warns.
visc
Viscous normal-stabilisation coefficient μ_c (-visc): a
velocity-proportional normal damper (p_visc = μ_c·gap_rate) that
bleeds chatter / snap-through energy in the pounding / rocking / uplift
regime. 0 ⇒ off (inert in statics, v ≡ 0). Mutually exclusive with
tie (a permanent bond has no contact-chatter to damp).
consistent_tan
Opt into the non-symmetric consistent friction tangent
(-consistanttan) for true quadratic Newton on frictional contact.
REQUIRES a non-symmetric solver (system FullGeneral / UmfPack /
BandGeneral) — a symmetric solver silently drops the off-diagonal
coupling and corrupts the solve. The default (symmetric) tangent is
correct on any solver.
geom_tan
Opt the NTS segment lane into the consistent ∂n/∂u geometric normal
tangent (-geomtan) for quadratic Newton on curved / large-sliding
interfaces. Symmetric ⇒ solver-safe on any system. NTS-only (the
fork refuses it on the mortar lane).
cell
Broad-phase cell-size scale (-cell): the spatial-hash bucket size as
a fraction of the median segment diagonal (must be > 0; a huge value ⇒ 1
bucket ⇒ brute force). A performance-tuning knob; omitted ⇒ the fork
default. Applies to both formulations.
edge_edge
Enable the perpendicular edge-edge contact fallback (-edgeedge,
fork ADR-57 E2): the cos_t→0 pairs the face-mortar clip degenerates
on get a dedicated segment-to-segment penalty. Mortar-only (the fork
routes it off the mortar lane); off ⇒ byte-identical. The other
edge_* knobs require edge_edge=True (the fork ignores -edge*
without -edgeedge).
edge_kn
Edge-edge normal penalty (-edgeKn auto|<val>): "auto" ⇒ sized per
master facet (like eps_n="auto"); a value ⇒ fixed; None (default)
⇒ the resolved mortar penalty.
edge_band
Gap activation band d_band for the edge-edge fallback (-edgeBand);
None ⇒ sized from the facet edge length at run time.
edge_mu, edge_kt, edge_cohesion, edge_tau_max
Edge-edge Coulomb/Tresca friction (-edgeMu/-edgeKt/
-edgeCohesion/-edgeTauMax) — the unified cone
min(μN+c, τmax). All None ⇒ frictionless edge contact.
edge_consistent_tan
Opt the edge-edge friction into the non-symmetric consistent Csl tangent
(-edgeConsistentTan). Like consistent_tan, REQUIRES a
non-symmetric solver (system FullGeneral / UmfPack /
BandGeneral) when edge friction is active — a symmetric solver
silently drops the off-diagonal coupling and corrupts the solve.
edge_soft
Explicit-only Courant-stable SOFT penalty on the edge-edge fallback
(-edgeSoft [SOFSCL], fork ADR-57 E5): True ⇒ the fork default
SOFSCL (0.10), a float ⇒ an explicit SOFSCL. Under explicit dynamics the
edge penalty becomes k_soft = SOFSCL·4·m_eff/dt²; inert under
implicit. A SOFSCL > 1 warns (ω·dt = 2√SOFSCL > 2).
edge_alm
Opt the edge-edge fallback into the one-scalar commit-cycle augmented
Lagrangian (-edgeAlm, fork ADR-57 E6); off ⇒ the E2 penalty path.
Implicit-only.
edge_aug_tol
Edge-edge ALM augmentation tolerance metadata (-edgeAugTol); the
held-load proc passes its own tol.
apeGmsh._kernel.defs.constraints.ContactPlaneDef
dataclass
¶
ContactPlaneDef(kind: str, master_label: str = '', slave_label: str = '', name: str | None = None, slave_entities: list[tuple[int, int]] | None = None, normal: tuple | None = None, point: tuple | None = None, kn: float | None = None, visc: float | None = None, soft: float | bool | None = None)
Bases: ConstraintDef
Rigid analytical-plane contact (fork contactPlane; ADR 0073).
A meshed slave surface contacts a fixed, infinite rigid plane defined by
an outward normal and a point on it, with a normal penalty kn.
The plane is frictionless and fixed (no master mesh) — use it for a rigid
floor / wall / foundation where the counter-body needn't be meshed. The
slave nodes are emitted as a contactSurface -slave set; the plane and
penalty go on the contactPlane verb (resolved by the same
LadrunoContact handler). Fork-only at run time.
Parameters¶
slave_label : str
The meshed surface PG / part label whose nodes contact the plane.
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), Z-PADDED here to
(nx, ny, 0.0) — see the note below.
point : (float, float, float) | (float, float)
Any point on the plane; (px, py) in a 2D model, z-padded likewise.
kn : float
Normal penalty stiffness (required; the fork reads it as a plain
value — there is no "auto" sizing on contactPlane).
visc : float, optional
Viscous normal-stabilisation coefficient μ_c (-visc).
soft : float | bool, optional
Explicit-only Courant-stable SOFT penalty (-soft): True ⇒ the
fork default SOFSCL 0.10, a float ⇒ an explicit SOFSCL.
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).
Tier 6 — Interface springs¶
Every tier above is a bond: the slave follows the master in tension
as in compression, for as long as the analysis runs.
g.constraints.interface(master, slave, ...) is the one that can let
go. It emits one zeroLength per coincident node pair between a 2D
continuum boundary and a node-for-node coincident wire, with a
unilateral normal law (compression only, separation free) and a
strength-capped tangential law (elastic until the bond shear
tau_b is reached, then slip). That is the soil- or rock-to-structure
bond. A tunnel liner in converging ground takes load until the bond
slips and no more; with a bilateral tie the ground keeps converging
and the liner's demand grows with it, without a ceiling.
The two laws are declared, not built. NormalLaw and
TangentialLaw are flat-scalar dataclasses carrying stiffness per
unit area — apeGmsh translates each into a typed uniaxial material
at emit, scaled by that pair's own tributary area. The Cerro Lindo
shape, a steel arch against a tunnel face, is an ent normal (no
tension whatsoever) with an epp tangential capped at the bond
strength:
from apeGmsh import NormalLaw, TangentialLaw
g.constraints.interface(
"face", "wire", # rock rim, liner rim
normal=NormalLaw(kind="ent", k_per_area=1.0e9), # [F/L³]
tangential=TangentialLaw(kind="epp", k_per_area=1.0e8, tau_b=2.5e5),
thickness=0.5, # out-of-plane, required
name="RockLinerInterface",
)
You never compute the tributary areas that scaling needs. The
resolver accumulates 0.5 × edge_length along the master polyline,
multiplies by thickness, and asserts the total closes on the master
face's length × thickness before a line is emitted; a pair with a zero
share is an error, not a quiet no-op. thickness itself is required
and has no default, because a guessed out-of-plane dimension would
scale every force in the interface and say nothing.
Local-x of each pair is the outward normal of the master face,
derived per pair from the master's own boundary edges, so a curved
master's frame swings with the arc instead of collapsing onto one
average direction. ZeroLength deformation is x̂·(u_j − u_i) with the
master always as i, so separation reads as positive elongation and
an ent normal carries exactly zero force there, while closure is
compression. That is observable and worth observing once on a new
model: pull the slave off the master and the pair's spring_force_0
must be zero. The mirror-image convention converges just as happily
into a tension-only interface that is wrong everywhere, which is why
the signs are applied by the emit-time translation and are never
yours to pass.
The slave's ndf is a declaration, not an inference. Left at None
(or 2) the slave is taken to match the 2D continuum and the spring
joins the two real nodes. Pass slave_ndf=3 when the wire will become
a beam: the engine refuses a zeroLength whose endpoints disagree on
ndf, so each pair instead gets a 2-DOF phantom at the slave's
coordinates, an equalDOF(retained=beam node, constrained=phantom,
dofs=[1, 2]), and a spring running master → phantom — leaving the
beam's rotation free, which is the hinge behaviour you want at a
liner-to-ground contact. It has to be explicit because element classes
are assigned at ops.element time, after resolution: when the
resolver runs there is genuinely nothing in the model that says
whether your wire becomes a truss or a beam. Declare it wrong and the
bridge refuses at emit, naming the ndf it actually found.
Two-dimensional line masters only, for now. A 3D model raises
NotImplementedError at the call, and an interior edge — material on
both sides, so no outward direction exists — raises at resolve; both
are loud, neither degrades into a guess. Partitioned (MPI) emit is
supported: each pair's whole unit lands on the one rank owning the
master node's backing continuum element, because the pair's nodes are
co-located and node-tally ownership cannot decide between the ranks.
And an interface given a name= can be claimed into a stage with
s.interface(name="RockLinerInterface"), which installs it on ground
the earlier stages already equilibrated — the liner-install pattern,
and the reason the springs are born strain-free instead of
pre-loaded by the convergence that happened before they existed.
apeGmsh._kernel.defs.constraints.InterfaceDef
dataclass
¶
InterfaceDef(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, normal: object | None = None, tangential: object | None = None, thickness: float | None = None, tolerance: float = 1e-06, slave_ndf: int | None = None)
Bases: ConstraintDef
Oriented coincident-pair zeroLength interface (ADR 0093).
The geometry-side def captured by g.constraints.interface(...):
one zeroLength per coincident (master, slave) node pair, with
per-pair local axes taken from the master face geometry and
per-pair tributary-scaled normal / tangential laws. The master is a
2D line boundary of a meshed continuum; the slave is a
node-for-node coincident wire (3D surface masters raise —
ADR 0093 D2).
Parameters¶
master_label, slave_label
The master curve PG / part label (a free boundary of the
continuum) and the coincident slave label. The two node sets
must be disjoint and node-for-node coincident.
normal, tangential
The declarative per-area laws
(:class:~apeGmsh._kernel.records._constraints.NormalLaw /
:class:~apeGmsh._kernel.records._constraints.TangentialLaw,
ADR 0093 D1). Stored on each record and translated to typed
uniaxial materials, scaled by that pair's A_trib, only at
emit — the verb carries no OpenSees types (INV-4).
thickness
Out-of-plane thickness, > 0 and required: A_trib =
ell_trib * thickness (D3). The verb refuses to guess it
(sign-off question 2, settled explicit-only).
tolerance
Coincidence radius for the node pairing.
slave_ndf
Which ndf the slave wire will be declared with — an
explicit decision, never inferred. At resolve time apeGmsh
cannot know whether the slave wire becomes an OpenSees truss
(ndf 2) or a beam (ndf 3): element classes are assigned at
ops.element declaration, i.e. after resolution. So:
* ``None`` (default) / ``2`` — the slave matches the 2D
continuum's ndf, and the zeroLength connects the two real
nodes directly. No phantom.
* ``3`` — a beam slave. ``ZeroLength::setDomain`` refuses
``dofNd1 != dofNd2``, so each pair gets a phantom bridge
(ADR 0093 D4): a 2-dof phantom at the slave's coordinates,
``equalDOF(retained=beam node, constrained=phantom,
dofs=[1,2])``, and the zeroLength running master continuum ->
phantom. The beam's rotation DOF is never touched (the hinge
semantics the campaign asks for).
Any other value raises. Validating this against the ndf
actually inferred from the emitted elements is ADR 0093 S5's
job (emit time is the first moment that ndf exists).
master_entities, slave_entities
Restrict each side to specific Gmsh entities (default = the
whole label).
name
Friendly name — carried onto every record, and the handle a
future s.interface(name=...) stage claim will use (INV-6).
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.
ADR 0038 §"Tag-reference rewrite checklist" — every concrete
subclass below declares a tag_rewrite_spec class attribute
(ClassVar) naming the tag-bearing + name-bearing fields the
Phase 3B.2a compose rewriter must offset / namespace-prefix. The
base class has no spec on its own — it is never instantiated bare.
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, master_dofs: list[int] | 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, control: 'CouplingControl | None' = None, as_element: bool = False, mass: float | None = None, omega: tuple[float, float, float] | 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, excess: float | None = None, stiffness: 'float | str' = 1e+18, stiffness_p: float | None = None, rotational: bool = False, pressure: bool = False, enforce: str = 'penalty', control: 'CouplingControl | 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.
excess : float or None
Barycentric excess of the slave node relative to the host
element — 0.0 when the slave is strictly inside, positive
when outside (extrapolation; the magnitude is how far outside
in barycentric coordinates). Populated by resolve_embedded;
None for records produced by other code paths. Enables
downstream tolerance gating and post-resolution introspection.
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
ReinforceTieRecord
dataclass
¶
ReinforceTieRecord(kind: str, name: str | None = None, rebar_node: int = 0, host_nodes: list[int] = list(), weights: ndarray | None = None, direction: ndarray | None = None, corot: bool = False, shape_b: ndarray | None = None, bond_scale: float | None = None, bond: str | None = None, perfect: float | None = None, kt: float | None = None, kt_alpha: float | None = None, enforce: str = 'penalty', bipenalty: bool = False, dtcr: float | None = None, excess: float | None = None, in_bounds: bool = True)
Bases: ConstraintRecord
One resolved LadrunoEmbeddedRebar tie (Ladruno fork).
Carries the inverse-map result for a single rebar node plus the
pass-through tie parameters, so the bridge build step can emit
element LadrunoEmbeddedRebar (via the R0 embedded_rebar_args
builder, resolving bond by name → tag). Solver-agnostic — no
OpenSees imports here.
Attributes¶
rebar_node
The rebar (slave) mesh node tag.
host_nodes
The host element's node tags the weights couple to (8 for a hex8
host, 4 for tet4 — the -shape host node list).
weights
Shape-function weights Nᵢ(ξ) at the rebar point (sum to 1),
parallel to host_nodes.
direction
Unit bar axis d̂ at this node (from the rebar segment) — the
frozen reference axis (-dir).
corot, shape_b
Co-rotated bar-axis option (-corot, ADR 20 §10.5). corot=True
⇒ shape_b carries the point-B shape weights NshapeB (parallel
to host_nodes, the -shapeB host-element-tag-free path); the
fork forms d̂_cur = normalize(Σ NshapeB·x − Σ Nshape·x) from current
host node positions. corot=False ⇒ shape_b is None (frozen
-dir).
bond_scale
π·d_b·L_trib (None for the perfect-bond law).
bond
LadrunoBondSlip material name for the axial law, or
None when perfect is set.
perfect
Perfect-bond axial penalty kAxial (or None for bond).
kt, kt_alpha, enforce
Transverse-penalty + enforcement pass-throughs.
excess, in_bounds
Inverse-map diagnostics (excess > tol with snap ⇒ extrapolated).
EmbedTieRecord
dataclass
¶
EmbedTieRecord(kind: str, name: str | None = None, node: int = 0, host_nodes: list[int] = list(), weights: ndarray | None = None, k: float | None = None, k_alpha: float | None = None, enforce: str = 'penalty', bipenalty: bool = False, dtcr: float | None = None, staged: bool = True, excess: float | None = None, in_bounds: bool = True)
Bases: ConstraintRecord
One resolved LadrunoEmbeddedNode tie (Ladruno fork).
The isotropic sibling of :class:ReinforceTieRecord: it ties a single
constrained node into the host element it falls inside (via the same
guarded inverse map), with no bar axis, bond law, or tributary length.
The bridge build step emits element LadrunoEmbeddedNode via the
embedded_node_args builder. Solver-agnostic — no OpenSees imports.
Attributes¶
node
The constrained (slave) mesh node tag.
host_nodes
The host element's node tags the weights couple to (8 for hex8,
4 for tet4 — the -shape host node list).
weights
Shape-function weights Nᵢ(ξ) at the node (sum to 1), parallel
to host_nodes.
k, k_alpha, enforce
Isotropic penalty + enforcement pass-throughs (-k / -kAlpha).
bipenalty, dtcr
Explicit bipenalty critical-time-step control.
staged
True (default) → g0 stress-free birth (no -absolute);
False → emit -absolute (legacy absolute tie).
excess, in_bounds
Inverse-map diagnostics (excess > tol with snap ⇒ extrapolated).
ContactRecord
dataclass
¶
ContactRecord(kind: str, name: str | None = None, formulation: str = 'nts', master_faces: ndarray | None = None, master_nps: int = 0, slave_nodes: list[int] | None = None, slave_faces: ndarray | None = None, slave_nps: int = 0, outward: tuple | None = None, kn: float | str | None = None, kt: float | None = None, mu: float | None = None, eps_n: float | str | None = None, eps_t: float | str | None = None, cohesion: float | None = None, tau_max: float | None = None, aug_tol: float | None = None, max_aug: int | None = None, ngp: int | None = None, tie: bool = False, thickness: float | None = None, soft: float | bool | None = None, visc: float | None = None, consistent_tan: bool = False, geom_tan: bool = False, cell: float | None = None, edge_edge: bool = False, edge_kn: float | str | None = None, edge_band: float | None = None, edge_mu: float | None = None, edge_kt: float | None = None, edge_cohesion: float | None = None, edge_tau_max: float | None = None, edge_consistent_tan: bool = False, edge_soft: float | bool | None = None, edge_alm: bool = False, edge_aug_tol: float | None = None)
Bases: ConstraintRecord
One resolved fork contact interaction (contactSurface + contact).
A face-to-face contact between two meshed surfaces, emitted as the fork's
contactSurface tag (-master|-slave|-slave-segments) … pair plus the
contact tag master slave … verb (and the LadrunoContact handler). The
master is always a faceted surface; the slave is a node set (NTS) or a
faceted surface (mortar). Solver-agnostic — no OpenSees imports here.
Partition-aware since ADR 0092 S4 (previously serial-only). Under
partitioned (MPI) emit the whole interaction lands inside exactly ONE
rank's block — the owner, picked master-side (INV-1) — with every
non-native interface node ghost-declared there as node + replayed
fix (INV-2). The record still carries no per-rank tag-rewrite
fields: nothing is ever split across ranks, so there is nothing to
rewrite. soft= / edge_soft= stay refused under partitioning
(INV-3), and staged partitioned contact is refused too (see
BuiltModel._plan_partitioned_contacts).
Attributes¶
formulation
"nts" (node-to-segment) or "mortar" (segment-to-segment ALM).
master_faces, master_nps
The master surface's flat face connectivity (n_faces, nps) and the
per-facet node count nps (2=2D line segment, 3=tri, 4=quad).
nps is the sole discriminator of the interaction's dimension
(see :attr:ndm), and at nps == 2 the rows must be one run
CHAINED head-to-tail — enforced in :meth:__post_init__.
slave_nodes
NTS slave node tags (None for mortar).
slave_faces, slave_nps
Mortar slave faceted connectivity + stride (None/0 for NTS).
outward
Unit outward normal (ox, oy, oz) toward the slave half-space, or
None (let the fork auto-derive), or the string "winding" —
the fork's declared-winding sentinel, which orients from the master
chain's own head-to-tail winding instead of any direction (2D NTS
only). A 2D direction is stored Z-PADDED here, (ox, oy, 0.0):
the third component is genuinely zero, so every consumer
(persistence, compose's rotation, emit) keeps one shape and the
emitter drops the oz back off for the fork's 2-component form.
kn, kt, mu
NTS penalty (normal/tangential) + friction.
eps_n, eps_t, cohesion, tau_max, aug_tol, max_aug, ngp, tie
Mortar ALM penalty / friction-cone / augmentation controls + mesh-tie.
thickness
2D mortar plane-model out-of-plane thickness h (fork
-thickness); None ⇒ the fork default 1.0. Emitted verbatim —
apeGmsh never scales anything with it. The fork applies h ONCE at
its 2D injection site to eps_n / eps_t / visc /
cohesion / tau_max and the tie stiffness, and deliberately
does NOT scale an eps_n="auto" (that value already absorbs the
element's own thickness via getInitialStiff(), so re-scaling it
is an h² error); an "auto"/defaulted eps_t inherits that
provenance and moves with it. Mortar-only and 2D-only, refused by
name on every path in — ContactDef at declaration,
resolve_contacts for a 3D model, contact_args at emit for a
record that reached it without passing a def.
soft, visc, consistent_tan, geom_tan
Extension modifiers (ADR 0073): soft = explicit Courant-stable SOFT
penalty (True ⇒ fork default SOFSCL 0.10, or a float SOFSCL);
visc = viscous normal-stabilisation coefficient μ_c; consistent_tan
= non-symmetric consistent friction tangent (needs an unsymmetric
solver); geom_tan = NTS ∂n/∂u geometric normal tangent (NTS-only).
cell
Broad-phase cell-size scale (-cell): the spatial-hash bucket size as a
fraction of the median segment diagonal (a positive performance-tuning
knob; omitted ⇒ the fork default). Applies to both formulations.
edge_edge, edge_kn, edge_band, edge_mu, edge_kt, edge_cohesion, edge_tau_max,
edge_consistent_tan, edge_soft, edge_alm, edge_aug_tol
Edge-edge fallback (ADR-57 E2–E7): edge_edge enables the
perpendicular segment-to-segment fallback (mortar-only); edge_kn
(float | "auto") its penalty; edge_band the gap activation band;
edge_mu/edge_kt/edge_cohesion/edge_tau_max its
Coulomb/Tresca friction; edge_consistent_tan the non-symmetric Csl
tangent; edge_soft (True ⇒ fork default SOFSCL, or a float) the
explicit Courant-stable SOFT penalty; edge_alm the commit-cycle ALM;
edge_aug_tol the ALM tolerance.
ndm
property
¶
The interaction's spatial dimension, DERIVED from master_nps.
master_nps == 2 (a line segment) is 2D; 3/4 (tri/quad
facets) is 3D. master_nps is the sole source of truth —
this is a read-only convenience, deliberately not a stored field,
because a second copy of the dimension is a second thing that can
disagree with the connectivity it describes.
ContactPlaneRecord
dataclass
¶
ContactPlaneRecord(kind: str, name: str | None = None, slave_nodes: list[int] | None = None, normal: tuple | None = None, point: tuple | None = None, kn: float | None = None, visc: float | None = None, soft: float | bool | None = None)
Bases: ConstraintRecord
One resolved rigid analytical-plane contact (fork contactPlane).
A meshed slave surface contacts a fixed infinite rigid plane (normal
+ point) with normal penalty kn — frictionless, no master mesh.
Emitted as a contactSurface -slave <nodes> set + the contactPlane
verb (+ the LadrunoContact handler). Solver-agnostic — no OpenSees
imports here. Partition-aware since ADR 0092 S4 (previously
serial-only): the interaction emits inside exactly one owner rank's
block — the plane has no master surface, so ownership is tallied from
the SLAVE nodes — with non-native slave nodes ghost-declared there
(INV-1/INV-2). soft= stays refused under partitioning (INV-3).
Parameters¶
slave_nodes
The slave surface node tags (the contactSurface -slave set).
normal, point
The plane's outward normal + a point on it — always 3-vectors here. A
2D plane is stored Z-PADDED, (nx, ny, 0.0) / (px, py, 0.0)
(the ContactRecord.outward decision): the third component is
genuinely zero, so persistence, compose's rotation and emit all keep
one shape, and the emitted line stays the zero-padded 9-argument form
the fork accepts on a 2D and a 3D slave surface alike.
kn
Normal penalty stiffness.
visc
Viscous normal-stabilisation coefficient μ_c (-visc); None ⇒ off.
soft
Explicit SOFT penalty (-soft): True ⇒ fork default SOFSCL, a float ⇒
an explicit SOFSCL, None ⇒ off.
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
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.
InterfaceRecord
dataclass
¶
InterfaceRecord(kind: str, name: str | None = None, master_node: int = 0, slave_node: int = 0, backing_element: int = 0, orient: tuple | None = None, a_trib: float = 0.0, normal_law: 'NormalLaw | None' = None, tangential_law: 'TangentialLaw | None' = None, phantom_node: int | None = None, phantom_coords: ndarray | None = None, phantom_ndf: int | None = None, equal_dof_records: list[NodePairRecord] = list())
Bases: ConstraintRecord
One resolved g.constraints.interface() coincident-pair spring
(ADR 0093).
A single zeroLength between a master continuum node and a slave
node (real, or a phantom bridging a mixed-ndf pair per D4), with
per-pair geometry-derived orientation and tributary-scaled normal /
tangential laws. An additive side-list record — like
:class:ContactRecord, it emits elements/materials via its own
emit_interfaces() pass (D5), bypassing the _DISPATCH
MP-constraint pipeline. Solver-agnostic — no OpenSees imports here.
Attributes¶
master_node
The real continuum node tag (INV-1: always iNode; local-x is
the outward normal of the master face).
slave_node
The real slave node tag (INV-1: always jNode). For a mixed-ndf
pair the zeroLength's actual second endpoint is phantom_node,
not this field — slave_node is still carried for the nested
equal_dof_records and for provenance.
backing_element
The tag of the highest-dimension (domain) continuum element
backing master_node (INV-5) — the partition-ownership anchor.
An element tag, offset by g.compose like the node tags —
see the note above :attr:tag_rewrite_spec for why one offset
covers both.
orient
The zeroLength -orient 6-tuple (x1, x2, x3, yp1, yp2, yp3)
— local-x is the master face's outward normal (D2). A direction,
not a tag; g.compose rotates it (never translates), the
:class:ContactRecord-style _transform_contact_geometry
extension (INV-2).
a_trib
Tributary area for this pair, ell_trib * thickness (D3).
normal_law, tangential_law
The declarative per-area laws (D1) — translated to typed
materials, scaled by a_trib, only at emit.
phantom_node
The minted phantom node's tag for a mixed-ndf pair (D4); None
for an equal-ndf pair (direct connection, no phantom).
phantom_coords
The phantom node's coordinates (identical to the pair's
coincident coordinates); None when phantom_node is
None.
phantom_ndf
The phantom node's ndf — the lower side's ndf (D4); None when
phantom_node is None. Carried explicitly because the
shared _emit_phantom_nodes helper hardcodes ndf=6, which does
not apply here.
equal_dof_records
The nested equalDOF(retained=slave_node, constrained=phantom_node,
dofs=[1,2]) record for a mixed-ndf pair (D4), as a one-element
list of :class:NodePairRecord; empty for an equal-ndf pair.
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_equal_dof_mixed ¶
resolve_equal_dof_mixed(defn: 'EqualDOFMixedDef', master_nodes: set[int], slave_nodes: set[int]) -> list[NodePairRecord]
Resolve an EqualDOF_Mixed definition into node pair records.
Identical co-location matching to :meth:resolve_equal_dof, but
each record carries BOTH the retained DOFs
(:attr:NodePairRecord.master_dofs) and the constrained DOFs
(:attr:NodePairRecord.dofs), paired by index, from
defn.dof_pairs — emitted downstream as
equalDOF_Mixed (RDOF_i / CDOF_i couples).
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]
Raises¶
ValueError
When ZERO slave nodes project within defn.tolerance —
a tie that resolves no records leaves the slave side
completely unattached while the model still solves
(converged-but-wrong). Shared choke point: both the
build-phase and chain-phase tie/tied_contact paths resolve
through here, so the guard covers all four routes.
Warns¶
UserWarning When only SOME slave nodes project (out-of-tolerance nodes are skipped). Legitimate when the slave surface extends past the master patch — otherwise the tolerance is too tight for the interface gap.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
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 | |
resolve_tie_mortar ¶
resolve_tie_mortar(defn: TieDef, master_face_conn: ndarray, slave_face_conn: ndarray) -> list[InterpolationRecord]
Resolve a method="mortar" tie (ADR 0086).
Dual-basis integral mortar over the slave/master facet overlaps
— one :class:InterpolationRecord per slave node, weights from
P = D_dual⁻¹ M instead of collocated shape functions, so
neither side's interpolation order is imposed on the other.
defn.tolerance is the out-of-plane coincidence tolerance;
defn.outward optionally orients the interface plane.
Fail-loud by design: every degenerate case (non-flat interface,
ambiguous normal, non-convex facet, curved edge, overlapping
master facets, coverage gap, partition-of-unity failure, tri6
slave facets) raises
:class:~apeGmsh._kernel.resolvers._mortar.MortarTieError — a
mortar tie never silently resolves to nothing.
Source code in src/apeGmsh/_kernel/resolvers/_constraint_resolver/_resolver.py
resolve_distributing ¶
resolve_distributing(defn: DistributingCouplingDef, master_nodes: set[int], slave_nodes: set[int], slave_face_conn: 'ndarray | None' = None) -> InterpolationRecord
Resolve an RBE3 distributing coupling to an InterpolationRecord.
The reference (dependent) node R is the master-side node closest
to defn.master_point; the independents are the slave-side
node set (minus R if it overlaps). The record carries R in
slave_node and the independents in master_nodes — the
field names read backwards for RBE3 (R is the dependent, the
"master_nodes" are the independents it is fit from), but the
geometry maps 1:1 onto the fork emit
element LadrunoDistributingCoupling $tag $R $N $i1..iN.
weighting="uniform" leaves weights None ⇒ the emit omits
-w and the fork element uses equal weights.
weighting="area" computes per-independent tributary areas
over slave_face_conn (the slave surface's face connectivity,
shape (n_faces, n_per_face)): each face's area is split
equally among its nodes and accumulated — the same lumping model
as g.loads surface-tributary resolution, so the RBE3 force
distribution matches a uniform traction lumped onto the same
surface. Weights are returned in the same sorted independent
order the record emits (-w[i] pairs with i_i); the fork
normalizes by W = Σw so only proportionality matters. Fails
loud if an independent node lies on no slave face (a node-set /
face-set mismatch would silently zero its share).
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_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
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 | |
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
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 | |
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
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 | |
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,
)