Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ jobs:
matrix:
target:
- black
- dapperdata
- mypy
- pytest
- ruff
Expand Down
53 changes: 24 additions & 29 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,31 +1,4 @@
repos:
- repo: local
hooks:
- id: pytest
name: pytest
entry: make test/pytest
language: system
pass_filenames: false
- id: ruff
name: ruff
entry: make test/ruff
language: system
pass_filenames: false
- id: black
name: black
entry: make test/black
language: system
pass_filenames: false
- id: mypy
name: mypy
entry: make test/mypy
language: system
pass_filenames: false
- id: tomlsort
name: tomlsort
entry: make test/tomlsort
language: system
pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # v6.0.0 , 9 Aug 2025
hooks:
Expand All @@ -34,14 +7,36 @@ repos:
- id: check-case-conflict
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
exclude: '\.\w+$'
- id: end-of-file-fixer
exclude: '\.(diff|patch)$'
exclude: '\.(diff|patch|py)$'
- id: trailing-whitespace
exclude: '\.(diff|patch)$'
exclude: '\.(diff|patch|py)$'
- id: check-json
- id: check-yaml

- repo: https://github.com/pappasam/toml-sort
rev: 2970ae9bb7124fe5117a27e10c10d2da051ce05a # v0.24.4, 24 Mar 2026
hooks:
- id: toml-sort-fix

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.13 # 14 May 2026 (immutable)
hooks:
- id: ruff-check # Linting
args: [ --fix ]
- id: ruff-format # Formatting

ci:
autofix_commit_msg: |
Apply fixes from pre-commit hooks: see detailed commit message ↴

This commit means that you must (please!) install pre-commit
on your development machine and run `pre-commit install --install-hooks`.
For more information, see
https://celeritas-project.github.io/celeritas/user/development/style.html#formatting

Autogenerated: https://pre-commit.ci
autoupdate_schedule: quarterly
autoupdate_commit_msg: "Update pre-commit version"

Expand Down
4 changes: 2 additions & 2 deletions celerpy/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
# See the top-level LICENSE file for details.
# SPDX-License-Identifier: Apache-2.0
from typing import Annotated, Optional
from typing import Annotated

import typer
from pydantic import ValidationError
Expand Down Expand Up @@ -31,7 +31,7 @@ def print_version(value: bool):
@app.command()
def main(
version: Annotated[
Optional[bool],
bool | None,
typer.Option("--version", callback=print_version, is_eager=True),
] = None,
):
Expand Down
5 changes: 2 additions & 3 deletions celerpy/conf/settings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
# See the top-level LICENSE file for details.
# SPDX-License-Identifier: Apache-2.0
from typing import Optional

from pydantic import DirectoryPath, FilePath
from pydantic_settings import BaseSettings, SettingsConfigDict
Expand All @@ -24,7 +23,7 @@ class Settings(BaseSettings):
use_attribute_docstrings=True,
)

prefix_path: Optional[DirectoryPath] = None
prefix_path: DirectoryPath | None = None
"Path to the Celeritas build/install directory"

# CELER_ environment variables
Expand All @@ -46,7 +45,7 @@ class Settings(BaseSettings):

# Geant4->ORANGE conversion

g4org_options: Optional[FilePath] = None
g4org_options: FilePath | None = None
"JSON file with conversion options"

