Skip to content

Data Structures API

Classes for managing dose distributions, structures, and structure sets.

Dose Class

Dose

Dose(dose_array: ndarray, spacing: Tuple[float, float, float], origin: Tuple[float, float, float], name: str = 'Dose', metadata: Optional[Dict] = None)

Represents a 3D dose distribution from RT-DOSE or NIfTI.

A Dose object is a pure data container representing dose distributions. Dose analysis is performed by combining Dose with Structure objects using functions from the metrics subpackage.

Attributes:

Name Type Description
dose_array ndarray

3D array of dose values in Gy

spacing Tuple[float, float, float]

Voxel spacing in (x, y, z) mm

origin Tuple[float, float, float]

Origin coordinates in mm

name str

Identifier for this dose distribution

metadata Dict

Additional metadata (DICOM tags, beam info, etc.)

Examples:

>>> from dosemetrics.dose import Dose
>>> from dosemetrics.metrics import dvh
>>>
>>> # Load dose from DICOM
>>> dose = Dose.from_dicom("path/to/rtdose.dcm", name="Plan_v1")
>>>
>>> # Load dose from NIfTI
>>> dose = Dose.from_nifti("path/to/dose.nii.gz", name="Predicted")
>>>
>>> # Compute dose statistics (use metrics module)
>>> ptv = structure_set.get_structure("PTV")
>>> stats = statistics.compute_dose_statistics(dose, ptv)
>>> print(f"Mean dose: {stats['mean_dose']:.2f} Gy")
>>>
>>> # Compute DVH (use metrics module)
>>> dose_bins, volumes = dvh.compute_dvh(dose, ptv)

Initialize a Dose distribution.

Parameters:

Name Type Description Default
dose_array ndarray

3D array of dose values (Gy)

required
spacing Tuple[float, float, float]

Voxel spacing in (x, y, z) mm

required
origin Tuple[float, float, float]

Origin coordinates in mm

required
name str

Identifier for this dose (e.g., "Plan_v1", "Sum", "Predicted")

'Dose'
metadata Optional[Dict]

Additional metadata (DICOM tags, beam info, etc.)

None

Raises:

Type Description
ValueError

If dose_array is not 3D

Source code in src/dosemetrics/dose.py
def __init__(
    self,
    dose_array: np.ndarray,
    spacing: Tuple[float, float, float],
    origin: Tuple[float, float, float],
    name: str = "Dose",
    metadata: Optional[Dict] = None,
):
    """
    Initialize a Dose distribution.

    Args:
        dose_array: 3D array of dose values (Gy)
        spacing: Voxel spacing in (x, y, z) mm
        origin: Origin coordinates in mm
        name: Identifier for this dose (e.g., "Plan_v1", "Sum", "Predicted")
        metadata: Additional metadata (DICOM tags, beam info, etc.)

    Raises:
        ValueError: If dose_array is not 3D
    """
    self.dose_array = np.asarray(dose_array)
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)
    self.name = name
    self.metadata = metadata or {}

    # Validate 3D
    if self.dose_array.ndim != 3:
        raise ValueError(
            f"Dose array must be 3D, got {self.dose_array.ndim}D array"
        )

Attributes

shape property
shape: Tuple[int, int, int]

Shape of the dose array.

max_dose property
max_dose: float

Maximum dose in the entire distribution (Gy).

mean_dose property
mean_dose: float

Mean dose across the entire volume (Gy).

min_dose property
min_dose: float

Minimum dose in the distribution (Gy).

Functions

is_compatible_with_structure
is_compatible_with_structure(structure: Structure) -> bool

Check if this dose is spatially compatible with a structure.

Parameters:

Name Type Description Default
structure Structure

Structure to check compatibility with

required

Returns:

Type Description
bool

True if shapes, spacing, and origin match

