Source code for CADETProcess.processModel.componentSystem

from collections import defaultdict
from functools import wraps

from addict import Dict

from CADETProcess import CADETProcessError
from CADETProcess.dataStructure import Structure
from CADETProcess.dataStructure import String, Integer, UnsignedFloat


__all__ = ['ComponentSystem', 'Component', 'Species']


[docs] class Species(Structure): """Species class. Represent a species in a chemical system. Attributes ---------- name : str The name of the species. charge : int, optional The charge of the species. Default is 0. molecular_weight : float The molecular weight of the species. """ name = String() charge = Integer(default=0) molecular_weight = UnsignedFloat()
[docs] class Component(Structure): """Information about single component. A component can contain subspecies (e.g. differently charged variants). Attributes ---------- name : String Name of the component. species : list List of Subspecies. n_species : int Number of Subspecies. label : list Name of component (including species). charge : list Charge of component (including species). molecular_weight : list Molecular weight of component (including species). See Also -------- Species ComponentSystem """ name = String() def __init__( self, name=None, species=None, charge=None, molecular_weight=None): """ Parameters ---------- name : str, optional Name of the component. species : str or list of str, optional Names of the subspecies. charge : int or list of int or None, optional Charges of the subspecies. molecular_weight : float or list of float or None, optional Molecular weights of the subspecies. """ self.name = name self._species = [] if species is None: self.add_species(name, charge, molecular_weight) elif isinstance(species, str): self.add_species(species, charge, molecular_weight) elif isinstance(species, list): if charge is None: charge = len(species) * [None] if molecular_weight is None: molecular_weight = len(species) * [None] for i, spec in enumerate(species): self.add_species(spec, charge[i], molecular_weight[i]) else: raise CADETProcessError("Could not determine number of species") @property def species(self): """list: The subspecies of the component.""" return self._species @wraps(Species.__init__) def add_species(self, species, *args, **kwargs): """ Add a subspecies to the component. Parameters ---------- *args Variable length argument list. **kwargs Arbitrary keyword arguments. Returns ------- Species The subspecies that was added. """ if not isinstance(species, Species): species = Species(species, *args, **kwargs) self._species.append(species) @property def n_species(self): """int: The number of subspecies in the component.""" return len(self.species) @property def label(self): """list of str: The names of the subspecies.""" return [spec.name for spec in self.species] @property def charge(self): """list of int or None: The charges of the subspecies.""" return [spec.charge for spec in self.species] @property def molecular_weight(self): """list of float or None: The molecular weights of the subspecies.""" return [spec.molecular_weight for spec in self.molecular_weight] def __str__(self): """String representation of the component.""" return self.name def __iter__(self): """Iterate over the subspecies of the component.""" yield from self.species
[docs] class ComponentSystem(Structure): """Information about components in system. A component can contain subspecies (e.g. differently charged variants). Attributes ---------- name : String Name of the component system. components : list List of individual components. n_species : int Number of Subspecies. n_comp : int Number of all component species. n_components : int Number of components. indices : dict Component indices. names : list Names of all components. species : list Names of all component species. charge : list Charges of all components species. molecular_weight : list Molecular weights of all component species. See Also -------- Species Component """ name = String() def __init__( self, components=None, name=None, charges=None, molecular_weights=None): """Initialize the ComponentSystem object. Parameters ---------- components : int, list, None The number of components or the list of components to be added. If None, no components are added. name : str, None The name of the ComponentSystem. charges : list, None The charges of each component. molecular_weights : list, None The molecular weights of each component. Raises ------ CADETProcessError If the `components` argument is neither an int nor a list. """ self.name = name self._components = [] if components is None: return if isinstance(components, int): n_comp = components components = [str(i) for i in range(n_comp)] elif isinstance(components, list): n_comp = len(components) else: raise CADETProcessError("Could not determine number of components") if charges is None: charges = n_comp * [None] if molecular_weights is None: molecular_weights = n_comp * [None] for i, comp in enumerate(components): self.add_component( comp, charge=charges[i], molecular_weight=molecular_weights[i], ) @property def components(self): """list: List of components in the system.""" return self._components @property def components_dict(self): """dict: Components indexed by name.""" return { name: comp for name, comp in zip(self.names, self.components) } @property def n_components(self): """int: Number of components.""" return len(self.components) @property def n_comp(self): """int: Number of species.""" return self.n_species @property def n_species(self): """int: Number of species.""" return sum([comp.n_species for comp in self.components])
[docs] @wraps(Component.__init__) def add_component(self, component, *args, **kwargs): """ Add a component to the system. Parameters ---------- component : {str, Component} The class of the component to be added. *args : list The positional arguments to be passed to the component class's constructor. **kwargs : dict The keyword arguments to be passed to the component class's constructor. """ if not isinstance(component, Component): component = Component(component, *args, **kwargs) if component.name in self.names: raise CADETProcessError( f"Component '{component.name}' " "already exists in ComponentSystem." ) self._components.append(component)
[docs] def remove_component(self, component): """Remove a component from the system. Parameters ---------- component : {str, Component} The name of the component or the component instance to be removed. Raises ------ CADETProcessError If the component is unknown or not present in the system. """ if isinstance(component, str): try: component = self.components_dict[component] except KeyError: raise CADETProcessError("Unknown Component.") if component not in self.components: raise CADETProcessError("Unknown Component.") self._components.remove(component)
@property def indices(self): """dict: List of species indices for each component name.""" indices = defaultdict(list) index = 0 for comp in self.components: for spec in comp.species: indices[comp.name].append(index) index += 1 return Dict(indices) @property def species_indices(self): """dict: Indices for each species.""" indices = Dict() index = 0 for comp in self.components: for spec in comp.species: indices[spec.name] = index index += 1 return indices @property def names(self): """list: List of component names.""" names = [ comp.name if comp.name is not None else str(i) for i, comp in enumerate(self.components) ] return names @property def species(self): """list: List of species names.""" species = [] index = 0 for comp in self.components: for label in comp.label: if label is None: species.append(str(index)) else: species.append(label) index += 1 return species @property def charges(self): """list: List of species charges.""" charges = [] for comp in self.components: charges += comp.charge return charges @property def molecular_weights(self): """list: List of species molecular weights.""" molecular_weights = [] for comp in self.components: molecular_weights += comp.molecular_weight return molecular_weights def __repr__(self): return f'{self.__class__.__name__}({self.names})' def __iter__(self): yield from self.components
[docs] def __getitem__(self, item): return self._components[item]