Skip to content

Compliance Checking

Guide to evaluating dose constraints and treatment plan compliance.

Try Compliance Checking in Live Demo


Overview

Compliance checking determines whether a treatment plan satisfies a set of dose constraints — typically protocol-mandated limits such as "spinal cord Dmax ≤ 45 Gy" or "PTV D95 ≥ 95% of prescription". Rather than reporting a scalar metric, compliance checking yields a binary pass/fail verdict for each constraint and aggregates them into summary statistics.


OAR Constraint Disagreement

OAR Constraint Disagreement OAR Constraint Disagreement — each cell in the grid corresponds to one OAR constraint. Dark red = violated by both plans (agreement on violation). White = satisfied by both (agreement on satisfaction). Light red with border = disagreement between the two plans on pass/fail. (Joseph Weibel, MSc Thesis Defense, University of Bern)

The OAR Constraint Disagreement metric quantifies how often a predicted dose distribution and a reference (clinical) dose distribution reach different pass/fail conclusions for the same set of constraints:

$$\text{Disagreement} = \frac{1}{N} \sum_{c=1}^{N} \mathbf{1}!\left[S_{\text{pred}}(c) \neq S_{\text{target}}(c)\right]$$

where $S(c) \in {0, 1}$ is the binary pass/fail status of constraint $c$ and $N$ is the total number of constraints evaluated.

  • 0.0: perfect agreement — the predicted plan makes the same pass/fail decision as the reference on every constraint
  • 1.0: complete disagreement — every constraint flips status between the two plans
  • Typical clinical threshold: < 0.05 (fewer than 5% of constraints disagreeing)

Use Cases

Scenario How to use
Evaluating an AI-predicted plan against the clinical plan Pass both dose arrays and the same constraint list; the disagreement score summarises clinical fidelity
Automated plan QA Run after each optimisation iteration to detect constraint regressions
Multi-OAR reporting Inspect the per-constraint breakdown to identify which structures are driving disagreement

Example

from dosemetrics.metrics.conformity import compute_coverage, compute_prescription_mae
from dosemetrics.metrics.dvh import compute_dose_at_volume

# Define constraints as (structure, metric_fn, threshold, direction) tuples
constraints = [
    ("PTV",         lambda d, s: compute_coverage(d, s, 60.0),           0.95, ">="),
    ("SpinalCord",  lambda d, s: compute_dose_at_volume(d, s, 0.0),      45.0, "<="),
    ("Parotid_L",   lambda d, s: compute_dose_at_volume(d, s, 50.0),     26.0, "<="),
]

def evaluate_constraints(dose, structures):
    results = {}
    for name, fn, threshold, direction in constraints:
        value = fn(dose, structures[name])
        if direction == ">=":
            results[name] = value >= threshold
        else:
            results[name] = value <= threshold
    return results

status_target = evaluate_constraints(dose_target, structures)
status_pred   = evaluate_constraints(dose_pred,   structures)

disagreements = sum(
    status_target[c] != status_pred[c] for c in status_target
)
disagreement_rate = disagreements / len(constraints)

print(f"Constraint Disagreement: {disagreement_rate:.2f}")
for name in status_target:
    match = "OK" if status_target[name] == status_pred[name] else "DISAGREE"
    print(f"  {name}: target={'PASS' if status_target[name] else 'FAIL'}, "
          f"pred={'PASS' if status_pred[name] else 'FAIL'}  [{match}]")

DVH-Based Constraint Evaluation

Most clinical constraints are expressed in DVH terms. The core DVH functions make it straightforward to evaluate them:

Constraint form Function
Dmax ≤ X Gy compute_max_dose(dose, structure)
D0.1cc ≤ X Gy compute_dose_at_volume_cc(dose, structure, volume_cc=0.1)
DX% ≤ X Gy compute_dose_at_volume(dose, structure, volume_fraction=X/100)
VX Gy ≤ Y% compute_volume_at_dose(dose, structure, dose_gy=X)
D95 ≥ Rx compute_dose_at_volume(dose, structure, volume_fraction=0.05)
Mean ≤ X Gy compute_mean_dose(dose, structure)
from dosemetrics.metrics.dvh import (
    compute_max_dose,
    compute_dose_at_volume,
    compute_dose_at_volume_cc,
    compute_volume_at_dose,
    compute_mean_dose,
)

# Typical head-and-neck OAR constraints
cord_dmax   = compute_max_dose(dose, spinal_cord)
cord_d01cc  = compute_dose_at_volume_cc(dose, spinal_cord, volume_cc=0.1)
parotid_mean = compute_mean_dose(dose, parotid_left)
ptv_d95     = compute_dose_at_volume(dose, ptv, volume_fraction=0.05)

print(f"Spinal cord Dmax:   {cord_dmax:.1f} Gy  (limit ≤ 45 Gy)")
print(f"Spinal cord D0.1cc: {cord_d01cc:.1f} Gy  (limit ≤ 48 Gy)")
print(f"Parotid mean:       {parotid_mean:.1f} Gy  (limit ≤ 26 Gy)")
print(f"PTV D95:            {ptv_d95:.1f} Gy  (target ≥ 57 Gy = 95% of 60 Gy)")

References

Metric Reference
OAR Constraint Disagreement Weibel J, MSc Thesis Defense, University of Bern, 2024
DVH constraint evaluation QUANTEC Working Group, Int J Radiat Oncol Biol Phys, 2010;76(3 Suppl)