Skip to content

k4bench.geometry.patcher

k4bench.geometry.patcher

Patch a DD4hep compact geometry to remove a single subdetector.

The patcher writes temporary XML files to the system temp directory so that the original geometry (which may live on a read-only filesystem such as CVMFS) is never modified. All relative <include ref="..."> paths in the patched XMLs are rewritten to absolute paths so that ddsim can resolve them regardless of where the temp files land.

Temporary files are prefixed with _k4bench_tmp_ so they are easy to identify and clean up. The recommended usage is via the :func:patched_geometry context manager, which guarantees cleanup even if the simulation run raises an exception.

DetectorNotFoundError

Bases: ValueError

Raised when the requested detector name is not in the geometry.

patched_geometry_keep_only

patched_geometry_keep_only(xml_path: Path, keep_names: set[str])

Context manager yielding a geometry with only keep_names detectors active.

All <detector> elements whose name attribute is not in keep_names are removed from every file in the include tree. Temp files are written to the system temp directory and deleted on exit.

Parameters:

Name Type Description Default
xml_path Path

Path to the original top-level compact XML.

required
keep_names set[str]

Detector names to keep. All others are removed.

required

Yields:

Type Description
Path

Path to the patched top-level XML file.

Source code in k4bench/geometry/patcher.py
@contextlib.contextmanager
def patched_geometry_keep_only(xml_path: Path, keep_names: set[str]):
    """Context manager yielding a geometry with only *keep_names* detectors active.

    All ``<detector>`` elements whose ``name`` attribute is not in *keep_names*
    are removed from every file in the include tree.  Temp files are written
    to the system temp directory and deleted on exit.

    Parameters
    ----------
    xml_path:
        Path to the original top-level compact XML.
    keep_names:
        Detector names to keep.  All others are removed.

    Yields
    ------
    Path
        Path to the patched top-level XML file.
    """
    tmp_files, top_tmp = _build_keep_only_xml(xml_path, keep_names)
    try:
        yield top_tmp
    finally:
        for tmp in tmp_files:
            tmp.unlink(missing_ok=True)

patched_geometry

patched_geometry(xml_path: Path, detector_name: str)

Context manager that yields a patched geometry path.

Creates temporary XML files with detector_name removed, yields the path to the patched top-level XML, then deletes the temp files on exit regardless of whether an exception was raised.

Parameters:

Name Type Description Default
xml_path Path

Path to the original top-level compact XML.

required
detector_name str

Name of the <detector> element to remove.

required

Yields:

Type Description
Path

Path to the patched top-level XML file.

Raises:

Type Description
DetectorNotFoundError

If detector_name is not found in any reachable XML file.

Example

::

with patched_geometry(Path("ALLEGRO.xml"), "EcalBarrel") as tmp_xml:
    result = run_ddsim(xml_path=tmp_xml, ...)
Source code in k4bench/geometry/patcher.py
@contextlib.contextmanager
def patched_geometry(xml_path: Path, detector_name: str):
    """Context manager that yields a patched geometry path.

    Creates temporary XML files with *detector_name* removed, yields the
    path to the patched top-level XML, then deletes the temp files on
    exit regardless of whether an exception was raised.

    Parameters
    ----------
    xml_path:
        Path to the original top-level compact XML.
    detector_name:
        Name of the ``<detector>`` element to remove.

    Yields
    ------
    Path
        Path to the patched top-level XML file.

    Raises
    ------
    DetectorNotFoundError
        If *detector_name* is not found in any reachable XML file.

    Example
    -------
    ::

        with patched_geometry(Path("ALLEGRO.xml"), "EcalBarrel") as tmp_xml:
            result = run_ddsim(xml_path=tmp_xml, ...)
    """
    top_tmp, sub_tmp = build_patched_xml(xml_path, detector_name)
    try:
        yield top_tmp
    finally:
        for tmp in (top_tmp, sub_tmp):
            if tmp is not None:
                tmp.unlink(missing_ok=True)

build_patched_xml

build_patched_xml(xml_path: Path, detector_name: str) -> tuple[Path, Path]

Write patched XML files with detector_name removed.

Locates the file that owns detector_name, removes the <detector> node from it, writes a temp copy, then writes a patched top-level XML whose include ref points at the temp copy.

Parameters:

Name Type Description Default
xml_path Path

Path to the original top-level compact XML.

required
detector_name str

Name of the <detector> element to remove.

required

Returns:

Type Description
tuple[Path, Path]

(top_tmp_path, sub_tmp_path) — the caller is responsible for deleting both files. Prefer :func:patched_geometry to handle cleanup automatically.

Raises:

Type Description
DetectorNotFoundError

If detector_name is not found in any reachable XML file.

Source code in k4bench/geometry/patcher.py
def build_patched_xml(
    xml_path: Path, detector_name: str
) -> tuple[Path, Path]:
    """Write patched XML files with *detector_name* removed.

    Locates the file that owns *detector_name*, removes the
    ``<detector>`` node from it, writes a temp copy, then writes a
    patched top-level XML whose include ref points at the temp copy.

    Parameters
    ----------
    xml_path:
        Path to the original top-level compact XML.
    detector_name:
        Name of the ``<detector>`` element to remove.

    Returns
    -------
    tuple[Path, Path]
        ``(top_tmp_path, sub_tmp_path)`` — the caller is responsible for
        deleting both files.  Prefer :func:`patched_geometry` to handle
        cleanup automatically.

    Raises
    ------
    DetectorNotFoundError
        If *detector_name* is not found in any reachable XML file.
    """
    xml_path = xml_path.resolve()

    owner, patched_doc = _find_and_remove_detector(xml_path, detector_name)

    _remove_orphaned_plugins(patched_doc, {detector_name})
    _absolutize_refs(patched_doc, owner.parent)
    sub_tmp_path = _write_tmp_xml(patched_doc, None, f"no_{detector_name}_sub_")
    top_tmp_path = _write_patched_top(xml_path, owner, sub_tmp_path, detector_name)

    return top_tmp_path, sub_tmp_path