Changelog
=========
This page lists user-visible changes for every tagged
femorph-solver release. Entries follow the
`Keep a Changelog `_ structure
(Added / Changed / Deprecated / Removed / Fixed / Security) so the
diff between any two releases reads consistently regardless of which
contributor authored it.
Versions follow `Semantic Versioning `_:
the public API is the surface enumerated by ``femorph_solver.__all__``
and the public methods of the classes it re-exports. Pre-1.0 minor
bumps may break the public API; bumps after 1.0 will not.
.. _changelog-unreleased:
Unreleased
----------
Tracking ``main``. Compare against the most recent tag on
GitHub: `v0.22.1…main
`_.
.. _changelog-v0.22.1:
v0.22.1 — 2026-05-08
--------------------
Docs deploy unblock. Compare against the prior tag: `v0.22.0…v0.22.1
`_.
Fixed
~~~~~
* **Tag-triggered docs deploy** no longer blocks on latent
``unknown document`` references in historical-tag snapshots.
``sphinx-multiversion`` re-checks out every released tag and
rebuilds its docs through the *current* Sphinx environment;
v0.20.0 tutorials reference doc tree paths that hadn't been
written yet (``/interop/nastran``, ``/interop/abaqus``,
``/getting-started/known-limitations``,
``/reference/theory/stress_recovery``). Under the shared ``-W``
(warnings as errors) those latent refs become hard failures and
the Cloudflare Pages deploy never runs. Split ``SPHINXOPTS_MV``
so the multiversion build keeps ``--keep-going`` without ``-W``;
single-version main / PR builds keep ``-W`` (`#912
`_).
.. _changelog-v0.22.0:
v0.22.0 — 2026-05-08
--------------------
MAPDL parity push, native geometry creation, harness rebuild.
Compare against the prior tag: `v0.21.0…v0.22.0
`_.
Added
~~~~~
* **MAPDL parity playbook + scoreboard discipline.**
``tests/mapdl_reference/PLAYBOOK.md`` is now the canonical
handoff doc for the 100-VM parity push. Documents the four-
layer strictness hierarchy (element K/M bit-level → assembled
K/M → probe values → reference targets), the curatorial
restraint rules for the deck-verb skip set, the multi-pass
deck families (``/CLEAR,NOSTART`` / FINISH+``/PREP7`` /
``ETCHG`` element-type swap), and a working-without-binary-
solver-access section so the same discipline applies to
Hexagon's MSC.Nastran / Abaqus parity (`#878
`_, `#886
`_, `#907
`_). Companion
``AGENTS.md`` at the repo root spells out the rules for any
AI / human contributor: element-first rule + solver-native
kernel naming convention.
* **Per-kernel parity tests against MAPDL ``.emat``.** Element
K / M now diff bit-level (~1e-12 relative) against MAPDL's
``file.emat`` for every implemented kernel: COMBIN14 (`#876
`_), HEX8
(SOLID185), QUAD4_PLANE (PLANE182), TRUSS2 (LINK180), BEAM2
(BEAM188), MASS21, PLANE55 + LINK33 + LINK34 thermal,
HEX20 (SOLID186). These tests are the gate every kernel
must pass before any probe-level VM closure.
* **Native geometry-creation pillar.** New
``femorph_solver.geometry`` module: Point / Line / Area /
Volume primitives (frozen dataclasses), structured pure-NumPy
meshers for rectangle (Q4) and box (Hex8) with bit-level
patch-test verification, gmsh fallback for unstructured
2-D / 3-D, lazy pyvista tessellation. Mesher choice locked
to gmsh-first (open-source robust) per RFC, native superior
later. Runnable from-scratch demo at
``docs/examples/geometry_from_scratch.py`` exercises the full
pipeline end-to-end (build geometry → mesh → solve → extract
results in pure Python) and is gated in CI by
``tests/geometry/test_example_from_scratch.py`` (`#890
`_, `#891
`_, `#892
`_, `#893
`_, `#901
`_, `#902
`_).
* **VM parity scoreboard.** 16 VMs in the first 100 now pass
end-to-end within the 5% relative-error gate (vm3, vm10,
vm12, vm16, vm45, vm47, vm48, vm50, vm57, vm61, vm84, vm89,
vm90, vm92, vm94, vm99). Closures this release: vm3 (CSYS,1
+ thermal pre-strain in LS,1 stress recovery, `#881
`_), vm10
(BEAM2 T-section fiber-stress recovery, `#900
`_), vm12
(BEAM2 PLNSOL principal-stress recovery, `#909
`_), vm16
(PLNSOL,0,MAX evaluator + F-range form, `#899
`_), vm21
(RFORCE / VARI POST26 reaction-force evaluator, `#898
`_), vm50 (DK
keypoint-displacement, `#884
`_).
* **Multi-pass deck routing.** ``/CLEAR,NOSTART``-segmented
decks (vm12 / vm33 / vm34) now route to the last solving
pass for the build/solve pipeline, with last-write semantics
on ``*VFILL`` so the comparison block at the deck tail
resolves against the correct ``*GET`` bindings (`#908
`_).
* **Harness probe-evaluator coverage.** PLNSOL / PLESOL stress
contours: principal stresses, intensity, von Mises, with
beam-element fiber-stress augmentation so PLNSOL,S,1
aggregates over both solid principals and BEAM2 cardinal
fibers (`#903 `_,
`#909 `_).
POST26 ``RFORCE`` / ``VARI`` reaction-time-history (`#898
`_). ``ETABLE``
/ ``LS`` element-table extraction expanded.
* **Solver-native kernel naming.** Element registry renamed
away from MAPDL-vendor names (``Solid185``, ``Plane182``,
…) to physics+topology names (``Hex8``, ``Quad4Plane``,
``Bar2Conduction``, ``LumpedSpringMass``, …). The MAPDL
catalogue dispatch lives at the interop boundary
(``_apdl_dialect._MAPDL_ALIASES``); core registry and
downstream code never see vendor names. Convention locked
in ``AGENTS.md`` (`#880
`_).
* **DAT loader: traceable skip table + unknown-verb tracking.**
Every verb the parser doesn't fully implement is recorded on
``model._deck_skipped`` (with a stated reason) or
``model._deck_unknown`` so the scoreboard surfaces the gap
loudly rather than silently dropping commands (`#885
`_). Curated
skip set covers post-only verbs (``*VWRITE``, ``*VFILL``,
``ETABLE``, ``PLNSOL``, ``PRRSOL``, ``OUTPR``, ``OUTRES``,
POST26 ``RFORCE`` / ``DERIV``), default-toggle no-ops
(``HROPT,FULL``, ``TRNOPT,FULL``, ``CSYS``), and deck-
formatting artifacts (FORTRAN format continuations,
``LABEL(i,j) = '...'`` array assignments) without ever
silently skipping a physics-relevant verb.
* **DAT loader: more handlers** — ``RMORE`` (extend real
constants beyond 6 slots, `#897
`_), stacked
``*IF / *DO / *DOWHILE`` block-skipper (`#896
`_),
``*CREATE / *END`` macro-body block-skip (`#889
`_), ``DSYM``
symmetry-plane handler (`#888
`_), ``SFBEAM``
with MAPDL→solver-native face remap (`#883
`_), ``DK``
keypoint-displacement (`#884
`_),
``TREF / BFUNIF / BF / CP`` (`#881
`_).
* **CHAN beam section** for vm14 (channel-section beam properties
derived from MAPDL ``SECTYPE,..,BEAM,CHAN`` SECDATA).
* **Result-file magic marker + version stamp** so disk-backed
results identify the femorph-solver version that wrote them.
Changed
~~~~~~~
* ``Model.assign`` is a no-op on empty kernels; new
``Model.assign_all`` helper attaches every registered kernel
to its matching element block in one call.
* ``solvers.linear.list_available()`` runtime introspection
reports which optional backends loaded (Cholmod / SuperLU
/ PARDISO).
* TTY-aware progress output: progress bars no longer overlap
via ``\r`` when redirected to a non-TTY pipe.
Fixed
~~~~~
* **FV2/FV4 ARPACK degenerate-pair flake**: deterministic
``v0`` initialisation for the K-σM eigensolver path
(`#114 `_,
PRs `#115 `_,
cyclic-modal companion fix).
* **BEAM2 xfail closure**: static condense MAPDL's 24×24
cubic-shape function (BEAM188 KEYOPT(1)=3) down to the
Hermite 12×12 we ship — element-K parity within ~1e-12 rel.
* **gmsh OSError on NixOS**: harden the lazy-import path against
both ``ImportError`` and ``OSError`` so a broken gmsh wheel
doesn't break the geometry pillar (`#893
`_).
Deprecated
~~~~~~~~~~
* MAPDL-vendor kernel names (``Solid185`` etc.) accepted at the
interop boundary; downstream code that still references them
via private imports should switch to the solver-native names
(``Hex8`` etc.). Vendor names will continue to translate
through the alias table indefinitely; the deprecation is
about *internal* uses only.
.. _changelog-v0.21.0:
v0.21.0 — 2026-05-04
--------------------
API consistency cleanup + documentation overhaul + tag-triggered
release infrastructure. Compare against the prior tag:
`v0.20.2…v0.21.0
`_.
Added
~~~~~
* **Documentation overhaul (`#583
`_).** Multi-week
push to bring documentation up to commercial-solver quality
ahead of the public release.
* Phase 0: doc style guide (`#604
`_),
``numpydoc.validate`` per-symbol CI gate (`#606
`_), doctest gate
+ nightly linkcheck (`#611
`_).
* Phase 1: bucket-A docstrings on the public ``Model`` solve
verbs (`#610
`_), the disk-
backed result hierarchy (`#614
`_),
``CyclicModel`` (`#616
`_), and the
interop / utility surface (`#618
`_).
* **Verification corpus.** MAPDL Verification Manual coverage
driven by the `#511
`_ tracker — VM6
(pinched cylinder) and VM10 (T-section beam) added under the
shared verification harness.
* **Interop coverage.** ``ET`` KEYOPT slot recognition for
PLANE182 / BEAM188 (`#607
`_); CSYS,1
cylindrical coordinates with NSEL / DSYM / D,ALL / F,ALL (`#588
`_, `#602
`_).
Changed
~~~~~~~
* **API consistency cleanup (`#750
`_).** Thirteen
PRs that close the long tail of API / docs / module-naming gaps
the v0.20 release surfaced. All renames ship a deprecation
alias for one minor release; the next minor drops them.
* **Solve-method renames** (`#764
`_). Every
analysis driver now reads ``solve_``:
.. code-block:: text
Model.solve(...) → Model.solve_static(...)
Model.modal_solve(...) → Model.solve_modal(...)
Model.transient_solve(...) → Model.solve_transient(...)
Model.harmonic_solve(...) → Model.solve_harmonic(...)
CyclicModel.modal_solve(...) → CyclicModel.solve_modal(...)
solve_harmonic_modal(...) → solve_harmonic(...)
solve_cyclic_modal(...) → solve_cyclic(...)
* **Kwarg consistency.** Every ``Model.*_solve`` now accepts
the same set of common kwargs (``progress``, ``thread_limit``,
``release_inputs``, ``mixed_precision``, ``linear_solver``).
``CyclicModel.solve_modal`` gains ``lumped`` (`#762
`_). The cyclic
side's ``eigensolver=`` is renamed to ``eigen_solver=`` to
match the rest of the modal-family API; defaults are aligned
to ``"auto"`` everywhere (`#754
`_).
* **Result types re-exported from the top level** (`#753
`_): ``from
femorph_solver import ModalResult, ModalResultDisk, read``
is now the canonical import. Frozen Result dataclasses
(`#759 `_).
* **Module renames** (drop false-private prefix on documented
public types):
``_cyclic_model.py`` → ``cyclic_model.py`` (`#756
`_),
``_labels.py`` → ``labels.py`` (`#765
`_).
* **Factory parity**: ``Model.from_cdb`` mirrors
``CyclicModel.from_cdb`` (`#758
`_).
* **Doctest gating** expanded from one module to thirteen so
docstring ``Examples`` blocks across ``Model``,
``CyclicModel``, the result hierarchy, and the solver
functions are now CI-tested (`#763
`_).
* Pre-existing illustrative ``>>>`` blocks in
``femorph_solver.Model`` docstrings (``save``,
``from_lines``, ``apply_force``) rewritten as runnable doctests
using the bundled ``cyclic_bladed_rotor_sector`` fixture.
Deprecated
~~~~~~~~~~
* All pre-PR-K solve method + function names continue to work
with a ``DeprecationWarning``; removed in the next minor release.
Fixed
~~~~~
* Three cyclic gallery examples and ``tutorial_06_cdb_to_results``
no longer crash for users walking the docs verbatim (`#752
`_).
* ``T``-section ``SECTYPE`` thickness convention bug (`#576
`_).
* PBAR / PBEAM ``I1`` / ``I2`` swap on non-canonical NASTRAN decks
(`#573 `_).
* Drilling-DOF false alarm on FV4 — turned out to be ``n_modes``
configuration, not a kernel bug (`#567
`_).
.. _changelog-v0.20.2:
v0.20.2 — 2026-04-25
--------------------
Documentation green-build patch. Compare against the prior tag:
`v0.20.1…v0.20.2
`_.
Fixed
~~~~~
* Documentation green-main fix: corrected the ``-j4`` Sphinx-build
flag spacing and dropped a broken release-only ``:doc:`` cross-
reference that prevented the build from running on PR branches
(`#416 `_).
* CI: cached sphinx-gallery output and dropped parallel from 8 to
4 to fit the runner pool budget (`#413
`_).
Changed
~~~~~~~
* Test coverage lifted to 100 % on
``femorph_solver.result.modal`` (`#418
`_),
``femorph_solver.result.static`` (`#410
`_), and to 99 % on
``femorph_solver.result._base`` (`#412
`_).
.. _changelog-v0.20.1:
v0.20.1 — 2026-04-25
--------------------
CI / verification follow-up release. Compare against the prior
tag: `v0.20.0…v0.20.1
`_.
Added
~~~~~
* New verification example: continuous beam over three supports,
cross-checked against the Clapeyron three-moment theorem (`#404
`_).
* CI failure-tracker workflow that auto-files (or updates) a
``ci-failure-main``-labeled GitHub issue when ``main`` breaks
(`#398 `_, `#406
`_).
Changed
~~~~~~~
* Pinned ``sphinx<9`` because Sphinx 9.1 changed
``Config.read``'s positional argument signature, which the
current ``sphinx-multiversion`` release passes (`#405
`_).
* Tightened the §1.7 verification mesh — now beats every MSC
element family on the curved-beam reference (`#400
`_).
Fixed
~~~~~
* ``femorph_solver.result.transient`` and
``femorph_solver.result.cyclic_modal`` coverage gaps (`#407
`_, `#403
`_) — no behaviour
changes, but the test sweep flushed out a handful of edge-case
paths that were previously unreachable from any test.
.. _changelog-v0.20.0:
v0.20.0 — 2026-04-25
--------------------
First milestone tag of the public-release cadence. Compare
against the project's git history: `everything before v0.20.0
`_.
This tag captures the state of femorph-solver after the four-phase
landing of the public-API + verification-corpus build-out:
Added
~~~~~
* Public ``femorph_solver.Model`` API with the full set of solve
verbs (``solve``, ``modal_solve``, ``harmonic_solve``,
``transient_solve``, ``cyclic_modal_solve``). *Note*:
``Model.cyclic_modal_solve`` was removed in v0.20.x; use
:meth:`~femorph_solver.CyclicModel.solve_modal` instead.
* Disk-backed result hierarchy (``StaticResult``, ``ModalResult``,
``TransientResult``, ``HarmonicResultDisk``,
``CyclicModalResult``) with lazy ``.pv`` IO via ``pyvista-zstd``.
*Note*: the disk-backed classes were renamed in v0.20.x to
``StaticResultDisk`` / ``ModalResultDisk`` / ``TransientResultDisk``
/ ``CyclicModalResultDisk`` to disambiguate from the in-memory
dataclasses returned by the solve methods.
* Element kernels for HEX8, HEX20, TET10, WEDGE15, PYR13, BEAM2,
TRUSS2, QUAD4 (plane + shell), POINT_MASS, SPRING — independently
derived from public FEM textbooks; sourcing tracked under
:doc:`/dev/provenance`.
* Pluggable solver backends:
Pardiso / CHOLMOD / MUMPS / UMFPACK / SuperLU (linear);
ARPACK / LOBPCG / PRIMME / dense LAPACK (eigen). Auto-dispatch
picks the fastest available SPD direct solver.
* Foreign-deck import readers under
``femorph_solver.interop``: NASTRAN BDF, Abaqus INP, MAPDL
CDB, OptiStruct FEM. Import-only — the resulting :class:`Model`
is identical to one built natively.
* Multi-version documentation site driven by ``sphinx-multiversion``
with a version switcher across every ``v*`` tag (`#392
`_).
* User-guide tutorials covering cantilever combined loading, modal
survey, pressure-vessel DBA, and cyclic-symmetry rotor design
(`#561 `_–`#569
`_).
* Cross-vendor terminology Rosetta and per-vendor migration
deep-dives for MAPDL, NASTRAN, Abaqus, and LS-DYNA (`#566
`_–`#574
`_).
Changed
~~~~~~~
* ``mapdl_api`` re-homed under
``femorph_solver.interop.mapdl``. The legacy import path
remains for one release with a ``DeprecationWarning``.
Removed
~~~~~~~
* Cross-solver tests that the verification-registry harness now
covers were retired to keep the suite focused (`#563
`_, `#565
`_).
Pre-v0.20.0 history
-------------------
The project's pre-tag history (the first eight months of
development) lived on ``main`` without formal releases — feature
branches landed via PR, with verification-coverage and CI-quality
gates as the de-facto release criteria. See the `GitHub commit
history `_ for the
detailed log.
How this page is maintained
---------------------------
* New entries go under :ref:`changelog-unreleased` as work
lands. When a tag is cut, the maintainer renames the
``Unreleased`` heading to the new version and date and starts a
fresh ``Unreleased`` block.
* PRs that warrant a changelog entry should propose one in their
description; the merging maintainer copies it across. Pure
test or CI churn (anything labelled ``ci-failure-*`` or whose
scope is ``test:`` / ``ci:``) is excluded by default.
* The ``release-readiness.yml`` workflow surfaces missing
changelog entries on a PR's checks page so the gap is visible
before merge.