From 2ca02e996030731eb592c0d8722f476086f013d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:49:42 +0000 Subject: [PATCH 1/2] chore(deps-dev): bump ruff from 0.15.22 to 0.16.0 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.22 to 0.16.0. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.22...0.16.0) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.16.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- uv.lock | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 13a880b..7af4514 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ docs = [ dev = [ "pytest>=8", "pytest-cov>=5", - "ruff>=0.5", + "ruff>=0.16.0", "mypy>=1.10", "pre-commit>=3.7", "types-pyyaml>=6", diff --git a/uv.lock b/uv.lock index fab8de0..6f96cc3 100644 --- a/uv.lock +++ b/uv.lock @@ -985,7 +985,7 @@ dev = [ { name = "pre-commit", specifier = ">=3.7" }, { name = "pytest", specifier = ">=8" }, { name = "pytest-cov", specifier = ">=5" }, - { name = "ruff", specifier = ">=0.5" }, + { name = "ruff", specifier = ">=0.16.0" }, { name = "types-pyyaml", specifier = ">=6" }, ] docs = [ @@ -1389,27 +1389,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, - { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, - { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, - { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, - { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, - { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, - { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, - { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, - { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" }, + { url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" }, + { url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" }, + { url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" }, + { url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" }, + { url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" }, + { url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" }, ] [[package]] From d373e1631fe85a1bf535755726c677050c1674d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:57:59 +0000 Subject: [PATCH 2/2] Format docs markdown code blocks for Ruff 0.16 --- docs/concepts/analysis-readiness.md | 16 ++--- docs/concepts/data-model.md | 26 +++++--- docs/concepts/missing-and-unknown-values.md | 4 +- docs/concepts/provenance-and-checksums.md | 18 +++--- docs/concepts/synthetic-data.md | 4 +- docs/concepts/units.md | 4 +- docs/concepts/validation-tiers.md | 30 ++++----- docs/formats/aucx.md | 10 +-- docs/formats/generic-delimited.md | 4 +- docs/getting-started/first-experiment.md | 4 +- docs/getting-started/installation.md | 3 +- docs/getting-started/quickstart.md | 10 +-- docs/how-to/assess-analysis-readiness.md | 2 +- docs/how-to/inspect-an-experiment.md | 31 ++++++---- docs/how-to/interpret-validation-findings.md | 22 +++---- docs/how-to/missing-and-unknown-metadata.md | 14 +++-- docs/how-to/per-scan-radius-axes.md | 11 ++-- docs/how-to/recipes.md | 35 +++++++---- docs/how-to/reproduce-synthetic-datasets.md | 10 ++- docs/how-to/troubleshooting.md | 1 + docs/how-to/verify-an-aucx-archive.md | 6 +- docs/tutorials/export-and-reload-aucx.md | 16 ++--- docs/tutorials/generate-synthetic-data.md | 6 +- docs/tutorials/load-generic-wide.md | 6 +- docs/tutorials/plot-scans.md | 11 ++-- docs/tutorials/python-workflow.md | 65 +++++++++++--------- docs/tutorials/validate-an-experiment.md | 31 ++++++---- 27 files changed, 224 insertions(+), 176 deletions(-) diff --git a/docs/concepts/analysis-readiness.md b/docs/concepts/analysis-readiness.md index 6c32839..8bd205b 100644 --- a/docs/concepts/analysis-readiness.md +++ b/docs/concepts/analysis-readiness.md @@ -10,9 +10,9 @@ scientifically suitable. ```python assessment = experiment.assess_readiness() -assessment.sedimentation_velocity.status # ReadinessStatus +assessment.sedimentation_velocity.status # ReadinessStatus assessment.sedimentation_equilibrium.status -assessment.scientific_suitability.status # always NOT_ASSESSED +assessment.scientific_suitability.status # always NOT_ASSESSED ``` ## What readiness is not @@ -69,11 +69,11 @@ appears in `advisory_issues` for the tiers it pertains to. ```python entry = experiment.assess_readiness().sedimentation_velocity -entry.status # ReadinessStatus -entry.is_blocked # bool -entry.blocking_issues # findings whose `blocks` names this tier -entry.advisory_issues # findings pertaining to this tier that block nothing -entry.note # why the status is what it is +entry.status # ReadinessStatus +entry.is_blocked # bool +entry.blocking_issues # findings whose `blocks` names this tier +entry.advisory_issues # findings pertaining to this tier that block nothing +entry.note # why the status is what it is entry.to_dict() ``` @@ -88,7 +88,7 @@ tier is blocked — that is the normal case for a historical dataset with sparse metadata, and it is a success, not a failure: ```python -assert experiment.validate_structure().is_valid # nothing wrong with it +assert experiment.validate_structure().is_valid # nothing wrong with it assert experiment.assess_readiness().sedimentation_velocity.is_blocked ``` diff --git a/docs/concepts/data-model.md b/docs/concepts/data-model.md index c1b7703..6e4923e 100644 --- a/docs/concepts/data-model.md +++ b/docs/concepts/data-model.md @@ -18,15 +18,21 @@ to serialise an array layer it does not own. ```python from openauc.models import ( - AUCExperiment, ExperimentMetadata, ScanMetadata, Observations, Quantity, - Unit, OpticalSystem, + AUCExperiment, + ExperimentMetadata, + ScanMetadata, + Observations, + Quantity, + Unit, + OpticalSystem, ) experiment = AUCExperiment( metadata=ExperimentMetadata(experiment_id="exp-1"), scans=( ScanMetadata( - scan_id="scan-1", index=0, + scan_id="scan-1", + index=0, elapsed_time=Quantity.of(0.0, Unit.SECOND), optical_system=OpticalSystem.ABSORBANCE, ), @@ -76,12 +82,12 @@ authoritative rather than relying on `NaN`. ```python obs = Observations.from_per_scan( - radii=[[6.0, 6.1, 6.2], [6.0, 6.05]], # different lengths + radii=[[6.0, 6.1, 6.2], [6.0, 6.05]], # different lengths signals=[[0.1, 0.2, 0.3], [0.4, 0.5]], scan_ids=["a", "b"], signal_unit=Unit.FRINGE, ) -obs.points_per_scan() # (3, 2) — real observations per scan +obs.points_per_scan() # (3, 2) — real observations per scan obs.valid_radius_values() # excludes padding ``` @@ -94,11 +100,11 @@ exist. Validation is **tiered**, and none of it raises: ```python -report = experiment.validate_structure() # archival + structural findings -report = experiment.validate() # all four tiers -assessment = experiment.assess_readiness() # metadata presence per workflow -summary = experiment.summary_data() # structured facts -print(experiment.summary()) # the human-readable rendering +report = experiment.validate_structure() # archival + structural findings +report = experiment.validate() # all four tiers +assessment = experiment.assess_readiness() # metadata presence per workflow +summary = experiment.summary_data() # structured facts +print(experiment.summary()) # the human-readable rendering ``` `validate_structure()` checks representational consistency only — identifier diff --git a/docs/concepts/missing-and-unknown-values.md b/docs/concepts/missing-and-unknown-values.md index 8ed4c70..2ab868f 100644 --- a/docs/concepts/missing-and-unknown-values.md +++ b/docs/concepts/missing-and-unknown-values.md @@ -25,8 +25,8 @@ A `PRESENT` quantity must carry a finite value; every other status must carry ```python from openauc.models import Quantity, ValueStatus -Quantity.unknown().status # ValueStatus.UNKNOWN — not the same as MISSING -Quantity.not_applicable().value # None +Quantity.unknown().status # ValueStatus.UNKNOWN — not the same as MISSING +Quantity.not_applicable().value # None Quantity.of(20.0, Unit.DEGREE_CELSIUS).is_present # True ``` diff --git a/docs/concepts/provenance-and-checksums.md b/docs/concepts/provenance-and-checksums.md index 22912b0..3686647 100644 --- a/docs/concepts/provenance-and-checksums.md +++ b/docs/concepts/provenance-and-checksums.md @@ -10,10 +10,10 @@ representation, not an audit claim, and nothing in it is inferred. ```python p = experiment.provenance p.source_path, p.source_filename -p.parser_name, p.parser_version # e.g. 'generic-long', '0.1.0a1' -p.imported_at # UTC timestamp -p.sha256 # digest of the primary data file -p.source_checksums # one typed entry per source file +p.parser_name, p.parser_version # e.g. 'generic-long', '0.1.0a1' +p.imported_at # UTC timestamp +p.sha256 # digest of the primary data file +p.source_checksums # one typed entry per source file p.warnings, p.assumptions ``` @@ -57,11 +57,11 @@ source**: ```python p = generated.provenance -p.parser_name # 'openauc.synthetic' -p.source_path # None — nothing was read from disk -p.sha256 # None -p.transformations # ('generated scenario=moving-boundary',) -p.assumptions # the synthetic disclaimer, seed, noise level +p.parser_name # 'openauc.synthetic' +p.source_path # None — nothing was read from disk +p.sha256 # None +p.transformations # ('generated scenario=moving-boundary',) +p.assumptions # the synthetic disclaimer, seed, noise level ``` ## Archive provenance diff --git a/docs/concepts/synthetic-data.md b/docs/concepts/synthetic-data.md index f6cb8d2..3b4b2ef 100644 --- a/docs/concepts/synthetic-data.md +++ b/docs/concepts/synthetic-data.md @@ -113,8 +113,8 @@ impossible domains are rejected at construction with a clear message. ```python from openauc.synthetic import write_generic_long, write_generic_wide, write_aucx -write_generic_long(experiment, "out/long") # manifest.json + scans.csv -write_generic_wide(experiment, "out/wide") # shared-axis only +write_generic_long(experiment, "out/long") # manifest.json + scans.csv +write_generic_wide(experiment, "out/wide") # shared-axis only write_aucx(experiment, "out/experiment.aucx") restored = openauc.load("out/experiment.aucx") diff --git a/docs/concepts/units.md b/docs/concepts/units.md index 4e57bce..2e97d7f 100644 --- a/docs/concepts/units.md +++ b/docs/concepts/units.md @@ -45,7 +45,7 @@ rejected. ```python from openauc.models import Quantity, Unit, ValueProvenance -Quantity.of(50000.0, Unit.RPM) # present, supplied -Quantity.of(0.5, Unit.OTHER, unit_label="mg/mL") # open-ended unit retained +Quantity.of(50000.0, Unit.RPM) # present, supplied +Quantity.of(0.5, Unit.OTHER, unit_label="mg/mL") # open-ended unit retained Quantity.of(4.3, Unit.SECOND, provenance=ValueProvenance.CONVERTED) # tagged ``` diff --git a/docs/concepts/validation-tiers.md b/docs/concepts/validation-tiers.md index 33451d7..4aec088 100644 --- a/docs/concepts/validation-tiers.md +++ b/docs/concepts/validation-tiers.md @@ -35,8 +35,8 @@ Only the first three exist in `openauc`, and they live in different places: ## The two entry points ```python -report = experiment.validate_structure() # tiers A+B, ERROR and WARNING only -report = experiment.validate() # all four tiers, all severities +report = experiment.validate_structure() # tiers A+B, ERROR and WARNING only +report = experiment.validate() # all four tiers, all severities ``` `validate_structure()` is unchanged in meaning: `report.is_valid` is `True` when @@ -152,21 +152,21 @@ no scientific interpretation. ## Finding structure ```python -issue.code # stable identifier, e.g. "rotor_speed_absent" -issue.severity # ERROR | WARNING | INFO -issue.tiers # tiers this finding speaks to (never empty) -issue.tier # the primary (first) tier -issue.blocks # tiers this finding prevents -issue.message # what was found -issue.observed # what was actually there -issue.expected # the condition that would satisfy the check -issue.remediation # a concrete suggestion -issue.component # the model field concerned -issue.location # the single affected subject, when there is exactly one -issue.scan_ids # every affected scan, sorted +issue.code # stable identifier, e.g. "rotor_speed_absent" +issue.severity # ERROR | WARNING | INFO +issue.tiers # tiers this finding speaks to (never empty) +issue.tier # the primary (first) tier +issue.blocks # tiers this finding prevents +issue.message # what was found +issue.observed # what was actually there +issue.expected # the condition that would satisfy the check +issue.remediation # a concrete suggestion +issue.component # the model field concerned +issue.location # the single affected subject, when there is exactly one +issue.scan_ids # every affected scan, sorted issue.blocks_structural_validity issue.blocks_tier(tier) -issue.describe() # multi-line rendering with the detail above +issue.describe() # multi-line rendering with the detail above issue.to_dict() ``` diff --git a/docs/formats/aucx.md b/docs/formats/aucx.md index fbc223b..fdf0afd 100644 --- a/docs/formats/aucx.md +++ b/docs/formats/aucx.md @@ -7,9 +7,9 @@ ordinary tools**, to preserve the canonical model exactly, and to be verifiable. ```python import openauc -experiment = openauc.load("path/to/experiment") # generic CSV/TSV -experiment.export("experiment.aucx") # write -restored = openauc.load("experiment.aucx") # read back +experiment = openauc.load("path/to/experiment") # generic CSV/TSV +experiment.export("experiment.aucx") # write +restored = openauc.load("experiment.aucx") # read back assert restored.to_dict() == experiment.to_dict() ``` @@ -156,14 +156,14 @@ the message. Archive integrity is **separate** from structural and scientific validation: ```python -info = openauc.inspect_aucx("experiment.aucx") # verifies; raises on problems +info = openauc.inspect_aucx("experiment.aucx") # verifies; raises on problems info.aucx_format_version, info.radius_axis_mode, info.n_scans, info.export report = openauc.validate_aucx("experiment.aucx") # never raises report.is_valid, report.issues restored = openauc.load("experiment.aucx") -restored.validate_structure() # a different question entirely +restored.validate_structure() # a different question entirely restored.validate() restored.assess_readiness() restored.summary_data() diff --git a/docs/formats/generic-delimited.md b/docs/formats/generic-delimited.md index d347cbd..af294ad 100644 --- a/docs/formats/generic-delimited.md +++ b/docs/formats/generic-delimited.md @@ -15,8 +15,8 @@ experiment identity and metadata. ```python import openauc -experiment = openauc.load("path/to/experiment") # a directory -experiment = openauc.load("path/to/experiment/scans.csv") # a data file +experiment = openauc.load("path/to/experiment") # a directory +experiment = openauc.load("path/to/experiment/scans.csv") # a data file experiment = openauc.load("path/to/experiment", format="generic-long") experiment = openauc.load("dir", manifest="dir/manifest.json") diff --git a/docs/getting-started/first-experiment.md b/docs/getting-started/first-experiment.md index 582573c..0c944a0 100644 --- a/docs/getting-started/first-experiment.md +++ b/docs/getting-started/first-experiment.md @@ -88,8 +88,8 @@ uv run openauc inspect my-experiment ```python report = experiment.validate_structure() -print(report) # 'structural validation: OK (no issues)' -print(report.is_valid) # True +print(report) # 'structural validation: OK (no issues)' +print(report.is_valid) # True ``` `is_valid` being `True` means the scans and observations correspond diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 01dacba..537e225 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -105,7 +105,8 @@ uv run openauc formats # lists aucx, generic-long, generic-wide ```python import openauc -print(openauc.__version__) # '0.1.0a1' + +print(openauc.__version__) # '0.1.0a1' print([f.format_id for f in openauc.available_formats()]) # ['aucx', 'generic-long', 'generic-wide'] ``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index b462827..e24fe56 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -96,7 +96,7 @@ import openauc from openauc.synthetic import SyntheticExperimentConfig, generate_experiment from openauc.plotting import plot_scans -config = SyntheticExperimentConfig( # (1)! +config = SyntheticExperimentConfig( # (1)! scenario="moving-boundary", n_scans=20, n_points=300, @@ -105,15 +105,15 @@ config = SyntheticExperimentConfig( # (1)! experiment = generate_experiment(config) # (2)! -print(experiment.summary()) # (3)! +print(experiment.summary()) # (3)! -report = experiment.validate() # (4)! +report = experiment.validate() # (4)! readiness = experiment.assess_readiness() # (5)! -ax = plot_scans(experiment) # (6)! +ax = plot_scans(experiment) # (6)! ax.figure.savefig("synthetic-scans.png") # (7)! -experiment.export("synthetic.aucx") # (8)! +experiment.export("synthetic.aucx") # (8)! restored = openauc.load("synthetic.aucx") # (9)! assert restored.to_dict() == experiment.to_dict() # (10)! ``` diff --git a/docs/how-to/assess-analysis-readiness.md b/docs/how-to/assess-analysis-readiness.md index bf6390f..8f442da 100644 --- a/docs/how-to/assess-analysis-readiness.md +++ b/docs/how-to/assess-analysis-readiness.md @@ -79,7 +79,7 @@ for issue in sv.advisory_issues: ## The permanent non-assessment ```python -assessment.scientific_suitability.status # always NOT_ASSESSED +assessment.scientific_suitability.status # always NOT_ASSESSED ``` Present in **every** assessment, as a machine-readable entry rather than a prose diff --git a/docs/how-to/inspect-an-experiment.md b/docs/how-to/inspect-an-experiment.md index 9629d62..9425fb4 100644 --- a/docs/how-to/inspect-an-experiment.md +++ b/docs/how-to/inspect-an-experiment.md @@ -18,8 +18,8 @@ uv run openauc inspect examples/data/demo_experiment --json import openauc experiment = openauc.load("examples/data/demo_experiment") -print(experiment.summary()) # human-readable text -summary = experiment.summary_data() # structured, frozen, JSON-friendly +print(experiment.summary()) # human-readable text +summary = experiment.summary_data() # structured, frozen, JSON-friendly ``` ## What the summary carries @@ -38,12 +38,12 @@ summary = experiment.summary_data() # structured, frozen, JSON-friendly ## Ranges ```python -summary.radius.minimum # float | None +summary.radius.minimum # float | None summary.radius.maximum -summary.radius.unit # declared unit, never converted +summary.radius.unit # declared unit, never converted summary.radius.n_present, summary.radius.n_absent -summary.radius.render() # '5.9 to 7.2 cm (observed)' or 'unknown' -summary.radius.is_observed # bool +summary.radius.render() # '5.9 to 7.2 cm (observed)' or 'unknown' +summary.radius.is_observed # bool ``` ## Counting absence honestly @@ -51,12 +51,18 @@ summary.radius.is_observed # bool ```python for entry in summary.metadata_presence: print( - entry.component, entry.field, - "present", entry.present, - "missing", entry.missing, - "unknown", entry.unknown, - "not_applicable", entry.not_applicable, - "absent", entry.absent, + entry.component, + entry.field, + "present", + entry.present, + "missing", + entry.missing, + "unknown", + entry.unknown, + "not_applicable", + entry.not_applicable, + "absent", + entry.absent, ) ``` @@ -67,6 +73,7 @@ value. ```python import json + print(json.dumps(summary.to_dict(), indent=2)) ``` diff --git a/docs/how-to/interpret-validation-findings.md b/docs/how-to/interpret-validation-findings.md index 47af127..a3055a0 100644 --- a/docs/how-to/interpret-validation-findings.md +++ b/docs/how-to/interpret-validation-findings.md @@ -7,17 +7,17 @@ Full rationale per rule: [Validation tiers](../concepts/validation-tiers.md). ## Anatomy of a finding ```python -issue.code # stable identifier, e.g. 'rotor_speed_absent' -issue.severity # ERROR | WARNING | INFO -issue.tiers # tier(s) it speaks to -issue.blocks # tier(s) it prevents — may be empty -issue.message # what was found -issue.observed # what was actually there -issue.expected # what would have satisfied the check -issue.remediation # a concrete suggestion -issue.component # the model field concerned -issue.location # the single affected subject, when there is exactly one -issue.scan_ids # every affected scan, sorted +issue.code # stable identifier, e.g. 'rotor_speed_absent' +issue.severity # ERROR | WARNING | INFO +issue.tiers # tier(s) it speaks to +issue.blocks # tier(s) it prevents — may be empty +issue.message # what was found +issue.observed # what was actually there +issue.expected # what would have satisfied the check +issue.remediation # a concrete suggestion +issue.component # the model field concerned +issue.location # the single affected subject, when there is exactly one +issue.scan_ids # every affected scan, sorted issue.describe() # all of the above, rendered ``` diff --git a/docs/how-to/missing-and-unknown-metadata.md b/docs/how-to/missing-and-unknown-metadata.md index 03c80d7..b9db6e0 100644 --- a/docs/how-to/missing-and-unknown-metadata.md +++ b/docs/how-to/missing-and-unknown-metadata.md @@ -20,9 +20,9 @@ model entirely. ```python from openauc.models import Quantity, ValueStatus -Quantity.unknown().status # ValueStatus.UNKNOWN — not MISSING -Quantity.not_applicable().value # None -Quantity.of(20.0, Unit.DEGREE_CELSIUS).is_present # True +Quantity.unknown().status # ValueStatus.UNKNOWN — not MISSING +Quantity.not_applicable().value # None +Quantity.of(20.0, Unit.DEGREE_CELSIUS).is_present # True ``` A `PRESENT` quantity must carry a finite value; every other status must carry @@ -61,8 +61,10 @@ Demonstrated: ```python from openauc.synthetic import ( - SyntheticExperimentConfig, generate_experiment, - write_generic_long, write_aucx, + SyntheticExperimentConfig, + generate_experiment, + write_generic_long, + write_aucx, ) sparse = generate_experiment( @@ -73,7 +75,7 @@ from_aucx = openauc.load(write_aucx(sparse, "out/sparse.aucx")) original = {s.elapsed_time.status for s in sparse.scans} assert ValueStatus.UNKNOWN in original -assert {s.elapsed_time.status for s in from_aucx.scans} == original # exact +assert {s.elapsed_time.status for s in from_aucx.scans} == original # exact assert ValueStatus.UNKNOWN not in {s.elapsed_time.status for s in from_csv.scans} ``` diff --git a/docs/how-to/per-scan-radius-axes.md b/docs/how-to/per-scan-radius-axes.md index 3c20511..54b60e9 100644 --- a/docs/how-to/per-scan-radius-axes.md +++ b/docs/how-to/per-scan-radius-axes.md @@ -12,8 +12,8 @@ which one is in use is an inspectable property rather than a guess. ```python from openauc.models import RadiusAxisMode -experiment.observations.mode is RadiusAxisMode.SHARED # one axis for all -experiment.observations.mode is RadiusAxisMode.PER_SCAN # each scan its own +experiment.observations.mode is RadiusAxisMode.SHARED # one axis for all +experiment.observations.mode is RadiusAxisMode.PER_SCAN # each scan its own ``` ## How the mode is chosen on import @@ -38,7 +38,7 @@ Values and order are exactly as stored. for scan_id, radius, signal in experiment.observations.iter_scan_vectors(): print(scan_id, len(radius), radius[0], radius[-1]) -experiment.observations.points_per_scan() # e.g. (300, 298, 300) +experiment.observations.points_per_scan() # e.g. (300, 298, 300) ``` ## How per-scan data is stored @@ -58,8 +58,9 @@ data: ```python from openauc.plotting import plot_scans + ax = plot_scans(ragged_experiment) -print([len(line.get_xdata()) for line in ax.lines]) # differing lengths +print([len(line.get_xdata()) for line in ax.lines]) # differing lengths ``` ## What refuses @@ -93,7 +94,7 @@ A scan may carry no observations at all. That is representable **only** in per-scan mode: ```python -experiment.observations.points_per_scan() # (300, 0, 300) +experiment.observations.points_per_scan() # (300, 0, 300) ``` It produces an `empty_scan` warning and does not invalidate the experiment. diff --git a/docs/how-to/recipes.md b/docs/how-to/recipes.md index 1f97534..4530c84 100644 --- a/docs/how-to/recipes.md +++ b/docs/how-to/recipes.md @@ -14,8 +14,9 @@ uv run openauc generate demo.aucx --format aucx \ from openauc.synthetic import SyntheticExperimentConfig, generate_experiment experiment = generate_experiment( - SyntheticExperimentConfig(scenario="moving-boundary", n_scans=20, - n_points=300, seed=42) + SyntheticExperimentConfig( + scenario="moving-boundary", n_scans=20, n_points=300, seed=42 + ) ) experiment.export("demo.aucx") ``` @@ -28,7 +29,8 @@ uv run openauc generate work/demo --format generic-long --scenario static-profil ```python from openauc.synthetic import write_generic_long -write_generic_long(experiment, "work/demo") # manifest.json + scans.csv + +write_generic_long(experiment, "work/demo") # manifest.json + scans.csv ``` ## 3. Inspect a CSV experiment @@ -79,7 +81,7 @@ for directory in sorted(Path("data").iterdir()): experiment = openauc.load(directory) ax = plot_scans(experiment, title=experiment.metadata.experiment_id) ax.figure.savefig(f"figures/{directory.name}.png", dpi=150, bbox_inches="tight") - ax.figure.clf() # release the figure between iterations + ax.figure.clf() # release the figure between iterations ``` No display is required; pyplot is never used. @@ -93,6 +95,7 @@ jq '.structural.counts' report.json ```python import json + print(json.dumps(experiment.validate().to_dict(), indent=2)) ``` @@ -152,8 +155,14 @@ for key in ("metadata", "instrument", "samples", "scans", "observations"): ```python from openauc.models import ( - AUCExperiment, ExperimentMetadata, ExperimentType, Observations, - OpticalSystem, Quantity, ScanMetadata, Unit, + AUCExperiment, + ExperimentMetadata, + ExperimentType, + Observations, + OpticalSystem, + Quantity, + ScanMetadata, + Unit, ) experiment = AUCExperiment( @@ -163,14 +172,16 @@ experiment = AUCExperiment( ), scans=( ScanMetadata( - scan_id="scan_001", index=0, + scan_id="scan_001", + index=0, elapsed_time=Quantity.of(0.0, Unit.SECOND), optical_system=OpticalSystem.ABSORBANCE, rotor_speed=Quantity.of(45000.0, Unit.RPM), - temperature=Quantity.unknown(), # explicitly unknown + temperature=Quantity.unknown(), # explicitly unknown ), ScanMetadata( - scan_id="scan_002", index=1, + scan_id="scan_002", + index=1, elapsed_time=Quantity.of(600.0, Unit.SECOND), optical_system=OpticalSystem.ABSORBANCE, rotor_speed=Quantity.of(45000.0, Unit.RPM), @@ -194,15 +205,15 @@ experiment.export("hand-built.aucx") from openauc.models import Observations, RadiusAxisMode, Unit observations = Observations.from_per_scan( - radii=[[6.00, 6.02, 6.04], [6.00, 6.02]], # differing lengths + radii=[[6.00, 6.02, 6.04], [6.00, 6.02]], # differing lengths signals=[[0.1, 0.2, 0.3], [0.4, 0.5]], scan_ids=["a", "b"], signal_unit=Unit.FRINGE, ) assert observations.mode is RadiusAxisMode.PER_SCAN -observations.points_per_scan() # (3, 2) +observations.points_per_scan() # (3, 2) -radius, signal = observations.scan_vectors("b") # padding removed +radius, signal = observations.scan_vectors("b") # padding removed for scan_id, radius, signal in observations.iter_scan_vectors(): print(scan_id, radius.tolist()) ``` diff --git a/docs/how-to/reproduce-synthetic-datasets.md b/docs/how-to/reproduce-synthetic-datasets.md index 6aa8b4f..485542a 100644 --- a/docs/how-to/reproduce-synthetic-datasets.md +++ b/docs/how-to/reproduce-synthetic-datasets.md @@ -22,10 +22,13 @@ assert a.to_dict() == b.to_dict() ```python import json + Path("config.json").write_text(json.dumps(config.model_dump(mode="json"), indent=2)) # later -restored = SyntheticExperimentConfig.model_validate(json.loads(Path("config.json").read_text())) +restored = SyntheticExperimentConfig.model_validate( + json.loads(Path("config.json").read_text()) +) assert generate_experiment(restored).to_dict() == a.to_dict() ``` @@ -57,10 +60,11 @@ generated data. ```python import numpy as np + np.random.seed(1234) before = np.random.get_state() generate_experiment(config) -assert np.array_equal(np.random.get_state()[1], before[1]) # untouched +assert np.array_equal(np.random.get_state()[1], before[1]) # untouched ``` ## With `noise_level=0` @@ -72,7 +76,7 @@ functions of the configuration: x = generate_experiment(SyntheticExperimentConfig(seed=1, noise_level=0.0)) y = generate_experiment(SyntheticExperimentConfig(seed=999, noise_level=0.0)) assert x.to_dict()["observations"] == y.to_dict()["observations"] -assert x.to_dict()["provenance"] != y.to_dict()["provenance"] # seed recorded +assert x.to_dict()["provenance"] != y.to_dict()["provenance"] # seed recorded ``` The seed is still recorded in provenance, honestly. diff --git a/docs/how-to/troubleshooting.md b/docs/how-to/troubleshooting.md index 73fdf8f..48a4fce 100644 --- a/docs/how-to/troubleshooting.md +++ b/docs/how-to/troubleshooting.md @@ -229,6 +229,7 @@ ax.figure.savefig("scans.png") ```python import matplotlib.pyplot as plt + fig, ax = plt.subplots() plot_scans(experiment, ax=ax) plt.show() diff --git a/docs/how-to/verify-an-aucx-archive.md b/docs/how-to/verify-an-aucx-archive.md index a9f4559..a666df2 100644 --- a/docs/how-to/verify-an-aucx-archive.md +++ b/docs/how-to/verify-an-aucx-archive.md @@ -22,9 +22,9 @@ to have examined the experiment. ```python import openauc -info = openauc.inspect_aucx("demo.aucx") # raises on any problem -info.checksum_verified # True -info.aucx_format_version # '1.0' +info = openauc.inspect_aucx("demo.aucx") # raises on any problem +info.checksum_verified # True +info.aucx_format_version # '1.0' info.n_scans, info.n_points info.members info.export.exported_at, info.export.software_version diff --git a/docs/tutorials/export-and-reload-aucx.md b/docs/tutorials/export-and-reload-aucx.md index 14ddc3c..a6d74e0 100644 --- a/docs/tutorials/export-and-reload-aucx.md +++ b/docs/tutorials/export-and-reload-aucx.md @@ -59,12 +59,12 @@ re-encoding and no heavy dependency. ```python info = openauc.inspect_aucx("demo.aucx") -info.aucx_format_version # '1.0' -info.radius_axis_mode # RadiusAxisMode.SHARED +info.aucx_format_version # '1.0' +info.radius_axis_mode # RadiusAxisMode.SHARED info.n_scans, info.n_points -info.checksum_verified # True -info.export.exported_at # when it was written -info.members # every member name +info.checksum_verified # True +info.export.exported_at # when it was written +info.members # every member name ``` `inspect_aucx` **verifies every checksum** and raises on any problem. @@ -73,7 +73,7 @@ info.members # every member name ```python report = openauc.validate_aucx("demo.aucx") -report.is_valid # bool +report.is_valid # bool for issue in report.issues: print(issue.code, issue.message) ``` @@ -88,8 +88,8 @@ uv run openauc validate demo.aucx --readiness ## Overwrite protection ```python -experiment.export("demo.aucx") # ArchiveError if it exists -experiment.export("demo.aucx", overwrite=True) # replaces it +experiment.export("demo.aucx") # ArchiveError if it exists +experiment.export("demo.aucx", overwrite=True) # replaces it ``` Writes are **atomic**: a temporary sibling file is written, read back and diff --git a/docs/tutorials/generate-synthetic-data.md b/docs/tutorials/generate-synthetic-data.md index 1c13c7f..862233e 100644 --- a/docs/tutorials/generate-synthetic-data.md +++ b/docs/tutorials/generate-synthetic-data.md @@ -72,7 +72,7 @@ record repeats the first identifier. **No construction invariant is bypassed.** ```python a = generate_experiment(config) b = generate_experiment(config) -assert a.to_dict() == b.to_dict() # identical +assert a.to_dict() == b.to_dict() # identical ``` Noise is drawn from `numpy.random.default_rng(config.seed)`. **NumPy's global @@ -117,8 +117,8 @@ SyntheticExperimentConfig(radius_min=7.0, radius_max=6.0) ```python from openauc.synthetic import write_generic_long, write_generic_wide, write_aucx -write_generic_long(experiment, "out/long") # manifest.json + scans.csv -write_generic_wide(experiment, "out/wide") # shared-axis only +write_generic_long(experiment, "out/long") # manifest.json + scans.csv +write_generic_wide(experiment, "out/wide") # shared-axis only write_aucx(experiment, "out/demo.aucx") ``` diff --git a/docs/tutorials/load-generic-wide.md b/docs/tutorials/load-generic-wide.md index fbfd891..8fb3823 100644 --- a/docs/tutorials/load-generic-wide.md +++ b/docs/tutorials/load-generic-wide.md @@ -55,7 +55,7 @@ Each entry in `columns.scans` maps one data column to one scan and may carry import openauc experiment = openauc.load("wide-example") -print(experiment.observations.mode) # RadiusAxisMode.SHARED +print(experiment.observations.mode) # RadiusAxisMode.SHARED print(experiment.validate_structure()) # structural validation: OK (no issues) ``` @@ -73,7 +73,9 @@ interpolating or resampling measured data. ```python from openauc.synthetic import ( - SyntheticExperimentConfig, generate_experiment, write_generic_wide, + SyntheticExperimentConfig, + generate_experiment, + write_generic_wide, ) ragged = generate_experiment( diff --git a/docs/tutorials/plot-scans.md b/docs/tutorials/plot-scans.md index 6ec0b01..8637124 100644 --- a/docs/tutorials/plot-scans.md +++ b/docs/tutorials/plot-scans.md @@ -34,14 +34,14 @@ ax = plot_scan(experiment, "scan_001") ```python ax = plot_scans( experiment, - ax=None, # draw on your own Axes instead + ax=None, # draw on your own Axes instead scan_ids=["scan_001", "scan_003"], # restrict and order title="Custom title", legend=True, - label_elapsed=True, # append 't = 600 s' to legend entries + label_elapsed=True, # append 't = 600 s' to legend entries colormap="viridis", linewidth=1.0, - marker=".", # mark individual observations + marker=".", # mark individual observations ) ``` @@ -91,14 +91,15 @@ collections beyond the lines and the axes furniture. ```python shared = openauc.load("examples/data/demo_experiment") -plot_scans(shared) # every line shares one radius axis +plot_scans(shared) # every line shares one radius axis from openauc.synthetic import SyntheticExperimentConfig, generate_experiment + ragged = generate_experiment( SyntheticExperimentConfig(scenario="per-scan-radius", n_scans=5) ) ax = plot_scans(ragged) -print([len(line.get_xdata()) for line in ax.lines]) # differing lengths +print([len(line.get_xdata()) for line in ax.lines]) # differing lengths ``` Scans carrying no observations are skipped. diff --git a/docs/tutorials/python-workflow.md b/docs/tutorials/python-workflow.md index a3012ea..567b8ac 100644 --- a/docs/tutorials/python-workflow.md +++ b/docs/tutorials/python-workflow.md @@ -60,12 +60,12 @@ statement that no scientific claim is made. ```python summary = experiment.summary_data() -summary.n_scans # int -summary.total_valid_observations # int -summary.points_per_scan # tuple[int, ...] -summary.radius.render() # '5.9 to 7.2 cm (observed)' -summary.elapsed_time.minimum # float | None -summary.to_dict() # JSON-friendly dict +summary.n_scans # int +summary.total_valid_observations # int +summary.points_per_scan # tuple[int, ...] +summary.radius.render() # '5.9 to 7.2 cm (observed)' +summary.elapsed_time.minimum # float | None +summary.to_dict() # JSON-friendly dict ``` A **frozen** pydantic model. Every collection is a tuple; there are no mutable @@ -83,8 +83,8 @@ for entry in summary.metadata_presence: ```python report = experiment.validate_structure() -report.is_valid # bool — True when there are no ERROR findings -report.errors # tuple[ValidationIssue, ...] +report.is_valid # bool — True when there are no ERROR findings +report.errors # tuple[ValidationIssue, ...] report.warnings ``` @@ -95,8 +95,8 @@ Returns the **`ARCHIVAL`** and **`STRUCTURAL`** findings of `ERROR` or ```python full = experiment.validate() -full.counts() # (errors, warnings, infos) -full.codes() # ('cell_absent', 'rotor_speed_absent', ...) +full.counts() # (errors, warnings, infos) +full.codes() # ('cell_absent', 'rotor_speed_absent', ...) ``` The superset: all four tiers, all severities, including informational findings. @@ -106,9 +106,9 @@ The superset: all four tiers, all severities, including informational findings. ```python from openauc.models import ValidationSeverity, ValidationTier -full.by_code("rotor_speed_absent") # tuple of matching issues -full.for_tiers(ValidationTier.SV_READINESS) # a narrowed report -full.blocking_for(ValidationTier.SV_READINESS) # what prevents that tier +full.by_code("rotor_speed_absent") # tuple of matching issues +full.for_tiers(ValidationTier.SV_READINESS) # a narrowed report +full.blocking_for(ValidationTier.SV_READINESS) # what prevents that tier full.for_tiers( ValidationTier.STRUCTURAL, severities=(ValidationSeverity.ERROR,), @@ -129,10 +129,10 @@ for issue in full.warnings: ```python assessment = experiment.assess_readiness() sv = assessment.sedimentation_velocity -sv.status # ReadinessStatus -sv.is_blocked # bool -sv.blocking_issues # findings that prevent this tier -sv.advisory_issues # findings that pertain to it but block nothing +sv.status # ReadinessStatus +sv.is_blocked # bool +sv.blocking_issues # findings that prevent this tier +sv.advisory_issues # findings that pertain to it but block nothing ``` ## 10. Reading the four statuses @@ -145,7 +145,7 @@ sv.advisory_issues # findings that pertain to it but block nothing | `NOT_ASSESSED` | Not evaluated — and, for scientific suitability, never will be. | ```python -assessment.scientific_suitability.status # always NOT_ASSESSED +assessment.scientific_suitability.status # always NOT_ASSESSED ``` That entry is a constant. It is never derived from any finding. @@ -166,10 +166,10 @@ each returns its own. Nothing is sorted or resampled. Other accessors: ```python -experiment.observations.mode # RadiusAxisMode.SHARED | PER_SCAN -experiment.observations.scan_ids # tuple[str, ...] -experiment.observations.points_per_scan() # tuple[int, ...] -experiment.observations.radius_range() # (min, max) | None +experiment.observations.mode # RadiusAxisMode.SHARED | PER_SCAN +experiment.observations.scan_ids # tuple[str, ...] +experiment.observations.points_per_scan() # tuple[int, ...] +experiment.observations.radius_range() # (min, max) | None ``` ## 12. Plotting @@ -177,9 +177,9 @@ experiment.observations.radius_range() # (min, max) | None ```python from openauc.plotting import plot_scan, plot_scans -ax = plot_scans(experiment) # all scans overlaid +ax = plot_scans(experiment) # all scans overlaid ax = plot_scans(experiment, scan_ids=["scan_001", "scan_002"]) -ax = plot_scan(experiment, "scan_001") # exactly one +ax = plot_scan(experiment, "scan_001") # exactly one ``` Both return a `matplotlib.axes.Axes`. matplotlib loads on the first draw. @@ -196,6 +196,7 @@ create your own axes and pass them in: ```python import matplotlib.pyplot as plt + fig, ax = plt.subplots() plot_scans(experiment, ax=ax) plt.show() @@ -204,7 +205,7 @@ plt.show() ## 14. Export to AUCX ```python -path = experiment.export("demo.aucx") # refuses to overwrite +path = experiment.export("demo.aucx") # refuses to overwrite path = experiment.export("demo.aucx", overwrite=True) ``` @@ -237,9 +238,15 @@ Everything derives from `OpenAUCError`: ```python from openauc.exceptions import ( - OpenAUCError, ManifestError, ParseError, DataConflictError, - ArchiveError, ArchiveIntegrityError, ArchiveVersionError, - PlottingError, StructuralValidationError, + OpenAUCError, + ManifestError, + ParseError, + DataConflictError, + ArchiveError, + ArchiveIntegrityError, + ArchiveVersionError, + PlottingError, + StructuralValidationError, ) try: @@ -255,7 +262,7 @@ except OpenAUCError as exc: Validation never raises — unless you ask it to: ```python -experiment.validate_structure().raise_if_invalid() # StructuralValidationError +experiment.validate_structure().raise_if_invalid() # StructuralValidationError ``` ## Next step diff --git a/docs/tutorials/validate-an-experiment.md b/docs/tutorials/validate-an-experiment.md index 39213e2..0e7ddad 100644 --- a/docs/tutorials/validate-an-experiment.md +++ b/docs/tutorials/validate-an-experiment.md @@ -13,7 +13,7 @@ import openauc experiment = openauc.load("examples/data/demo_experiment") structural = experiment.validate_structure() # ARCHIVAL + STRUCTURAL, ERROR/WARNING -everything = experiment.validate() # all four tiers, all severities +everything = experiment.validate() # all four tiers, all severities ``` Neither raises. `validate_structure()` is the narrower, historical view; @@ -23,9 +23,9 @@ findings. ## The verdict ```python -structural.is_valid # True when there are no ERROR-severity findings -structural.counts() # (errors, warnings, infos) -print(structural) # 'structural validation: OK (no issues)' +structural.is_valid # True when there are no ERROR-severity findings +structural.counts() # (errors, warnings, infos) +print(structural) # 'structural validation: OK (no issues)' ``` !!! warning "`is_valid` is a statement about structure only" @@ -97,9 +97,9 @@ sparse = generate_experiment( ) ) -assert sparse.validate_structure().is_valid # nothing wrong with it +assert sparse.validate_structure().is_valid # nothing wrong with it assessment = sparse.assess_readiness() -print(assessment.sedimentation_velocity.status) # BLOCKED +print(assessment.sedimentation_velocity.status) # BLOCKED for issue in assessment.sedimentation_velocity.blocking_issues: print(" blocked by:", issue.code) ``` @@ -115,8 +115,8 @@ broken = generate_experiment( SyntheticExperimentConfig(scenario="invalid-structure", n_scans=4) ) report = broken.validate_structure() -print(report.is_valid) # False -print(sorted(set(report.codes()))) # ['duplicate_scan_id', 'scan_id_mismatch'] +print(report.is_valid) # False +print(sorted(set(report.codes()))) # ['duplicate_scan_id', 'scan_id_mismatch'] ``` ## Worked example: UNKNOWN vs OTHER experiment type @@ -136,8 +136,8 @@ other = generate_experiment( SyntheticExperimentConfig(experiment_type=ExperimentType.OTHER, n_scans=4) ) b = other.assess_readiness() -print(b.sedimentation_velocity.status) # NOT_APPLICABLE -print(b.sedimentation_equilibrium.status) # NOT_APPLICABLE +print(b.sedimentation_velocity.status) # NOT_APPLICABLE +print(b.sedimentation_equilibrium.status) # NOT_APPLICABLE ``` `OTHER` is an explicit statement that the run is neither velocity nor @@ -147,7 +147,12 @@ equilibrium. `UNKNOWN` is the *absence* of a statement, so both are assessed. ```python from openauc.models import ( - AUCExperiment, ExperimentMetadata, Observations, Quantity, ScanMetadata, Unit, + AUCExperiment, + ExperimentMetadata, + Observations, + Quantity, + ScanMetadata, + Unit, ) hand_built = AUCExperiment( @@ -158,8 +163,8 @@ hand_built = AUCExperiment( ), ) report = hand_built.validate() -print(report.is_valid) # True — still perfectly valid -print("provenance_absent" in report.codes()) # True, but INFO only +print(report.is_valid) # True — still perfectly valid +print("provenance_absent" in report.codes()) # True, but INFO only ``` `provenance_absent` is informational: a hand-built experiment legitimately has