Source code for simpa.utils.libraries.molecule_library

# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ
# SPDX-FileCopyrightText: 2021 Janek Groehl
# SPDX-License-Identifier: MIT

import numpy as np
import torch
from simpa.utils import Tags

from simpa.utils.tissue_properties import TissueProperties
from simpa.utils.libraries.literature_values import OpticalTissueProperties, StandardProperties
from simpa.utils.libraries.spectrum_library import AnisotropySpectrumLibrary, ScatteringSpectrumLibrary
from simpa.utils import Spectrum
from simpa.utils.calculate import calculate_oxygenation, calculate_gruneisen_parameter_from_temperature, calculate_bvf
from simpa.utils.serializer import SerializableSIMPAClass
from simpa.utils.libraries.spectrum_library import AbsorptionSpectrumLibrary
from simpa.log import Logger
from typing import Optional, Union


[docs]class MolecularComposition(SerializableSIMPAClass, list): """ A class representing a molecular composition which is a list of Molecules. Attributes: segmentation_type (str): The type of segmentation. internal_properties (TissueProperties): The internal tissue properties. """ def __init__(self, segmentation_type: Optional[str] = None, molecular_composition_settings: Optional[dict] = None): """ Initialize the MolecularComposition object. :param segmentation_type: The segmentation class associated with this molecular composition. :type segmentation_type: str, optional :param molecular_composition_settings: A settings dictionary or dict containing the molecules that constitute this composition :type molecular_composition_settings: dict, optional """ super().__init__() self.segmentation_type = segmentation_type self.internal_properties: TissueProperties = None self.logger = Logger() if molecular_composition_settings is None: return _keys = molecular_composition_settings.keys() for molecule_name in _keys: self.append(molecular_composition_settings[molecule_name])
[docs] def update_internal_properties(self, settings): """ Re-defines the internal properties of the molecular composition. For each data field and molecule, a linear mixing model is used to arrive at the final parameters. Raises: AssertionError: If the total volume fraction of all molecules is not exactly 100%. """ self.internal_properties = TissueProperties(settings) self.internal_properties[Tags.DATA_FIELD_SEGMENTATION] = self.segmentation_type self.internal_properties[Tags.DATA_FIELD_OXYGENATION] = calculate_oxygenation(self) self.internal_properties[Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION] = calculate_bvf(self) search_list = self.copy() for molecule in search_list: self.internal_properties.volume_fraction += molecule.get_volume_fraction() self.internal_properties[Tags.DATA_FIELD_GRUNEISEN_PARAMETER] += \ molecule.volume_fraction * molecule.gruneisen_parameter self.internal_properties[Tags.DATA_FIELD_DENSITY] += molecule.volume_fraction * molecule.density self.internal_properties[Tags.DATA_FIELD_SPEED_OF_SOUND] += molecule.volume_fraction * \ molecule.speed_of_sound self.internal_properties[Tags.DATA_FIELD_ALPHA_COEFF] += molecule.volume_fraction * \ molecule.alpha_coefficient if (torch.abs(self.internal_properties.volume_fraction - 1.0) > 1e-5).any(): if not (torch.abs(self.internal_properties.volume_fraction - 1.0) < 1e-5).any(): raise AssertionError("Invalid Molecular composition! The volume fractions of all molecules must be" "exactly 100% somewhere!") self.logger.warning("Some of the volume has not been filled by this molecular composition. Please check" "that this is correct")
[docs] def get_properties_for_wavelength(self, settings, wavelength) -> TissueProperties: """ Get the tissue properties for a specific wavelength. :param wavelength: The wavelength to get properties for. :return: The updated tissue properties. """ self.update_internal_properties(settings) self.internal_properties[Tags.DATA_FIELD_ABSORPTION_PER_CM] = 0 self.internal_properties[Tags.DATA_FIELD_SCATTERING_PER_CM] = 0 self.internal_properties[Tags.DATA_FIELD_ANISOTROPY] = 0 search_list = self.copy() for molecule in search_list: self.internal_properties[Tags.DATA_FIELD_ABSORPTION_PER_CM] += \ (molecule.volume_fraction * molecule.spectrum.get_value_for_wavelength(wavelength)) self.internal_properties[Tags.DATA_FIELD_SCATTERING_PER_CM] += \ (molecule.volume_fraction * (molecule.scattering_spectrum.get_value_for_wavelength(wavelength))) self.internal_properties[Tags.DATA_FIELD_ANISOTROPY] += \ molecule.volume_fraction * molecule.anisotropy_spectrum.get_value_for_wavelength(wavelength) return self.internal_properties
[docs] def serialize(self) -> dict: """ Serialize the molecular composition to a dictionary. :return: The serialized molecular composition. """ dict_items = self.__dict__ dict_items["internal_properties"] = None list_items = [molecule for molecule in self] return {"MolecularComposition": {"dict_items": dict_items, "list_items": list_items}}
[docs] @staticmethod def deserialize(dictionary_to_deserialize: dict): """ Deserialize a dictionary into a MolecularComposition object. :param dictionary_to_deserialize: The dictionary to deserialize. :return: The deserialized MolecularComposition object. """ deserialized_molecular_composition = MolecularCompositionGenerator() for molecule in dictionary_to_deserialize["list_items"]: deserialized_molecular_composition.append(molecule) deserialized_molecular_composition = deserialized_molecular_composition.get_molecular_composition( dictionary_to_deserialize["dict_items"]["segmentation_type"] ) return deserialized_molecular_composition
[docs]class Molecule(SerializableSIMPAClass, object): """ A class representing a molecule with various properties. Attributes: name (str): The name of the molecule. spectrum (Spectrum): The absorption spectrum of the molecule. volume_fraction (float): The volume fraction of the molecule. scattering_spectrum (Spectrum): The scattering spectrum of the molecule. anisotropy_spectrum (Spectrum): The anisotropy spectrum of the molecule. gruneisen_parameter (float): The Grüneisen parameter of the molecule. density (float): The density of the molecule. speed_of_sound (float): The speed of sound in the molecule. alpha_coefficient (float): The alpha coefficient of the molecule. """ def __init__(self, name: str = None, absorption_spectrum: Spectrum = None, volume_fraction: float = None, scattering_spectrum: Spectrum = None, anisotropy_spectrum: Spectrum = None, gruneisen_parameter: float = None, density: float = None, speed_of_sound: float = None, alpha_coefficient: float = None): """ Initialize the Molecule object. :param name: The name of the molecule. :param absorption_spectrum: The absorption spectrum of the molecule. :param volume_fraction: The volume fraction of the molecule. :param scattering_spectrum: The scattering spectrum of the molecule. :param anisotropy_spectrum: The anisotropy spectrum of the molecule. :param gruneisen_parameter: The Grüneisen parameter of the molecule. :param density: The density of the molecule. :param speed_of_sound: The speed of sound in the molecule. :param alpha_coefficient: The alpha coefficient of the molecule. """ if name is None: name = "GenericMoleculeName" if not isinstance(name, str): if isinstance(name, bytes): name = name.decode("utf-8") else: raise TypeError("Molecule name must be of type str or bytes instead of {}!".format(type(name))) self.name = name if absorption_spectrum is None: absorption_spectrum = AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ZERO if isinstance(absorption_spectrum, dict): absorption_spectrum = absorption_spectrum[list(absorption_spectrum.keys())[0]] if not isinstance(absorption_spectrum, Spectrum): raise TypeError(f"The given spectrum was not of type AbsorptionSpectrum! Instead: " f"{type(absorption_spectrum)} and reads: {absorption_spectrum}") self.spectrum = absorption_spectrum if volume_fraction is None: volume_fraction = 0.0 if not isinstance(volume_fraction, (int, float, np.int64, np.ndarray)): raise TypeError(f"The given volume_fraction was not of type float or array instead of " f"{type(volume_fraction)}!") if isinstance(volume_fraction, np.ndarray): volume_fraction = torch.tensor(volume_fraction, dtype=torch.float32) self.volume_fraction = volume_fraction if scattering_spectrum is None: scattering_spectrum = ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(1e-15) if not isinstance(scattering_spectrum, Spectrum): raise TypeError(f"The given scattering_spectrum was not of type Spectrum instead of " f"{type(scattering_spectrum)}!") self.scattering_spectrum = scattering_spectrum if anisotropy_spectrum is None: anisotropy_spectrum = 0.0 if not isinstance(anisotropy_spectrum, Spectrum): raise TypeError(f"The given anisotropy was not of type Spectrum instead of {type(anisotropy_spectrum)}!") self.anisotropy_spectrum = anisotropy_spectrum if gruneisen_parameter is None: gruneisen_parameter = calculate_gruneisen_parameter_from_temperature( StandardProperties.BODY_TEMPERATURE_CELCIUS) if not isinstance(gruneisen_parameter, (np.int32, np.int64, int, float, np.ndarray)): raise TypeError(f"The given gruneisen_parameter was not of type int or float instead " f"of {type(gruneisen_parameter)}!") if isinstance(gruneisen_parameter, np.ndarray): gruneisen_parameter = torch.tensor(gruneisen_parameter, dtype=torch.float32) self.gruneisen_parameter = gruneisen_parameter if density is None: density = StandardProperties.DENSITY_GENERIC if not isinstance(density, (np.int32, np.int64, int, float, np.ndarray)): raise TypeError(f"The given density was not of type int or float instead of {type(density)}!") if isinstance(density, np.ndarray): density = torch.tensor(density, dtype=torch.float32) self.density = density if speed_of_sound is None: speed_of_sound = StandardProperties.SPEED_OF_SOUND_GENERIC if not isinstance(speed_of_sound, (np.int32, np.int64, int, float, np.ndarray)): raise TypeError("The given speed_of_sound was not of type int or float instead of {}!" .format(type(speed_of_sound))) if isinstance(speed_of_sound, np.ndarray): speed_of_sound = torch.tensor(speed_of_sound, dtype=torch.float32) self.speed_of_sound = speed_of_sound if alpha_coefficient is None: alpha_coefficient = StandardProperties.ALPHA_COEFF_GENERIC if not isinstance(alpha_coefficient, (np.int32, np.int64, int, float, np.ndarray)): raise TypeError("The given alpha_coefficient was not of type int or float instead of {}!" .format(type(alpha_coefficient))) if isinstance(alpha_coefficient, np.ndarray): alpha_coefficient = torch.tensor(alpha_coefficient, dtype=torch.float32) self.alpha_coefficient = alpha_coefficient
[docs] def __eq__(self, other) -> bool: """ Check equality between two Molecule objects. :param other: The other Molecule object to compare. :return: True if the Molecule objects are equal, False otherwise. """ if isinstance(other, Molecule): return (self.name == other.name and self.spectrum == other.spectrum and self.volume_fraction == other.volume_fraction and self.scattering_spectrum == other.scattering_spectrum and self.alpha_coefficient == other.alpha_coefficient and self.speed_of_sound == other.speed_of_sound and self.gruneisen_parameter == other.gruneisen_parameter and self.anisotropy_spectrum == other.anisotropy_spectrum and self.density == other.density ) else: return super().__eq__(other)
[docs] def get_volume_fraction(self): return self.volume_fraction
[docs] def serialize(self): """ Serialize the molecule to a dictionary. :return: The serialized molecule. :rtype: dict """ serialized_molecule = self.__dict__ return {"Molecule": serialized_molecule}
[docs] @staticmethod def deserialize(dictionary_to_deserialize: dict): """ Deserialize a dictionary into a Molecule object. :param dictionary_to_deserialize: The dictionary to deserialize. :return: The deserialized Molecule object. """ deserialized_molecule = Molecule(name=dictionary_to_deserialize["name"], absorption_spectrum=dictionary_to_deserialize["spectrum"], volume_fraction=dictionary_to_deserialize["volume_fraction"], scattering_spectrum=dictionary_to_deserialize["scattering_spectrum"], alpha_coefficient=dictionary_to_deserialize["alpha_coefficient"], speed_of_sound=dictionary_to_deserialize["speed_of_sound"], gruneisen_parameter=dictionary_to_deserialize["gruneisen_parameter"], anisotropy_spectrum=dictionary_to_deserialize["anisotropy_spectrum"], density=dictionary_to_deserialize["density"]) return deserialized_molecule
[docs]class MoleculeLibrary(object): """ A class to create predefined molecules with specific properties. Each method in this class returns a Molecule object with predefined settings representing different types of tissues or substances. """ # Main absorbers
[docs] @staticmethod def water(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a water molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing water """ return Molecule(name="water", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Water"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY( StandardProperties.WATER_MUS), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( StandardProperties.WATER_G), density=StandardProperties.DENSITY_WATER, speed_of_sound=StandardProperties.SPEED_OF_SOUND_WATER, alpha_coefficient=StandardProperties.ALPHA_COEFF_WATER )
[docs] @staticmethod def oxyhemoglobin(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create an oxyhemoglobin molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing oxyhemoglobin """ return Molecule(name="oxyhemoglobin", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Oxyhemoglobin"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("blood_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.BLOOD_ANISOTROPY), density=StandardProperties.DENSITY_BLOOD, speed_of_sound=StandardProperties.SPEED_OF_SOUND_BLOOD, alpha_coefficient=StandardProperties.ALPHA_COEFF_BLOOD )
[docs] @staticmethod def deoxyhemoglobin(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a deoxyhemoglobin molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing deoxyhemoglobin """ return Molecule(name="deoxyhemoglobin", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Deoxyhemoglobin"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("blood_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.BLOOD_ANISOTROPY), density=StandardProperties.DENSITY_BLOOD, speed_of_sound=StandardProperties.SPEED_OF_SOUND_BLOOD, alpha_coefficient=StandardProperties.ALPHA_COEFF_BLOOD )
[docs] @staticmethod def melanin(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a melanin molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing melanin """ return Molecule(name="melanin", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Melanin"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.scattering_from_rayleigh_and_mie_theory( "epidermis", OpticalTissueProperties.MUS500_EPIDERMIS, OpticalTissueProperties.FRAY_EPIDERMIS, OpticalTissueProperties.BMIE_EPIDERMIS), anisotropy_spectrum=AnisotropySpectrumLibrary().get_spectrum_by_name("Epidermis_Anisotropy"), density=StandardProperties.DENSITY_SKIN, speed_of_sound=StandardProperties.SPEED_OF_SOUND_SKIN, alpha_coefficient=StandardProperties.ALPHA_COEFF_SKIN )
[docs] @staticmethod def fat(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a fat molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing fat """ return Molecule(name="fat", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Fat"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("fat_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.STANDARD_ANISOTROPY), density=StandardProperties.DENSITY_FAT, speed_of_sound=StandardProperties.SPEED_OF_SOUND_FAT, alpha_coefficient=StandardProperties.ALPHA_COEFF_FAT )
# Scatterers
[docs] @staticmethod def constant_scatterer(scattering_coefficient: float = 100.0, anisotropy: float = 0.9, volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a constant scatterer molecule with predefined properties. :param scattering_coefficient: The scattering coefficient, defaults to 100.0 :param anisotropy: The anisotropy factor, defaults to 0.9 :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing a constant scatterer """ return Molecule(name="constant_scatterer", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY(1e-20), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY( scattering_coefficient), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(anisotropy), density=StandardProperties.DENSITY_GENERIC, speed_of_sound=StandardProperties.SPEED_OF_SOUND_GENERIC, alpha_coefficient=StandardProperties.ALPHA_COEFF_GENERIC )
[docs] @staticmethod def soft_tissue_scatterer(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a soft tissue scatterer molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing a soft tissue scatterer """ return Molecule(name="soft_tissue_scatterer", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY(1e-20), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("background_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.STANDARD_ANISOTROPY), density=StandardProperties.DENSITY_GENERIC, speed_of_sound=StandardProperties.SPEED_OF_SOUND_GENERIC, alpha_coefficient=StandardProperties.ALPHA_COEFF_GENERIC )
[docs] @staticmethod def muscle_scatterer(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a muscle scatterer molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing a muscle scatterer """ return Molecule(name="muscle_scatterer", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY(1e-20), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("muscle_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.STANDARD_ANISOTROPY), density=StandardProperties.DENSITY_GENERIC, speed_of_sound=StandardProperties.SPEED_OF_SOUND_GENERIC, alpha_coefficient=StandardProperties.ALPHA_COEFF_GENERIC )
[docs] @staticmethod def epidermal_scatterer(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create an epidermal scatterer molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing an epidermal scatterer """ return Molecule(name="epidermal_scatterer", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY(1e-20), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.scattering_from_rayleigh_and_mie_theory( "epidermis", OpticalTissueProperties.MUS500_EPIDERMIS, OpticalTissueProperties.FRAY_EPIDERMIS, OpticalTissueProperties.BMIE_EPIDERMIS), anisotropy_spectrum=AnisotropySpectrumLibrary().get_spectrum_by_name("Epidermis_Anisotropy"), density=StandardProperties.DENSITY_SKIN, speed_of_sound=StandardProperties.SPEED_OF_SOUND_SKIN, alpha_coefficient=StandardProperties.ALPHA_COEFF_SKIN )
[docs] @staticmethod def dermal_scatterer(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a dermal scatterer molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing a dermal scatterer """ return Molecule(name="dermal_scatterer", absorption_spectrum=AbsorptionSpectrumLibrary().get_spectrum_by_name("Skin_Baseline"), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.scattering_from_rayleigh_and_mie_theory( "dermis", OpticalTissueProperties.MUS500_DERMIS, OpticalTissueProperties.FRAY_DERMIS, OpticalTissueProperties.BMIE_DERMIS), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.DERMIS_ANISOTROPY), density=StandardProperties.DENSITY_SKIN, speed_of_sound=StandardProperties.SPEED_OF_SOUND_SKIN, alpha_coefficient=StandardProperties.ALPHA_COEFF_SKIN )
[docs] @staticmethod def bone(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a bone molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing bone """ return Molecule(name="bone", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY( OpticalTissueProperties.BONE_ABSORPTION), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary().get_spectrum_by_name("bone_scattering"), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( OpticalTissueProperties.STANDARD_ANISOTROPY), density=StandardProperties.DENSITY_BONE, speed_of_sound=StandardProperties.SPEED_OF_SOUND_BONE, alpha_coefficient=StandardProperties.ALPHA_COEFF_BONE )
[docs] @staticmethod def mediprene(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a mediprene molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing mediprene """ return Molecule(name="mediprene", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY(-np.log(0.85) / 10), # FIXME volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY((-np.log(0.85)) - (-np.log(0.85) / 10)), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), density=StandardProperties.DENSITY_GEL_PAD, speed_of_sound=StandardProperties.SPEED_OF_SOUND_GEL_PAD, alpha_coefficient=StandardProperties.ALPHA_COEFF_GEL_PAD )
[docs] @staticmethod def heavy_water(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create a heavy water molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing heavy water """ return Molecule(name="heavy_water", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY( StandardProperties.HEAVY_WATER_MUA), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY( StandardProperties.WATER_MUS), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( StandardProperties.WATER_G), density=StandardProperties.DENSITY_HEAVY_WATER, speed_of_sound=StandardProperties.SPEED_OF_SOUND_HEAVY_WATER, alpha_coefficient=StandardProperties.ALPHA_COEFF_WATER )
[docs] @staticmethod def air(volume_fraction: (float, torch.Tensor) = 1.0) -> Molecule: """ Create an air molecule with predefined properties. :param volume_fraction: The volume fraction of the molecule, defaults to 1.0 :return: A Molecule object representing air """ return Molecule(name="air", absorption_spectrum=AbsorptionSpectrumLibrary().CONSTANT_ABSORBER_ARBITRARY( StandardProperties.AIR_MUA), volume_fraction=volume_fraction, scattering_spectrum=ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY( StandardProperties.AIR_MUS), anisotropy_spectrum=AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( StandardProperties.AIR_G), density=StandardProperties.DENSITY_AIR, speed_of_sound=StandardProperties.SPEED_OF_SOUND_AIR, alpha_coefficient=StandardProperties.ALPHA_COEFF_AIR )
MOLECULE_LIBRARY = MoleculeLibrary()
[docs]class MolecularCompositionGenerator(object): """ The MolecularCompositionGenerator is a helper class to facilitate the creation of a MolecularComposition instance. This class provides methods to build and retrieve a molecular composition by appending Molecule objects to a dictionary, which is then used to create a MolecularComposition instance. """ def __init__(self): """ Initialize a new MolecularCompositionGenerator. The constructor initializes an empty dictionary to store the molecular composition. """ self.molecular_composition_dictionary = dict()
[docs] def append(self, value: Molecule = None, key: str = None): """ Append a Molecule to the molecular composition. Adds the given Molecule object to the molecular composition dictionary with the specified key. If no key is provided, the name attribute of the Molecule is used as the key. :param value: The Molecule object to add to the molecular composition. :param key: The key under which the Molecule should be stored. If None, the Molecule's name is used. :raises KeyError: If the specified key already exists in the molecular composition. :return: The current instance of MolecularCompositionGenerator. """ if key is None: key = value.name if key in self.molecular_composition_dictionary: raise KeyError(key + " already in the molecular composition!") self.molecular_composition_dictionary[key] = value return self
[docs] def get_molecular_composition(self, segmentation_type): """ Retrieve the molecular composition as a MolecularComposition instance. Creates a MolecularComposition instance using the current state of the molecular composition dictionary and the specified segmentation type. :param segmentation_type: The type of segmentation to be used in the MolecularComposition. :return: A MolecularComposition instance. """ return MolecularComposition(segmentation_type=segmentation_type, molecular_composition_settings=self.molecular_composition_dictionary)