Source code in src/dosemetrics/dose.py
def is_compatible_with_structure(self, structure: Structure) -> bool:
    """
    Check if this dose is spatially compatible with a structure.

    Args:
        structure: Structure to check compatibility with

    Returns:
        True if shapes, spacing, and origin match
    """
    if structure.mask is None:
        return False

    return (
        self.shape == structure.mask.shape
        and np.allclose(self.spacing, structure.spacing, rtol=1e-5)
        and np.allclose(self.origin, structure.origin, rtol=1e-3, atol=1.0)
    )
get_dose_in_structure
get_dose_in_structure(structure: Structure) -> np.ndarray

Extract dose values within a structure mask.

Parameters:

Name Type Description Default
structure Structure

Structure to extract dose from

required

Returns:

Type Description
ndarray

1D array of dose values inside the structure

Raises:

Type Description
ValueError

If dose and structure are not spatially compatible

Source code in src/dosemetrics/dose.py
def get_dose_in_structure(self, structure: Structure) -> np.ndarray:
    """
    Extract dose values within a structure mask.

    Args:
        structure: Structure to extract dose from

    Returns:
        1D array of dose values inside the structure

    Raises:
        ValueError: If dose and structure are not spatially compatible
    """
    if not self.is_compatible_with_structure(structure):
        raise ValueError(
            f"Dose '{self.name}' (shape={self.shape}) is not compatible "
            f"with structure '{structure.name}' (shape={structure.mask.shape}). "
            f"Dose spacing: {self.spacing}, Structure spacing: {structure.spacing}"
        )

    return self.dose_array[structure.mask]
from_nifti classmethod
from_nifti(file_path: Union[str, Path], name: Optional[str] = None) -> Dose

Load dose distribution from a NIfTI file.

Parameters:

Name Type Description Default
file_path Union[str, Path]

Path to NIfTI file (.nii or .nii.gz)

required
name Optional[str]

Name for this dose (uses filename stem if None)

None

Returns:

Type Description
Dose

Dose object

Raises:

Type Description
FileNotFoundError

If file doesn't exist

ValueError

If file cannot be loaded

Source code in src/dosemetrics/dose.py
@classmethod
def from_nifti(
    cls, file_path: Union[str, Path], name: Optional[str] = None
) -> Dose:
    """
    Load dose distribution from a NIfTI file.

    Args:
        file_path: Path to NIfTI file (.nii or .nii.gz)
        name: Name for this dose (uses filename stem if None)

    Returns:
        Dose object

    Raises:
        FileNotFoundError: If file doesn't exist
        ValueError: If file cannot be loaded
    """
    from .io.data_io import load_volume

    file_path = Path(file_path)
    volume, spacing, origin = load_volume(file_path)

    if name is None:
        name = file_path.stem.replace(".nii", "")

    return cls(volume, spacing, origin, name=name)
from_dicom classmethod
from_dicom(file_path: Union[str, Path], name: Optional[str] = None) -> Dose

Load dose distribution from a DICOM RT-DOSE file.

Parameters:

Name Type Description Default
file_path Union[str, Path]

Path to RT-DOSE DICOM file

required
name Optional[str]

Name for this dose (uses filename stem if None)

None

Returns:

Type Description
Dose

Dose object

Raises:

Type Description
FileNotFoundError

If file doesn't exist

ValueError

If file is not a valid RT-DOSE

Source code in src/dosemetrics/dose.py
@classmethod
def from_dicom(
    cls, file_path: Union[str, Path], name: Optional[str] = None
) -> Dose:
    """
    Load dose distribution from a DICOM RT-DOSE file.

    Args:
        file_path: Path to RT-DOSE DICOM file
        name: Name for this dose (uses filename stem if None)

    Returns:
        Dose object

    Raises:
        FileNotFoundError: If file doesn't exist
        ValueError: If file is not a valid RT-DOSE
    """
    from .io.dicom_io import read_dicom_rtdose

    file_path = Path(file_path)
    dose_array, spacing, origin, scaling = read_dicom_rtdose(file_path)

    if name is None:
        name = file_path.stem

    metadata = {"dose_scaling": scaling}

    return cls(dose_array, spacing, origin, name=name, metadata=metadata)
