示例#1
0
    def test_init_params(self):

        # testing to see if parameters for generating spectra are defined correctly
        env = gym.make(ENV_NAME)

        # converting materials to class object representation
        material_classes = convert_to_class(materials=REACTANTS + PRODUCTS)

        # define parameters for generating spectra
        params = []
        for material in material_classes:
            params.append(material().get_spectra_no_overlap())

        self.assertEqual(params, env.reaction.params)
示例#2
0
    def _prepare_materials(materials=[]):
        """
        Method to prepare a list of materials into a material dictionary.
        The provided materials are expected to be of the following form:
            materials = [
                {"Material": INSERT MATERIAL NAME, "Initial": INSERT INITIAL AMOUNT}
                {"Material": INSERT MATERIAL NAME, "Initial": INSERT INITIAL AMOUNT}
                .
                .
                .
            ]

        Parameters
        ---------------
        `materials` : `list` (default=`None`)
            A list of dictionaries including initial material names and amounts.

        Returns
        ---------------
        `material_dict` : `dict`
            A dictionary containing all the inputted materials, class representations, and their molar amounts.

        Raises
        ---------------
        None
        """

        # prepare lists to maintain the names and amounts of the materials
        material_names = []
        material_amounts = []

        # iterate through the provided list of materials and obtain the names and amounts
        for material_params in materials:
            material_names.append(material_params["Material"])
            material_amounts.append(material_params["Initial"])

        # acquire the material class representations from the material names
        material_classes = convert_to_class(materials=material_names)

        # create a dictionary to contain the material and its properties
        material_dict = {}

        # iterate through each of the material names, classes, and amounts lists;
        # it is assumed the materials are provided in units of mol
        for i, material in enumerate(material_names):
            material_dict[material] = [
                material_classes[i](), material_amounts[i], 'mol'
            ]

        return material_dict
示例#3
0
    def _prepare_solutes(material_dict=None, solvents=None):
        """
        Method to prepare a list of materials into a material dictionary.
        The provided materials are expected to be of the following form:
            materials = [
                {"Material": INSERT MATERIAL NAME, "Initial": INSERT INITIAL AMOUNT}
                {"Material": INSERT MATERIAL NAME, "Initial": INSERT INITIAL AMOUNT}
                .
                .
                .
            ]

        Parameters
        ---------------
        `solvents` : `list` (default=`None`)
            A list of dictionaries including initial solvent names and amounts.

        Returns
        ---------------
        `solvent_dict` : `dict`
            A dictionary containing all the inputted solvents, class representations, and their molar amounts.

        Raises
        ---------------
        None
        """

        solute_dict = {}
        for name, material in material_dict.items():
            if name not in solute_dict and material[0]._solute:
                solute_dict[name] = {}
                for solvent in solvents:
                    solvent_class = convert_to_class([solvent['Material']])
                    solute_dict[name][solvent['Material']] = [
                        solvent_class[0](), solvent["Initial"], 'mol'
                    ]

        return solute_dict
