Documentation style guide#
This is the single document that any contributor reads before writing or editing a docstring, an RST page, or a tutorial. Phase 0 of the commercial-release documentation overhaul (issue #583) makes it the authoritative bar; every Phase 1+ change is reviewed against the rules below.
Anything not specified here defaults to the PyVista documentation conventions, which this guide is closely modelled on.
Why a style guide#
femorph-solver targets a paying-customer audience that is comparing us against Ansys / Abaqus / COMSOL doc sets. Inconsistent voice, half-written docstrings, or “Examples” blocks that don’t actually run all read the same way to a reader: not ready. The rules below keep the surface uniform regardless of which contributor authored a given page.
Docstring format#
Every public callable uses NumPy-style docstrings (the format
parsed by numpydoc), with the section ordering and headings
shown in the canonical template:
def something(arg, *, opt=None):
"""One-line summary, ending with a period.
Longer description with the *why*, not just the *what*.
Cross-link related concepts with ``:class:``, ``:meth:``,
``:func:``, ``:term:``, and ``:ref:`` roles.
Parameters
----------
arg : type
Plain-English description of role and units.
opt : type, optional
Default ``None``. What it does when set.
Returns
-------
type
What the caller gets and what shape / units.
See Also
--------
related_function : one-line pointer.
Notes
-----
Hidden constraints, numerical-stability remarks, references
to the theory page that derives the formula.
References
----------
.. [1] Author, *Title*, Publisher, Year, §section.
Examples
--------
Plain-English lead-in, one sentence.
>>> import femorph_solver as fs
>>> model = fs.examples.cyclic_bladed_rotor_sector()
>>> model.something(arg, opt=value) # doctest: +ELLIPSIS
...
"""
The summary line is mandatory for every public callable. The remaining sections are required only when the signature warrants them — see Section taxonomy by callable type below.
Section taxonomy by callable type#
Callable type |
Summary |
Parameters |
Returns |
Raises |
Examples |
See Also |
|---|---|---|---|---|---|---|
Public function |
required |
required |
required |
if any |
required |
encouraged |
Public method |
required |
required |
required |
if any |
required |
encouraged |
|
required |
required |
n/a |
if any |
on the class |
n/a |
Public property |
required |
n/a |
required (as a Returns block, not a Yields) |
if any |
encouraged |
encouraged |
Public class |
required |
n/a |
n/a |
n/a |
required (one per class) |
encouraged |
Enum / dataclass |
required at class level |
n/a |
n/a |
n/a |
encouraged |
n/a |
Private ( |
encouraged |
encouraged |
encouraged |
n/a |
n/a |
n/a |
The rule of thumb: every public surface a user can reach without
underscores has a runnable Examples block. The block is
what readers look at first; if it works, the page works.
Examples blocks#
Use the
>>>doctest format.Examplesblocks are collected bypytest --doctest-modulesand must run on every CI build (see CI gates).Lead with one sentence of plain English explaining what the block demonstrates. Then the code.
Prefer the bundled fixtures in
femorph_solver.examplesover re-building meshes inline, so the doctest stays cheap and self-contained:cantilever_hex8— 40-cell clamped HEX8 cantilever, pre-clamped, 0.3 s to a six-mode modal. The default fixture forExamplesblocks across the public API.quarter_arc_sector— annular HEX20 sector for sector / cylindrical-coord demos.cyclic_bladed_rotor_sector— full bladed-rotor base sector for cyclic-symmetry demos.
For floating-point output, append the
# doctest: +ELLIPSISdirective and elide the digits with...rather than pinning exact decimals. Numerical drift across BLAS / OS / threading modes will otherwise break the doctest on different runners.Never reach the network or write outside
tmp_pathfrom a doctest. If a method writes to disk, use the standard librarytempfilepattern shown in the canonical template.
A worked example, for Model.solve_modal:
"""Solve the generalised eigenproblem :math:`K\\,\\varphi = \\omega^2 M\\,\\varphi`.
...
Examples
--------
Run a modal solve on the bundled cantilever and print the first
six natural frequencies.
>>> import femorph_solver as fs
>>> model = fs.examples.cyclic_bladed_rotor_sector()
>>> result = model.solve_modal(n_modes=6)
>>> result.frequency.shape
(6,)
>>> float(result.frequency[0]) > 0.0
True
"""
Voice and tone#
Present tense, technical, no marketing language. We are documenting what the code does today, not pitching what it could do tomorrow.
Explain the why, not just the what. The signature already shows the what; the prose carries the why. “Returns the lowest n_modes natural frequencies and the corresponding mass-normalised mode shapes — mass normalisation lets the user superpose modes without re-orthogonalising” is a useful description. “Returns the lowest n_modes natural frequencies” is a re-statement of the signature.
Plain past tense in change-log entries; present tense everywhere else. Voice is consistent inside a page even if a reader skims many in sequence.
No emoji, no exclamation points, no “we recommend” hedging. Engineering documentation states behaviour and constraints; if there’s a recommendation, give the reason and let the reader decide.
Comments in code blocks explain the why only when it isn’t obvious from the code itself — otherwise omit them. Same rule as the runtime source code (see
CLAUDE.md).
Cross-reference conventions#
:class:for a class —:class:`~femorph_solver.Model`.:meth:for a method —:meth:`~femorph_solver.Model.solve_modal`.:func:for a free function —:func:`~femorph_solver.recover.compute_nodal_stress`.:term:for a glossary entry —:term:`shift-invert`.:ref:for a labelled section —:ref:`section-taxonomy`.:doc:for a relative document path —:doc:`/user-guide/solving/modal`.The leading
~shortens the rendered link to just the final attribute name. Use it everywhere except when the fully-qualified path is informative.
External cross-references resolve via intersphinx for
numpy, scipy, pyvista, matplotlib, and
pymapdl. Examples:
:class:`numpy.ndarray`
:class:`pyvista.UnstructuredGrid`
:class:`scipy.sparse.csr_array`
:func:`scipy.sparse.linalg.eigsh`
Sphinx -W strict mode treats unresolved references as errors,
so a typo here fails the build immediately — that is on purpose.
Math#
Inline math uses the role
:math:—:math:`\omega^2 = K / M`.Displayed math uses the directive
.. math::on its own line, with a blank line above and below.Symbol conventions inherit from Theory. Use \(K, M, C\) for the global stiffness / mass / damping matrices, \(\\varphi\) for a mode shape, \(\\omega\) for a circular frequency, \(\\xi\) for a damping ratio. Bold lowercase for vectors (\(\\mathbf{u}\)); upright capitals for matrices.
No vendor symbol conventions. MAPDL’s
{F}for load vectors is not the convention here — write \(\\mathbf{f}_{\\text{ext}}\) instead.
Figures#
SVG is preferred for diagrams (architecture, element topology, free-body diagrams). Vector, search-friendly, diff-friendly, and renders sharply at every zoom level.
PNG for screenshots (gallery output, GUI captures).
Both are checked in under
doc/source/_static/figures/<topic>/with the convention<page>_<short>.svg— e.g._static/figures/elements/hex8_topology.svg.Reference a figure with the
figuredirective, never the bareimagedirective — captions are required for everything that isn’t purely decorative:.. figure:: /_static/figures/elements/hex8_topology.svg :alt: HEX8 reference geometry showing local node order 1–8. :width: 60% HEX8 reference element with the local 1–8 node order used by femorph-solver and by MAPDL ``SOLID185``.
Vendor-citation pattern#
femorph-solver is independently developed from public FEM literature. When citing a vendor verification problem, follow the fair-use posture established in Verification:
One-line factual citations only — e.g. “The published target for tip deflection is \(\\delta = 0.0163\) m (NAFEMS LE5).”
Never reproduce vendor input-deck text. When a verification case is re-authored from a published problem statement, write the deck from scratch and add the footnote
(re-authored from \\[VM-N\\] under fair use; no deck text lifted).Vendor element names (
SOLID185,BEAM188, …) appear only in the interop layer. The neutral user-guide and reference layers use the topology-first names (HEX8,BEAM2, …).
Examples gallery rules#
The example filename pattern is
(?:example_|tutorial_), matched bysphinx_gallery_conf["filename_pattern"].The first triple-quoted string in an example file is its page title and intro paragraph — write it as if it were the first thing a reader sees, because it is.
Every example must be runnable on a CI runner with no network access and no GPU. Long runs (>30 s) are gated behind
DOC_FULL_GALLERYorDOC_RELEASE_GALLERYinconf.py.PyVista plotting in examples must use off-screen mode. The gallery driver sets
PYVISTA_OFF_SCREEN=trueinconf.py; do not callPlotter.show()interactively from an example. Examples must never callPlotter.show()interactively from a gallery script.Use
femorph_solver.examples.cyclic_bladed_rotor_sectorand friends for fixtures rather than re-building meshes inline, except when the example is about mesh construction.
Acceptance for any new docstring#
A new public-API docstring is “done” when:
The summary line is one sentence ending in a period.
Parameterslists every positional and keyword argument.Returnsdescribes every return value (including its shape / units / lazy-evaluation contract forResulttypes).Examplesruns underpytest --doctest-moduleswith no warnings or errors.numpydoc.validatereports zero violations from the enforced class set (see Documentation CI gates).The cross-references resolve under
sphinx-build -W -n.
A new RST page is “done” when:
The page title is a single H1 underline of the same length as the title.
Every internal cross-reference resolves.
Every code block either uses
.. code-block:: python(orbash,rst, …) or is a>>>doctest block.make linkcheckreturns zero broken external links.The page renders with no Sphinx warnings under
-W.
See also#
Provenance inventory — sourcing policy for numerical kernels.
Glossary — domain-term definitions.
Theory — symbol conventions.