Changing the report format¶
The JSON report is read by people outside MolClusters, often without its reader, so its format is versioned and documented in one place.
Where things live¶
report.py: the format (FORMAT,VERSION,DECIMALS,UNITS,CONNECTION_COLUMNS), the record encoders (cluster_record,group_record,connection_rows,rounded,dumps) and the reader (read_report,Report). Its module docstring is the schema.analysis/report.py:JsonReport, which writes the header inprepareand one line per frame inanalyse, appended throughRunOutput, so nothing accumulates in memory and an interrupted run leaves its frames readable.- The user documentation: The JSON report.
Rules¶
- Change
VERSIONwhenever the format changes, and say what changed in the comment next to it. The reader refuses reports newer than the version it knows. - Update the schema in
report.py's docstring and the user page. - Round every float where it is computed to
DECIMALSplaces (_roundfor built-in fields; contributions go throughrounded). NaN is written asnull(orjson does this; the stdlibjsonwrote invalid bareNaN). - Keep derived fields (diameter, volume, density) even though they follow from others: people read the file without the reader.
- Units go in
UNITS, and get a test intests/test_units.py.
Adding fields from an analysis¶
An analysis adds fields by overriding FrameAnalysis.report_frame and/or
report_cluster. JsonReport.prepare finds the analyses that do (through
Run.analyses; it runs last, so it sees all of them), files what they return under
their class name, and lists them in the header's contributors. Two contributors of
the same class name are an error, since their fields would collide.
A built-in's contribution is part of the format: document it in the schema and
bump VERSION. A user analysis' contribution isn't.
Compression¶
report_compression picks the suffix (report_name), and RunOutput compresses
any .zst/.gz name into one stream per run, ending a block on every flush. The
reader tells the compression from the magic bytes, not the name.