__repr__
__repr__() -> str

String representation of the Dose object.

Source code in src/dosemetrics/dose.py
def __repr__(self) -> str:
    """String representation of the Dose object."""
    return (
        f"Dose(name='{self.name}', shape={self.shape}, "
        f"max={self.max_dose:.2f} Gy, mean={self.mean_dose:.2f} Gy)"
    )
__str__
__str__() -> str

Human-readable string representation.

Source code in src/dosemetrics/dose.py
def __str__(self) -> str:
    """Human-readable string representation."""
    return (
        f"Dose Distribution '{self.name}':\n"
        f"  Shape: {self.shape}\n"
        f"  Spacing: {self.spacing} mm\n"
        f"  Max dose: {self.max_dose:.2f} Gy\n"
        f"  Mean dose: {self.mean_dose:.2f} Gy\n"
        f"  Min dose: {self.min_dose:.2f} Gy"
    )

Structure Classes

structures

Radiotherapy structure classes for anatomical regions of interest.

This module provides core data structures to represent radiotherapy structures from RTSS DICOM files or NIfTI files. These structures are 3D volumes with binary masks representing anatomical regions of interest such as organs at risk (OARs), target volumes, and avoidance regions.

Structures represent pure geometry. Dose analysis is performed by combining Structure objects with Dose objects using the dosemetrics.dose module.

Classes

StructureType

Bases: Enum

Enumeration for different types of radiotherapy structures.

Structure
Structure(name: str, mask: Optional[ndarray] = None, spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0), origin: Tuple[float, float, float] = (0.0, 0.0, 0.0))

Bases: ABC

Base class for radiotherapy structures.

Represents a 3D anatomical structure derived from RTSS DICOM files or equivalent NIfTI masks. Contains geometric information only - dose analysis is performed by combining with Dose objects.

Attributes:

Name Type Description
name str

Name/identifier of the structure

mask ndarray

3D binary mask array (boolean)

spacing Tuple[float, float, float]

Voxel spacing in (x, y, z) mm

origin Tuple[float, float, float]

Origin coordinates in mm

Examples:

>>> # Create a structure
>>> ptv = Target(name="PTV", mask=mask_array, spacing=(1.0, 1.0, 3.0))
>>> 
>>> # Get volume
>>> volume_cc = ptv.volume_cc()
>>> 
>>> # Compute dose statistics (using Dose object)
>>> from dosemetrics.dose import Dose
>>> dose = Dose.from_dicom("rtdose.dcm")
>>> stats = dose.compute_statistics(ptv)

Initialize a Structure.

Parameters:

Name Type Description Default
name str

Name/identifier of the structure

required
mask Optional[ndarray]

3D binary mask array (will be converted to bool)

None
spacing Tuple[float, float, float]

Voxel spacing in (x, y, z) mm

(1.0, 1.0, 1.0)
origin Tuple[float, float, float]

Origin coordinates in mm

(0.0, 0.0, 0.0)
Source code in src/dosemetrics/structures.py
def __init__(
    self,
    name: str,
    mask: Optional[np.ndarray] = None,
    spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0),
    origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
):
    """
    Initialize a Structure.

    Args:
        name: Name/identifier of the structure
        mask: 3D binary mask array (will be converted to bool)
        spacing: Voxel spacing in (x, y, z) mm
        origin: Origin coordinates in mm
    """
    self.name = name
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)

    # Set and validate mask
    if mask is not None:
        self.set_mask(mask)
    else:
        self._mask = None
Attributes
mask property
mask: Optional[ndarray]

Get the binary mask array.

structure_type abstractmethod property
structure_type: StructureType

Return the type of this structure.

has_mask property
has_mask: bool

Check if structure has a valid mask.

Functions
set_mask
set_mask(mask: ndarray) -> None

Set the binary mask for this structure.

Parameters:

Name Type Description Default
mask ndarray

3D array that will be converted to binary mask

required

Raises:

Type Description
ValueError

If mask is not 3D

