Skip to content

Command line

physverify run SPEC [SPEC ...]   run the checks in spec files (glob patterns are expanded)
physverify init [SPEC]           write a starter spec for a fixed-step ODE solver
physverify smoke PATH            compile and run each script; report crashes, hangs and NaNs
physverify references            list the exact references
physverify hook | mcp            for AI coding assistants (see that page)

physverify run

physverify run "tests/**/verify_*.py"
physverify run my_spec.py --html report.html      # a page with convergence plots
physverify run my_spec.py --json results.json     # or --json - for stdout
physverify run my_spec.py --markdown summary.md   # appends a table (for $GITHUB_STEP_SUMMARY)
physverify run my_spec.py --quiet

Exit codes: 0 when no check fails (PASS, EXACT or SKIP), 1 when any check fails, is INCONCLUSIVE or raises, 2 when a spec cannot be loaded or a pattern matches nothing.

SKIP and time budget

"""SKIP when an optional package is missing; a time budget for a slow check."""
import time

from physverify import Check, Result, requires
from physverify.report import run_checks, to_text


def polygon_area():
    requires("shapely_not_installed_here")  # raises SkipCheck when the package is missing
    return Result("PASS", "polygon area matches")


def slow():
    time.sleep(10)
    return Result("PASS", "")


checks = [Check("limit", "polygon area", polygon_area), Check("limit", "slow check", slow, budget_s=1)]
print(to_text(run_checks(checks)))

Output:

limit      SKIP         polygon area: needs shapely_not_installed_here, which is not installed (No module named 'shapely_not_installed_here')
limit      INCONCLUSIVE slow check: did not finish within twice its time budget (2 s); use coarser resolutions or a larger budget
0 of 2 checks passed, 1 skipped

SKIP

Call requires("package") at the start of a check (or raise SkipCheck(reason)) when it needs an optional package. Without it, a check that quietly returned PASS when the package was missing would be counted as a pass.

Time budget

--budget SECONDS on the command line, or Check(..., budget_s=...) for one check.

  • Order studies stop adding finer resolutions when the next one would not finish inside the budget, fit what they have, and add "time budget: ran 3 of 5 resolutions" to the message.
  • A check still running at twice its budget is reported INCONCLUSIVE, and the remaining checks run.
  • Python cannot stop a running thread, so such a check keeps running in the background until the program exits; its result is discarded.

physverify scan

New in 0.5.0.

physverify scan path/to/project
physverify scan path/to/project --only tables,grid --json scan.json

Reads every .py file with Python's ast module and runs nothing, so it is safe on code you have not read. Four stages, each from a kind of bug found in public code:

Stage Looks for Level
constants literals close to, but not equal to, a physical constant: within 1% for constants with units (6.67e-11; a long numeric table, or a file with more than 20 near-constants, is summarised in one NOTE), within 0.2% for pure numbers (3.14, 2.27), within 0.1% for values fixed by definition (the astronomical unit, the torr, the nautical mile, the SI 2019 constants and their CGS values, so an old h_CGS = 6.626196e-27 is noted), and g only when assigned to a gravity name (g = 9.7); one line per constant and file NOTE
tables Runge-Kutta tables written as literals: the order the coefficients really have, row sums against c, error weights that must sum to 0, and the order a name, docstring or order attribute claims (rk4, heun3, 3rd order, def order(self): return 3). Tables are read from assignments, from constructor keywords and dicts (ButcherTableau(a=..., b=..., b_error=..., order=5)), from classes that inherit them, and from A = np.zeros((s, s)) filled by A[i, :k] = [...]. Also stages written as code (k2 = h*f(t + h/2, y + k1/2)): the table is read back from the calls, then checked the same way, against the stage times, and against a Butcher table drawn in the docstring WARN when a claim, c or the docstring does not match, when a stage leaves out h, or when a combination of the stages sums to neither 1 nor 0; else NOTE
grid a spacing (b - a)/D next to linspace(a, b, M) with D not equal to M - 1; compared by substituting random values, so W/(N-1) with linspace(0, W, N+1) is caught too. D must differ from M - 1 by a constant, so an element length L/n next to a plotting grid of 100*n points is not flagged WARN in the same function, NOTE across functions or files
names a name used but never defined or imported in the file (a misspelt parameter, np without import numpy, with a "did you mean" for one-letter slips), and a file that does not parse. Names inside try ... except NameError/ImportError and in branches for another Python version are skipped; files with from x import * or exec are not checked WARN
api features removed from NumPy (np.NaN, np.float, np.product, np.array(x, copy=False)), SciPy (scipy.integrate.simps, scipy.misc) and Python (imp, distutils) WARN; deprecated ones (np.trapz) NOTE
literals 1e0 - 5 (meant 1e-5?), x ^ 2 (exclusive or, not a power), x == np.nan (never true) WARN
units a table of (unit expression, "physical type") whose dimension does not match the name, such as (si.W * si.m**2 / si.Hz, "surface tension") WARN
pairs functions whose names say they undo each other (x_to_y / y_to_x, s2z / z2s), to check with identities.check_roundtrip NOTE

From Python, physverify.scan.extract_tables(path) returns every table the scan found (A, b, c, the claimed order and its source), to pass on to check_tableau for stage order or stability claims.

Exit code 1 if there is any WARN, 0 otherwise, 2 for a bad path or stage name. Files that do not parse are listed, not skipped silently. A clean scan means only that these patterns were not found. Stages are read only for explicit methods on one state variable: implicit methods and systems split into parts (x and v advanced side by side) are skipped, and no check of the physics is made.

physverify smoke

physverify smoke path/to/project --timeout 60

Each script is compiled, then run in its own process with a time limit, stdin closed and plotting off. It reports scripts that do not parse, crash, wait for input or emit NaN/overflow warnings. It runs the code, so use it on trusted code or in the sandbox.

pytest

With physverify installed, pytest my_spec.py turns each check into a pytest test (SKIP becomes a pytest skip). See Continuous integration.