class TestGetterSetter(unittest.TestCase): database = '_data_for_unittest' def setUp(self): _stack = { 'CoAg': { 'elements': ['Co', 'Ag'], 'stoichiometric_ratio': [1, 2], 'thickness': { 'value': 0.025, 'units': 'mm' }, 'density': { 'value': np.NaN, 'units': 'g/cm3' }, }, 'U': { 'elements': ['U'], 'stoichiometric_ratio': [1], 'thickness': { 'value': 0.03, 'units': 'mm' }, 'density': { 'value': np.NaN, 'units': 'g/cm3' }, }, } self.o_reso = Resonance(stack=_stack, database=self.database) # stoichiometric ratio def test_retrieve_stoichiometric_of_uo3_sample(self): """assert retrieve stoichiometric work for complex sample such as UO3""" o_reso = self.o_reso o_reso.add_layer(formula='UO3', thickness=0.25, density=0.5) o_reso.add_layer(formula='AgCo', thickness=0.5, density=0.8) _stoichiometric_ratio = o_reso.get_isotopic_ratio(compound='UO3', element='U') self.assertEqual(_stoichiometric_ratio['238-U'], 0.992745) def test_retrieve_stoichiometric_ratio_raises_error_if_unknown_compound( self): """assert ValueError raised if wrong compound when getting stoichiometric ratio""" self.assertRaises(ValueError, self.o_reso.get_isotopic_ratio, compound='unknown') def test_if_element_misssing_use_compound_and_raises_error_if_wrong(self): """assert ValueError raised if element does not exist""" self.assertRaises(ValueError, self.o_reso.get_isotopic_ratio, compound='CoAg') def test_stoichiometric_ratio_returned(self): """assert the stoichiometric ratio are correctly returned""" _stoichiometric_ratio = self.o_reso.get_isotopic_ratio(compound='U') _expected_dict = { '233-U': 0.0, '234-U': 5.5e-5, '235-U': 0.0072, '238-U': 0.992745 } self.assertEqual(_expected_dict['233-U'], _stoichiometric_ratio['233-U']) self.assertEqual(_expected_dict['235-U'], _stoichiometric_ratio['235-U']) self.assertEqual(_expected_dict['238-U'], _stoichiometric_ratio['238-U']) def test_list_all_stoichiometric_ratio(self): """assert the entire stoichiometric list is returned""" _stoichiometric_ratio = self.o_reso.get_isotopic_ratio() _expected_dict = { 'U': { 'U': { '233-U': 0.0, '234-U': 5.5e-5, '235-U': 0.0072, '238-U': 0.992745 }, }, 'CoAg': { 'Ag': { '107-Ag': 0.51839, '109-Ag': 0.4816 }, 'Co': { '58-Co': 0.0, '59-Co': 1.0 }, }, } self.assertEqual(_expected_dict['U']['U']['233-U'], _stoichiometric_ratio['U']['U']['233-U']) self.assertEqual(_expected_dict['U']['U']['238-U'], _stoichiometric_ratio['U']['U']['238-U']) self.assertEqual(_expected_dict['CoAg']['Ag']['107-Ag'], _stoichiometric_ratio['CoAg']['Ag']['107-Ag']) def test_set_stoichiometric_ratio_raises_value_error_if_wrong_compound( self): """assert ValueError raised if wrong compound in stoichiometric ratio setter""" self.assertRaises(ValueError, self.o_reso.set_isotopic_ratio) self.assertRaises(ValueError, self.o_reso.set_isotopic_ratio, compound='unknown') def test_set_stoichiometric_ratio_raises_value_error_if_wrong_element( self): """assert ValueError raised if wrong element in stoichiometric ratio setter""" self.assertRaises(ValueError, self.o_reso.set_isotopic_ratio, compound='CoAg') self.assertRaises(ValueError, self.o_reso.set_isotopic_ratio, compound='CoAg', element='unknown') def test_set_stoichiometric_ratio_has_correct_new_list_size(self): """assert ValueRror raised if new list of ratio does not match old list size""" self.assertRaises(ValueError, self.o_reso.set_isotopic_ratio, compound='U', element='U', list_ratio=[0, 1, 2]) def test_set_stoichiometric_ratio_correctly_calculates_new_molar_mass( self): """assert molar mass is correctly calculated for new set of stoichiometric coefficient""" self.o_reso.set_isotopic_ratio(compound='CoAg', element='Co', list_ratio=[0.5, 0.5]) new_molar_mass = self.o_reso.stack['CoAg']['Co']['molar_mass']['value'] list_isotopes_mass = self.o_reso.stack['CoAg']['Co']['isotopes'][ 'mass']['value'] list_isotopes_ratio = self.o_reso.stack['CoAg']['Co']['isotopes'][ 'isotopic_ratio'] mass_ratio = zip(list_isotopes_mass, list_isotopes_ratio) expected_molar_mass = np.array([_m * _r for _m, _r in mass_ratio]).sum() self.assertAlmostEqual(new_molar_mass, expected_molar_mass, delta=0.0001) def test_set_stoichiometric_ratio_correctly_calculates_new_density(self): """assert density is correctly calculated for new set of stoichiometric coefficient""" self.o_reso.set_isotopic_ratio(compound='CoAg', element='Co', list_ratio=[0.5, 0.5]) new_density = self.o_reso.stack['CoAg']['Co']['density']['value'] list_density = self.o_reso.stack['CoAg']['Co']['isotopes']['density'][ 'value'] list_isotopes_ratio = self.o_reso.stack['CoAg']['Co']['isotopes'][ 'isotopic_ratio'] density_ratio = zip(list_density, list_isotopes_ratio) expected_density = np.array([_d * _r for _d, _r in density_ratio]).sum() self.assertAlmostEqual(new_density, expected_density, delta=0.0001) # density def test_retrieve_density_raises_error_if_unknown_compound(self): """assert ValueError raised if wrong compound when getting density""" self.assertRaises(ValueError, self.o_reso.get_density, compound='unknown') def test_if_element_misssing_use_compound_and_raises_error_if_wrong_when_getting_density( self): """assert ValueError raised if element does not exist when getting density""" self.assertRaises(ValueError, self.o_reso.get_density, compound='CoAg') def test_density_returned(self): """assert the density are correctly returned""" _density = self.o_reso.get_density(compound='U') _expected_density = 18.95 self.assertEqual(_density, _expected_density) def test_density_returned_all(self): """assert the density of all element works""" _density_list = self.o_reso.get_density() _expected_density = { 'CoAg': { 'Co': 8.9, 'Ag': 10.5, }, 'U': { 'U': 18.95, }, } self.assertEqual(_expected_density, _density_list)
class Simulation(object): # Input sample name or names as str, case sensitive def __init__(self, energy_min=1e-5, energy_max=1000, energy_step=0.01, database='ENDF_VIII'): """ initialize the a Simulation() using the Resonance() in ImagingReso :param energy_min: :type energy_min: :param energy_max: :type energy_max: :param energy_step: :type energy_step: :param database: :type database: """ self.energy_min = energy_min self.energy_max = energy_max self.energy_step = energy_step self.database = database self.o_reso = Resonance(energy_min=energy_min, energy_max=energy_max, energy_step=energy_step, database=database) self.neutron_pulse = None self.layer_list = [] self.layer = fit_util.Layer() self.x_tof_us = None self.y_att = None def add_layer(self, layer: str, thickness_mm: float, density_gcm3=np.NaN): """ :param layer: :type layer: :param thickness_mm: :type thickness_mm: :param density_gcm3: :type density_gcm3: :return: :rtype: """ self.o_reso.add_layer(formula=layer, thickness=thickness_mm, density=density_gcm3) self.layer_list.append(layer) self.layer.add_layer(layer=layer, thickness_mm=thickness_mm, density_gcm3=density_gcm3) def add_Layer(self, layer: Layer): """ Add layer using Layer class :param layer: """ for _each_layer in list(layer.info.keys()): self.add_layer(layer=_each_layer, thickness_mm=layer.info[_each_layer]['thickness']['value'], density_gcm3=layer.info[_each_layer]['density']['value']) def set_isotopic_ratio(self, layer, element, new_isotopic_ratio_list): """ Set isotopic ratios for picked element and update x y values to pass :param layer: :param element: :param new_isotopic_ratio_list: :return: x in eV y in attenuation """ if type(new_isotopic_ratio_list) is not list: raise ValueError("{} is not a list".format(new_isotopic_ratio_list)) # Check if layer exist if layer not in self.layer_list: raise ValueError('Layer {} does not exist.'.format(layer)) # Check if element exist _formula = re.findall(r'([A-Z][a-z]*)(\d*)', layer) _elements = [] for _element in _formula: _single_element = list(_element)[0] _elements.append(_single_element) if element not in _elements: raise ValueError('Element {} specified does not exist in {} layer.'.format(element, layer)) self.o_reso.set_isotopic_ratio(compound=layer, element=element, list_ratio=new_isotopic_ratio_list) # self.x_simu = np.array(self.o_reso.total_signal['energy_eV']).round(5) # self.y_simu = np.array(self.o_reso.total_signal['attenuation']) def get_x(self, x_type, offset_us=None, source_to_detector_m=None, t_unit='us', t_start_us=None, time_resolution_us=None, num_offset=0): """ Get x by specified type :param x_type: :type x_type: :param offset_us: :type offset_us: :param source_to_detector_m: :type source_to_detector_m: :return: x in specified type :rtype: np.array """ fit_util.check_if_in_list(x_type, fit_util.x_type_list) _x = np.array(self.o_reso.total_signal['energy_eV']).round(5) x = fit_util.convert_energy_to(x=_x, x_type=x_type, offset_us=offset_us, source_to_detector_m=source_to_detector_m, t_unit=t_unit, t_start_us=t_start_us, time_resolution_us=time_resolution_us, num_offset=num_offset) return x def get_y(self, y_type): """ Get x by specified type :param y_type: :type y_type: :return: y in specified type :rtype: np.array """ fit_util.check_if_in_list(y_type, fit_util.y_type_list) y = self.o_reso.total_signal[y_type] return y def _convolve_beam_shapes(self, source_to_detector_m, conv_proton, proton_params={}, model_index=1): _file_path = os.path.abspath(os.path.dirname(__file__)) _rel_path_to_neutron1 = 'data/_data_for_tutorial/neutron_pulse/source_section_1.dat' _rel_path_to_neutron2 = 'data/_data_for_tutorial/neutron_pulse/source_section_2.dat' path1 = os.path.join(_file_path, _rel_path_to_neutron1) path2 = os.path.join(_file_path, _rel_path_to_neutron2) self.neutron_pulse = NeutronPulse(path1, model_index=model_index) self.neutron_pulse.load_shape_each(path2) self.neutron_pulse.fit_shape(e_min=1, e_max=500, drop=False, norm=True, check_each=False, save_fig=False, overwrite_csv=False) self.neutron_pulse.fit_params(check_each=False, loglog_fit=True, overwrite_csv=False) self.neutron_pulse.make_shape(e_ev=self.get_x(x_type='energy'), t_interp=None, for_sum=True, norm=False, source_to_detector_m=source_to_detector_m, conv_proton=conv_proton, proton_params=proton_params, overwrite_csv=False) tof_beam_shape_df = self.neutron_pulse.shape_tof_df_interp.set_index('tof_us') tof_trans_df = tof_beam_shape_df * self.o_reso.total_signal['transmission'] tof_beam_shape_df['sum'] = tof_beam_shape_df.sum(axis=1) tof_trans_df['sum'] = tof_trans_df.sum(axis=1) # print(tof_beam_shape_df) # print(tof_trans_df) self.x_tof_us = np.array(tof_beam_shape_df.index) self.y_att = 1 - np.array(tof_trans_df['sum'] / tof_beam_shape_df['sum']) def peak_map(self, x_type, y_type, thres=0.15, min_dist=20, impr_reso=True, offset_us=None, source_to_detector_m=None, t_unit='us', t_start_us=None, time_resolution_us=None, num_offset=0, ): """ Get peak map for each element and/or isotope :param thres: :type thres: :param min_dist: :type min_dist: :param impr_reso: :type impr_reso: :return: :rtype: """ if len(self.layer_list) == 0: raise ValueError("No layer has been added.") _stack_signal = self.o_reso.stack_signal _layer_list = self.layer_list # _x_energy = _stack_signal[_layer_list[0]][_layer_list[0]]['energy_eV'] _x = self.get_x(x_type=x_type, offset_us=offset_us, source_to_detector_m=source_to_detector_m, t_unit=t_unit, t_start_us=t_start_us, time_resolution_us=time_resolution_us, num_offset=num_offset ) # _x = sorted(_x) peak_map = {} for _ele in _layer_list: # Isotope for _iso in self.o_reso.stack[_ele][_ele]['isotopes']['list']: peak_map[_iso] = {} _iso_y = _stack_signal[_ele][_ele][_iso]['attenuation'] _peak_df = fit_util.find_peak(x=_x, y=_iso_y, x_name='x', thres=thres, min_dist=min_dist, imprv_reso=impr_reso) if y_type == 'transmission': _peak_df['y'] = 1 - _peak_df['y'] peak_map[_iso]['ideal'] = _peak_df # Element peak_map[_ele] = {} _ele_y = _stack_signal[_ele][_ele]['attenuation'] _peak_df = fit_util.find_peak(x=_x, y=_ele_y, x_name='x', thres=thres, min_dist=min_dist, imprv_reso=impr_reso) if y_type == 'transmission': _peak_df['y'] = 1 - _peak_df['y'] peak_map[_ele]['ideal'] = _peak_df peak_map_dict = {'peak_map': peak_map, 'x_type': x_type, 'y_type': y_type} return peak_map_dict def plot(self, y_type='attenuation', x_type='energy', logx=False, logy=False, mixed=True, all_layers=False, all_elements=False, all_isotopes=False, items_to_plot=None, time_unit='us', offset_us=0., source_to_detector_m=16., t_start_us=1, time_resolution_us=0.16, ax_mpl=None, fmt='-', ms=2, lw=1.5, alpha=1): if len(self.layer_list) == 0: raise ValueError("No layer has been added.") if items_to_plot is not None: # shape format of items items = fit_util.Items(o_reso=self.o_reso, database=self.database) items_to_plot = items.shaped(items_list=items_to_plot) ax = self.o_reso.plot(y_axis=y_type, x_axis=x_type, mixed=mixed, all_layers=all_layers, all_elements=all_elements, all_isotopes=all_isotopes, items_to_plot=items_to_plot, source_to_detector_m=source_to_detector_m, offset_us=offset_us, time_resolution_us=time_resolution_us, time_unit=time_unit, t_start_us=t_start_us, ax_mpl=ax_mpl, logx=logx, logy=logy, # plotly=plotly fmt=fmt, ms=ms, lw=lw, alpha=alpha) return ax def export(self, output_type='clip', filename=None, x_type='energy', y_type='attenuation', all_layers=False, all_elements=False, all_isotopes=False, items_to_export=None, offset_us=0., source_to_detector_m=16., t_start_us=1, time_resolution_us=0.16, time_unit='us'): if items_to_export is not None: # Shape items items = fit_util.Items(o_reso=self.o_reso, database=self.database) items_to_export = items.shaped(items_list=items_to_export) _df = self.o_reso.export(output_type=output_type, filename=filename, x_axis=x_type, y_axis=y_type, all_layers=all_layers, all_elements=all_elements, all_isotopes=all_isotopes, items_to_export=items_to_export, offset_us=offset_us, source_to_detector_m=source_to_detector_m, t_start_us=t_start_us, time_resolution_us=time_resolution_us, time_unit=time_unit) return _df