def add_connection(self, origin, destination): """Add connection between units 'origin' and 'destination'. Parameters ---------- origin : UnitBaseClass UnitBaseClass from which the connection originates. destination : UnitBaseClass UnitBaseClass where the connection terminates. Raises ------ CADETProcessError If origin OR destination do not exist in the current flow sheet. If connection already exists in the current flow sheet. See Also -------- connections remove_connection output_state """ if origin not in self._units: raise CADETProcessError('Origin not in flow sheet') if destination not in self._units: raise CADETProcessError('Destination not in flow sheet') if destination in self.connections[origin].destinations: raise CADETProcessError('Connection already exists') self._connections[origin].destinations.append(destination) self._connections[destination].origins.append(origin) self.set_output_state(origin, 0)
def check_cadet(self): """Wrapper around a basic CADET example for testing functionality""" if platform.system() == 'Windows': lwe_path = self.install_path / "bin" / "createLWE.exe" else: lwe_path = self.install_path / "bin" / "createLWE" ret = subprocess.run( [lwe_path.as_posix()], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.temp_dir ) if ret.returncode != 0: if ret.stdout: print('Output', ret.stdout.decode('utf-8')) if ret.stderr: print('Errors', ret.stderr.decode('utf-8')) raise CADETProcessError( "Failure: Creation of test simulation ran into problems" ) lwe_hdf5_path = Path(self.temp_dir) / 'LWE.h5' sim = CadetAPI() sim.filename = lwe_hdf5_path.as_posix() data = sim.run() os.remove(sim.filename) if data.returncode == 0: print("Test simulation completed successfully") else: print(data) raise CADETProcessError( "Simulation failed" )
def add_section(self, section, entry_index=None): """Add section to TimeLine. Parameters ---------- section : Section Section to be added. Raises ------ TypeError If section is not instance of Section. CADETProcessError If polynomial degree does not match. CADETProcessError If section introduces a gap. """ if not isinstance(section, Section): raise TypeError('Expected Section') if len(self.sections) > 0: if section.degree != self.degree: raise CADETProcessError('Polynomial degree does not match') if not (section.start == self.end or section.end == self.start): raise CADETProcessError('Sections times must be without gaps') self._sections.append(section) self._sections = sorted(self._sections, key=lambda sec: sec.start) self.update_piecewise_poly()
def remove_connection(self, origin, destination): """Remove connection between units 'origin' and 'destination'. Parameters ---------- origin : UnitBaseClass UnitBaseClass from which the connection originates. destination : UnitBaseClass UnitBaseClass where the connection terminates. Raises ------ CADETProcessError If origin OR destination do not exist in the current flow sheet. If connection does not exists in the current flow sheet. See Also -------- connections add_connection """ if origin not in self._units: raise CADETProcessError('Origin not in flow sheet') if destination not in self._units: raise CADETProcessError('Destination not in flow sheet') try: self._connections[origin].destinations.remove(destination) self._connections[destination].origins.remove(origin) except KeyError: raise CADETProcessError('Connection does not exist.')
def add_fraction(self, fraction): if not isinstance(fraction, Fraction): raise CADETProcessError('Expected Fraction') if fraction.n_comp != self.n_comp: raise CADETProcessError('Number of components does not match.') self._fractions.append(fraction)
def temp_dir(self, temp_dir): if temp_dir is not None: try: exists = Path(temp_dir).exists() except TypeError: raise CADETProcessError('Not a valid path') if not exists: raise CADETProcessError('Not a valid path') tempfile.tempdir = temp_dir
def bulk_reaction_model(self, bulk_reaction_model): if not isinstance(bulk_reaction_model, ReactionBaseClass): raise TypeError('Expected ReactionBaseClass') if not self.supports_bulk_reaction: raise CADETProcessError('Unit does not support bulk reactions.') if bulk_reaction_model.component_system is not self.component_system \ and not isinstance(bulk_reaction_model, NoReaction): raise CADETProcessError('Component systems do not match.') self._bulk_reaction_model = bulk_reaction_model
def __init__(self, wrapped_object): if not isinstance(wrapped_object, self._baseClass): raise CADETProcessError("Expected {}".format(self._baseClass)) model = wrapped_object.model try: self.model_parameters = self._model_parameters[model] except KeyError: raise CADETProcessError("Model Type not defined") self._wrapped_object = wrapped_object
def add_unit( self, unit, feed_source=False, eluent_source=False, chromatogram_sink=False ): """Add unit to the flow sheet. Parameters ---------- unit : UnitBaseClass UnitBaseClass object to be added to the flow sheet. feed_source : bool If True, add unit to feed sources. eluent_source : bool If True, add unit to eluent sources. chromatogram_sink : bool If True, add unit to chromatogram sinks. Raises ------ TypeError If unit is no instance of UnitBaseClass. CADETProcessError If unit already exists in flow sheet. If n_comp does not match with FlowSheet. See Also -------- remove_unit """ if not isinstance(unit, UnitBaseClass): raise TypeError('Expected UnitOperation') if unit in self._units: raise CADETProcessError('Unit already part of System') if unit.component_system is not self.component_system: raise CADETProcessError('Component systems do not match.') self._units.append(unit) self._connections[unit] = Dict({ 'origins': [], 'destinations': [], }) self._output_states[unit] = [] self._flow_rates[unit] = [] super().__setattr__(unit.name, unit) if feed_source: self.add_feed_source(unit) if eluent_source: self.add_eluent_source(unit) if chromatogram_sink: self.add_chromatogram_sink(unit)
def parameters(self, parameters): try: self.binding_model.parameters = parameters.pop('binding_model') except KeyError: pass try: self.bulk_reaction_model.parameters = parameters.pop( 'bulk_reaction_model') except KeyError: pass try: self.particle_reaction_model.parameters = parameters.pop( 'particle_reaction_model') except KeyError: pass try: self.discretization.parameters = parameters.pop('discretization') except KeyError: pass for param, value in parameters.items(): if param not in self._parameters: raise CADETProcessError('Not a valid parameter') if value is not None: setattr(self, param, value)
def setup_optimization_problem(self, frac): opt = OptimizationProblem(frac) opt.logger.setLevel(logging.WARNING) opt.add_objective(self.obj_fun) opt.add_nonlinear_constraint( nonlin_bounds_decorator(self.purity_required)(purity)) for evt in frac.events: opt.add_variable(evt.name + '.time', evt.name) for chrom_index, chrom in enumerate(frac.chromatograms): chrom_events = frac.chromatogram_events[chrom] evt_names = [evt.name for evt in chrom_events] for evt_index, evt in enumerate(chrom_events): if evt_index < len(chrom_events) - 1: opt.add_linear_constraint( [evt_names[evt_index], evt_names[evt_index + 1]], [1, -1]) else: opt.add_linear_constraint([evt_names[0], evt_names[-1]], [-1, 1], frac.cycle_time) opt.x0 = [evt.time for evt in frac.events] if not opt.check_nonlinear_constraints(opt.x0): raise CADETProcessError("No areas found with sufficient purity.") return opt
def remove_event(self, evt_name): """Remove event from the EventHandler. Parameters ---------- evt_name : str Name of the event to be removed Raises ------ CADETProcessError If Event is not found. Note ---- !!! Check remove_event_dependencies See also -------- add_event remove_event_dependency Event """ try: evt = self.events_dict[evt_name] except KeyError: raise CADETProcessError("Event does not exist") self._events.remove(evt) self.__dict__.pop(evt_name)
def add_inlet_profile(self, unit, time, c, component_index=None, s=1e-6): if not isinstance(unit, Source): raise TypeError('Expected Source') if max(time) > self.cycle_time: raise ValueError('Inlet profile exceeds cycle time') if component_index == -1: # Assume same profile for all components if c.ndim > 1: raise ValueError('Expected single concentration profile') c = np.column_stack([c] * 2) elif component_index is None and c.shape[1] != self.n_comp: # Assume c is given for all components raise CADETProcessError('Number of components does not match') for comp in range(self.n_comp): tck = interpolate.splrep(time, c[:, comp], s=s) ppoly = interpolate.PPoly.from_spline(tck) for i, (t, sec) in enumerate(zip(ppoly.x, ppoly.c.T)): if i < 3: continue elif i > len(ppoly.x) - 5: continue evt = self.add_event(f'{unit}_inlet_{comp}_{i-3}', f'flow_sheet.{unit}.c', np.flip(sec), t, comp)
def add_duration(self, name, time=0.0): """Add duration to the EventHandler. Parameters ---------- name: str Name of the event. time : float Time point for perfoming the event. Raises ------ CADETProcessError If Duration already exists. See also -------- durations remove_duration Duration add_event add_event_dependency """ if name in self.events_dict: raise CADETProcessError("Duration already exists") dur = Duration(name, self, time) self._durations.append(dur) super().__setattr__(name, dur)
def remove_duration(self, duration_name): """Remove duration from list of durations. Parameters ---------- duration : str Name of the duration be removed from the EventHandler. Raises ------ CADETProcessError If Duration is not found. See also -------- Duration add_duration remove_event_dependency """ try: dur = self.events_dict[duration_name] except KeyError: raise CADETProcessError("Duration does not exist") self._durations.remove(dur) self.__dict__.pop(duration_name)
def set_style(style='medium'): """Defines the sytle of a plot. Can set the sytle of a plot for small, medium and large plots. The figuresize of the figure, the linewitdth and color of the lines and the size of the font can be changed by switching the style. Parameters ---------- style : str Style of a figure plot, default set to small. Raises ------ CADETProcessError If no valid style has been chosen as parameter. """ if style == 'small': plt.rcParams['figure.figsize'] = (5, 3) plt.rcParams['lines.linewidth'] = 2 plt.rcParams['font.size'] = 12 plt.rcParams['axes.prop_cycle'] = chromapy_cycler elif style == 'medium': plt.rcParams['figure.figsize'] = (10, 6) plt.rcParams['lines.linewidth'] = 4 plt.rcParams['font.size'] = 24 plt.rcParams['axes.prop_cycle'] = chromapy_cycler elif style == 'large': plt.rcParams['figure.figsize'] = (15, 9) plt.rcParams['lines.linewidth'] = 6 plt.rcParams['font.size'] = 30 plt.rcParams['axes.prop_cycle'] = chromapy_cycler else: raise CADETProcessError('Not a valid style')
def parameters(self, parameters): try: self.cycle_time = parameters.pop('cycle_time') except KeyError: pass for evt_name, evt_parameters in parameters.items(): try: evt = self.events_dict[evt_name] except AttributeError: raise CADETProcessError('Not a valid event') if evt not in self.independent_events + self.durations: raise CADETProcessError('{} is not a valid event'.format( str(evt))) evt.parameters = evt_parameters
def binding_model(self, binding_model): if not isinstance(binding_model, BindingBaseClass): raise TypeError('Expected BindingBaseClass') if binding_model.component_system is not self.component_system: raise CADETProcessError('Component systems do not match.') self._binding_model = binding_model
def parameters(self, parameters): if isinstance(parameters, (float, int)): self.time = parameters else: for param, value in parameters.items(): if param not in self._parameters: raise CADETProcessError('Not a valid parameter') setattr(self, param, value)
def time(self, time): if not isinstance(time, (int, float, np.int64, np.float64)): raise TypeError("Expected {}".format(float)) if self.isIndependent: self._time = time else: raise CADETProcessError("Cannot set time for dependent events")
def entry_index(self, entry_index): if entry_index is not None: parameter = get_nested_value(self.event_handler.parameters, self.parameter_sequence) if entry_index > len(parameter) - 1: raise CADETProcessError('Index exceeds components') self._entry_index = entry_index
def component_index(self, component_index): if component_index is not None: parameter = get_nested_value(self.evaluation_object.parameters, self.parameter_sequence) if component_index > len(parameter) - 1: raise CADETProcessError('Index exceeds components') self._component_index = component_index
def wrapper(self, unit, *args, **kwargs): """Enable calling functions with unit object or unit name. """ if isinstance(unit, str): try: unit = self.units_dict[unit] except KeyError: raise CADETProcessError('Not a valid unit') return func(self, unit, *args, **kwargs)
def set_output_state(self, unit, state): """Set split ratio of outgoing streams for UnitOperation. Parameters ---------- unit : UnitBaseClass UnitOperation of flowsheet. state : int or list of floats new output state of the unit. Raises ------ CADETProcessError If unit not in flowSheet If state is integer and the state >= the state_length. If the length of the states is unequal the state_length. If the sum of the states is not equal to 1. """ if unit not in self._units: raise CADETProcessError('Unit not in flow sheet') state_length = len(self.connections[unit].destinations) if state_length == 0: output_state = [] if isinstance(state, (int, np.int64)): if state >= state_length: raise CADETProcessError('Index exceeds destinations') output_state = [0] * state_length output_state[state] = 1 else: if len(state) != state_length: raise CADETProcessError( 'Expected length {}.'.format(state_length)) elif sum(state) != 1: raise CADETProcessError('Sum of fractions must be 1') output_state = state self._output_states[unit] = output_state
def initial_state(self, initial_state): try: self.flow_sheet.initial_state = initial_state.pop('flow_sheet') except KeyError: pass for state_name, state_value in initial_state.items(): if state_name not in self._initial_state: raise CADETProcessError('Not an valid state') setattr(self, state_name, state_value)
def add_eluent_source(self, eluent_source): """Add source to list of units to be considered for eluent consumption. Parameters ---------- eluent_source : SourceMixin Unit to be added to list of eluent sources. Raises ------ CADETProcessError If unit is not in a source object If unit is already marked as eluent source """ if eluent_source not in self.sources: raise CADETProcessError('Expected Source') if eluent_source in self._eluent_sources: raise CADETProcessError('{} is already eluent source'.format( eluent_source)) self._eluent_sources.append(eluent_source)
def initial_state(self, initial_state): if initial_state is None: self._initial_state = initial_state return if not isinstance(initial_state, list): initial_state = self.n_columns * [initial_state] if len(initial_state) != self.n_columns: raise CADETProcessError(f"Expected size {self.n_columns}") self._initial_state = initial_state
def set_variables(self, x, make_copy=False): """Sets the values from the x-vector to the OptimizationVariables. Parameters ---------- x : array_like Value of the optimization variables make_copy : Bool If True, a copy of the evaluation_object attribute is made on which the values are set. Otherwise, the values are set on the attribute. Returns ------- evaluation_object : object Returns copy of evaluation object if make_copy is True, else returns the attribute evaluation_object with the values set. Raises ------ ValueError If value of variable exceeds bounds See also -------- OptimizationVariable evaluate """ if len(x) != self.n_variables: raise CADETProcessError('Expected {} variables'.format( self.n_variables)) if make_copy: evaluation_object = copy.deepcopy(self.evaluation_object) else: evaluation_object = self.evaluation_object for variable, value in zip(self.variables, x): if value < variable.lb: raise ValueError("Exceeds lower bound") if value > variable.ub: raise ValueError("Exceeds upper bound") if variable.component_index is not None: value_list = get_nested_value(evaluation_object.parameters, variable.parameter_path) value_list[variable.component_index] = value parameters = generate_nested_dict(variable.parameter_path, value_list) else: parameters = generate_nested_dict(variable.parameter_path, value) evaluation_object.parameters = parameters return evaluation_object
def setup_fractionator(self, process_meta, chromatograms): frac = Fractionator(process_meta) for chrom in chromatograms: frac.add_chromatogram(chrom) frac.initial_values(self.purity_required) if len(frac.events) == 0: raise CADETProcessError("No areas found with sufficient purity.") return frac
def __init__(self, performance, ranking=1.0): if not isinstance(performance, Performance): raise TypeError('Expected Performance') self._performance = performance if isinstance(ranking, (float, int)): ranking = [ranking] * performance.n_comp elif len(ranking) != performance.n_comp: raise CADETProcessError('Number of components does not match.') self._ranking = ranking