Source code for CADETProcess.simulationResults

"""
==========================================================
Simulation Results (:mod:`CADETProcess.simulationResults`)
==========================================================

.. currentmodule:: CADETProcess.simulationResults

This module provides a class for storing simulation results.


.. autosummary::
    :toctree: generated/

    SimulationResults

"""

import copy
import os

import numpy as np
from addict import Dict

from CADETProcess import CADETProcessError
from CADETProcess import settings
from CADETProcess.dataStructure import Structure
from CADETProcess.dataStructure import (
    Dictionary, String, List, UnsignedInteger, UnsignedFloat
)


__all__ = ['SimulationResults']


[docs] class SimulationResults(Structure): """Class for storing simulation results including the solver configuration Attributes ---------- solver_name : str Name of the solver used to simulate the process solver_parameters : dict Dictionary with parameters used by the solver exit_flag : int Information about the solver termination. exit_message : str Additional information about the solver status time_elapsed : float Execution time of simulation. process: Process Simulated Process. solution : dict Solution objects for all cycles of all Unit Operations. solution_cycles : dict Solution objects for individual cycles of all Unit Operations. sensitivity : dict Solution objects for all sensitivities of all cycles of all Unit Operations. sensitivity_cycles : dict Solution objects for all sensitivities of individual cycles of all Unit Operations. system_state : dict Final state and state_derivative of the system. chromatograms : List of chromatogram Solution of the final cycle of the product outlets. n_cycles : int Number of cycles that were simulated. Notes ----- Ideally, the final state for each unit operation should be saved. However, CADET does currently provide this functionality. """ solver_name = String() solver_parameters = Dictionary() exit_flag = UnsignedInteger() exit_message = String() time_elapsed = UnsignedFloat() solution_cycles = Dictionary() sensitivity_cycles = Dictionary() system_state = Dictionary() chromatograms = List() def __init__( self, solver_name, solver_parameters, exit_flag, exit_message, time_elapsed, process, solution_cycles, sensitivity_cycles, system_state, chromatograms ): self.solver_name = solver_name self.solver_parameters = solver_parameters self.exit_flag = exit_flag self.exit_message = exit_message self.time_elapsed = time_elapsed self.process = process self.solution_cycles = solution_cycles self.sensitivity_cycles = sensitivity_cycles self.system_state = system_state self.chromatograms = chromatograms self._time_complete = None self._solution = None self._sensitivity = None
[docs] def update(self, new_results): if self.process.name != new_results.process.name: raise CADETProcessError('Process does not match') self.exit_flag = new_results.exit_flag self.exit_message = new_results.exit_message self.time_elapsed += new_results.time_elapsed self.system_state = new_results.system_state self.chromatograms = new_results.chromatograms for unit, solutions in self.solution_cycles.items(): for sol in solutions: solution = new_results.solution_cycles[unit][sol] self.solution_cycles[unit][sol] += solution self._time_complete = None self._solution = None self._sensitivity = None
@property def component_system(self): solution = self.solution_cycles[self._first_unit][self._first_solution] return solution[0].component_system @property def solution(self): """Construct complete solution from individual cyles.""" if self._solution is not None: return self._solution time_complete = self.time_complete solution = Dict() for unit, solutions in self.solution_cycles.items(): for sol, cycles in solutions.items(): solution[unit][sol] = copy.deepcopy(cycles[0]) solution_complete = cycles[0].solution_original for i in range(1, self.n_cycles): solution_complete = np.vstack(( solution_complete, cycles[i].solution_original[1:] )) solution[unit][sol].time_original = time_complete solution[unit][sol].solution_original = solution_complete solution[unit][sol].reset() self._solution = solution return solution @property def sensitivity(self): """Construct complete sensitivity from individual cyles.""" if self._sensitivity is not None: return self._sensitivity time_complete = self.time_complete sensitivity = Dict() for unit, sensitivities in self.sensitivity_cycles.items(): for sens_name, sensitivities in sensitivities.items(): for sens, cycles in sensitivities.items(): sensitivity[unit][sens_name][sens] = copy.deepcopy(cycles[0]) sensitivity_complete = cycles[0].solution_original for i in range(1, self.n_cycles): sensitivity_complete = np.vstack(( sensitivity_complete, cycles[i].solution_original[1:] )) sensitivity[unit][sens_name][sens].time_original = time_complete sensitivity[unit][sens_name][sens].solution_original = sensitivity_complete sensitivity[unit][sens_name][sens].reset() self._sensitivity = sensitivity return sensitivity @property def n_cycles(self): return len( self.solution_cycles[self._first_unit][self._first_solution] ) @property def _first_unit(self): return next(iter(self.solution_cycles)) @property def _first_solution(self): return next(iter(self.solution_cycles[self._first_unit])) @property def time_cycle(self): """np.array: Solution times vector""" return self.solution_cycles[self._first_unit][self._first_solution][0].time_original @property def time_complete(self): if self._time_complete is not None: return self._time_complete time_complete = self.time_cycle for i in range(1, self.n_cycles): time_complete = np.hstack(( time_complete, self.time_cycle[1:] + i*self.process.cycle_time )) self._time_complete = time_complete return time_complete
[docs] def save(self, case_dir=None, unit=None, start=0, end=None): path = settings.working_directory if case_dir is not None: path = os.path.join(settings.working_directory, case_dir) if unit is None: units = self.solution.keys() else: units = self.solution[unit] for unit in units: self.solution[unit][-1].plot( save_path=path + '/' + unit + '_last.png', start=start, end=end ) for unit in units: self.solution_complete[unit].plot( save_path=path + '/' + unit + '_complete.png', start=start, end=end ) for unit in units: self.solution[unit][-1].plot( save_path=path + '/' + unit + '_overlay.png', overlay=[cyc.signal for cyc in self.solution[unit][0:-1]], start=start, end=end )