Source code for pyRadPlan.dose.engines._factory
import warnings
import logging
from typing import Union, Type
from pyRadPlan.plan import validate_pln, Plan
from pyRadPlan.dose.engines import DoseEngineBase
DOSE_ENGINES = {}
logger = logging.getLogger(__name__)
[docs]
def register_engine(engine_cls: Type[DoseEngineBase]) -> None:
"""
Register a new engine.
Parameters
----------
engine_cls : type
A Dose Engine class.
"""
if not issubclass(engine_cls, DoseEngineBase):
raise ValueError("Engine must be a subclass of DoseEngineBase.")
if engine_cls.short_name is None:
raise ValueError("Engine must have a 'short_name' attribute.")
if engine_cls.name is None:
raise ValueError("Engine must have a 'name' attribute.")
if engine_cls.possible_radiation_modes is None:
raise ValueError("Engine must have a 'possible_radiation_modes' attribute.")
engine_name = engine_cls.short_name
if engine_name in DOSE_ENGINES:
warnings.warn(f"Engine '{engine_name}' is already registered.")
else:
DOSE_ENGINES[engine_name] = engine_cls
[docs]
def get_available_engines(pln: Union[Plan, dict[str]]) -> dict[str, Type[DoseEngineBase]]:
"""
Get a list of available engines based on the plan.
Parameters
----------
pln : Union[Plan,dict]
A Plan object.
Returns
-------
dict[str, Type[DoseEngineBase]]
A dictionary containing the available engines.
"""
pln = validate_pln(pln)
return {
name: cls
for name, cls in DOSE_ENGINES.items()
if pln.radiation_mode in cls.possible_radiation_modes
}
[docs]
def get_engine(pln: Union[Plan, dict]) -> DoseEngineBase:
"""
Get the appropriate engine based on the plan.
Parameters
----------
pln : Plan
A Plan object.
Returns
-------
Engine
A Dose Engine object.
"""
pln = validate_pln(pln)
# Available engines
engines = get_available_engines(pln)
if len(engines) <= 0:
raise ValueError(f"No engine available for radiation mode '{pln.radiation_mode}'.")
engine_names = list(engines.keys())
# Did the user provide an engine in the pln?
if isinstance(pln.prop_dose_calc, DoseEngineBase):
# The user provided an engine object, so lets use it
# but warn the user if it is not in the available engines
engine_name = pln.prop_dose_calc.short_name
if engine_name not in engines:
warnings.warn(f"Engine '{engine_name}' seems not to be valid for Plan setup.")
return pln.prop_dose_calc
if isinstance(pln.prop_dose_calc, dict):
# The user provided a dictionary with engine parameters, so we need to find the engine name
if "engine" in pln.prop_dose_calc:
if pln.prop_dose_calc["engine"] in engines:
return engines[pln.prop_dose_calc["engine"]](pln)
warnings.warn(f"Engine '{pln.prop_dose_calc['engine']}' not available for Plan.")
# If no engine name was found, we choose the first as default
logger.warning(
"No engine specified in Plan. Using first available engine %s.", engine_names[0]
)
return engines[engine_names[0]](pln)
raise ValueError(f"No engine available for radiation mode '{pln.radiation_mode}'.")