Source code in src/dosemetrics/structures.py
def set_mask(self, mask: np.ndarray) -> None:
    """
    Set the binary mask for this structure.

    Args:
        mask: 3D array that will be converted to binary mask

    Raises:
        ValueError: If mask is not 3D
    """
    mask_array = np.asarray(mask)
    if mask_array.ndim != 3:
        raise ValueError(f"Mask must be 3D, got {mask_array.ndim}D")

    # Convert to boolean mask
    self._mask = mask_array.astype(bool)
volume_voxels
volume_voxels() -> int

Get structure volume in voxels.

Returns:

Type Description
int

Number of voxels in the structure (sum of mask)

Source code in src/dosemetrics/structures.py
def volume_voxels(self) -> int:
    """
    Get structure volume in voxels.

    Returns:
        Number of voxels in the structure (sum of mask)
    """
    if not self.has_mask or self._mask is None:
        return 0
    return int(np.sum(self._mask))
volume_cc
volume_cc() -> float

Get structure volume in cubic centimeters.

Returns:

Type Description
float

Volume in cc (considering voxel spacing)

Source code in src/dosemetrics/structures.py
def volume_cc(self) -> float:
    """
    Get structure volume in cubic centimeters.

    Returns:
        Volume in cc (considering voxel spacing)
    """
    voxel_volume_mm3 = np.prod(self.spacing)  # mm³
    voxel_volume_cc = float(voxel_volume_mm3 / 1000.0)  # Convert mm³ to cc
    return float(self.volume_voxels() * voxel_volume_cc)
centroid
centroid() -> Optional[Tuple[float, float, float]]

Calculate the centroid of the structure in world coordinates.

Returns:

Type Description
Optional[Tuple[float, float, float]]

Tuple of (x, y, z) coordinates in mm, or None if no mask

Source code in src/dosemetrics/structures.py
def centroid(self) -> Optional[Tuple[float, float, float]]:
    """
    Calculate the centroid of the structure in world coordinates.

    Returns:
        Tuple of (x, y, z) coordinates in mm, or None if no mask
    """
    if not self.has_mask or self._mask is None:
        return None

    # Get indices of mask voxels
    mask_indices = np.where(self._mask)

    if len(mask_indices[0]) == 0:
        return None

    # Calculate centroid in voxel coordinates as mean of all voxel indices
    centroid_voxel = [
        float(np.mean(indices))
        for indices in mask_indices
    ]

    # Convert to world coordinates
    centroid_world = [
        float(self.origin[i] + centroid_voxel[i] * self.spacing[i])
        for i in range(3)
    ]

    return (centroid_world[0], centroid_world[1], centroid_world[2])
bounding_box
bounding_box() -> Optional[Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int]]]

Get bounding box of the structure in voxel coordinates.

Returns:

Type Description
Optional[Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int]]]

Tuple of ((min_x, max_x), (min_y, max_y), (min_z, max_z)), or None if no mask

Source code in src/dosemetrics/structures.py
def bounding_box(
    self,
) -> Optional[Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int]]]:
    """
    Get bounding box of the structure in voxel coordinates.

    Returns:
        Tuple of ((min_x, max_x), (min_y, max_y), (min_z, max_z)), or None if no mask
    """
    if not self.has_mask or self._mask is None:
        return None

    mask_indices = np.where(self._mask)

    if len(mask_indices[0]) == 0:
        return None

    bounds = []
    for i in range(3):
        min_idx = int(np.min(mask_indices[i]))
        max_idx = int(np.max(mask_indices[i]))
        bounds.append((min_idx, max_idx))

    return (bounds[0], bounds[1], bounds[2])
__str__
__str__() -> str

String representation of the structure.

Source code in src/dosemetrics/structures.py
def __str__(self) -> str:
    """String representation of the structure."""
    volume_cc = self.volume_cc() if self.has_mask else 0
    return (
        f"{self.structure_type.value.upper()}: {self.name} "
        f"(Volume: {volume_cc:.2f} cc)"
    )
