Skip to content

k4bench.geometry.patcher

k4bench.geometry.patcher

Build temporary, validated DD4hep geometries with detectors removed.

RemovedPlugin dataclass

RemovedPlugin(file: Path, plugin_type: str, matched_value: str)

A plugin removed because one of its arguments named a detector.

PatchResult dataclass

PatchResult(top_path: Path, directory: Path, subfile_map: dict[Path, Path], removed_detectors: frozenset[str], collateral_detectors: frozenset[str], present_detectors: frozenset[str], removed_plugins: tuple[RemovedPlugin, ...], unresolved_refs: tuple[FilesystemRef, ...])

Generated patch paths and the facts measured during validation.

cleanup

cleanup() -> None

Remove this patch's private temporary directory.

Source code in k4bench/geometry/patcher.py
def cleanup(self) -> None:
    """Remove this patch's private temporary directory."""
    shutil.rmtree(self.directory, ignore_errors=True)

build_patch

build_patch(index: GeometryIndex, remove: AbstractSet[str]) -> PatchResult

Build and validate one geometry patch.

The supplied index must be complete. Every declaration of each requested detector is removed, along with plugins whose argument values name it.

Source code in k4bench/geometry/patcher.py
def build_patch(index: GeometryIndex, remove: AbstractSet[str]) -> PatchResult:
    """Build and validate one geometry patch.

    The supplied index must be complete.  Every declaration of each requested
    detector is removed, along with plugins whose argument values name it.
    """
    if index.parse_errors:
        raise index.parse_errors[0]

    removed = frozenset(remove)
    unknown = removed - index.detectors.keys()
    if unknown:
        names = ", ".join(repr(name) for name in sorted(unknown))
        raise DetectorNotFoundError(
            f"Detector(s) {names} not found in any XML reachable from {index.top}."
        )

    for name in sorted(removed):
        if len(index.detectors[name]) > 1:
            warnings.warn(
                f"Detector {name!r} is declared more than once; every declaration "
                "will be removed.",
                stacklevel=2,
            )

    detector_targets = index.files_declaring(removed)
    plugin_targets: set[Path] = set()
    plugin_names = set(removed)

    while True:
        attempt = _write_patch_attempt(
            index,
            removed=removed,
            detector_targets=detector_targets,
            plugin_targets=plugin_targets,
            plugin_names=plugin_names,
        )
        present = frozenset(attempt.generated.detector_names)
        collateral = frozenset(index.detector_names) - removed - present
        orphan_names = set(removed) | set(collateral)
        orphan_files = attempt.generated.files_with_plugins_for(orphan_names)
        if not orphan_files:
            break

        generated_to_original = {
            attempt.top_path: index.top,
            **{
                generated: original
                for original, generated in attempt.subfile_map.items()
            },
        }
        additional_targets = {
            generated_to_original.get(path, path)
            for path in orphan_files
        } - plugin_targets
        if not additional_targets:
            shutil.rmtree(attempt.directory, ignore_errors=True)
            raise PatchValidationError(
                "Generated geometry still contains plugin(s) naming removed "
                f"detector(s): {', '.join(sorted(orphan_names))}"
            )

        shutil.rmtree(attempt.directory, ignore_errors=True)
        plugin_targets.update(additional_targets)
        plugin_names.update(orphan_names)

    unresolved = attempt.generated.unresolved
    result = PatchResult(
        top_path=attempt.top_path,
        directory=attempt.directory,
        subfile_map=attempt.subfile_map,
        removed_detectors=removed,
        collateral_detectors=collateral,
        present_detectors=present,
        removed_plugins=tuple(attempt.removed_plugins),
        unresolved_refs=unresolved,
    )
    try:
        _report_diagnostics(attempt.removed_plugins, collateral, unresolved)
    except BaseException:
        result.cleanup()
        raise
    return result

patched

patched(index: GeometryIndex, remove: AbstractSet[str]) -> Generator[PatchResult, None, None]

Yield one validated patch and always clean up its directory.

Source code in k4bench/geometry/patcher.py
@contextlib.contextmanager
def patched(
    index: GeometryIndex,
    remove: AbstractSet[str],
) -> Generator[PatchResult, None, None]:
    """Yield one validated patch and always clean up its directory."""
    result = build_patch(index, remove)
    try:
        yield result
    finally:
        result.cleanup()

patched_geometry

patched_geometry(xml_path: Path, detector_name: str) -> Generator[Path, None, None]

Yield a strict, validated geometry with detector_name removed.

Source code in k4bench/geometry/patcher.py
@contextlib.contextmanager
def patched_geometry(
    xml_path: Path,
    detector_name: str,
) -> Generator[Path, None, None]:
    """Yield a strict, validated geometry with *detector_name* removed."""
    index = GeometryIndex.load(xml_path, strict=True)
    with patched(index, {detector_name}) as result:
        yield result.top_path

patched_geometry_keep_only

patched_geometry_keep_only(xml_path: Path, keep_names: set[str]) -> Generator[Path, None, None]

Yield a strict, validated geometry containing only keep_names.

Source code in k4bench/geometry/patcher.py
@contextlib.contextmanager
def patched_geometry_keep_only(
    xml_path: Path,
    keep_names: set[str],
) -> Generator[Path, None, None]:
    """Yield a strict, validated geometry containing only *keep_names*."""
    index = GeometryIndex.load(xml_path, strict=True)
    remove = set(index.detector_names) - keep_names
    with patched(index, remove) as result:
        yield result.top_path