示例#4
0
    def __init__(self,
                 materials=None,
                 solutes=None,
                 desired="",
                 overlap=False):
        '''
        Constructor class module for the Reaction class.

        Parameters
        ---------------
        `materials` : `list` (default=`None`)
            A list of dictionaries containing initial material names, classes, and amounts.
        `solutes` : `list` (default=`None`)
            A list of dictionaries containing initial solute names, classes, and amounts.
        `desired` : `str` (default="")
            A string indicating the name of the desired material.
        `overlap` : `bool` (default=`False`)
            Indicate if the spectral plot includes overlapping plots.

        Returns
        ---------------
        None

        Raises
        ---------------
        None
        '''

        self.name = "demo_reaction"

        # get the initial amounts of each reactant material
        initial_materials = np.zeros(len(REACTANTS))
        for material in materials:
            if material["Material"] in REACTANTS:
                index = REACTANTS.index(material["Material"])
                initial_materials[index] = material["Initial"]
        self.initial_in_hand = initial_materials

        # get the initial amount of each solute
        initial_solutes = np.zeros(len(SOLUTES))
        for solute in solutes:
            if solute["Solute"] in SOLUTES:
                index = SOLUTES.index(solute["Solute"])
                initial_solutes[index] = solute["Initial"]
        self.initial_solutes = initial_solutes
        self.solute_labels = SOLUTES

        # specify the desired material
        self.desired_material = desired

        # convert the reactants and products to their class object representations
        self.reactant_classes = convert_to_class(materials=REACTANTS)
        self.product_classes = convert_to_class(materials=PRODUCTS)
        self.material_classes = convert_to_class(materials=ALL_MATERIALS)
        self.solute_classes = convert_to_class(materials=SOLUTES)

        # define the maximum of each chemical allowed at one time (in mol)
        self.nmax = np.array([1.0 for __ in ALL_MATERIALS])

        # create labels for each of the chemicals involved
        self.labels = ALL_MATERIALS

        # define a space to record all six reaction rates
        self.rate = np.zeros(6)

        # define the maximal number of moles available for any chemical
        self.max_mol = 2.0

        # define parameters for generating spectra
        self.params = []
        if overlap:
            self.params.append(spec.S_3_3)  # spectra for NaCl
            self.params.append(spec.S_6)  # spectra for the Na
            self.params.append(spec.S_7)  # spectra for the Cl
        else:
            self.params.append(spec.S_8)  # spectra for NaCl
            self.params.append(spec.S_1)  # spectra for the Na
            self.params.append(spec.S_3)  # spectra for the Cl
示例#5
0
    def reset(self, extraction_vessel):
        '''
        Method to reset the environment.

        Parameters
        ---------------
        `extraction_vessel` : `vessel` (default=`None`)
            A vessel object containing state variables, materials, solutes, and spectral data.

        Returns
        ---------------
        `vessels` : `list`
            A list of all the vessel objects that contain materials and solutes.
        `external_vessels` : `list`
            A list of the external vessels, beakers, to be used in the extraction.
        `state` : `np.array`
            An array containing state variables, material concentrations, and spectral data.

        Raises
        ---------------
        None
        '''

        # delete the extraction vessel's solute_dict and copy it into a list of vessels
        solute_dict = extraction_vessel._solute_dict
        extraction_vessel._solute_dict = {}
        vessels = [copy.deepcopy(extraction_vessel)]

        # create all the necessary beakers and add them to the list
        for i in range(self.n_empty_vessels):
            temp_vessel = vessel.Vessel(label='beaker_{}'.format(i + 1),
                                        v_max=self.max_vessel_volume,
                                        default_dt=0.05,
                                        n_pixels=self.n_vessel_pixels)
            vessels.append(temp_vessel)

        # generate a list of external vessels to contain solutes
        external_vessels = []

        # generate a vessel to contain the main solute
        solute_vessel = vessel.Vessel(
            label='solute_vessel0',
            v_max=self.solute_volume,
            n_pixels=self.n_vessel_pixels,
            settling_switch=False,
            layer_switch=False,
        )

        # create the material dictionary for the solute vessel
        solute_material_dict = {}
        solute_class = convert_to_class(materials=[self.solute])[0]
        solute_material_dict[self.solute] = [solute_class, self.solute_volume]

        # check for overflow
        solute_material_dict, _, _ = util.check_overflow(
            material_dict=solute_material_dict,
            solute_dict={},
            v_max=solute_vessel.get_max_volume())

        # instruct the vessel to update its material dictionary
        event = ['update material dict', solute_material_dict]
        solute_vessel.push_event_to_queue(feedback=[event], dt=0)

        # add the main solute vessel to the list of external vessels
        external_vessels.append(solute_vessel)

        # generate vessels for each solute in the extraction vessel
        for solute_name in solute_dict:
            # generate an empty vessel to be filled with a single solute
            solute_vessel = vessel.Vessel(label='solute_vessel{}'.format(
                len(external_vessels)),
                                          v_max=extraction_vessel.v_max,
                                          n_pixels=self.n_vessel_pixels,
                                          settling_switch=False,
                                          layer_switch=False)
            solute_material_dict = {}
            solute_material_dict[solute_name] = solute_dict[solute_name]

            # check for overflow
            solute_material_dict, _, _ = util.check_overflow(
                material_dict=solute_material_dict,
                solute_dict={},
                v_max=solute_vessel.get_max_volume())

            # instruct the vessel to update its material dictionary
            event = ['update material dict', solute_material_dict]
            solute_vessel.push_event_to_queue(feedback=[event], dt=0)

            # add this solute vessel to the list of external vessels
            external_vessels.append(solute_vessel)

        # generate the state
        state = util.generate_state(vessel_list=vessels,
                                    max_n_vessel=self.n_total_vessels)

        return vessels, external_vessels, state