__repr__
__repr__() -> str

Detailed representation of the structure.

Source code in src/dosemetrics/structures.py
def __repr__(self) -> str:
    """Detailed representation of the structure."""
    return (
        f"{self.__class__.__name__}(name='{self.name}', "
        f"type={self.structure_type.value}, "
        f"has_mask={self.has_mask}, "
        f"volume_cc={self.volume_cc():.2f})"
    )
OAR
OAR(name: str, mask: Optional[ndarray] = None, spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0), origin: Tuple[float, float, float] = (0.0, 0.0, 0.0))

Bases: Structure

Organ at Risk (OAR) structure.

Represents critical normal organs that should receive limited radiation dose to avoid complications (e.g., spinal cord, eyes, heart, brainstem).

Source code in src/dosemetrics/structures.py
def __init__(
    self,
    name: str,
    mask: Optional[np.ndarray] = None,
    spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0),
    origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
):
    """
    Initialize a Structure.

    Args:
        name: Name/identifier of the structure
        mask: 3D binary mask array (will be converted to bool)
        spacing: Voxel spacing in (x, y, z) mm
        origin: Origin coordinates in mm
    """
    self.name = name
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)

    # Set and validate mask
    if mask is not None:
        self.set_mask(mask)
    else:
        self._mask = None
Attributes
structure_type property
structure_type: StructureType

Return OAR structure type.

Target
Target(name: str, mask: Optional[ndarray] = None, spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0), origin: Tuple[float, float, float] = (0.0, 0.0, 0.0))

Bases: Structure

Target volume structure.

Represents volumes that should receive the prescribed radiation dose (e.g., PTV, CTV, GTV).

Source code in src/dosemetrics/structures.py
def __init__(
    self,
    name: str,
    mask: Optional[np.ndarray] = None,
    spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0),
    origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
):
    """
    Initialize a Structure.

    Args:
        name: Name/identifier of the structure
        mask: 3D binary mask array (will be converted to bool)
        spacing: Voxel spacing in (x, y, z) mm
        origin: Origin coordinates in mm
    """
    self.name = name
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)

    # Set and validate mask
    if mask is not None:
        self.set_mask(mask)
    else:
        self._mask = None
Attributes
structure_type property
structure_type: StructureType

Return Target structure type.

AvoidanceStructure
AvoidanceStructure(name: str, mask: Optional[ndarray] = None, spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0), origin: Tuple[float, float, float] = (0.0, 0.0, 0.0))

Bases: Structure

Avoidance structure.

Represents regions where dose should be minimized during planning (e.g., critical OAR expansions, sensitive areas).

Source code in src/dosemetrics/structures.py
def __init__(
    self,
    name: str,
    mask: Optional[np.ndarray] = None,
    spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0),
    origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
):
    """
    Initialize a Structure.

    Args:
        name: Name/identifier of the structure
        mask: 3D binary mask array (will be converted to bool)
        spacing: Voxel spacing in (x, y, z) mm
        origin: Origin coordinates in mm
    """
    self.name = name
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)

    # Set and validate mask
    if mask is not None:
        self.set_mask(mask)
    else:
        self._mask = None
Attributes
structure_type property
structure_type: StructureType

Return Avoidance structure type.

StructureSet Class

StructureSet

StructureSet(spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0), origin: Tuple[float, float, float] = (0.0, 0.0, 0.0), name: str = 'StructureSet')

Collection of radiotherapy structures representing a complete structure set.

Similar to a DICOM RTSS file, this class manages multiple structures (OARs, targets, avoidance regions) with common geometric properties. Contains only geometric information - dose analysis is performed separately by combining with Dose objects.

Attributes:

Name Type Description
structures Dict[str, Structure]

Dictionary mapping structure names to Structure objects

spacing Tuple[float, float, float]

Common voxel spacing for all structures

origin Tuple[float, float, float]

Common origin for all structures

name str

Identifier for this structure set

Examples:

