xtalate 0.1.0.dev0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xtalate-0.2.0/.github/ISSUE_TEMPLATE/bug_incorrect_conversion.yml +50 -0
- xtalate-0.2.0/.github/ISSUE_TEMPLATE/bug_parse_failure.yml +54 -0
- xtalate-0.2.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- xtalate-0.2.0/.github/ISSUE_TEMPLATE/format_request.yml +48 -0
- xtalate-0.2.0/.github/ISSUE_TEMPLATE/general.yml +16 -0
- xtalate-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +32 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/.github/workflows/ci.yml +7 -1
- xtalate-0.2.0/CHANGELOG.md +364 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/CITATION.cff +2 -2
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/CLAUDE.md +13 -3
- xtalate-0.2.0/CONTRIBUTING.md +140 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/PKG-INFO +24 -11
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/README.md +19 -10
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/DECISIONS.md +151 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/MASTER_SPEC.md +17 -7
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/pyproject.toml +22 -1
- xtalate-0.2.0/src/xtalate/__init__.py +13 -0
- xtalate-0.2.0/src/xtalate/_time.py +20 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/cli/main.py +135 -22
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/conversion/__init__.py +3 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/conversion/engine.py +283 -61
- xtalate-0.2.0/src/xtalate/conversion/parse_recovery.py +203 -0
- xtalate-0.2.0/src/xtalate/conversion/preflight.py +327 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/exporters/extxyz.py +8 -5
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/exporters/poscar.py +39 -4
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/exporters/xyz.py +10 -1
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/parsers/_common.py +31 -4
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/parsers/extxyz.py +32 -8
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/parsers/poscar.py +240 -22
- xtalate-0.2.0/src/xtalate/parsers/xyz.py +357 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/recovery/__init__.py +10 -2
- xtalate-0.2.0/src/xtalate/recovery/engine.py +868 -0
- xtalate-0.2.0/src/xtalate/recovery/scenarios.py +172 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/models.py +24 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/paths.py +8 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/sdk/capabilities.py +21 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/sdk/plugins.py +27 -0
- xtalate-0.2.0/src/xtalate/validation/_shared.py +34 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/validation/engine.py +15 -23
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/validation/rethreshold.py +5 -18
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/validation/tolerance.py +77 -3
- xtalate-0.2.0/tests/capabilities/test_velocity_mass_rows.py +49 -0
- xtalate-0.2.0/tests/cli/test_cli.py +537 -0
- xtalate-0.2.0/tests/conversion/test_engine.py +416 -0
- xtalate-0.2.0/tests/conversion/test_frame_reduction_completeness.py +226 -0
- xtalate-0.2.0/tests/conversion/test_parse_recovery.py +195 -0
- xtalate-0.2.0/tests/conversion/test_preflight.py +215 -0
- xtalate-0.2.0/tests/conversion/test_velocity_recovery.py +237 -0
- xtalate-0.2.0/tests/exporters/test_poscar.py +86 -0
- xtalate-0.2.0/tests/golden/ATTRIBUTIONS.md +41 -0
- xtalate-0.2.0/tests/golden/_governance.py +371 -0
- xtalate-0.2.0/tests/golden/contcar/co-md-restart/CONTCAR +14 -0
- xtalate-0.2.0/tests/golden/contcar/co-md-restart/expected.canonical.json +118 -0
- xtalate-0.2.0/tests/golden/contcar/co-md-restart/manifest.yaml +19 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/extxyz/co-in-cell/manifest.yaml +1 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/poscar/nacl-primitive/expected.canonical.json +3 -13
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/poscar/nacl-primitive/manifest.yaml +3 -2
- xtalate-0.2.0/tests/golden/test_corpus_governance.py +107 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/xyz/water-traj/manifest.yaml +1 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/parsers/test_extxyz.py +28 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/parsers/test_poscar.py +106 -2
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/parsers/test_xyz.py +7 -0
- xtalate-0.2.0/tests/property/_generators.py +215 -0
- xtalate-0.2.0/tests/property/_properties.py +106 -0
- xtalate-0.2.0/tests/property/_strategies.py +188 -0
- xtalate-0.2.0/tests/property/test_fabricative_recovery_completeness.py +159 -0
- xtalate-0.2.0/tests/property/test_report_completeness.py +161 -0
- xtalate-0.2.0/tests/property/test_report_completeness_hypothesis.py +74 -0
- xtalate-0.2.0/tests/recovery/test_engine.py +437 -0
- xtalate-0.2.0/tests/recovery/test_missing_masses.py +117 -0
- xtalate-0.2.0/tests/recovery/test_missing_velocities.py +209 -0
- xtalate-0.2.0/tests/recovery/test_physics_maxwell_boltzmann.py +56 -0
- xtalate-0.2.0/tests/recovery/test_scenarios.py +128 -0
- xtalate-0.2.0/tests/roundtrip/_compare.py +85 -0
- xtalate-0.2.0/tests/roundtrip/_matrix.py +163 -0
- xtalate-0.2.0/tests/roundtrip/test_matrix_enumeration.py +102 -0
- xtalate-0.2.0/tests/roundtrip/test_three_hop.py +86 -0
- xtalate-0.2.0/tests/roundtrip/test_two_hop.py +98 -0
- {xtalate-0.1.0.dev0/tests/golden/schema → xtalate-0.2.0/tests/schema/fixtures}/poscar_nacl.json +3 -13
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/schema/test_golden_roundtrip.py +5 -3
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/schema/test_models.py +30 -0
- xtalate-0.2.0/tests/sdk/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/test_smoke.py +1 -1
- xtalate-0.2.0/tests/validation/__init__.py +0 -0
- xtalate-0.2.0/tests/validation/test_tolerance.py +105 -0
- xtalate-0.1.0.dev0/.claude/settings.local.json +0 -39
- xtalate-0.1.0.dev0/.import_linter_cache/.gitignore +0 -2
- xtalate-0.1.0.dev0/.import_linter_cache/711309a5eafe379c50451bc7ebdb0a6d9c6d28e6.data.json +0 -1
- xtalate-0.1.0.dev0/.import_linter_cache/CACHEDIR.TAG +0 -3
- xtalate-0.1.0.dev0/.import_linter_cache/xtalate.meta.json +0 -1
- xtalate-0.1.0.dev0/CHANGELOG.md +0 -47
- xtalate-0.1.0.dev0/src/xtalate/__init__.py +0 -10
- xtalate-0.1.0.dev0/src/xtalate/conversion/preflight.py +0 -127
- xtalate-0.1.0.dev0/src/xtalate/parsers/xyz.py +0 -286
- xtalate-0.1.0.dev0/src/xtalate/recovery/engine.py +0 -321
- xtalate-0.1.0.dev0/src/xtalate/recovery/scenarios.py +0 -86
- xtalate-0.1.0.dev0/tests/cli/test_cli.py +0 -220
- xtalate-0.1.0.dev0/tests/conversion/test_engine.py +0 -193
- xtalate-0.1.0.dev0/tests/conversion/test_preflight.py +0 -111
- xtalate-0.1.0.dev0/tests/recovery/test_engine.py +0 -175
- xtalate-0.1.0.dev0/tests/recovery/test_scenarios.py +0 -35
- xtalate-0.1.0.dev0/tests/validation/test_tolerance.py +0 -56
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/.gitignore +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/LICENSE +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/NOTICE +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/ARCHITECTURE_REVIEW.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.1.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.2.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.3.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.4.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.5.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.6.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v0.7.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/IMPLEMENTATION_PLAN_v1.0.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/docs/Incremental_Roadmap_v1.0.md +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/examples/.gitkeep +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/examples/convert_extxyz_to_poscar.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/capabilities/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/capabilities/registry.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/cli/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/cli/render.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/conversion/report.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/discovery/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/discovery/engine.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/discovery/report.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/discovery/sniffer.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/exporters/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/parsers/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/py.typed +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/registry.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/arrays.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/elements.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/schema/presence.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/sdk/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/sdk/results.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/validation/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/src/xtalate/validation/report.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/_dummy_plugins.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/_format_helpers.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/capabilities/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/capabilities/test_registry.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/cli/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/conversion/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/discovery/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/discovery/test_engine.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/discovery/test_sniffer.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/exporters/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/.gitkeep +0 -0
- {xtalate-0.1.0.dev0/tests/recovery → xtalate-0.2.0/tests/golden}/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/extxyz/co-in-cell/expected.canonical.json +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/extxyz/co-in-cell/sample.extxyz +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/poscar/nacl-primitive/POSCAR +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/xyz/water-traj/expected.canonical.json +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/golden/xyz/water-traj/water_traj.xyz +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/parsers/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/parsers/test_builtins_registration.py +0 -0
- {xtalate-0.1.0.dev0/tests/schema → xtalate-0.2.0/tests/property}/__init__.py +0 -0
- {xtalate-0.1.0.dev0/tests/sdk → xtalate-0.2.0/tests/recovery}/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/roundtrip/.gitkeep +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/roundtrip/__init__.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/roundtrip/test_identity.py +0 -0
- {xtalate-0.1.0.dev0/tests/validation → xtalate-0.2.0/tests/schema}/__init__.py +0 -0
- {xtalate-0.1.0.dev0/tests/golden/schema → xtalate-0.2.0/tests/schema/fixtures}/xyz_2frame_3atom.json +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/schema/test_arrays.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/schema/test_paths.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/schema/test_presence.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/sdk/test_contracts.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/validation/test_engine.py +0 -0
- {xtalate-0.1.0.dev0 → xtalate-0.2.0}/tests/validation/test_rethreshold.py +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: "Bug: incorrect conversion"
|
|
2
|
+
description: A conversion kept, dropped, transformed, or fabricated something wrong.
|
|
3
|
+
labels: ["bug", "conversion"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: >-
|
|
8
|
+
The Conversion Report and Validation Report are **designed to be the bug report**
|
|
9
|
+
(`docs/MASTER_SPEC.md` Part 4 §2, Part 5 §3). Paste them below — they tell us far more
|
|
10
|
+
than a prose description can.
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: source
|
|
13
|
+
attributes:
|
|
14
|
+
label: Source file (or minimal reproducer)
|
|
15
|
+
description: >-
|
|
16
|
+
Paste the smallest file that reproduces the problem, or attach it. If it can be
|
|
17
|
+
donated to the golden corpus, say so and its license (see the license field below).
|
|
18
|
+
render: text
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: input
|
|
22
|
+
id: target
|
|
23
|
+
attributes:
|
|
24
|
+
label: Target format
|
|
25
|
+
placeholder: "e.g. poscar"
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
- type: textarea
|
|
29
|
+
id: reports
|
|
30
|
+
attributes:
|
|
31
|
+
label: Conversion Report + Validation Report (JSON)
|
|
32
|
+
description: "Output of `xtalate convert ... --json` (and `xtalate validate ... --json` if relevant)."
|
|
33
|
+
render: json
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: expected_vs_observed
|
|
38
|
+
attributes:
|
|
39
|
+
label: Expected vs observed
|
|
40
|
+
description: What should the report/output have said or done, and what did it actually do?
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
- type: input
|
|
44
|
+
id: versions
|
|
45
|
+
attributes:
|
|
46
|
+
label: Xtalate + schema versions
|
|
47
|
+
description: "`xtalate --version`, and the `schema_version` from the report."
|
|
48
|
+
placeholder: "xtalate 0.2.0, schema 0.1.0"
|
|
49
|
+
validations:
|
|
50
|
+
required: true
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: "Bug: parse failure"
|
|
2
|
+
description: A file failed to parse, or parsed with a wrong or missing ParseIssue.
|
|
3
|
+
labels: ["bug", "parser"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: file
|
|
7
|
+
attributes:
|
|
8
|
+
label: File (or minimal reproducer)
|
|
9
|
+
description: Paste or attach the file that fails to parse.
|
|
10
|
+
render: text
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: error
|
|
15
|
+
attributes:
|
|
16
|
+
label: Full error envelope / ParseIssue list
|
|
17
|
+
description: >-
|
|
18
|
+
The complete error output (`docs/MASTER_SPEC.md` Part 3 §5, Part 6 §6) — codes,
|
|
19
|
+
messages, line numbers. Run with `--json` if using the CLI.
|
|
20
|
+
render: text
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
23
|
+
- type: input
|
|
24
|
+
id: producer
|
|
25
|
+
attributes:
|
|
26
|
+
label: What produced the file?
|
|
27
|
+
placeholder: "e.g. VASP 6.4, ASE 3.23, hand-written"
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
- type: dropdown
|
|
31
|
+
id: donate
|
|
32
|
+
attributes:
|
|
33
|
+
label: Can this file be donated to the golden corpus?
|
|
34
|
+
description: >-
|
|
35
|
+
If yes, we may add it as a regression fixture. This requires an explicit data
|
|
36
|
+
license (see below) — a file without one cannot be admitted.
|
|
37
|
+
options:
|
|
38
|
+
- "No"
|
|
39
|
+
- "Yes — I hold the rights and grant Apache-2.0 / a compatible license"
|
|
40
|
+
- "Yes — it is published open data (I'll give the license + attribution)"
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
- type: input
|
|
44
|
+
id: license
|
|
45
|
+
attributes:
|
|
46
|
+
label: License status of the file (if donating)
|
|
47
|
+
placeholder: "e.g. CC-BY-4.0 (Materials Project), CC0, my own work"
|
|
48
|
+
- type: input
|
|
49
|
+
id: versions
|
|
50
|
+
attributes:
|
|
51
|
+
label: Xtalate + schema versions
|
|
52
|
+
placeholder: "xtalate 0.2.0, schema 0.1.0"
|
|
53
|
+
validations:
|
|
54
|
+
required: true
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: "Format request"
|
|
2
|
+
description: Request support for a new file format.
|
|
3
|
+
labels: ["enhancement", "format-request"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: >-
|
|
8
|
+
A well-scoped format request — especially with example files and a draft capability
|
|
9
|
+
row — is a genuinely valuable contribution even if you never write a line of code.
|
|
10
|
+
- type: input
|
|
11
|
+
id: name
|
|
12
|
+
attributes:
|
|
13
|
+
label: Format name / spec link
|
|
14
|
+
placeholder: "e.g. CIF — https://www.iucr.org/resources/cif/spec"
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: capability_draft
|
|
19
|
+
attributes:
|
|
20
|
+
label: Which canonical fields can it express? (a draft capability row!)
|
|
21
|
+
description: >-
|
|
22
|
+
Which of the canonical categories (Geometry, Cell, Trajectory, Dynamics, Electronic,
|
|
23
|
+
Metadata) can this format hold, and which can it not? Honest PARTIAL beats optimistic
|
|
24
|
+
FULL — this becomes the format's Capability Matrix row (`docs/MASTER_SPEC.md` Part 3 §3).
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: examples
|
|
29
|
+
attributes:
|
|
30
|
+
label: Example files + license status
|
|
31
|
+
description: >-
|
|
32
|
+
Links or attachments, each with its data license. Licensed examples can seed the
|
|
33
|
+
golden corpus; unlicensed ones cannot be admitted.
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
- type: dropdown
|
|
37
|
+
id: contribute
|
|
38
|
+
attributes:
|
|
39
|
+
label: Would you be willing to contribute a plugin?
|
|
40
|
+
description: >-
|
|
41
|
+
Heads up: the plugin SDK is not frozen until v1.0 (risk R12), so a plugin may need to
|
|
42
|
+
follow SDK changes between minor versions until then.
|
|
43
|
+
options:
|
|
44
|
+
- "No — requesting only"
|
|
45
|
+
- "Maybe, with guidance"
|
|
46
|
+
- "Yes"
|
|
47
|
+
validations:
|
|
48
|
+
required: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: "General bug / docs issue"
|
|
2
|
+
description: Anything that doesn't fit the other templates.
|
|
3
|
+
labels: ["triage"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: Description
|
|
9
|
+
description: What's the issue? For a docs issue, link the doc/section.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: input
|
|
13
|
+
id: versions
|
|
14
|
+
attributes:
|
|
15
|
+
label: Xtalate + schema versions (if applicable)
|
|
16
|
+
placeholder: "xtalate 0.2.0, schema 0.1.0"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!-- Thanks for contributing to Xtalate. Keep PRs small and single-purpose. -->
|
|
2
|
+
|
|
3
|
+
## What & why
|
|
4
|
+
|
|
5
|
+
<!-- What does this change do, and why? For any nontrivial design decision, name at least
|
|
6
|
+
one reasonable rejected alternative — the standard the whole doc set holds itself to. -->
|
|
7
|
+
|
|
8
|
+
## Rejected alternative (for design changes)
|
|
9
|
+
|
|
10
|
+
<!-- e.g. "Considered threading choices into build_preflight; rejected because it breaks the
|
|
11
|
+
draft's purity (D46)." Delete this section only for pure docs/typo PRs. -->
|
|
12
|
+
|
|
13
|
+
## Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] **Docs updated or confirmed unaffected.** The docs are the constitution; a behavior
|
|
16
|
+
change and its doc change are one PR (`docs/MASTER_SPEC.md` Part 10 §4.6).
|
|
17
|
+
- [ ] **Golden cases added** for new behavior, each with a licensed `manifest.yaml`
|
|
18
|
+
(`08 §3.2`), and `tests/golden/ATTRIBUTIONS.md` regenerated (`python
|
|
19
|
+
tests/golden/_governance.py`) if any manifest changed.
|
|
20
|
+
- [ ] **No parser defaulting introduced** — the absence convention holds and the
|
|
21
|
+
default-laundering suite passes (P3, Part 3 §2).
|
|
22
|
+
- [ ] **Completeness invariant green** — the runtime assertion and the property suite
|
|
23
|
+
(`tests/property/`) both pass; no field is lost, dropped, or fabricated silently (P1/P4).
|
|
24
|
+
- [ ] **Capability declarations updated** and the Part 3 §3 table sync test is green.
|
|
25
|
+
- [ ] **Rejected alternative named** in the description for design changes.
|
|
26
|
+
- [ ] **Attribution file regenerates cleanly** — the governance suite
|
|
27
|
+
(`tests/golden/test_corpus_governance.py`) is green.
|
|
28
|
+
- [ ] **Contributed files carry license grants** — for any file added under
|
|
29
|
+
`origin.kind: contributed`, I have the right to contribute it and grant it under the
|
|
30
|
+
recorded license (Apache-2.0 or a compatible data license).
|
|
31
|
+
- [ ] **Full local gate passed:** `ruff check .`, `ruff format --check .`, `mypy`,
|
|
32
|
+
`lint-imports`, `pytest`.
|
|
@@ -41,8 +41,14 @@ jobs:
|
|
|
41
41
|
- name: Mypy
|
|
42
42
|
run: mypy
|
|
43
43
|
|
|
44
|
+
# Promoted to a required check in v0.2 M11 (Part 10 §1 deferral table, "earliest
|
|
45
|
+
# v0.2"): the import-linter contract from M0 confirmed as a blocking gate.
|
|
44
46
|
- name: Import-graph lint (P2)
|
|
45
47
|
run: lint-imports
|
|
46
48
|
|
|
47
|
-
-
|
|
49
|
+
# Pytest enforces the coverage ratchet (--cov-fail-under in pyproject addopts) and the
|
|
50
|
+
# golden-corpus governance suite: manifest schema + license, per-source sha256, the
|
|
51
|
+
# schema-version migration-chain load, and the ATTRIBUTIONS.md regeneration diff
|
|
52
|
+
# (Part 8 §3; v0.2 M11). A silent fixture edit or a lapsed attribution fails here.
|
|
53
|
+
- name: Pytest (unit + golden + governance, with coverage gate)
|
|
48
54
|
run: pytest
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Xtalate are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). The canonical schema version is
|
|
6
|
+
tracked separately from the package version and reaches `1.0.0` only in the v1.0 release
|
|
7
|
+
(`docs/MASTER_SPEC.md` Part 2 §5); v0.1 objects carry `schema_version = "0.1.0"`.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
Post-`0.2.0` architectural-review pass: report-semantics and JSON-tolerance correctness fixes,
|
|
12
|
+
golden-corpus governance hardening, a velocity-bearing corpus case, and internal de-duplication.
|
|
13
|
+
Schema stays `0.1.0`; no normative report/field shapes change.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **A CONTCAR-with-velocities golden case** (`tests/golden/contcar/co-md-restart/`). CONTCAR was a
|
|
18
|
+
round-trip *target* only; this synthetic case gives it a golden *source* with a Cartesian velocity
|
|
19
|
+
block, so velocities now flow through the identity, two-hop, and three-hop round-trip matrices and
|
|
20
|
+
the completeness invariant — previously the M8 velocity block was unit-tested but never exercised
|
|
21
|
+
as system-level round-trip content.
|
|
22
|
+
- **Report-completeness property coverage for the fabricative recovery family**
|
|
23
|
+
(`tests/property/test_fabricative_recovery_completeness.py`). `missing_velocities`
|
|
24
|
+
(`zero_init`/`maxwell_boltzmann`) and the `maxwell_boltzmann → missing_masses` chain now flow
|
|
25
|
+
through the **independently re-derived** M10 properties, not just the runtime completeness
|
|
26
|
+
assertion — closing the gap where the opt-in fabricative path (never in the shared round-trip
|
|
27
|
+
presets) reached only the runtime guard.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **Golden-corpus governance hardened (`docs/DECISIONS.md` D54).** Every data file under
|
|
32
|
+
`tests/golden/` must now be claimed by a manifest — an unmanifested source/expectation, or the
|
|
33
|
+
`manifest.yml` misspelling, fails CI rather than silently bypassing the license/hash/schema
|
|
34
|
+
guarantees. Manifests gain a required `expected_sha256`, verified against `expected.canonical.json`
|
|
35
|
+
exactly as `sha256` guards the source. The schema-lag bound is now two-sided (an expectation
|
|
36
|
+
*ahead* of the current schema major is rejected as impossible, not just one too far behind). The
|
|
37
|
+
two schema-serialization fixtures moved from `tests/golden/schema/` to `tests/schema/fixtures/`, so
|
|
38
|
+
the `ATTRIBUTIONS.md` "every file is admitted only with a license" claim is now literally true.
|
|
39
|
+
- **Internal de-duplication (no behavior change).** The validation status-precedence and
|
|
40
|
+
numeric-field→quantity tables are single-sourced in `xtalate.validation._shared`; the derived-path
|
|
41
|
+
exclusion in `xtalate.schema.paths.DERIVED_PATHS`; the UTC-timestamp helper in `xtalate._time`.
|
|
42
|
+
The single-frame reduction (re-index, `custom_per_frame` slice, `trajectory` drop) shared by
|
|
43
|
+
`frame_selection` recovery and `split_all` export is now one `CanonicalObject.single_frame` method,
|
|
44
|
+
so the two paths can never slice a reduced object differently. The M10 property harness deliberately
|
|
45
|
+
keeps its own independent copies (D50).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- **A fabricated (`supplied`) field is no longer also listed `preserved` in the D51 flow
|
|
50
|
+
(`docs/DECISIONS.md` D53).** When a `mixed` cell's only cell-bearing frame was dropped by
|
|
51
|
+
`frame_selection` and `missing_lattice` fabricated a replacement, the report listed
|
|
52
|
+
`cell.lattice_vectors`/`cell.pbc` in `preserved` **and** `removed` **and** `supplied` at once — the
|
|
53
|
+
stale pre-flight optimistic-preserve prediction was never struck once recovery falsified it. The
|
|
54
|
+
Conversion Engine now removes any `supplied` path from `preserved` (the two are mutually exclusive
|
|
55
|
+
per path), leaving the honest **removed + supplied** pair D51 always documented.
|
|
56
|
+
`absence_conformance` correspondingly exempts `supplied` paths from its must-be-absent check (a
|
|
57
|
+
fabricated replacement is expected to reappear), applied identically in the runtime guard and,
|
|
58
|
+
independently, in the M10 property re-derivation. Regression coverage in
|
|
59
|
+
`tests/conversion/test_frame_reduction_completeness.py`.
|
|
60
|
+
- **JSON custom tolerance-table files with scientific-notation bounds now parse
|
|
61
|
+
(`docs/DECISIONS.md` D55).** `--tolerance-profile ./table.json` routed through `yaml.safe_load`,
|
|
62
|
+
whose YAML 1.1 float grammar reads dotless `1e-8` as the *string* `"1e-8"`, so a valid JSON table
|
|
63
|
+
(exactly what `json.dumps` emits) failed with a confusing "must be a number". The CLI now parses
|
|
64
|
+
`.json` files with `json.load` and other extensions with `yaml.safe_load`. Regression test in
|
|
65
|
+
`tests/cli/test_cli.py`.
|
|
66
|
+
|
|
67
|
+
## [0.2.0] — 2026-07-15
|
|
68
|
+
|
|
69
|
+
v0.2 — **"trustworthy core complete."** The full Part 4 §3.3 recovery scenario catalog, the
|
|
70
|
+
POSCAR/CONTCAR velocity block with velocity/mass recovery, the cross-format round-trip matrix
|
|
71
|
+
with custom tolerance tables, the report-completeness property test, and the corpus-governance
|
|
72
|
+
and contributor surface that make outside golden-corpus contributions realistic. Schema
|
|
73
|
+
version is unchanged at `0.1.0`; the four v0.1 formats (XYZ, extXYZ, POSCAR, CONTCAR) are the
|
|
74
|
+
supported set. Pre-1.0, a minor bump may still break — the plugin SDK is not frozen until v1.0
|
|
75
|
+
(risk R12).
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- **Golden-corpus governance, contributor surface, and the v0.2 release (v0.2 M11).** The last
|
|
80
|
+
milestone of the break: the governance that makes the golden corpus a corpus a stranger can
|
|
81
|
+
extend without a maintainer in the loop, mechanized so it cannot silently rot.
|
|
82
|
+
- **Manifest governance suite** (`tests/golden/test_corpus_governance.py`, `_governance.py`;
|
|
83
|
+
`docs/MASTER_SPEC.md` Part 8 §3): every `manifest.yaml` is schema-validated (required
|
|
84
|
+
fields incl. `origin.kind`, `origin.license`, `sha256`); a **missing or blank license is a
|
|
85
|
+
hard CI failure** — *no manifest, no license, no merge* (§3.2). The recorded `sha256` of
|
|
86
|
+
every source file is re-verified, so a silent fixture edit is impossible. CC-BY origins
|
|
87
|
+
require an `attribution`, and published-dataset origins require a `source`.
|
|
88
|
+
- **Schema-version sync check** (§3.3): every `expected.canonical.json` loads **through the
|
|
89
|
+
migration chain** (`load_expected_through_migration_chain` — the identity today, the seam
|
|
90
|
+
for real migrations when the schema versions past `0.1.0`), its embedded `schema_version`
|
|
91
|
+
is cross-checked against the manifest's `canonical_schema_version`, and CI fails if any
|
|
92
|
+
manifest lags more than one **major** version behind current.
|
|
93
|
+
- **`tests/golden/ATTRIBUTIONS.md` regenerated from manifests and diffed in CI** (§3.2): the
|
|
94
|
+
aggregate attribution file is *generated* (`python tests/golden/_governance.py`), never
|
|
95
|
+
hand-edited, and the suite fails on any drift — an attribution obligation can never silently
|
|
96
|
+
lapse.
|
|
97
|
+
- **CI gates promoted** (Part 10 §1 deferral table, earliest v0.2): a **coverage ratchet**
|
|
98
|
+
(`--cov-fail-under=91` in `pyproject.toml`, set below the measured ~92.6% branch coverage with slack — a floor
|
|
99
|
+
that rises, never lowers, adding **`pytest-cov`** as a dev dependency), and the M0
|
|
100
|
+
import-linter contract confirmed as a required check.
|
|
101
|
+
- **`CONTRIBUTING.md`** (Part 10 §4.3): the docs-are-the-constitution rule, the non-negotiables
|
|
102
|
+
(absence convention, completeness invariant, glossary), the add-a-format checklist, and the
|
|
103
|
+
Tier 0 dev loop — plus the two honesty clauses (golden-corpus contributions are the invited
|
|
104
|
+
path now; parser contributions are welcome-with-churn-warning until the SDK freezes at v1.0,
|
|
105
|
+
risk R12).
|
|
106
|
+
- **Issue + PR templates** (Part 10 §4.4–4.5, `.github/ISSUE_TEMPLATE/`,
|
|
107
|
+
`.github/PULL_REQUEST_TEMPLATE.md`): the reports-are-the-bug-report intake for incorrect
|
|
108
|
+
conversions and parse failures, a format-request template with a draft capability row, and a
|
|
109
|
+
PR checklist including the **license-grant checkbox** for contributed files.
|
|
110
|
+
- **Release:** version bumped to **0.2.0**; README "what v0.2 does / does not do" scope
|
|
111
|
+
statement updated honestly (the round-trip matrix, custom tolerance tables, and recovery
|
|
112
|
+
catalog now shipped). Tag + PyPI/GitHub publish remain the maintainer's manual step
|
|
113
|
+
(`docs/DECISIONS.md` D52).
|
|
114
|
+
- **Report-completeness property test (v0.2 M10).** The single most important test in the repository
|
|
115
|
+
(`docs/MASTER_SPEC.md` Part 8 §1.2), mechanically enforcing **P1** (no silent loss) and **P4** (no
|
|
116
|
+
misfiled fabrication) over conversions that *have not happened yet* — the test-time generalization
|
|
117
|
+
of the v0.1-M4 runtime completeness assertion.
|
|
118
|
+
- **Two properties, re-derived in test code** (`tests/property/_properties.py`, deliberately not
|
|
119
|
+
importing the production guard so it is an independent check — D50): **Property 1 — the
|
|
120
|
+
completeness invariant** (every source-`present`/`mixed` path appears in `preserved` ∪ `removed`;
|
|
121
|
+
every `supplied` entry names a source-`absent` path traced to a recorded Assumption) and
|
|
122
|
+
**Property 2 — absence conformance** (every `removed` path is absent in the re-parsed output).
|
|
123
|
+
- **Stage-1 generator — parametrized golden mutations** (`tests/property/_generators.py`):
|
|
124
|
+
systematically nulls or populates **each optional canonical field-path** of the worked-example
|
|
125
|
+
goldens (all eight schema categories reached) plus a per-frame `mixed` configuration, yielding 59
|
|
126
|
+
valid Canonical Objects; `tests/property/test_report_completeness.py` drives every `(mutant,
|
|
127
|
+
target)` pair through the real Conversion Engine under the `strict` profile with fixed recovery
|
|
128
|
+
presets, asserting both properties on every report — including refused reports, which must still
|
|
129
|
+
satisfy the completeness invariant. Every mutant re-validates through the model validators, so an
|
|
130
|
+
invariant-violating mutation fails loudly at generation rather than producing a dead test.
|
|
131
|
+
- **Stage-2 generator — hypothesis strategies over randomized objects** (`tests/property/
|
|
132
|
+
_strategies.py`, `test_report_completeness_hypothesis.py`): randomized Canonical Objects with
|
|
133
|
+
independent presence draws across all categories and **shrinking** on failure, exercising the
|
|
134
|
+
field-*combinations* and multi-field `mixed` configs the one-at-a-time sweep cannot. Bounded to
|
|
135
|
+
`max_examples=200` for the PR suite (Part 8 §5); v0.3's nightly workflow hosts the extended budget.
|
|
136
|
+
Adds **`hypothesis`** as a **test-only** dev dependency (`docs/DECISIONS.md` D50) — the
|
|
137
|
+
minimal-dependency posture governs runtime deps, which are unchanged.
|
|
138
|
+
- **Independent-guard proof** (M10 done-means): feeding a tampered report — one `removed` entry
|
|
139
|
+
dropped, or a `supplied` entry's Assumption removed — to the property checker is caught as silent
|
|
140
|
+
loss / silent fabrication, demonstrating the property catches the class of bug the runtime
|
|
141
|
+
assertion does, without the runtime assertion in the loop. A non-vacuity guard asserts the
|
|
142
|
+
stage-1 lattice actually exercises both `removed` and `supplied` across its pairs.
|
|
143
|
+
|
|
144
|
+
- **Cross-format round-trip matrix suites + custom tolerance-table files (v0.2 M9).** v0.1 proved
|
|
145
|
+
*identity* round-trips (`A → Canonical → A`); v0.2 adds the cross-format matrix that catches
|
|
146
|
+
parser/exporter **asymmetry**, plus the deferred tolerance-file feature.
|
|
147
|
+
- **Two-hop suite** (`A → Canonical → B → Canonical′`, `tests/roundtrip/test_two_hop.py`):
|
|
148
|
+
parametrized over **every `(source, target)` pair enumerated from the registry**, driven through
|
|
149
|
+
the real Conversion + Validation engines under the **`strict`** profile, with fabricative/
|
|
150
|
+
selective gaps resolved by fixed recovery presets so Assumption recording is exercised end to end.
|
|
151
|
+
The comparable subspace is **computed from the Capability Matrix at test time, never hand-listed**
|
|
152
|
+
(`tests/roundtrip/_matrix.py`); reusing the Validation Engine's diff is deliberate (D49).
|
|
153
|
+
- **Three-hop return** (`A → B → A`, `test_three_hop.py`): the symmetric-bug catcher over the
|
|
154
|
+
curated high-risk pairs (`xyz↔extxyz`, `poscar↔extxyz`, `poscar↔contcar`), each anchored to its
|
|
155
|
+
golden `expected.canonical.json` and diffed over the matrix-computed subspace by a dedicated
|
|
156
|
+
comparator (`_compare.py`). Velocity round-trips (POSCAR↔extXYZ) join automatically via M8's
|
|
157
|
+
capability rows — the matrix, not a hand-list, governs coverage.
|
|
158
|
+
- **Registry-driven enumeration** (`test_matrix_enumeration.py`): registering a dummy in-test
|
|
159
|
+
format grows the enumerated targets, sources, and pair list — and the comparable-subspace
|
|
160
|
+
machinery answers for it — with zero suite edits, the mechanical guarantee behind **P6**.
|
|
161
|
+
- **Custom tolerance-table files.** `--tolerance-profile FILE` now accepts a YAML/JSON table
|
|
162
|
+
(`ToleranceProfile.from_mapping`) of per-quantity `{warn, fail}` overrides on the `default`
|
|
163
|
+
bases; omitted quantities inherit their default. The non-configurable Part 5 §4.4 rules are
|
|
164
|
+
enforced by rejection (the `k_*` multipliers and representational-bound floor are fixed; discrete
|
|
165
|
+
checks admit no tolerance), with actionable errors. `xtalate convert … --tolerance-profile
|
|
166
|
+
./custom.yaml` and `xtalate validate --validation-report … --tolerance-profile ./custom.yaml`
|
|
167
|
+
(offline re-threshold) both work; the profile name is embedded in the Validation Report.
|
|
168
|
+
- **New dependency: PyYAML** (`[project].dependencies`) — the config language for tolerance tables
|
|
169
|
+
and the golden-corpus manifests M11 governs (D48). See `docs/DECISIONS.md` D48–D49.
|
|
170
|
+
|
|
171
|
+
- **POSCAR velocity block + Maxwell–Boltzmann velocity/mass recovery (v0.2 M8).** The one deliberate
|
|
172
|
+
v0.1 format deferral lands, together with the two velocity-family recovery scenarios it unlocks.
|
|
173
|
+
- **POSCAR/CONTCAR Direct-mode velocities.** The velocity block is now read in both Cartesian
|
|
174
|
+
(Å/fs, stored verbatim) and Direct (fractional) conventions; Direct velocities are converted to
|
|
175
|
+
Cartesian Å/fs via the lattice, with a parse note, and an ambiguous mode line is read as Direct
|
|
176
|
+
with a `POSCAR_AMBIGUOUS_VELOCITY_MODE` warning. Absence is preserved (`velocities = None`, never
|
|
177
|
+
zero-filled). Export stays Cartesian (Direct-mode export is the M8 cut line).
|
|
178
|
+
- **`missing_velocities`** resolves with `zero_init` (an explicit rest state), `maxwell_boltzmann`
|
|
179
|
+
(`temperature_K`, `seed` — both recorded for reproducibility; the raw sample is emitted with no
|
|
180
|
+
centre-of-mass-drift removal, `docs/DECISIONS.md` D43/D45), `upload_reference` (velocities
|
|
181
|
+
borrowed from a second structure, shape-checked), and ✳`omit` (leave velocities absent — offered
|
|
182
|
+
only when the target field is optional and the mode is permissive).
|
|
183
|
+
- **`missing_masses`** resolves with `standard_masses` (IUPAC standard atomic weights from ASE, a
|
|
184
|
+
*reported default* — D44) and `manual_input`. A `maxwell_boltzmann` draw over a source without
|
|
185
|
+
masses **chains** a `missing_masses` recovery, resolving masses first and recording two
|
|
186
|
+
Assumptions; for a target that cannot store masses (POSCAR) the masses are audited in `supplied`
|
|
187
|
+
but not written (D47).
|
|
188
|
+
- **Opt-in emission.** Velocity/mass fabrication is requested by supplying the recovery choice —
|
|
189
|
+
it is never auto-triggered, since no v0.1 target *requires* these fields. The wiring lives in
|
|
190
|
+
`ConversionEngine.convert` via a new `on_demand_fabricative_scenarios` helper (D46), which
|
|
191
|
+
refuses incoherent requests (field already present on the source; or emission to a target that
|
|
192
|
+
cannot store it) as caller errors.
|
|
193
|
+
- CLI: `xtalate convert traj.extxyz --to poscar --recover missing_lattice=… --recover
|
|
194
|
+
frame_selection=last --recover missing_velocities=maxwell_boltzmann,temperature_K=300,seed=42`
|
|
195
|
+
produces a POSCAR with a velocity block, byte-identical on re-run. See `docs/DECISIONS.md`
|
|
196
|
+
D43–D47.
|
|
197
|
+
|
|
198
|
+
- **Recovery scenario catalog completion — Slice 2 (v0.2 M7).** The remaining catalog resolvers land,
|
|
199
|
+
completing M7 for the four v0.1 formats.
|
|
200
|
+
- **Parse-time recovery.** `missing_species` (a VASP-4 POSCAR with atom counts but no element
|
|
201
|
+
symbols) and `truncate_corrupt_tail` (a trajectory with a corrupt final frame) fire before a
|
|
202
|
+
Canonical Object exists; they are resolved through a new optional `ParserPlugin.parse_recover`
|
|
203
|
+
hook and a `parse_with_recovery` orchestrator that re-parses under the caller's preset and threads
|
|
204
|
+
the resulting Assumption into the Conversion Report. `missing_species` supports `species_map`
|
|
205
|
+
(ordered symbols, `--recover missing_species=species_map,species=H:O`) and `upload_reference`;
|
|
206
|
+
`truncate_corrupt_tail` supports `truncate` (keep the valid prefix) and `abort`. A recoverable
|
|
207
|
+
parse error without a preset now prints an actionable hint.
|
|
208
|
+
- **`upload_reference`** is offered for `missing_lattice` (and `missing_species`): the lattice or
|
|
209
|
+
symbols are borrowed from a second structure named with `--recover …=upload_reference,file=PATH`,
|
|
210
|
+
behind atom-count / alignment compatibility checks.
|
|
211
|
+
- **`split_all`** (`frame_selection=split_all`) writes **one output file per frame** into the
|
|
212
|
+
directory named by `-o`, via a new `ConversionResult.outputs`; each file is validated and the
|
|
213
|
+
per-file Validation Reports are merged into one. This closes the Slice-1 cut — `frame_selection`
|
|
214
|
+
now offers `split_all` wherever it triggers.
|
|
215
|
+
- New: the optional `ParserPlugin.parse_recover` SDK hook (additive), `conversion.parse_with_recovery`
|
|
216
|
+
/ `ParseRecovery`, and `ConversionResult.outputs`. See `docs/DECISIONS.md` D38–D40.
|
|
217
|
+
|
|
218
|
+
- **Recovery scenario catalog completion (v0.2 M7, Slice 1).** The Recovery Engine, which v0.1
|
|
219
|
+
shipped resolving two scenarios preset-only, now registers and hazard-classifies the **full
|
|
220
|
+
MASTER_SPEC Part 4 §3.3 catalog of eight scenarios**, so classification and the honest-option-list
|
|
221
|
+
rule are mechanically complete for the four v0.1 formats.
|
|
222
|
+
- **`constraint_representation`** resolves with `project` (keep the target's representable
|
|
223
|
+
constraint subset, e.g. POSCAR `selective_dynamics`; the unrepresentable remainder is reported
|
|
224
|
+
in `removed`) or `drop_all`. Either way one Assumption is recorded and **no** field is supplied
|
|
225
|
+
— the kept constraints are genuine source data (selective-reductive, never fabricative).
|
|
226
|
+
- `missing_velocities`, `missing_masses`, `missing_energy`, `missing_species`, and
|
|
227
|
+
`truncate_corrupt_tail` are registered and classified but honestly **refuse** in this slice —
|
|
228
|
+
their resolvers land in M8 (the velocity/mass family) and v0.2 Slice 2 (the parse-time
|
|
229
|
+
scenarios). `missing_energy` is deliberately optionless (no scientifically defensible synthetic
|
|
230
|
+
energy exists).
|
|
231
|
+
- Option lists are **computed per source/target pair**, not static: the ✳`non_periodic` option of
|
|
232
|
+
`missing_lattice` is offered only for a target that can express an open cell (extXYZ, never
|
|
233
|
+
POSCAR), driven by a new machine-readable `allows_open_boundaries` write-capability flag
|
|
234
|
+
(`docs/DECISIONS.md` D35). (`split_all` was the Slice-1 cut line — it lands in Slice 2, above.)
|
|
235
|
+
- The Recovery Engine's dispatch is now a generalized dependency-ordered resolver table
|
|
236
|
+
(`frame_selection` → `constraint_representation` → `missing_lattice`), replacing the hard-coded
|
|
237
|
+
two-scenario branch (`docs/DECISIONS.md` D37).
|
|
238
|
+
|
|
239
|
+
### Changed
|
|
240
|
+
|
|
241
|
+
- **A PARTIAL constraint capability now triggers recovery instead of auto-preserving.** A source
|
|
242
|
+
carrying a non-empty `dynamics.constraints` list converted to a target that can represent only a
|
|
243
|
+
*subset* of constraint kinds (POSCAR: `selective_dynamics`) no longer silently keeps-what-fits:
|
|
244
|
+
*which* constraints survive changes the physics of a downstream relaxation, so it is now a recorded
|
|
245
|
+
`constraint_representation` choice, and such a conversion **refuses without an explicit preset**.
|
|
246
|
+
`NONE` capability stays ordinary bulk-reductive loss; `FULL` stays preserved; an empty
|
|
247
|
+
`constraints=[]` preserves normally (`docs/DECISIONS.md` D36; `MASTER_SPEC` Revision 1.8).
|
|
248
|
+
- **Honest-loss annotations tightened.** An extXYZ `momenta` column now records the
|
|
249
|
+
"velocities converted" parse-note even when it is explicitly all-zero (a source stating the atoms
|
|
250
|
+
are at rest is information, §2 rule 3), and a CONTCAR velocity tail now annotates
|
|
251
|
+
`source_units["velocities"] = "angstrom/fs"` with a parse-note, rather than storing the block
|
|
252
|
+
with its unit left implicit.
|
|
253
|
+
|
|
254
|
+
### Fixed
|
|
255
|
+
|
|
256
|
+
- **`frame_selection` no longer silently drops a per-frame field that lived only in a dropped
|
|
257
|
+
frame (v0.2 M10).** Found by the stage-2 property test. When `frame_selection` reduced a trajectory
|
|
258
|
+
to one structure, a per-frame path present *only* in the dropped frames (e.g. a `mixed`
|
|
259
|
+
`dynamics.constraints`) was eliminated with no `removed` entry — silent loss (**P1**) that the
|
|
260
|
+
runtime completeness invariant caught as a crash. `frame_selection` now records a `removed` entry
|
|
261
|
+
for every per-frame path the reduction eliminates (`recovery.engine._per_frame_paths_lost`), and
|
|
262
|
+
`conversion.engine` dedupes `removed` by path so a NONE-capability field flagged by both the
|
|
263
|
+
capability diff and the reduction is listed once. Regression fixtures in
|
|
264
|
+
`tests/conversion/test_frame_reduction_completeness.py`.
|
|
265
|
+
- **`constraint_representation=drop_all` now records the removal of an explicitly-unconstrained
|
|
266
|
+
`constraints=[]` (v0.2 M10).** Also found by stage 2: an empty (present, §3.6) constraint list on
|
|
267
|
+
the retained frame was nulled out of the write plan with a zero dropped-count and recorded in
|
|
268
|
+
neither `preserved` nor `removed`. It is now reported `removed`.
|
|
269
|
+
- **A `mixed` cell converted to a lattice-requiring target no longer crashes the exporter (v0.2
|
|
270
|
+
M10, D51).** A cell present in only some frames (`mixed`) whose cell-bearing frame `frame_selection`
|
|
271
|
+
dropped left the POSCAR/CONTCAR exporter with no lattice and raised `ValueError`, because
|
|
272
|
+
`missing_lattice` was detected only on a fully-`absent` required field. Pre-flight now offers
|
|
273
|
+
`missing_lattice` on any *not-uniformly-present* required field, and the Recovery Engine resolves it
|
|
274
|
+
**lazily** against the retained frame: fabricate a lattice for the cell-less frame (with a preset,
|
|
275
|
+
never overwriting a real cell), refuse cleanly (without one), or no-op when the retained frame kept
|
|
276
|
+
a real cell. The completeness invariant's P4 supplied-check is correspondingly relaxed to permit a
|
|
277
|
+
path that is *both* `removed` (the dropped frame's cell) and `supplied` (the fabricated
|
|
278
|
+
replacement) — honest, since both are reported. Regression fixtures in
|
|
279
|
+
`tests/conversion/test_frame_reduction_completeness.py`; the M10 stage-2 generator now exercises
|
|
280
|
+
`mixed` cells freely.
|
|
281
|
+
- **XYZ-with-comments → extXYZ no longer false-fails validation.** The extXYZ exporter writes a
|
|
282
|
+
carried-through comment key (`xyz:comment`) faithfully, but the parser re-namespaced *every*
|
|
283
|
+
comment key under `extxyz:`, so the value round-tripped under a changed path
|
|
284
|
+
(`extxyz:xyz:comment`) and the Validation Engine's `metadata_preservation` check reported the
|
|
285
|
+
planned path absent — marking every such conversion `failed`. The parser now skips the `extxyz:`
|
|
286
|
+
tag for a key that already carries a `<format>:` namespace, so foreign keys round-trip verbatim
|
|
287
|
+
while bare extXYZ keys are namespaced as before (`docs/DECISIONS.md` D41).
|
|
288
|
+
- **extXYZ → plain XYZ no longer false-fails on a foreign per-frame key (the D41 sibling).** Plain
|
|
289
|
+
XYZ holds one free-text comment line per frame (`xyz:comment`) and nothing else, but its exporter
|
|
290
|
+
declared the whole `custom_per_frame` container writable — so pre-flight predicted a foreign key
|
|
291
|
+
(an extXYZ `config_type`) Preserved, the exporter silently dropped it, and `metadata_preservation`
|
|
292
|
+
marked the conversion `failed`. `FormatCapabilities` gains `writable_custom_keys` (the per-key
|
|
293
|
+
analogue of `representable_constraint_kinds`): an unwritable per-frame key is now honestly reported
|
|
294
|
+
`removed`, while `xyz:comment` is still preserved (and the identity round-trip still passes) via a
|
|
295
|
+
per-key write plan (`docs/DECISIONS.md` D42).
|
|
296
|
+
The remaining eight entries are a **post-v0.1 correctness pass** — defects found by a review that
|
|
297
|
+
exercised the shipped v0.1 code against real inputs, each reproduced, fixed, and pinned with a
|
|
298
|
+
regression test, folded into this release:
|
|
299
|
+
|
|
300
|
+
- **POSCAR/CONTCAR conversions no longer false-fail validation.** The scaling factor is now
|
|
301
|
+
recorded as a `provenance` parse-note instead of `simulation.extra` (it is already folded into
|
|
302
|
+
the lattice vectors, §4). Storing it in `simulation.extra` — which no exporter can carry — made
|
|
303
|
+
*every* POSCAR→POSCAR/CONTCAR conversion fail `absence_conformance`, since the re-parse always
|
|
304
|
+
re-derives a scale (`docs/DECISIONS.md` D34).
|
|
305
|
+
- **POSCAR exporter reports its element-grouping permutation** (`atom_permutation`). Any
|
|
306
|
+
element-interleaved source (e.g. XYZ `H O H`) to POSCAR previously false-failed
|
|
307
|
+
`species_preservation`/`positions_rmsd` as "chemistry lost" because validation compared under
|
|
308
|
+
source order while the exporter had regrouped by element.
|
|
309
|
+
- **POSCAR coordinate-mode line now follows VASP semantics** — only `C/c/K/k` is Cartesian; every
|
|
310
|
+
other line (`Direct`, `Fractional`, blank, garbage) is fractional, with an ambiguous line flagged
|
|
311
|
+
(`POSCAR_AMBIGUOUS_COORDINATE_MODE`). The prior logic misread any non-`d` mode as Cartesian Å —
|
|
312
|
+
silent scientific corruption.
|
|
313
|
+
- **Text parsers honor the error contract on non-UTF-8 input.** XYZ, extXYZ, and POSCAR now raise a
|
|
314
|
+
structured `ParseError` (`*_ENCODING_ERROR`) instead of a raw `UnicodeDecodeError`.
|
|
315
|
+
- **extXYZ string-typed per-atom columns** (a `:S:` property such as a per-atom label) are carried
|
|
316
|
+
as a JSON-scalar list instead of crashing on `astype(float)`.
|
|
317
|
+
- **CLI `convert --json -o PATH` writes the output file.** It previously reported success while
|
|
318
|
+
silently writing nothing; the report JSON and the artifact are now independent outputs, and the
|
|
319
|
+
file-write notice goes to stderr so stdout stays pure JSON.
|
|
320
|
+
- **Invalid `--recover` presets** exit cleanly (usage error) instead of printing a traceback.
|
|
321
|
+
- **POSCAR exporter rejects unrepresentable constraints** with a clear error instead of an
|
|
322
|
+
`IndexError` when handed a non-`selective_dynamics` constraint.
|
|
323
|
+
|
|
324
|
+
## [0.1.0] — 2026-07-10
|
|
325
|
+
|
|
326
|
+
First release: the complete pure-Python **library + CLI** core. It converts between four
|
|
327
|
+
computational-chemistry formats while reporting — and independently re-validating — every
|
|
328
|
+
byte of scientific information kept, dropped, or fabricated.
|
|
329
|
+
|
|
330
|
+
### Added
|
|
331
|
+
|
|
332
|
+
- **Canonical Data Model** (`xtalate.schema`) — the single internal schema every parser
|
|
333
|
+
writes and every exporter reads, with the normative absence convention (`None` = "never in
|
|
334
|
+
the source" vs a real zero) and an on-demand `field_presence()` introspection.
|
|
335
|
+
- **Plugin SDK, Format Sniffer, and Capability Matrix** (`xtalate.sdk`,
|
|
336
|
+
`xtalate.discovery`, `xtalate.capabilities`) — stable parser/exporter contracts, a
|
|
337
|
+
generic confidence-scored sniffer, and a per-format, per-field read/write capability registry.
|
|
338
|
+
- **Formats** (`xtalate.parsers`, `xtalate.exporters`) — read and write for plain **XYZ**,
|
|
339
|
+
**extended XYZ** (ASE-backed, with default-laundering), **POSCAR**, and **CONTCAR**, each with a
|
|
340
|
+
golden round-trip and error-fixture suite.
|
|
341
|
+
- **Information Discovery Engine** (`xtalate.discovery`) — the ✓/✗ Discovery Report: a file's
|
|
342
|
+
canonical-field inventory annotated with the detected format's read capability.
|
|
343
|
+
- **Conversion Engine** (`xtalate.conversion`) — the pre-flight capability diff, the
|
|
344
|
+
`write_plan` discipline (materialized as a filtered `canonical′`), the Conversion Report, and
|
|
345
|
+
the completeness invariant enforced as an always-on runtime assertion.
|
|
346
|
+
- **Recovery Engine** (`xtalate.recovery`) — explicit, preset-only resolution of the
|
|
347
|
+
`frame_selection` and `missing_lattice` scenarios under the three-way hazard model; every
|
|
348
|
+
choice recorded as an Assumption, and a structured **refusal** when no choice is supplied.
|
|
349
|
+
- **Validation Engine** (`xtalate.validation`) — the unconditional post-conversion re-parse
|
|
350
|
+
and nine-check diff (Part 5 §2) under named tolerance profiles (`default`/`strict`/`loose`),
|
|
351
|
+
plus stored-report re-thresholding.
|
|
352
|
+
- **CLI** (`xtalate`) — `inspect`, `convert`, `validate`, and `capabilities`, with the
|
|
353
|
+
CI-native exit-code contract (`0`/`2`/`3`/`4`/`5`/`1`) and `--json` structured output.
|
|
354
|
+
|
|
355
|
+
### Known limitations (v0.1 scope)
|
|
356
|
+
|
|
357
|
+
- No web service, REST API, or UI (v0.5 / v0.6).
|
|
358
|
+
- CIF, XDATCAR, and ASE `.traj` — the remaining Phase-1 formats — are not yet implemented (v0.2+).
|
|
359
|
+
- Recovery is preset-only; tolerance profiles are the three named ones (custom tables are later
|
|
360
|
+
seams).
|
|
361
|
+
|
|
362
|
+
[Unreleased]: https://github.com/jsong1218/Xtalate/compare/v0.2.0...HEAD
|
|
363
|
+
[0.2.0]: https://github.com/jsong1218/Xtalate/compare/v0.1.0...v0.2.0
|
|
364
|
+
[0.1.0]: https://github.com/jsong1218/Xtalate/releases/tag/v0.1.0
|
|
@@ -10,8 +10,8 @@ type: software
|
|
|
10
10
|
authors:
|
|
11
11
|
- family-names: Song
|
|
12
12
|
given-names: Jiayu
|
|
13
|
-
version: 0.
|
|
14
|
-
date-released: 2026-07-
|
|
13
|
+
version: 0.2.0
|
|
14
|
+
date-released: 2026-07-15
|
|
15
15
|
license: Apache-2.0
|
|
16
16
|
repository-code: "https://github.com/jsong1218/Xtalate"
|
|
17
17
|
keywords:
|