# Geant4 configuration
Expand Down
24 changes: 12 additions & 12 deletions celerpy/model/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""Input models for commands and configuration with Celeritas."""

from enum import StrEnum, auto
from typing import Literal, Optional
from typing import Literal

from pydantic import FilePath, NonNegativeInt, PositiveFloat, PositiveInt
from pydantic_core import to_json
Expand Down Expand Up @@ -38,7 +38,7 @@ class OrangeGeoFromCsg(_Model):
unit_length: PositiveFloat = 1.0
"Scale factor (input unit length), customizable for unit testing"

tol: Optional[Tolerance] = None
tol: Tolerance | None = None
"Construction and tracking tolerance (native units)"

# Structural conversion
Expand All @@ -51,13 +51,13 @@ class OrangeGeoFromCsg(_Model):

# Debug output

objects_output_file: Optional[str] = None
objects_output_file: str | None = None
"Write converted Geant4 object structure to a JSON file"

csg_output_file: Optional[str] = None
csg_output_file: str | None = None
"Write constructed CSG surfaces and tree to a JSON file"

org_output_file: Optional[str] = None
org_output_file: str | None = None
"Write final org.json to a JSON file"


Expand Down Expand Up @@ -95,13 +95,13 @@ class OrangeGeoFromGeant(OrangeGeoFromCsg):

# celer-geo/GeoInput.hh
class ModelSetup(_Model):
cuda_stack_size: Optional[NonNegativeInt] = None
cuda_heap_size: Optional[NonNegativeInt] = None
cuda_stack_size: NonNegativeInt | None = None
cuda_heap_size: NonNegativeInt | None = None

geometry_file: FilePath
"Path to the GDML input file"

perfetto_file: Optional[FilePath] = None
perfetto_file: FilePath | None = None
"Path to write Perfetto profiling output"


Expand All @@ -110,10 +110,10 @@ class TraceSetup(_Model):
_cmd: Literal["trace"] = "trace"
"Command name in the JSON file"

geometry: Optional[GeometryEngine] = None
geometry: GeometryEngine | None = None
"Geometry engine with which to perform the trace"

memspace: Optional[MemSpace] = None
memspace: MemSpace | None = None
"Whether to perform the trace on CPU or GPU"

volumes: bool = True
Expand All @@ -137,13 +137,13 @@ class ImageInput(_Model):
vertical_pixels: NonNegativeInt = 512
"Number of pixels along the y axis"

horizontal_divisor: Optional[PositiveInt] = None
horizontal_divisor: PositiveInt | None = None
"Increase the horizontal window to be divisible by this number"


# ad hoc: input to a 'trace' command
class TraceInput(TraceSetup):
image: Optional[ImageInput] = None
image: ImageInput | None = None
"Reuse the existing image"


Expand Down
10 changes: 5 additions & 5 deletions celerpy/model/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ImageParams(_Model):
class TraceOutput(_Model):
trace: TraceSetup
image: ImageParams
volumes: Optional[list[str]] = None
volumes: list[str] | None = None
sizeof_int: PositiveInt


Expand Down Expand Up @@ -140,8 +140,8 @@ class ExceptionDump(_Model):
context: Optional["ExceptionDump"] = None

# corecel/AssertIO.json.cc
what: Optional[str]
what: str | None
which: str
condition: Optional[str] = None
file: Optional[str] = None
line: Optional[int] = None
condition: str | None = None
file: str | None = None
line: int | None = None
4 changes: 2 additions & 2 deletions celerpy/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import os
from signal import SIGINT, SIGKILL, SIGTERM
from subprocess import PIPE, Popen, TimeoutExpired
from typing import Optional, TypeVar
from typing import TypeVar

from pydantic import BaseModel, ValidationError

Expand Down Expand Up @@ -81,7 +81,7 @@ def close(process: P, *, timeout: float = 0.1):
return out


def communicate(process: P, line: str) -> Optional[str]:
def communicate(process: P, line: str) -> str | None:
"""Write a line and read a line of response.

For this to work, the child application *must* write a single line of
Expand Down
26 changes: 13 additions & 13 deletions celerpy/visualize.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from pathlib import Path
from subprocess import TimeoutExpired
from tempfile import NamedTemporaryFile
from typing import Any, NamedTuple, Optional, Union
from typing import Any, NamedTuple