示例#6
0
    def __init__(self,
                 reaction_file_identifier="",
                 dt=0.01,
                 overlap=False,
                 solver='RK45'):
        """
        Constructor class module for the Reaction class.

        Parameters
        ---------------
        `reaction_file_identifier` : `str` (default=`""`)
            The basename of the reaction file that contains the parameters
            necessary to perform the desired reaction(s).
        `overlap` : `bool` (default=`False`)
            Indicate if the spectral plot includes overlapping plots.

        Returns
        ---------------
        None

        Raises
        ---------------
        None
        """

        # define a name/label for this reaction base class
        self.name = "base_reaction"

        # ensure the requested reaction file is found and accessible
        reaction_filepath = self._find_reaction_file(
            reaction_file=reaction_file_identifier)

        # acquire the necessary parameters from the reaction file
        reaction_params = self._get_reaction_params(
            reaction_filepath=reaction_filepath)

        # UNPACK THE REACTION PARAMETERS:
        # materials used and the desired material
        self.reactants = reaction_params["REACTANTS"]
        self.products = reaction_params["PRODUCTS"]
        self.solvents = reaction_params["SOLVENTS"]
        self.desired = reaction_params["DESIRED"]

        # initial vessel properties (used in reseting the reaction vessel)
        self.Ti = reaction_params["Ti"]
        self.Vi = reaction_params["Vi"]

        # additional vessel properties (to be assigned during the reset function)
        self.Tmin = reaction_params["Tmin"]
        self.Tmax = reaction_params["Tmax"]
        self.Vmin = reaction_params["Vmin"]
        self.Vmax = reaction_params["Vmax"]

        # thermodynamic increment values (used when performing the reactions)
        self.dt = dt
        self.dT = reaction_params["dT"]
        self.dV = reaction_params["dV"]

        # the arrays used in dictating how reaction calculations are performed
        self.activ_energy_arr = reaction_params["activ_energy_arr"]
        self.stoich_coeff_arr = reaction_params["stoich_coeff_arr"]
        self.conc_coeff_arr = reaction_params["conc_coeff_arr"]

        self.de = De(self.stoich_coeff_arr, self.activ_energy_arr,
                     self.conc_coeff_arr, len(self.reactants))

        # specify the full list of materials
        self.materials = []

        for mat in self.reactants + self.products + self.solvents:
            if mat not in self.materials:
                self.materials.append(mat)

        # set a threshold value for the minimum, non-negligible material molar amount
        self.threshold = 1e-8

        # define these parameters initially as they are to be given properly in the reset function
        self.initial_in_hand = np.zeros(len(self.reactants))
        self.cur_in_hand = np.zeros(len(self.reactants))
        self.initial_materials = np.zeros(len(self.materials))

        # convert the reactants and products to their class object representations
        self.reactant_classes = convert_to_class(materials=self.reactants)
        self.product_classes = convert_to_class(materials=self.products)
        self.material_classes = convert_to_class(materials=self.materials)

        # create the (empty) n array which will contain the molar amounts of materials in use
        self.n = np.zeros(len(self.materials))

        # define the maximal number of moles available for any chemical
        self.max_mol = 2.0

        # define the maximal number of moles of each chemical allowed at one time
        self.nmax = self.max_mol * np.ones(len(self.materials))

        # include the available solvers
        self.solvers = {'RK45', 'RK23', 'DOP853', 'DBF', 'LSODA'}

        # select the intended solver or use the default
        if solver in self.solvers:
            self.solver = solver
        else:
            self.solver = 'newton'

        self._solver = solve_ivp

        # define parameters for generating spectra
        self.params = []
        for material in self.material_classes:
            if overlap:
                self.params.append(material().get_spectra_overlap())
            else:
                self.params.append(material().get_spectra_no_overlap())