diff --git a/lib/python/picongpu/pypicongpu/grid.py b/lib/python/picongpu/pypicongpu/grid.py index 7c5c647822..f487773409 100644 --- a/lib/python/picongpu/pypicongpu/grid.py +++ b/lib/python/picongpu/pypicongpu/grid.py @@ -8,7 +8,7 @@ import enum from typing import Annotated -from pydantic import BaseModel, Field, PlainSerializer, model_validator +from pydantic import AfterValidator, BaseModel, Field, PlainSerializer, model_validator from typing_extensions import Self from .rendering import RenderedObject @@ -59,6 +59,21 @@ def serialise_grid_dist(value): ) +def all_gt(iterable, m): + if all(correct := [x > m for x in iterable]): + return iterable + else: + message = f"{iterable=} contains values <= {m=} while all should be greater than m. Valid are the following: {correct=}." + raise ValueError(message) + + +def grid_dist_validate(grid_dist): + if grid_dist is None: + return None + if all_gt(sum(grid_dist, []), 0): + return grid_dist + + class Grid3D(BaseModel, RenderedObject): """ PIConGPU 3 dimensional (cartesian) grid @@ -68,10 +83,10 @@ class Grid3D(BaseModel, RenderedObject): The bounding box is implicitly given as TODO. """ - cell_size: Vec3_float = Field(alias="cell_size_si") + cell_size: Annotated[Vec3_float, AfterValidator(lambda x: all_gt(x, 0))] = Field(alias="cell_size_si") """Width of individual cell in each direction""" - cell_cnt: Vec3_int + cell_cnt: Annotated[Vec3_int, AfterValidator(lambda x: all_gt(x, 0))] """total number of cells in each direction""" boundary_condition: Annotated[ @@ -80,10 +95,14 @@ class Grid3D(BaseModel, RenderedObject): ] """behavior towards particles crossing each boundary""" - gpu_cnt: Vec3_int = Field((1, 1, 1), alias="n_gpus") + gpu_cnt: Annotated[Vec3_int, AfterValidator(lambda x: all_gt(x, 0))] = Field((1, 1, 1), alias="n_gpus") """number of GPUs in x y and z direction as 3-integer tuple""" - grid_dist: Annotated[tuple[list[int], list[int], list[int]] | None, PlainSerializer(serialise_grid_dist)] = None + grid_dist: Annotated[ + tuple[list[int], list[int], list[int]] | None, + PlainSerializer(serialise_grid_dist), + AfterValidator(grid_dist_validate), + ] = None """distribution of grid cells to GPUs for each axis""" super_cell_size: Vec3_int @@ -92,8 +111,6 @@ class Grid3D(BaseModel, RenderedObject): @model_validator(mode="after") def check(self) -> Self: """serialized representation provided for RenderedObject""" - assert all(x > 0 for x in self.cell_cnt), "cell_cnt must be greater than 0" - assert all(x > 0 for x in self.gpu_cnt), "all n_gpus entries must be greater than 0" if self.grid_dist is not None: assert sum(self.grid_dist[0]) == self.cell_cnt[0], "sum of grid_dists in x must be equal to number_of_cells" assert sum(self.grid_dist[1]) == self.cell_cnt[1], "sum of grid_dists in y must be equal to number_of_cells" diff --git a/lib/python/picongpu/pypicongpu/rendering/renderedobject.py b/lib/python/picongpu/pypicongpu/rendering/renderedobject.py index a019603a0c..59b3928bd4 100644 --- a/lib/python/picongpu/pypicongpu/rendering/renderedobject.py +++ b/lib/python/picongpu/pypicongpu/rendering/renderedobject.py @@ -165,7 +165,12 @@ def _get_schema_from_class(class_type: type) -> typing.Any: try: schema = RenderedObject._registry.contents(uri) except referencing.exceptions.NoSuchResource: - raise referencing.exceptions.NoSuchResource("schema not found for FQN {}: URI {}".format(fqn, uri)) + try: + schema = class_type.model_json_schema(mode="serialization", by_alias=False) | {"$id": uri} + except Exception as second_error: + raise referencing.exceptions.NoSuchResource( + "schema not found for FQN {}: URI {}".format(fqn, uri) + ) from second_error # validate schema validator = jsonschema.Draft202012Validator(schema=schema) diff --git a/share/picongpu/pypicongpu/schema/grid.Grid3D.json b/share/picongpu/pypicongpu/schema/grid.Grid3D.json deleted file mode 100644 index d79ad6820a..0000000000 --- a/share/picongpu/pypicongpu/schema/grid.Grid3D.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "$id": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.grid.Grid3D", - "description": "Specification of a (cartesian) grid of cells with 3 spacial dimensions.", - "type": "object", - "properties": { - "cell_size": { - "description": "width of a single cell in m", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "cell_size_component", - "type": "number", - "exclusiveMinimum": 0 - }, - "y": { - "$ref": "#cell_size_component" - }, - "z": { - "$ref": "#cell_size_component" - } - } - }, - "cell_cnt": { - "description": "number of cells", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "cell_cnt_component", - "type": "integer", - "minimum": 1 - }, - "y": { - "$ref": "#cell_cnt_component" - }, - "z": { - "$ref": "#cell_cnt_component" - } - } - }, - "super_cell_size": { - "description": "super cell size in cells", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "super_cell_cnt_component", - "type": "integer", - "minimum": 1 - }, - "y": { - "$ref": "#super_cell_cnt_component" - }, - "z": { - "$ref": "#super_cell_cnt_component" - } - } - }, - "gpu_cnt": { - "description": "number of gpus", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "gpu_cnt_component", - "type": "integer", - "minimum": 1 - }, - "y": { - "$ref": "#gpu_cnt_component" - }, - "z": { - "$ref": "#gpu_cnt_component" - } - } - }, - "boundary_condition": { - "description": "boundary condition to be passed to --periodic (encoded as number)", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "boundary_condition_component", - "type": "string", - "pattern": "^(0|1)$" - }, - "y": { - "$ref": "#boundary_condition_component" - }, - "z": { - "$ref": "#boundary_condition_component" - } - } - }, - "grid_dist": { - "anyOf": [ - { - "type": "null" - }, - { - "description": " grid distributionover devices in cells", - "type": "object", - "unevaluatedProperties": false, - "required": [ - "x", - "y", - "z" - ], - "properties": { - "x": { - "$anchor": "grid_dist_component", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "device_cells" - ], - "unevaluatedProperties": false, - "properties": { - "device_cells": { - "type": "integer", - "minimum": 1 - } - } - } - }, - "y": { - "$ref": "#grid_dist_component" - }, - "z": { - "$ref": "#grid_dist_component" - } - } - } - ] - } - }, - "required": [ - "cell_size", - "cell_cnt", - "boundary_condition", - "super_cell_size", - "grid_dist" - ], - "unevaluatedProperties": false -} diff --git a/share/picongpu/pypicongpu/schema/output/auto.Auto.json b/share/picongpu/pypicongpu/schema/output/auto.Auto.json index 2bece5639b..a48f6d1906 100644 --- a/share/picongpu/pypicongpu/schema/output/auto.Auto.json +++ b/share/picongpu/pypicongpu/schema/output/auto.Auto.json @@ -8,9 +8,7 @@ "png_axis" ], "properties": { - "period": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec" - }, + "period": {}, "png_axis": { "description": "axis pairs (i.e. planes) for which to generate png output", "type": "array", diff --git a/share/picongpu/pypicongpu/schema/output/binning.Binning.json b/share/picongpu/pypicongpu/schema/output/binning.Binning.json index 8d78e41875..fe6f13a8fc 100644 --- a/share/picongpu/pypicongpu/schema/output/binning.Binning.json +++ b/share/picongpu/pypicongpu/schema/output/binning.Binning.json @@ -31,13 +31,7 @@ }, "description": "Species to bin" }, - "period": { - "type": [ - "object" - ], - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec", - "description": "Create binning output on specified steps" - }, + "period": {}, "openPMD": { "type": [ "string", diff --git a/share/picongpu/pypicongpu/schema/output/checkpoint.Checkpoint.json b/share/picongpu/pypicongpu/schema/output/checkpoint.Checkpoint.json index 992660942d..4464f0d617 100644 --- a/share/picongpu/pypicongpu/schema/output/checkpoint.Checkpoint.json +++ b/share/picongpu/pypicongpu/schema/output/checkpoint.Checkpoint.json @@ -16,14 +16,7 @@ } ], "properties": { - "period": { - "type": [ - "object", - "null" - ], - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec", - "description": "Create checkpoints in specified steps" - }, + "period": {}, "timePeriod": { "type": [ "integer", diff --git a/share/picongpu/pypicongpu/schema/output/energy_histogram.EnergyHistogram.json b/share/picongpu/pypicongpu/schema/output/energy_histogram.EnergyHistogram.json index 8e657ef641..796c88b7c8 100644 --- a/share/picongpu/pypicongpu/schema/output/energy_histogram.EnergyHistogram.json +++ b/share/picongpu/pypicongpu/schema/output/energy_histogram.EnergyHistogram.json @@ -14,9 +14,7 @@ "species": { "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.species.species.Species" }, - "period": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec" - }, + "period": {}, "bin_count": { "type": "integer", "minimum": 1, diff --git a/share/picongpu/pypicongpu/schema/output/macro_particle_count.MacroParticleCount.json b/share/picongpu/pypicongpu/schema/output/macro_particle_count.MacroParticleCount.json index 3917a40490..c074565477 100644 --- a/share/picongpu/pypicongpu/schema/output/macro_particle_count.MacroParticleCount.json +++ b/share/picongpu/pypicongpu/schema/output/macro_particle_count.MacroParticleCount.json @@ -11,8 +11,6 @@ "species": { "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.species.species.Species" }, - "period": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec" - } + "period": {} } } diff --git a/share/picongpu/pypicongpu/schema/output/phase_space.PhaseSpace.json b/share/picongpu/pypicongpu/schema/output/phase_space.PhaseSpace.json index 28ec154878..8e93341d0f 100644 --- a/share/picongpu/pypicongpu/schema/output/phase_space.PhaseSpace.json +++ b/share/picongpu/pypicongpu/schema/output/phase_space.PhaseSpace.json @@ -15,9 +15,7 @@ "species": { "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.species.species.Species" }, - "period": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec" - }, + "period": {}, "spatial_coordinate": { "type": "string", "enum": [ diff --git a/share/picongpu/pypicongpu/schema/output/png.Png.json b/share/picongpu/pypicongpu/schema/output/png.Png.json index fc752697a1..e604035e32 100644 --- a/share/picongpu/pypicongpu/schema/output/png.Png.json +++ b/share/picongpu/pypicongpu/schema/output/png.Png.json @@ -29,9 +29,7 @@ "preChannel3" ], "properties": { - "period": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec" - }, + "period": {}, "axis": { "type": "string", "description": "2D plane for PNG output (e.g., 'yx')." diff --git a/share/picongpu/pypicongpu/schema/output/timestepspec.json b/share/picongpu/pypicongpu/schema/output/timestepspec.json deleted file mode 100644 index 02facf3428..0000000000 --- a/share/picongpu/pypicongpu/schema/output/timestepspec.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$id": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.output.timestepspec.TimeStepSpec", - "type": "object", - "description": "Select time steps (e.g. for notifying a plugin)", - "unevaluatedProperties": false, - "properties": { - "specs": { - "type": "array", - "items": { - "type": "object", - "description": "slice", - "properties": { - "start": { - "type": "integer" - }, - "stop": { - "type": "integer" - }, - "step": { - "type": "integer" - } - } - } - } - } -} diff --git a/share/picongpu/pypicongpu/schema/simulation.Simulation.json b/share/picongpu/pypicongpu/schema/simulation.Simulation.json index bdf6c261ea..ccd154f928 100644 --- a/share/picongpu/pypicongpu/schema/simulation.Simulation.json +++ b/share/picongpu/pypicongpu/schema/simulation.Simulation.json @@ -31,9 +31,7 @@ } ] }, - "grid": { - "$ref": "https://registry.hzdr.de/crp/picongpu/schema/picongpu.pypicongpu.grid.Grid3D" - }, + "grid": {}, "laser": { "anyOf": [ { diff --git a/test/python/picongpu/quick/pypicongpu/grid.py b/test/python/picongpu/quick/pypicongpu/grid.py index 6a972ac536..83f9c30b35 100644 --- a/test/python/picongpu/quick/pypicongpu/grid.py +++ b/test/python/picongpu/quick/pypicongpu/grid.py @@ -58,17 +58,17 @@ def test_types(self): def test_gpu_and_cell_cnt_positive(self): """test if n_gpus and cell number s are >0""" - with self.assertRaisesRegex(ValidationError, ".*cell_cnt.*greater than 0.*"): + with self.assertRaisesRegex(ValidationError, ".*contains values <= m=0.*"): Grid3D(**self.kwargs | dict(cell_cnt=(-1, 7, 8))) - with self.assertRaisesRegex(ValidationError, ".*cell_cnt.*greater than 0.*"): + with self.assertRaisesRegex(ValidationError, ".*contains values <= m=0.*"): Grid3D(**self.kwargs | dict(cell_cnt=(6, -2, 8))) - with self.assertRaisesRegex(ValidationError, ".*cell_cnt.*greater than 0.*"): + with self.assertRaisesRegex(ValidationError, ".*contains values <= m=0.*"): Grid3D(**self.kwargs | dict(cell_cnt=(6, 7, 0))) for wrong_n_gpus in [tuple([-1, 1, 1]), tuple([1, 1, 0])]: - with self.assertRaisesRegex(ValidationError, ".*greater than 0.*"): + with self.assertRaisesRegex(ValidationError, ".*contains values <= m=0.*"): Grid3D(**self.kwargs | dict(n_gpus=wrong_n_gpus)) def test_mandatory(self):