import matplotlib.pyplot as plt
import numpy as np
Expand Down Expand Up @@ -126,7 +126,7 @@ class CelerGeo:
refactor.
"""

image: Optional[ImageParams]
image: ImageParams | None
volumes: dict[GeometryEngine, list[str]]

@classmethod
Expand Down Expand Up @@ -170,9 +170,9 @@ def reset_id_map(self):

def trace(
self,
image: Optional[ImageInput] = None,
image: ImageInput | None = None,
*,
geometry: Optional[GeometryEngine] = None,
geometry: GeometryEngine | None = None,
**kwargs,
):
"""Trace with a geometry, memspace, etc."""
Expand Down Expand Up @@ -216,7 +216,7 @@ def trace(

return (result, npimg)

def close(self, *, timeout: float = 0.25) -> Union[dict[str, dict], str]:
def close(self, *, timeout: float = 0.25) -> dict[str, dict] | str:
"""Cleanly exit the ray trace loop, returning run statistics if
possible.
"""
Expand Down Expand Up @@ -286,7 +286,7 @@ def calc_axes(length, dir):


class Imager:
axes: Optional[LabeledAxes] = None
axes: LabeledAxes | None = None

def __init__(self, celer_geo: CelerGeo, image: ImageInput):
self.celer_geo = celer_geo
Expand All @@ -296,9 +296,9 @@ def __init__(self, celer_geo: CelerGeo, image: ImageInput):
def __call__(
self,
ax: mpl_Axes,
geometry: Optional[GeometryEngine] = None,
memspace: Optional[MemSpace] = None,
colorbar: Union[bool, None, mpl_Axes] = None,
geometry: GeometryEngine | None = None,
memspace: MemSpace | None = None,
colorbar: bool | None | mpl_Axes = None,
) -> dict[str, Any]:
(trace_output, img) = self.celer_geo.trace(
self.image, geometry=geometry, memspace=memspace
Expand Down Expand Up @@ -354,8 +354,8 @@ def plot_all_geometry(
trace_image: Imager,
*,
colorbar: bool = True,
figsize: Optional[tuple] = None,
engines: Optional[Iterable] = None,
figsize: tuple | None = None,
engines: Iterable | None = None,
) -> Mapping[GeometryEngine, Any]:
"""Convenience function for plotting all available geometry types."""
if engines is None:
Expand All @@ -376,7 +376,7 @@ def plot_all_geometry(
if colorbar:
all_cbar[:0] = [all_ax[-1]]

for ax, g, cb in zip(all_ax, engines, all_cbar):
for ax, g, cb in zip(all_ax, engines, all_cbar, strict=True):
try:
result[g] = trace_image(ax, geometry=g, colorbar=cb)
except Exception as e:
Expand All @@ -387,7 +387,7 @@ def plot_all_geometry(
def centered_image(
center: ArrayLike = (0, 0, 0),
*,
width: Union[float, tuple, np.ndarray],
width: float | tuple | np.ndarray,
xdir: ArrayLike = (0, 0, 1),
outdir: ArrayLike = (0, 1, 0),
**kwargs: Any,
Expand Down
14 changes: 10 additions & 4 deletions test/mock-prefix/bin/celer-geo
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ setup_signals()
celer_log = environ.get("CELER_LOG")
assert celer_log == "debug", f"Expected CELER_LOG=debug, got {celer_log}"
celer_log_local = environ.get("CELER_LOG_LOCAL")
assert celer_log_local == "warning", f"Expected CELER_LOG_LOCAL=warning, got {celer_log_local}"
assert celer_log_local == "warning", (
f"Expected CELER_LOG_LOCAL=warning, got {celer_log_local}"
)
g4org_opts = environ.get("G4ORG_OPTIONS")
assert g4org_opts and Path(g4org_opts).is_file(), f"Expected G4ORG_OPTIONS=1, got {g4org_opts}"
assert g4org_opts and Path(g4org_opts).is_file(), (
f"Expected G4ORG_OPTIONS=1, got {g4org_opts}"
)
profiling = environ.get("CELER_ENABLE_PROFILING")
assert profiling == "True", f"Expected CELER_ENABLE_PROFILING=1, got {profiling}"
assert profiling == "True", (
f"Expected CELER_ENABLE_PROFILING=1, got {profiling}"
)


# Read the initial command and echo it (with version)
Expand All @@ -46,7 +52,7 @@ expect_trace(
)

cmd = read_input()
assert cmd == None
assert cmd is None
dump(
{
"runtime": {"device": None, "kernels": [], "version": "0.7.0-dev"},
Expand Down
8 changes: 4 additions & 4 deletions test/mock-prefix/bin/mock-process
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
# See the top-level COPYRIGHT file for details.
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Mock a 'JSON lines' style process.
"""Mock a 'JSON lines' style process."""

"""
from mockutils import log, dump, read_input, setup_signals
import sys

from mockutils import dump, log, read_input, setup_signals

setup_signals()

cmd = input()
log("read command", repr(cmd))
dump("success")
log("entering loop")
while (cmd := read_input()) != None:
while (cmd := read_input()) is not None:
log("read command", cmd)
if cmd == "abort":
log("Aborting!")
Expand Down
Loading