>>> # Create a structure set
>>> structure_set = StructureSet(spacing=(1.0, 1.0, 3.0), name="Patient001")
>>> 
>>> # Add structures
>>> structure_set.add_structure("PTV", ptv_mask, StructureType.TARGET)
>>> structure_set.add_structure("Brainstem", brain_mask, StructureType.OAR)
>>> 
>>> # Access structures
>>> ptv = structure_set.get_structure("PTV")
>>> print(f"PTV volume: {ptv.volume_cc():.2f} cc")
>>> 
>>> # For dose analysis, combine with Dose object
>>> from dosemetrics.dose import Dose
>>> dose = Dose.from_dicom("rtdose.dcm")
>>> stats = dose.compute_statistics(ptv)

Initialize an empty StructureSet.

Parameters:

Name Type Description Default
spacing Tuple[float, float, float]

Common voxel spacing in (x, y, z) mm

(1.0, 1.0, 1.0)
origin Tuple[float, float, float]

Common origin coordinates in mm

(0.0, 0.0, 0.0)
name str

Name identifier for this structure set

'StructureSet'
Source code in src/dosemetrics/structure_set.py
def __init__(
    self,
    spacing: Tuple[float, float, float] = (1.0, 1.0, 1.0),
    origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
    name: str = "StructureSet",
):
    """
    Initialize an empty StructureSet.

    Args:
        spacing: Common voxel spacing in (x, y, z) mm
        origin: Common origin coordinates in mm
        name: Name identifier for this structure set
    """
    self.structures: Dict[str, Structure] = {}
    self.spacing = tuple(spacing)
    self.origin = tuple(origin)
    self.name = name

Attributes

structure_names property
structure_names: List[str]

Get list of all structure names.

oar_names property
oar_names: List[str]

Get list of OAR structure names.

target_names property
target_names: List[str]

Get list of target structure names.

structure_count property
structure_count: int

Get total number of structures.

Functions

add_structure
add_structure(name: str, mask: ndarray, structure_type: StructureType, structure_class: Optional[type] = None) -> Structure

Add a structure to the set.

Parameters:

Name Type Description Default
name str

Name of the structure

required
mask ndarray

3D binary mask array

required
structure_type StructureType

Type of structure (OAR, TARGET, etc.)

required
structure_class Optional[type]

Specific structure class to use (defaults based on type)

None

Returns:

Type Description
Structure

The created Structure object

Raises:

Type Description
ValueError

If structure name already exists or mask dimensions are invalid

Source code in src/dosemetrics/structure_set.py
def add_structure(
    self,
    name: str,
    mask: np.ndarray,
    structure_type: StructureType,
    structure_class: Optional[type] = None,
) -> Structure:
    """
    Add a structure to the set.

    Args:
        name: Name of the structure
        mask: 3D binary mask array
        structure_type: Type of structure (OAR, TARGET, etc.)
        structure_class: Specific structure class to use (defaults based on type)

    Returns:
        The created Structure object

    Raises:
        ValueError: If structure name already exists or mask dimensions are invalid
    """
    if name in self.structures:
        raise ValueError(f"Structure '{name}' already exists in the set")

    # Determine structure class if not specified
    if structure_class is None:
        if structure_type == StructureType.OAR:
            structure_class = OAR
        elif structure_type == StructureType.TARGET:
            structure_class = Target
        elif structure_type == StructureType.AVOIDANCE:
            structure_class = AvoidanceStructure
        else:
            # For SUPPORT, EXTERNAL, or custom types, create dynamic class
            structure_class = type(
                f"{structure_type.value.title()}Structure",
                (Structure,),
                {"structure_type": property(lambda self: structure_type)},
            )

    # Create structure instance
    structure = structure_class(
        name=name, mask=mask, spacing=self.spacing, origin=self.origin
    )

    self.structures[name] = structure
    return structure
remove_structure
remove_structure(name: str) -> None

Remove a structure from the set.

Parameters:

Name Type Description Default
name str

Name of the structure to remove

required

Raises:

Type Description
ValueError

If structure name not found

Source code in src/dosemetrics/structure_set.py
def remove_structure(self, name: str) -> None:
    """
    Remove a structure from the set.

    Args:
        name: Name of the structure to remove

    Raises:
        ValueError: If structure name not found
    """
    if name not in self.structures:
        raise ValueError(f"Structure '{name}' not found in the set")
    del self.structures[name]
add_structure_object
add_structure_object(structure: Structure) -> Structure

Add an existing Structure instance to the set.

Convenience wrapper to support tests and workflows that create Structure objects independently and then attach them to a StructureSet.

Parameters:

Name Type Description Default
structure Structure

A Structure instance to add.

required

Returns:

Type Description
Structure

The same Structure instance after being added to the set.

Raises:

Type Description
ValueError

If a structure with the same name already exists, or if spacing/origin are incompatible with the set.

Source code in src/dosemetrics/structure_set.py
def add_structure_object(self, structure: Structure) -> Structure:
    """
    Add an existing `Structure` instance to the set.

    Convenience wrapper to support tests and workflows that create
    `Structure` objects independently and then attach them to a `StructureSet`.

    Args:
        structure: A `Structure` instance to add.

    Returns:
        The same `Structure` instance after being added to the set.

    Raises:
        ValueError: If a structure with the same name already exists,
                   or if spacing/origin are incompatible with the set.
    """
    name = structure.name
    if name in self.structures:
        raise ValueError(f"Structure '{name}' already exists in the set")

    if tuple(structure.spacing) != tuple(self.spacing):
        raise ValueError("Structure spacing incompatible with StructureSet")
    if tuple(structure.origin) != tuple(self.origin):
        raise ValueError("Structure origin incompatible with StructureSet")

    self.structures[name] = structure
    return structure
get_structure
get_structure(name: str) -> Structure

Get a structure by name.

Parameters:

Name Type Description Default
name str

Name of the structure

required

Returns:

Type Description
Structure

Structure object

Raises:

Type Description
ValueError

If structure name not found

Source code in src/dosemetrics/structure_set.py
def get_structure(self, name: str) -> Structure:
    """
    Get a structure by name.

    Args:
        name: Name of the structure

    Returns:
        Structure object

    Raises:
        ValueError: If structure name not found
    """
    if name not in self.structures:
        raise ValueError(f"Structure '{name}' not found in the set")
    return self.structures[name]
get_structures_by_type
get_structures_by_type(structure_type: StructureType) -> Dict[str, Structure]

Get all structures of a specific type.

Parameters:

Name Type Description Default
structure_type StructureType

Type to filter by

required

Returns:

Type Description
Dict[str, Structure]

Dictionary of structures matching the type

Source code in src/dosemetrics/structure_set.py
def get_structures_by_type(
    self, structure_type: StructureType
) -> Dict[str, Structure]:
    """
    Get all structures of a specific type.

    Args:
        structure_type: Type to filter by

    Returns:
        Dictionary of structures matching the type
    """
    return {
        name: struct
        for name, struct in self.structures.items()
        if struct.structure_type == structure_type
    }
get_oars
get_oars() -> Dict[str, OAR]

Get all OAR structures.

Source code in src/dosemetrics/structure_set.py
def get_oars(self) -> Dict[str, OAR]:
    """Get all OAR structures."""
    return self.get_structures_by_type(StructureType.OAR)
get_targets
get_targets() -> Dict[str, Target]

Get all target structures.

Source code in src/dosemetrics/structure_set.py
def get_targets(self) -> Dict[str, Target]:
    """Get all target structures."""
    return self.get_structures_by_type(StructureType.TARGET)
get_avoidance_structures
get_avoidance_structures() -> Dict[str, AvoidanceStructure]

Get all avoidance structures.

Source code in src/dosemetrics/structure_set.py
def get_avoidance_structures(self) -> Dict[str, AvoidanceStructure]:
    """Get all avoidance structures."""
    return self.get_structures_by_type(StructureType.AVOIDANCE)
