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: :meth:`~femorph_solver.Model.d` for Dirichlet and :meth:`~femorph_solver.Model.f` for loads. DOF labels ---------- Every node has at most six DOFs, drawn from the enum :class:`femorph_solver.DOFLabel`: .. list-table:: :widths: 15 85 :header-rows: 1 * - 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 :class:`femorph_solver.ForceLabel` — the parallel enum with ``FX`` / ``FY`` / ``FZ`` and ``MX`` / ``MY`` / ``MZ``. D — Dirichlet constraints ------------------------- .. code-block:: python 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 :meth:`Model.d` accepts either the raw string or a :class:`~femorph_solver.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 -------------------------- .. code-block:: python 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 :meth:`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) :meth:`Model.solve` / :meth:`Model.modal_solve` / :meth:`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 :func:`~femorph_solver.solvers.cyclic.solve_cyclic_modal` rather than a ``D`` record — see :doc:`/user-guide/solving/cyclic`. Limitations (today) ------------------- - **Only zero-value Dirichlet is lossless.** Non-zero ``d(node, label, value)`` constraints work in :func:`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 -------- - :doc:`model` — the ``n`` / ``e`` / ``et`` / ``mat`` / ``real`` verbs that bracket ``d`` / ``f``. - :doc:`/user-guide/solving/static` — how ``K u = F`` is partitioned when Dirichlet DOFs are present.