File indexing completed on 2026-10-16 08:46:35
0001
0002 """Offline schema/fixture QA, not a production material reader.
0003
0004 Requires jsonschema>=4.18. Run from any directory. No network references are used.
0005 Direct execution validates the schema and examples. Run the self-tests with
0006 ``python -m pytest CI/check_material_schema.py`` (also requires pytest).
0007 """
0008
0009 import copy
0010 import json
0011 from pathlib import Path
0012
0013 from jsonschema import Draft202012Validator
0014
0015 ROOT = Path(__file__).resolve().parents[1]
0016 SCHEMA = ROOT / "Plugins/Json/schema/material-map-v1.schema.json"
0017 EXAMPLES = ROOT / "docs/examples/material-map-v1"
0018 ID_COMPONENTS = ("volume", "portal", "layer", "passive", "sensitive", "extra")
0019
0020
0021 def load(path):
0022 return json.loads(path.read_text())
0023
0024
0025 def changed(document, path, value):
0026 result = copy.deepcopy(document)
0027 cursor = result
0028 for key in path[:-1]:
0029 cursor = cursor[key]
0030 cursor[path[-1]] = value
0031 return result
0032
0033
0034 def validation_inputs():
0035 schema = load(SCHEMA)
0036 Draft202012Validator.check_schema(schema)
0037 validator = Draft202012Validator(schema)
0038 examples = {p.stem: load(p) for p in sorted(EXAMPLES.glob("*.json"))}
0039 return validator, examples
0040
0041
0042 def main():
0043 validator, examples = validation_inputs()
0044 for name, document in examples.items():
0045 validator.validate(document)
0046 print(f"Validated {name}.json (schema)")
0047
0048
0049 def test_schema_and_examples():
0050 main()
0051
0052
0053 def test_valid_geometry_ids():
0054 validator, examples = validation_inputs()
0055 m = examples["minimal"]
0056
0057 maxima = dict(zip(ID_COMPONENTS, (255, 255, 4095, 255, 1048575, 255)))
0058 for identifier in ({}, {"volume": 0}, maxima):
0059 document = changed(m, ["surfaces", 0, "target", "geometry_id"], identifier)
0060 validator.validate(document)
0061
0062
0063 def test_invalid_structures():
0064 validator, examples = validation_inputs()
0065 m, s = (examples[n] for n in ("minimal", "surfaces"))
0066 maxima = dict(zip(ID_COMPONENTS, (255, 255, 4095, 255, 1048575, 255)))
0067 structural = [
0068 changed(m, ["header", "version"], 2),
0069 changed(m, ["header", "units"], {}),
0070 changed(m, ["header", "units", "length"], "GeV"),
0071 changed(m, ["header", "units", "material_amount"], "mol/mm^3"),
0072 changed(m, ["header"], {}),
0073 changed(m, ["header"], None),
0074 changed(m, ["header", "description"], 42),
0075 changed(m, ["description"], "misplaced"),
0076 changed(m, ["surfaces", 0, "target", "geometry_id"], 72057594037927936),
0077 changed(m, ["surfaces", 0, "target", "geometry_id"], "72057594037927936"),
0078 changed(m, ["surfaces", 0, "target", "geometry_id"], {"volume": -1}),
0079 changed(m, ["surfaces", 0, "target", "geometry_id"], {"volume": 1.5}),
0080 changed(m, ["surfaces", 0, "target", "geometry_id"], {"approach": 1}),
0081 changed(m, ["surfaces", 0, "target", "geometry_id"], {"boundary": 1}),
0082 changed(m, ["surfaces", 0, "material", "kind"], "unknown"),
0083 changed(m, ["surfaces", 0, "material", "slab", "thickness"], -1),
0084 changed(
0085 s,
0086 ["surfaces", 3, "material", "axes"],
0087 [{"kind": "equidistant", "bins": 2}] * 2,
0088 ),
0089 changed(s, ["surfaces", 5, "material"], None),
0090 changed(m, ["volumes"], []),
0091 changed(m, ["volumes"], [{"geometry_id": {"volume": 1}, "material": None}]),
0092 changed(m, ["typo"], True),
0093 ]
0094 for name, maximum in maxima.items():
0095 structural.append(
0096 changed(m, ["surfaces", 0, "target", "geometry_id"], {name: maximum + 1})
0097 )
0098 for document in structural:
0099 assert not validator.is_valid(document), document
0100
0101
0102 def test_unit_symbols():
0103 validator, examples = validation_inputs()
0104 for dimension, definition in validator.schema["properties"]["header"]["properties"][
0105 "units"
0106 ]["properties"].items():
0107 for symbol in definition["enum"]:
0108 validator.validate(
0109 changed(examples["minimal"], ["header", "units", dimension], symbol)
0110 )
0111
0112
0113 if __name__ == "__main__":
0114 main()