total_volume_cc
total_volume_cc() -> float

Calculate total volume of all structures in cc.

Returns:

Type Description
float

Sum of all structure volumes in cubic centimeters

Source code in src/dosemetrics/structure_set.py
def total_volume_cc(self) -> float:
    """
    Calculate total volume of all structures in cc.

    Returns:
        Sum of all structure volumes in cubic centimeters
    """
    return sum(struct.volume_cc() for struct in self.structures.values())
geometric_summary
geometric_summary() -> pd.DataFrame

Generate geometric summary for all structures.

Returns:

Type Description
DataFrame

DataFrame with geometric properties of each structure including:

DataFrame
  • Structure name and type
DataFrame
  • Volume in cc and voxels
DataFrame
  • Centroid coordinates
DataFrame
  • Bounding box ranges
Source code in src/dosemetrics/structure_set.py
def geometric_summary(self) -> pd.DataFrame:
    """
    Generate geometric summary for all structures.

    Returns:
        DataFrame with geometric properties of each structure including:
        - Structure name and type
        - Volume in cc and voxels
        - Centroid coordinates
        - Bounding box ranges
    """
    geom_data = []
    for name, structure in self.structures.items():
        centroid = structure.centroid()
        bbox = structure.bounding_box()

        geom = {
            "Structure": name,
            "Type": structure.structure_type.value.upper(),
            "Volume_cc": structure.volume_cc(),
            "Volume_voxels": structure.volume_voxels(),
            "Centroid_X": centroid[0] if centroid is not None else None,
            "Centroid_Y": centroid[1] if centroid is not None else None,
            "Centroid_Z": centroid[2] if centroid is not None else None,
            "BBox_X_Range": f"{bbox[0][0]}-{bbox[0][1]}" if bbox is not None else None,
            "BBox_Y_Range": f"{bbox[1][0]}-{bbox[1][1]}" if bbox is not None else None,
            "BBox_Z_Range": f"{bbox[2][0]}-{bbox[2][1]}" if bbox is not None else None,
        }
        geom_data.append(geom)

    return pd.DataFrame(geom_data)
__len__
__len__() -> int

Return number of structures in the set.

Source code in src/dosemetrics/structure_set.py
def __len__(self) -> int:
    """Return number of structures in the set."""
    return len(self.structures)
__iter__
__iter__() -> Iterator[Tuple[str, Structure]]

Iterate over structure name-object pairs.

Source code in src/dosemetrics/structure_set.py
def __iter__(self) -> Iterator[Tuple[str, Structure]]:
    """Iterate over structure name-object pairs."""
    return iter(self.structures.items())
__getitem__
__getitem__(name: str) -> Structure

Access structure by name using bracket notation.

Source code in src/dosemetrics/structure_set.py
def __getitem__(self, name: str) -> Structure:
    """Access structure by name using bracket notation."""
    return self.get_structure(name)
__contains__
__contains__(name: str) -> bool

Check if structure name exists in the set.

Source code in src/dosemetrics/structure_set.py
def __contains__(self, name: str) -> bool:
    """Check if structure name exists in the set."""
    return name in self.structures
__str__
__str__() -> str

String representation of the structure set.

Source code in src/dosemetrics/structure_set.py
def __str__(self) -> str:
    """String representation of the structure set."""
    oar_count = len(self.get_oars())
    target_count = len(self.get_targets())
    total_volume = self.total_volume_cc()

    return (
        f"StructureSet '{self.name}': {self.structure_count} structures "
        f"({target_count} targets, {oar_count} OARs) "
        f"- Total volume: {total_volume:.1f} cc"
    )
__repr__
__repr__() -> str

Detailed representation of the structure set.

Source code in src/dosemetrics/structure_set.py
def __repr__(self) -> str:
    """Detailed representation of the structure set."""
    return (
        f"StructureSet(name='{self.name}', "
        f"structures={self.structure_count}, "
        f"spacing={self.spacing}, "
        f"origin={self.origin})"
    )