def test_get_material_position(self): vessel = Vessel("test", materials={ 'C6H14': [material.C6H14, 1, 'mol'], 'H2O': [material.H2O, 1, 'mol'] }) vessel.get_material_position("H2O")
def test__mix(self): vessel = Vessel("test", materials={ 'Na': [material.Na, 1, 'mol'], 'H2O': [material.H2O, 1, 'mol'] }) event = ['mix', 10] vessel.push_event_to_queue(feedback=[event], dt=0)
def test__update_material_dict(self): vessel = Vessel("test") material_dict = { 'H2O': [material.H2O, 100, 'mol'], 'C6H14': [material.C6H14, 30, 'mol'] } event = ['update material dict', material_dict] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertIn('H2O', vessel.get_material_dict())
def test__update_solute_dict(self): vessel = Vessel("test", materials={ 'Na': [material.Na, 100, 'mol'], 'H2O': [material.H2O, 1, 'mol'] }) solute_dict = {'Na': {'H2O': [material.H2O, 100, 'mol']}} event = ['update solute dict', solute_dict] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertIn('Na', vessel.get_solute_dict())
def test_save_load(self): vessel = Vessel("test", materials={ 'Na': [material.Na, 1, 'mol'], 'H2O': [material.H2O, 1, 'mol'] }) vessel.save_vessel("test") new_vessel = Vessel("test") new_vessel.load_vessel("test.pickle") os.remove("test.pickle")
def test__pour_by_volume(self): vessel = Vessel("test", materials={"H2O": [material.H2O, 100, 'mol']}) initial_volume = vessel.get_current_volume() event = ['pour by volume', Vessel('test_2'), 0.1] vessel.push_event_to_queue(feedback=[event], dt=0) final_volume = vessel.get_current_volume() self.assertLess(final_volume[1], initial_volume[1])
def test_heat_change(self): vessel = Vessel("test", materials={'H2O': [material.H2O, 100, 'mol']}) new_vessel = Vessel("test_2", materials={'H2O': [material.H2O, 100, 'mol']}) temp = vessel.get_temperature() event = ["change_heat", 10, new_vessel] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertLess(temp, vessel.get_temperature())
def create_new_vessel(self): """ This function simple creates a new vessel and adds it to the shelf Returns ------- None """ index = self.open_slot assert index < self.max_num_vessels self.vessels.append(Vessel(label=index)) self.open_slot += 1
def test__drain_by_pixel_less(self): vessel = Vessel("test", materials={ 'C6H14': [material.C6H14, 1, 'mol'], 'H2O': [material.H2O, 1, 'mol'] }) initial_volume = vessel.get_current_volume() vessel2 = Vessel('test_2') event = ['drain by pixel', vessel2, 10] vessel.push_event_to_queue(events=[event], dt=1) final_volume = vessel.get_current_volume() self.assertLess(final_volume[1], initial_volume[1])
def __init__(self, max_num_vessels: int, vessel_paths: [np.array, list] = None): """ initializes the shelf class Parameters ---------- max_num_vessels: int: the maximum number of vessels that the shelf can store vessel_paths: a list of paths to vessel pickle files that are to be initialized into the shelf """ # the next available slot for inserting a vessel self.open_slot = 0 # maximum number of vessels that can be stored in the shelf self.max_num_vessels = max_num_vessels # a list that keeps all the vessels self.vessels = [] if vessel_paths: for i, path in enumerate(vessel_paths): # function to load vessels from the path self.vessels[i] = Vessel(label=i) self.vessels[i].load_vessel(path) self.open_slot = len(vessel_paths)
def update_vessel(self, new_vessel: vessel.Vessel): """ Method to initialize a vessel and reset the environment to properly populate the vessel. Parameters --------------- `new_vessel` : `vessel.Vessel` The vessel that has been requested to be updated. Returns --------------- None Raises --------------- None """ # set up a new, empty n array intended to contain the vessel materials new_n = np.zeros(self.reaction.nmax.shape[0], dtype=np.float32) # acquire the appropriate material dictionary from the inputted vessel mat_dict = util.convert_material_dict_units( new_vessel.get_material_dict()) # iterate through the material dictionary populating the n array for i, mat in enumerate(self.reaction.materials): if mat in mat_dict: amount = mat_dict[mat][1] new_n[i] = amount # set the inputted vessel as the vessels variable self.vessels = new_vessel # modify the vessel appropriately and reset the reaction environment self.vessels = self.reaction.reset(self.vessels)
def perform_action(self, action, vessels: vessel.Vessel, t, n_steps, step_num): """ Update the environment with processes defined in `action`. Parameters --------------- `action` : `np.array` An array containing elements describing the changes to be made, during the current step, to each modifiable thermodynamic variable and reactant used in the reaction. `vessels` : `vessel.Vessel` A vessel containing initial materials to be used in a series of reactions. `n_steps` : `int` The number of increments into which the action is split. Returns --------------- `vessels` : `vessel.Vessel` A vessel that has been updated to have the proper materials and thermodynamic properties. Raises --------------- None """ self.perform_compatibility_check(action=action, vessels=vessels, n_steps=n_steps, step_num=step_num) # deconstruct the action temperature_change, volume_change, delta_n_array = self.action_deconstruct( action=action) # deconstruct the vessel: acquire the vessel temperature and volume and create the n array temperature, volume = self.vessel_deconstruct(vessels=vessels) current_volume = vessels.get_current_volume()[-1] # perform the complete action over a series of increments if self.solver != 'newton': n_steps = 1 for __ in range(n_steps): # split the overall temperature change into increments, # ensure it does not exceed the maximal and minimal temperature values temperature += temperature_change / n_steps temperature = np.min([ np.max([temperature, vessels.get_Tmin()]), vessels.get_Tmax() ]) # split the overall volume change into increments, # ensure it does not exceed the maximal and minimal volume values volume += volume_change / n_steps volume = np.min([ np.max([volume, vessels.get_min_volume()]), vessels.get_max_volume() ]) # split the overall molar changes into increments for j, delta_n in enumerate(delta_n_array): dn = delta_n / n_steps self.n[j] += dn self.cur_in_hand[j] -= dn # set a penalty for trying to add unavailable material (tentatively set to 0) # reward -= 0 # if the amount that is in hand is below a certain threshold set it to 0 if self.cur_in_hand[j] < self.threshold: self.cur_in_hand[j] = 0.0 # perform the reaction and update the molar concentrations of the reactants and products self.update(self.n / current_volume, temperature, current_volume, t, vessels.get_defaultdt(), n_steps) # create a new reaction vessel containing the final materials vessels = self.update_vessel(temperature, volume) return vessels
def plot_full_render(self, first_render, wave_data_dict, plot_data_state, plot_data_mol, plot_data_concentration, n_steps, vessels: vessel.Vessel, step_num): ''' Method to plot thermodynamic variables and spectral data. Plots a significant amount of data for a more in-depth understanding of the information portrayed. Parameters --------------- `first_render` : `boolean` Indicates whether a plot has been already rendered `wave_data_dict` : `dict` A dictionary containing all the wave data `plot_data_state` : `list` A list containing the states to be plotted such as time, temperature, volume, pressure, and reactant amounts `plot_data_mol` : `list` A list containing the molar amounts of reactants and products `plot_data_concentration` : `list` A list containing the concentration of reactants and products `n_steps` : `int` The number of increments into which the action is split `vessels` : `vessel.Vessel` A vessel containing methods to obtain thermodynamic data `step_num` : `int` The step number the environment is currently on Returns --------------- None Raises --------------- None ''' num_list = [ plot_data_state[0][0], plot_data_state[1][0], plot_data_state[2][0], plot_data_state[3][0] ] + self.get_ni_num() reactants = [] for reactant in self.reactants: name = reactant.replace("[", "").replace("]", "") short_name = name[0:5] reactants.append(short_name) label_list = ['t', 'T', 'V', 'P'] + reactants spectra_len = wave_data_dict["spectra_len"] absorb = wave_data_dict["absorb"] wave = wave_data_dict["wave"] wave_min = wave_data_dict["wave_min"] wave_max = wave_data_dict["wave_max"] peak = CharacterizationBench().get_spectra_peak( vessels, materials=self.materials) dash_spectra = CharacterizationBench().get_dash_line_spectra( vessels, materials=self.materials) # The first render is required to initialize the figure if first_render: plt.close('all') plt.ion() self._plot_fig, self._plot_axs = plt.subplots(3, 3, figsize=(24, 12)) # Time vs. Molar_Amount graph ********************** Index: (0, 0) self._plot_lines_amount = np.zeros((self.n.shape[0], 2, 20)) for i in range(self.n.shape[0]): self._plot_lines_amount[i][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_amount[i][1][step_num - 1:] = (plot_data_mol[i][0]) self._plot_axs[0, 0].plot(self._plot_lines_amount[i][0], self._plot_lines_amount[i][1], label=self.materials[i]) self._plot_axs[0, 0].set_xlim( [0.0, vessels.get_defaultdt() * n_steps]) self._plot_axs[0, 0].set_ylim([0.0, np.mean(plot_data_mol)]) self._plot_axs[0, 0].set_xlabel('Time (s)') self._plot_axs[0, 0].set_ylabel('Molar Amount (mol)') self._plot_axs[0, 0].legend() # Time vs. Molar_Concentration graph *************** Index: (0, 1) self._plot_lines_concentration = np.zeros((self.n.shape[0], 2, 20)) total_conc = 0 for i in range(self.n.shape[0]): total_conc += plot_data_concentration[i][0] for i in range(self.n.shape[0]): self._plot_lines_concentration[i][0][step_num - 1:] = ( plot_data_state[0][0]) self._plot_lines_concentration[i][1][step_num - 1:] = ( plot_data_concentration[i][0]) self._plot_axs[0, 1].plot(self._plot_lines_concentration[i][0], self._plot_lines_concentration[i][1], label=self.materials[i]) self._plot_axs[2, 0].plot( self._plot_lines_concentration[i][0], self._plot_lines_concentration[i][1] / total_conc, label=self.materials[i]) self._plot_axs[0, 1].set_xlim( [0.0, vessels.get_defaultdt() * n_steps]) self._plot_axs[0, 1].set_ylim([0.0, np.mean(plot_data_concentration)]) self._plot_axs[0, 1].set_xlabel('Time (s)') self._plot_axs[0, 1].set_ylabel('Molar Concentration (mol/L)') self._plot_axs[0, 1].legend() # Time vs. Temperature + Time vs. Volume graph *************** Index: (0, 2) self._plot_lines_temp = np.zeros((1, 2, 20)) self._plot_lines_temp[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_temp[0][1][step_num - 1:] = (plot_data_state[1][0]) self._plot_axs[0, 2].plot(self._plot_lines_temp[0][0], self._plot_lines_temp[0][1], label='T') self._plot_lines_vol = np.zeros((1, 2, 20)) self._plot_lines_vol[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_vol[0][1][step_num - 1:] = (plot_data_state[2][0]) self._plot_axs[0, 2].plot(self._plot_lines_vol[0][0], self._plot_lines_vol[0][1], label='V') self._plot_axs[0, 2].set_xlim( [0.0, vessels.get_defaultdt() * n_steps]) self._plot_axs[0, 2].set_ylim([0, 1.2]) self._plot_axs[0, 2].set_xlabel('Time (s)') self._plot_axs[0, 2].set_ylabel('T and V (map to range [0, 1])') self._plot_axs[0, 2].legend() # Time vs. Pressure graph *************** Index: (1, 0) self._plot_lines_pressure = np.zeros((1, 2, 20)) self._plot_lines_pressure[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_pressure[0][1][step_num - 1:] = (plot_data_state[3][0]) self._plot_axs[1, 0].plot(self._plot_lines_pressure[0][0], self._plot_lines_pressure[0][1], label='V') self._plot_axs[1, 0].set_xlim( [0.0, vessels.get_defaultdt() * n_steps]) self._plot_axs[1, 0].set_ylim([0, vessels.get_pmax()]) self._plot_axs[1, 0].set_xlabel('Time (s)') self._plot_axs[1, 0].set_ylabel('Pressure (kPa)') # Solid Spectra graph *************** Index: (1, 1) __ = self._plot_axs[1, 1].plot( wave, # wavelength space (ranging from wave_min to wave_max) absorb # absorb spectra )[0] # dash spectra for spectra in dash_spectra: self._plot_axs[1, 1].plot(wave, spectra, linestyle='dashed') # include the labelling when plotting spectral peaks for i in range(len(peak)): self._plot_axs[1, 1].scatter(peak[i][0], peak[i][1], label=peak[i][2]) self._plot_axs[1, 1].set_xlim([wave_min, wave_max]) self._plot_axs[1, 1].set_ylim([0, 1.2]) self._plot_axs[1, 1].set_xlabel('Wavelength (nm)') self._plot_axs[1, 1].set_ylabel('Absorbance') self._plot_axs[1, 1].legend() # bar chart plotting the time, tempurature, pressure, and amount of each # species left in hand *************** Index: (1, 2) __ = self._plot_axs[1, 2].bar(range(len(num_list)), num_list, tick_label=label_list)[0] self._plot_axs[1, 2].set_ylim([0, 1]) self._plot_axs[2, 0].set_xlim( [0.0, vessels.get_defaultdt() * n_steps]) self._plot_axs[2, 0].set_ylim([0.0, 1.0]) self._plot_axs[2, 0].set_xlabel('Time (s)') self._plot_axs[2, 0].set_ylabel('Purity') self._plot_axs[2, 0].legend() # draw and show the full graph self._plot_fig.canvas.draw() plt.show() # All other renders serve to update the existing figure with the new current state; # it is assumed that the above actions have already been carried out else: # set data for the Time vs. Molar_Amount graph *************** Index: (0, 0) curent_time = plot_data_state[0][-1] # update the lines data total_conc = 0 for i in range(self.n.shape[0]): total_conc += plot_data_concentration[i][0] for i in range(self.n.shape[0]): # populate the lines amount array with the updated number of mols self._plot_lines_amount[i][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_amount[i][1][step_num - 1:] = (plot_data_mol[i][0]) self._plot_axs[0, 0].lines[i].set_xdata( self._plot_lines_amount[i][0]) self._plot_axs[0, 0].lines[i].set_ydata( self._plot_lines_amount[i][1]) # populate the lines concentration array with the updated number of concentration self._plot_lines_concentration[i][0][step_num - 1:] = ( plot_data_state[0][0]) self._plot_lines_concentration[i][1][step_num - 1:] = ( plot_data_concentration[i][0]) self._plot_axs[0, 1].lines[i].set_xdata( self._plot_lines_concentration[i][0]) self._plot_axs[0, 1].lines[i].set_ydata( self._plot_lines_concentration[i][1]) self._plot_axs[2, 0].lines[i].set_xdata( self._plot_lines_concentration[i][0]) self._plot_axs[2, 0].lines[i].set_ydata( self._plot_lines_concentration[i][1] / total_conc) # reset each plot's x-limit because the latest action occurred at a greater time-value self._plot_axs[0, 0].set_xlim([0.0, curent_time]) self._plot_axs[0, 1].set_xlim([0.0, curent_time]) # reset each plot's y-limit in order to fit and show all materials on the graph self._plot_axs[0, 0].set_ylim([0.0, np.mean(plot_data_mol)]) self._plot_axs[0, 1].set_ylim([0.0, np.mean(plot_data_concentration)]) # set data for Time vs. Temp and Time vs. Volume graphs *************** Index: (0, 2) self._plot_lines_temp[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_temp[0][1][step_num - 1:] = (plot_data_state[1][0]) self._plot_lines_vol[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_vol[0][1][step_num - 1:] = (plot_data_state[2][0]) self._plot_axs[0, 2].lines[0].set_xdata(self._plot_lines_temp[0][0]) self._plot_axs[0, 2].lines[0].set_ydata(self._plot_lines_temp[0][1]) self._plot_axs[0, 2].lines[1].set_xdata(self._plot_lines_vol[0][0]) self._plot_axs[0, 2].lines[1].set_ydata(self._plot_lines_vol[0][1]) # reset xlimit since t extend self._plot_axs[0, 2].set_xlim([0.0, curent_time]) # set data for Time vs. Pressure graph *************** Index: (1, 0) self._plot_lines_pressure[0][0][step_num - 1:] = (plot_data_state[0][0]) self._plot_lines_pressure[0][1][step_num - 1:] = (plot_data_state[3][0]) self._plot_axs[1, 0].lines[0].set_xdata( self._plot_lines_pressure[0][0]) self._plot_axs[1, 0].lines[0].set_ydata( self._plot_lines_pressure[0][1]) self._plot_axs[1, 0].set_xlim([0.0, curent_time ]) # reset xlime since t extend self._plot_axs[1, 0].set_ylim([0.0, np.max(plot_data_state[3]) * 1.1]) # reset the Solid Spectra graph self._plot_axs[1, 1].cla() __ = self._plot_axs[1, 1].plot( wave, # wavelength space (ranging from wave_min to wave_max) absorb, # absorb spectra )[0] # obtain the new dash spectra data for __, item in enumerate(dash_spectra): self._plot_axs[1, 1].plot(wave, item, linestyle='dashed') # reset the spectra peak labelling for i in range(len(peak)): self._plot_axs[1, 1].scatter(peak[i][0], peak[i][1], label=peak[i][2]) self._plot_axs[1, 1].set_xlim([wave_min, wave_max]) self._plot_axs[1, 1].set_ylim([0, 1.2]) self._plot_axs[1, 1].set_xlabel('Wavelength (nm)') self._plot_axs[1, 1].set_ylabel('Absorbance') self._plot_axs[1, 1].legend() # bar chart self._plot_axs[1, 2].cla() __ = self._plot_axs[1, 2].bar( range(len(num_list)), num_list, tick_label=label_list, )[0] self._plot_axs[1, 2].set_ylim([0, 1]) # re-draw the graph self._plot_fig.canvas.draw() plt.pause(0.000001)
def test_return_vessel_to_shelf(self): test_vessel = Vessel('test') shelf = Shelf(100) shelf.return_vessel_to_shelf(test_vessel) self.assertEqual(shelf.open_slot, 1) self.assertEqual(len(shelf.vessels), 1)
def test_analyze(self): char_bench = CharacterizationBench() vessel = Vessel("test", materials={'Na': [material.Na, 1, 'mol']}) analysis = char_bench.analyze(vessel, 'spectra') self.assertEqual(len(analysis[0]), 200)
def test__update_temperature(self): vessel = Vessel("test") initial_temp = vessel.get_temperature() event = ['temperature change', 400, True] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertEqual(vessel.get_temperature(), 400)
def test_wait(self): vessel = Vessel("test", materials={'H2O': [material.H2O, 100, 'mol']}) new_vessel = Vessel("test_2", materials={'H2O': [material.H2O, 100, 'mol']}) temp = vessel.get_temperature() event = ["change_heat", 10, new_vessel] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertLess(temp, vessel.get_temperature()) event = ["wait", temp, new_vessel, True] vessel.push_event_to_queue(feedback=[event], dt=0) self.assertEqual(temp, vessel.get_temperature()) event = ["wait", temp + 30, new_vessel, False] vessel.push_event_to_queue(feedback=[event], dt=10) self.assertLess(temp, vessel.get_temperature())
def plotting_step(self, t, tmax, vessels: vessel.Vessel): ''' Set up a method to handle the acquisition of the necessary plotting parameters from an input vessel. Parameters --------------- `t` : `float` Current amount of time `tmax` : `float` Maximum time allowed `vessels` : `vessel.Vessel` A vessel containing methods to acquire the necessary parameters Returns --------------- `plot_data_state` : `list` A list containing the states to be plotted such as time, temperature, volume, pressure, and reactant amounts `plot_data_mol` : `list` A list containing the molar amounts of reactants and products `plot_data_concentration` : `list` A list containing the concentration of reactants and products Raises --------------- None ''' # acquire the necessary parameters from the vessel T = vessels.get_temperature() V = vessels.get_volume() # V = 0.0025 # create containers to hold the plotting parameters plot_data_state = [[], [], [], []] plot_data_mol = [[] for _ in range(self.n.shape[0])] plot_data_concentration = [[] for _ in range(self.n.shape[0])] # Record time data plot_data_state[0].append(t / tmax) # record temperature data Tmin = vessels.get_Tmin() Tmax = vessels.get_Tmax() plot_data_state[1].append((T - Tmin) / (Tmax - Tmin)) # record volume data Vmin = vessels.get_min_volume() Vmax = vessels.get_max_volume() plot_data_state[2].append((V - Vmin) / (Vmax - Vmin)) # record pressure data P = vessels.get_pressure() plot_data_state[3].append(P / vessels.get_pmax()) # calculate and record the molar concentrations of the reactants and products C = vessels.get_concentration(materials=self.materials) for j in range(self.n.shape[0]): plot_data_mol[j].append(self.n[j]) plot_data_concentration[j].append(C[j]) return plot_data_state, plot_data_mol, plot_data_concentration