Boundary conditions and loads#

Dirichlet constraints (fixed DOFs) and nodal forces / moments are the minimum set of boundary conditions every structural analysis needs. femorph-solver exposes both with MAPDL’s verbs: d() for Dirichlet and f() for loads.

DOF labels#

Every node has at most six DOFs, drawn from the enum femorph_solver.DOFLabel:

Label

Meaning

UX, UY, UZ

Translational displacement along global x / y / z.

ROTX, ROTY, ROTZ

Rotation about global x / y / z (beam / shell only).

The effective DOF set at a node is the union of the DOFs every adjacent element needs — a SHELL181 node has all six; a SOLID185 node has three (UX, UY, UZ); a node touched by both sees all six. Rotational DOFs on a solid-only region are silently dropped.

Loads use femorph_solver.ForceLabel — the parallel enum with FX / FY / FZ and MX / MY / MZ.

D — Dirichlet constraints#

m.d(1, "UX", 0.0)        # pin node 1 in x
m.d(1, "UY", 0.0)
m.d(1, "UZ", 0.0)
m.d(2, "UZ", 1e-3)       # prescribe z-displacement at node 2

Model.d() accepts either the raw string or a DOFLabel member; the two calls below are identical:

from femorph_solver import DOFLabel

m.d(1, DOFLabel.UX, 0.0)
m.d(1, "UX", 0.0)

Multiple calls with the same (node, label) pair overwrite — re-pinning a DOF updates its value instead of duplicating.

F — Nodal forces / moments#

m.f(101, "FY", -1000.0)  # 1 kN downward at node 101
m.f(101, "MZ",  +50.0)   # 50 N·m moment about z at node 101

As with d(), either ForceLabel members or raw strings work.

The prescribed= argument#

Every solver entry point also accepts a prescribed dict so you can bypass the Model’s D records and set constraints directly from an array-driven script:

K = m.stiffness_matrix()
M = m.mass_matrix()

# Fix DOFs for nodes 1..10 in all three translational directions.
dof_map = m.dof_map()
fixed_dofs = {
    i for i, (node, d) in enumerate(dof_map)
    if node <= 10 and d < 3      # 0=UX, 1=UY, 2=UZ
}
prescribed = {i: 0.0 for i in fixed_dofs}

result = solve_modal(K, M, prescribed=prescribed, n_modes=10)

Model.solve() / Model.modal_solve() / Model.transient_solve() merge the Model’s D records into this dict automatically — use prescribed only when you’re calling solve_modal / solve_static / solve_transient directly.

Cyclic-symmetry face constraints#

Cyclic-symmetry analyses add a second kind of constraint: paired low-face / high-face DOFs relate by a phase-dependent rotation. That’s a first-class argument to solve_cyclic_modal() rather than a D record — see Cyclic-symmetry modal analysis.

Limitations (today)#

  • Only zero-value Dirichlet is lossless. Non-zero d(node, label, value) constraints work in solve_static() but the modal / cyclic paths require zero-value BCs at prescribed DOFs (they drop the row/column entirely rather than shifting the eigenproblem).

  • No inertia / body-force loads. ACEL-style gravity isn’t exposed yet; build the equivalent nodal force yourself if you need a body load.

  • No element-face pressure. SFE pressure loads are parsed from a CDB but not converted into equivalent nodal forces.

See also#

  • The Model — the n / e / et / mat / real verbs that bracket d / f.

  • Static analysis — how K u = F is partitioned when Dirichlet DOFs are present.