def test_initialization(self): """Tests the initialization of a DepthProfile object""" dp = DepthProfile([1], [2]) self.assertEqual((1, ), dp.depths) self.assertEqual((2, ), dp.concentrations) self.assertIsNone(dp.events) self.assertEqual("total", dp.get_profile_name()) # Currently the order of depth counts, or validity of # numerical values is not checked so following is ok. dp = DepthProfile([2, 1], [True, "Foo"]) self.assertEqual((2, 1), dp.depths) self.assertEqual((True, "Foo"), dp.concentrations) dp = DepthProfile([1], [2], [3], element=Element.from_string("Si")) self.assertEqual((1, ), dp.depths) self.assertEqual((2, ), dp.concentrations) self.assertEqual((3, ), dp.events) self.assertEqual("Si", dp.get_profile_name()) self.assertRaises(ValueError, lambda: DepthProfile([], [], [])) self.assertRaises(ValueError, lambda: DepthProfile([], [1])) self.assertRaises( ValueError, lambda: DepthProfile([1], [], [1], element=Element.from_string("Si"))) self.assertRaises( ValueError, lambda: DepthProfile([1], [1], [], element=Element.from_string("Si")))
def setUpClass(cls): cls.directory = Path(tempfile.gettempdir()) target = Target(layers=[ Layer("layer1", [ Element.from_string("Li 1.0") ], 0.01, 0.01, start_depth=0.0), Layer("layer2", [ Element.from_string("Li 0.048"), Element.from_string("O 0.649"), Element.from_string("Mn 0.303") ], 90.0, 4.0, start_depth=0.01), Layer("subtrate", [ Element.from_string("Si 1.0") ], 1000.0, 2.32, start_depth=90.01) ]) cls.mcerd = MCERD(101, { "recoil_element": mo.get_recoil_element(), "sim_dir": tempfile.gettempdir(), "simulation_type": SimulationType.ERD, "target": target, "detector": mo.get_detector(), "beam": mo.get_beam(), # Following simulation parameters have been determined by the # rigorous application of the Stetson-Harrison method. "minimum_scattering_angle": 5.5, "minimum_main_scattering_angle": 6.5, "minimum_energy_of_ions": 8.15, "number_of_recoils": 15, "simulation_mode": SimulationMode.NARROW, "number_of_scaling_ions": 14, "number_of_ions_in_presimu": 100, "number_of_ions": 1000 }, mo.get_element_simulation().get_full_name())
def test_get_full_name(self): self.assertEqual("He-bar", self.rec_elem.get_full_name()) rec_elem = RecoilElement(Element.from_string("16O"), [], name=None) self.assertEqual("16O-Default", rec_elem.get_full_name()) rec_elem = RecoilElement(Element.from_string("16O"), [], name="") self.assertEqual("16O-Default", rec_elem.get_full_name())
def test_subtraction(self): dp1 = DepthProfile([0, 1], [2, 3], [1, 2], Element.from_string("Si")) dp2 = DepthProfile([0, 1], [3, 4], [1, 2], Element.from_string("Si")) dp3 = dp2 - dp1 self.assertEqual((1, 1), dp3.concentrations) self.assertEqual((0, 1), dp3.depths) self.assertIsNone(dp3.events) self.assertIsNone(dp3.element) dp3 -= dp3 self.assertEqual((0, 0), dp3.concentrations)
def set_element(self, element: Element): """Sets currently selected element. """ if not isinstance(element, Element): try: element = Element.from_string(element) except (ValueError, AttributeError): self._isotope_input.clear() self.validate() self.selection_changed.emit() return show_st_mass_in_combobox = self._st_mass_input is None self._symbol_input.setText(element.symbol) gutils.load_isotopes(element.symbol, self._isotope_input, show_std_mass=show_st_mass_in_combobox, current_isotope=element.isotope) if self._amount_input is not None and element.amount: self._amount_input.setValue(element.amount) self.validate() self.selection_changed.emit()
def __sortt(self, key): cut_file = key.split('.') # TODO use RBS selection to sort (for example m1.35Cl.RBS_Mn.0.cut # should be sorted by Mn, not Cl) # TODO provide elements as parameter instead of reinitializing them # over and over again return Element.from_string(cut_file[0].strip())
def __sortt(key): cut_file = key.split('.') # TODO sort by RBS selection # TODO provide elements as parameters, do not initialize them here. # Better yet, use CutFile objects here. # TODO is measurement removed from the cut file at this point? If # not, this sorts by measurement instead of element return Element.from_string(cut_file[0].strip())
def test_equals_prop_based(self): n = 1000 for _ in range(n): elem1 = mo.get_element(randomize=True) elem1_str = str(elem1) elem2 = Element.from_string(elem1_str) self.assertIsNot(elem1, elem2) self.assertEqual(elem1, elem2) self.assertEqual(hash(elem1), hash(elem2))
def test_hash(self): e = Element.from_string("H") hashed_elems = { e, e, Element.from_string("H"), Element.from_string("1H"), Element.from_string("1H 4.0"), Element.from_string("1H 4.0"), Element.from_string("1H 5.0") } self.assertEqual( { Element.from_string("H"), Element.from_string("1H"), Element.from_string("1H 4.0"), Element.from_string("1H 5.0") }, hashed_elems)
def test_merging(self): dp1 = DepthProfile([0, 1, 2, 3, 4], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], element=Element.from_string("Si")) dp2 = DepthProfile([0, 1, 2, 3, 4], [2, 2, 2, 2, 2], [2, 2, 2, 2, 2], element=Element.from_string("Si")) dp3 = dp1.merge(dp2, 1, 3) self.assertEqual(dp1.depths, dp3.depths) self.assertEqual((1, 2, 2, 2, 1), dp3.concentrations) self.assertEqual((1, 1, 1, 1, 1), dp3.events) self.assertEqual("Si", dp3.get_profile_name()) # Original depth profiles remain unchanged self.assertEqual(dp1.depths, (0, 1, 2, 3, 4)) self.assertEqual(dp2.depths, (0, 1, 2, 3, 4)) self.assertEqual(dp1.concentrations, (1, 1, 1, 1, 1)) self.assertEqual(dp2.concentrations, (2, 2, 2, 2, 2)) self.assertEqual(dp1.events, (1, 1, 1, 1, 1)) self.assertEqual(dp2.events, (2, 2, 2, 2, 2)) self.assertEqual(dp1.element, Element.from_string("Si")) self.assertEqual(dp2.element, Element.from_string("Si")) # Testing merging at different depths dp3 = dp1.merge(dp2, 1, 3.5) self.assertEqual((1, 2, 2, 2, 1), dp3.concentrations) dp3 = dp1.merge(dp2, 0.5, 4) self.assertEqual((1, 2, 2, 2, 2), dp3.concentrations) dp3 = dp1.merge(dp2, -1, 6) self.assertEqual((2, 2, 2, 2, 2), dp3.concentrations) dp3 = dp2.merge(dp1, 4, 6) self.assertEqual((2, 2, 2, 2, 1), dp3.concentrations) dp3 = dp2.merge(dp1, 3, 2) self.assertEqual((2, 2, 2, 2, 2), dp3.concentrations)
def test_adding(self): dp1 = DepthProfile([0, 1, 2], [10, 11, 12], [21, 22, 23], element=Element.from_string("Si")) dp2 = DepthProfile([0, 1, 2], [15, 16, 17], [31, 32, 33], element=Element.from_string("C")) dp3 = dp1 + dp2 self.assertIsInstance(dp3, DepthProfile) self.assertEqual(dp3.depths, (0, 1, 2)) self.assertEqual(dp3.concentrations, (25, 27, 29)) self.assertIsNone(dp3.events) self.assertEqual(dp3.get_profile_name(), "total") # DepthProfile can be incremented by another DepthProfile dp3 += dp3 self.assertEqual(dp3.depths, (0, 1, 2)) self.assertEqual(dp3.concentrations, (50, 54, 58)) self.assertIsNone(dp3.events) self.assertEqual(dp3.get_profile_name(), "total") # Arbitrary collection of tuples cannot be added self.assertRaises( TypeError, lambda: dp3 + ((0, 1, 2), (10, 11, 12), (21, 22, 23)))
def test_match_elements_to_strs(self): strs = ["Si", "10C", "C", "12"] elements = [ Element.from_string("Si"), Element.from_string("10C"), Element.from_string("12C"), Element.from_string("Br") ] matches = comp.match_elements_to_strs(elements, strs) self.assertEqual(next(matches), (Element.from_string("Si"), "Si")) self.assertEqual(next(matches), (Element.from_string("10C"), "10C")) self.assertEqual(next(matches), (Element.from_string("12C"), "C")) self.assertEqual(next(matches), (Element.from_string("Br"), None)) # Matches has been exhausted and this will produce an # exception self.assertRaises(StopIteration, lambda: next(matches))
def setUpClass(cls): # default recoil element cls.elem_4he = RecoilElement(Element.from_string("4He"), [], "red") # Valid file names for the recoil element cls.valid_erd_files = ["4He-Default.101.erd", "4He-Default.102.erd"] # Invalid file names for the recoil element cls.invalid_erd_files = [ "4He-Default.101", ".4He-Default.102.erd", ".4He-Default..erd", "4He-Default.101.erf", "4He-Default./.103.erd", "4He-Default.\\.104.erd", "3He-Default.102.erd" ] cls.expected_values = [(Path(f), s + 101) for s, f in enumerate(cls.valid_erd_files)]
def load_properties_from_file(self, file_path: Path): # TODO create a base class for settings widgets to get rid of this # copy-paste code def err_func(err: Exception): if self.preset_widget is not None: self.preset_widget.set_status_msg( f"Failed to load preset: {err}") values = self._load_json_file(file_path, error_func=err_func) if not values: return try: values["beam_ion"] = Element.from_string(values["beam_ion"]) self.set_properties(**values) except KeyError as e: err_func(f"file contained no {e} key.") except ValueError as e: err_func(e)
def test_match_strs_to_elements(self): strs = ["Si", "10C", "C", "12"] elements = [ Element.from_string("Si"), Element.from_string("10C"), Element.from_string("12C") ] matches = comp.match_strs_to_elements(strs, elements) self.assertEqual(("Si", Element.from_string("Si")), next(matches)) self.assertEqual(("10C", Element.from_string("10C")), next(matches)) self.assertEqual(("C", Element.from_string("12C")), next(matches)) self.assertEqual(("12", None), next(matches))
def test_serialization(self): with tempfile.TemporaryDirectory() as tmp_dir: file_path = fp.get_recoil_file_path(self.rec_elem, tmp_dir) self.rec_elem.to_file(tmp_dir) rec_elem2 = RecoilElement.from_file(file_path, channel_width=self.ch_width, rec_type=self.rec_type) self.compare_rec_elems(self.rec_elem, rec_elem2) # Test with an empty list of points and no rec_type or ch_width rec_elem3 = RecoilElement(Element.from_string("O"), []) file_path = fp.get_recoil_file_path(rec_elem3, tmp_dir) rec_elem3.to_file(tmp_dir) rec_elem4 = RecoilElement.from_file(file_path) self.compare_rec_elems(rec_elem3, rec_elem4) self.assertFalse(os.path.exists(tmp_dir))
def test_get_erd_file_path(self): rec_elem = RecoilElement(Element.from_string("He"), [], "red") self.assertEqual("He-Default.101.erd", fp.get_erd_file_name(rec_elem, 101)) self.assertEqual("He-Default.102.erd", fp.get_erd_file_name(rec_elem, 102)) self.assertEqual( "He-opt.101.erd", fp.get_erd_file_name( rec_elem, 101, optim_mode=OptimizationType.RECOIL)) self.assertEqual( "He-optfl.101.erd", fp.get_erd_file_name( rec_elem, 101, optim_mode=OptimizationType.FLUENCE)) self.assertRaises(ValueError, lambda: fp.get_erd_file_name(rec_elem, 101, optim_mode="foo"))
def test_iteration(self): """Tests that a DepthProfile object can be iterated over.""" # Testing element profile dp = DepthProfile([i for i in range(10)], [i for i in range(10, 20)], [i for i in range(20, 30)], element=Element.from_string("Si")) x1 = (i for i in range(10)) x2 = (i for i in range(10, 20)) x3 = (i for i in range(20, 30)) for val in dp: self.assertEqual((next(x1), next(x2), next(x3)), val) self.assertEqual(10, len(list(dp))) # Testing total profile dp = DepthProfile([i for i in range(10)], [i for i in range(10, 20)]) x1 = (i for i in range(10)) x2 = (i for i in range(10, 20)) for val in dp: self.assertEqual((next(x1), next(x2), 0), val) self.assertEqual(10, len(list(dp))) # profile can be iterated over multiple times for i in range(3): x1 = (i for i in range(10)) x2 = (i for i in range(10, 20)) for val in dp: self.assertEqual((next(x1), next(x2), 0), val) # iteration restarts after break for val in dp: self.assertEqual((0, 10, 0), val) break for val in dp: self.assertEqual((0, 10, 0), val) break
def test_ion_binding(self): # This is the default beam ion elem = Element("Cl", 35) self.assertEqual(elem, self.mesu_widget.beam_ion) self.assertFalse(self.mesu_widget.are_values_changed()) # Change the ion and assert that values are changed and correct # values are shown in inputs elem = Element.from_string("13C") self.mesu_widget.beam_ion = elem self.assertTrue(self.mesu_widget.are_values_changed()) self.assertIsNot(elem, self.mesu_widget.beam_ion) self.assertEqual(elem, self.mesu_widget.beam_ion) self.assertEqual("C", self.mesu_widget.beamIonButton.text()) self.assertEqual( 13, self.mesu_widget.isotopeComboBox.currentData()["element"].isotope) # Set ion to None self.mesu_widget.beam_ion = None self.assertIsNone(self.mesu_widget.beam_ion) self.assertEqual("Select", self.mesu_widget.beamIonButton.text()) self.assertFalse(self.mesu_widget.isotopeComboBox.isEnabled())
def test_from_string(self): self.assertRaises(ValueError, lambda: Element.from_string("")) self.assertRaises(ValueError, lambda: Element.from_string("4")) self.assertRaises(ValueError, lambda: Element.from_string("2 2")) e = Element.from_string("He") self.assertEqual("He", e.symbol) self.assertIsNone(e.isotope) self.assertEqual(0, e.amount) e = Element.from_string("4He") self.assertEqual("He", e.symbol) self.assertEqual(4, e.isotope) e = Element.from_string("3H 2") self.assertEqual("H", e.symbol) self.assertEqual(3, e.isotope) self.assertEqual(2, e.amount)
def _accept_params(self, *_): """Accept given parameters. Args: *_: unused event args """ self.status_msg = "" sbh = StatusBarHandler(self.statusbar) sbh.reporter.report(10) try: output_dir = self.measurement.get_depth_profile_dir() # Get the filepaths of the selected items used_cuts = self.used_cuts DepthProfileDialog.checked_cuts[self.measurement.name] = set( used_cuts) # TODO could take care of RBS selection here elements = [ Element.from_string(fp.name.split(".")[1]) for fp in used_cuts ] x_unit = self.x_axis_units DepthProfileDialog.x_unit = x_unit DepthProfileDialog.line_zero = self.show_zero_line DepthProfileDialog.line_scale = self.show_scale_line DepthProfileDialog.systerr = self.systematic_error DepthProfileDialog.used_eff = self.show_used_eff DepthProfileDialog.eff_files_str = self.eff_files_str sbh.reporter.report(20) # If items are selected, proceed to generating the depth profile if used_cuts: self.status_msg = "Please wait. Creating depth profile." if self.parent.depth_profile_widget: self.parent.del_widget(self.parent.depth_profile_widget) # If reference density changed, update value to measurement if x_unit == DepthProfileUnit.NM: _, _, _, profile, measurement = \ self.measurement.get_used_settings() if profile.reference_density != self.reference_density: profile.reference_density = self.reference_density measurement.to_file() self.parent.depth_profile_widget = DepthProfileWidget( self.parent, output_dir, used_cuts, elements, x_unit, DepthProfileDialog.line_zero, DepthProfileDialog.used_eff, DepthProfileDialog.line_scale, DepthProfileDialog.systerr, DepthProfileDialog.eff_files_str, progress=sbh.reporter.get_sub_reporter( lambda x: 30 + 0.6 * x)) sbh.reporter.report(90) icon = self.parent.icon_manager.get_icon( "depth_profile_icon_2_16.png") self.parent.add_widget(self.parent.depth_profile_widget, icon=icon) self.close() else: self.status_msg = "Please select .cut file[s] to create " \ "depth profiles." except Exception as e: error_log = f"Exception occurred when trying to create depth " \ f"profiles: {e}" self.measurement.log_error(error_log) finally: sbh.reporter.report(100)
def test_element_simulation_settings(self): """This tests that ElementSimulation is run with the correct settings depending on how 'use_default_settings' and 'use_request_settings' have been set. """ with tempfile.TemporaryDirectory() as tmp_dir: # File paths tmp_dir = Path(tmp_dir).resolve() req_dir = tmp_dir / "req" sim_dir = req_dir / "Sample_01-foo" / \ f"{Simulation.DIRECTORY_PREFIX}01-bar" simu_file = sim_dir / "bar.simulation" # Request request = Request( req_dir, "test_req", GlobalSettings(config_dir=tmp_dir), {}, enable_logging=False) self.assertEqual(req_dir, request.directory) self.assertEqual("test_req", request.request_name) # Sample sample = request.samples.add_sample(name="foo") self.assertIs(sample, request.samples.samples[0]) self.assertIsInstance(sample, Sample) self.assertEqual("foo", sample.name) self.assertEqual(request, sample.request) self.assertEqual("Sample_01-foo", sample.directory) # Simulation sim = sample.simulations.add_simulation_file( sample, simu_file, 0) # Disable logging so the logging file handlers do not cause # an exception when the tmp dir is removed sim.close_log_files() self.assertIs(sim, sample.simulations.get_key_value(0)) self.assertIsInstance(sim.detector, Detector) self.assertIsInstance(sim.run, Run) self.assertEqual("bar", sim.name) self.assertEqual(request, sim.request) self.assertEqual(sample, sim.sample) # ElementSimulation rec_elem = RecoilElement( Element.from_string("Fe"), [Point((1, 1))], name="recoil_name") elem_sim = sim.add_element_simulation(rec_elem) self.assertIs(elem_sim, sim.element_simulations[0]) elem_sim.number_of_preions = 2 elem_sim.number_of_ions = 3 self.assertEqual(request, elem_sim.request) self.assertEqual("Fe-Default", elem_sim.get_full_name()) # Some pre-simulation checks self.assertIsNot(sim.target, request.default_target) self.assertIsNot(elem_sim, request.default_element_simulation) self.assertIsNot(sim.detector, request.default_detector) self.assertIsNot(sim.run, request.default_run) self.assertIsNot(sim.run.beam, request.default_run.beam) self.assertNotEqual( elem_sim.number_of_ions, request.default_element_simulation.number_of_ions) self.assert_expected_settings(elem_sim, request, sim)
def test_eq(self): self.assertEqual(Element.from_string("He"), Element.from_string("He")) self.assertEqual(Element.from_string("4He"), Element.from_string("4He")) self.assertEqual(Element.from_string("4He 2"), Element.from_string("4He 2.00")) self.assertEqual(Element("He", 4, 2), Element("He", 4, 2.00)) self.assertEqual(Element("He", 4, 0), Element("He", 4)) self.assertEqual(Element("He"), Element("He", None)) self.assertNotEqual(Element.from_string("4He 2"), Element.from_string("4He 1")) self.assertNotEqual(Element.from_string("3He"), Element.from_string("4He")) self.assertNotEqual(Element.from_string("He"), Element.from_string("4He")) self.assertNotEqual(Element.from_string("He"), Element.from_string("H")) self.assertNotEqual(Element.from_string("H"), "H")
def test_lt(self): elems = [ Element.from_string("H"), Element.from_string("He"), Element.from_string("C"), Element.from_string("C 2"), Element.from_string("4C"), Element.from_string("5C"), Element.from_string("15Si"), Element.from_string("Mn"), Element.from_string("Mn 2"), Element.from_string("16Mn"), Element.from_string("243Bk"), Element.from_string("250Cf") ] orig_elems = list(elems) random.shuffle(elems) self.assertEqual(orig_elems, sorted(elems))
def test_get_mcerd_params(self): beam = Beam(ion=Element.from_string("4He"), energy=14) self.assertEqual(["Beam ion: 4He", "Beam energy: 14 MeV"], beam.get_mcerd_params())
def _make_legend_box(self) -> None: """Make legend box for the graph. """ box = self.axes.get_position() if not self._position_set: self.axes.set_position( [box.x0, box.y0, box.width * 0.8, box.height]) self._position_set = True handles, labels = self.axes.get_legend_handles_labels() # Calculate values to be displayed in the legend box # TODO make profile_handler use Element objects as keys so # there is no need to do this conversion ignored_str = set(str(elem) for elem in self._ignore_from_ratio) lim_a, lim_b = self._limit_lines.get_range() if self._absolute_values: concentrations = self._profile_handler.integrate_concentrations( lim_a, lim_b) percentages, moes = {}, {} else: percentages, moes = self._profile_handler.calculate_ratios( ignored_str, lim_a, lim_b, self._systematic_error) concentrations = {} # Fixes labels to proper format, with MoE labels_w_percentages = [] rbs_values = {elem.symbol for elem in self._rbs_list.values()} for element_str in labels: element = Element.from_string(element_str) if element.isotope is None: element_isotope = "" else: element_isotope = str(element.isotope) rbs_asterisk = "*" if element_str in rbs_values else "" element_name = f"{element.symbol}{rbs_asterisk}" elem_lbl = f"$^{{{element_isotope}}}${element_name}" if not self._absolute_values and percentages[element_str] is not \ None: percentage = percentages[element_str] moe = moes[element_str] rounding = mf.get_rounding_decimals(moe) if rounding: str_ratio = f"{round(percentage, rounding)}%" str_err = f"± {round(moe, rounding)}%" else: str_ratio = f"{int(percentage)}%" str_err = f"± {int(moe)}%" lbl_str = f"{elem_lbl} {str_ratio:<6} {str_err}" else: lbl_str = f"{element_isotope:>3}{element_name:<3}" # Uses absolute values for the elements instead of percentages. if self._absolute_values: conc = concentrations[element_str] lbl_str = f"{elem_lbl} {round(conc, 3):<7} at./1e15 at./cm²" labels_w_percentages.append(lbl_str) leg = self.axes.legend(handles, labels_w_percentages, loc=3, bbox_to_anchor=(1, 0), borderaxespad=0, prop={ "size": 11, "family": "monospace" }) # If "Show used efficiency files" is checked and text-object is not # yet created. if self._show_eff_files and self.eff_text is None: eff_str = self.used_eff_str.replace("\t", "") # Set position of text according to amount of lines in the string line_count = eff_str.count("\n") + 1 y_position_txt = 1 - 0.08 * line_count x_position_txt = 1.01 self.eff_text = self.axes.text(x_position_txt, y_position_txt, eff_str, transform=self.axes.transAxes, fontsize=11, fontfamily="monospace") for handle in leg.legendHandles: handle.set_linewidth(3.0)
def on_draw(self): """Draw method for matplotlib. """ # Values for zoom x_min, x_max = self.axes.get_xlim() y_min, y_max = self.axes.get_ylim() x_min_changed = False self.axes.clear() # Clear old stuff self.axes.set_ylabel("Yield (counts)") self.axes.set_xlabel("Energy (MeV)") # TODO refactor the draw function so that measurement and simulation # do not use so many lines of different code if isinstance(self.parent.parent.obj, Measurement): element_counts = {} keys = [item[0] for item in sorted(self.histed_files.items(), key=lambda x: self.__sortt( x[0]))] for key in keys: cut_file = key.split('.') cut = self.histed_files[key] element_object = Element.from_string(cut_file[0]) element = element_object.symbol isotope = element_object.isotope if key in self.__ignore_elements: continue # Check RBS selection rbs_string = "" if len(cut_file) == 3: if key + ".cut" in self.__rbs_list: element_object = self.__rbs_list[key + ".cut"] element = element_object.symbol isotope = element_object.isotope rbs_string = "*" else: if key in self.__rbs_list: element_object = self.__rbs_list[key] element = element_object.symbol isotope = element_object.isotope rbs_string = "*" x, y = get_axis_values(cut) x_min, x_min_changed = fix_minimum(x, x_min) if isotope is None: isotope = "" # Get color for selection dirtyinteger = 0 if rbs_string == "*": color_string = "{0}{1}{2}{3}".format("RBS_", isotope, element, dirtyinteger) else: color_string = "{0}{1}{2}".format(isotope, element, dirtyinteger) while color_string in element_counts: dirtyinteger += 1 if rbs_string == "*": color_string = "{0}{1}{2}{3}".format("RBS_", isotope, element, dirtyinteger) else: color_string = "{0}{1}{2}".format(isotope, element, dirtyinteger) element_counts[color_string] = 1 if color_string not in self.__selection_colors: color = "red" else: color = self.__selection_colors[color_string] if len(cut_file) == 3: label = r"$^{" + str(isotope) + "}$" + element + rbs_string else: label = r"$^{" + str(isotope) + "}$" + element \ + rbs_string + "$_{split: " + cut_file[2] + "}$" line, = self.axes.plot(x, y, color=color, label=label, linestyle=self.default_linestyle) self.plots[key] = line else: # Simulation energy spectrum if self.__ignore_elements: self.files_to_draw = self.remove_ignored_elements() else: self.files_to_draw = copy.deepcopy(self.histed_files) for key, data in self.files_to_draw.items(): # Parse the element symbol and isotope. file_name = key.name isotope = "" symbol = "" color = None suffix = "" if file_name.endswith(".hist"): measurement_name, isotope_and_symbol, erd_or_rbs, rest = \ file_name.split('.', 3) if "ERD" in erd_or_rbs: element = Element.from_string(isotope_and_symbol) else: if "RBS_" in erd_or_rbs: i = erd_or_rbs.index("RBS_") scatter_element_str = erd_or_rbs[i + len("RBS_"):] element = Element.from_string(scatter_element_str) else: element = Element("") suffix = "*" isotope = element.isotope if isotope is None: isotope = "" symbol = element.symbol rest_split = rest.split(".") if "no_foil" in rest_split: # TODO make a function that splits all the necessary # parts from a file name at once so there is no # need to do these kinds of checks rest_split.remove("no_foil") if len(rest_split) == 2: # regular hist file label = r"$^{" + str(isotope) + "}$" + symbol + suffix \ + " (exp)" else: # split label = r"$^{" + str(isotope) + "}$" + symbol + suffix \ + "$_{split: " + rest_split[1] + "}$"" (exp)" elif file_name.endswith(".simu"): for s in file_name: if s != "-": if s.isdigit(): isotope += s else: symbol += s else: break recoil_name_with_end = file_name.split('-', 1)[1] recoil_name = recoil_name_with_end.split('.')[0] label = r"$^{" + isotope + "}$" + symbol + " " + recoil_name for used_recoil in self.__used_recoils: used_recoil_file_name = \ f"{used_recoil.get_full_name()}.simu" if used_recoil_file_name == file_name: color = used_recoil.color break else: label = file_name x, y = get_axis_values(data) x_min, x_min_changed = fix_minimum(x, x_min) if not color: line, = self.axes.plot(x, y, label=label, linestyle=self.default_linestyle) else: line, = self.axes.plot(x, y, label=label, color=color, linestyle=self.default_linestyle) self.plots[key] = line if self.draw_legend: if not self.__initiated_box: self.fig.tight_layout(pad=0.5) box = self.axes.get_position() self.axes.set_position([box.x0, box.y0, box.width * 0.9, box.height]) self.__initiated_box = True handles, labels = self.axes.get_legend_handles_labels() self.leg = self.axes.legend(handles, labels, loc=3, bbox_to_anchor=(1, 0), borderaxespad=0, prop={'size': 12}) for handle in self.leg.legendHandles: handle.set_linewidth(3.0) if 0.09 < x_max < 1.01: # This works... x_max = self.axes.get_xlim()[1] if 0.09 < y_max < 1.01: y_max = self.axes.get_ylim()[1] # Set limits accordingly self.axes.set_ylim([y_min, y_max]) if x_min_changed: self.axes.set_xlim([x_min - self.parent.bin_width, x_max]) else: self.axes.set_xlim([x_min, x_max]) if self.__log_scale: self.axes.set_yscale("symlog") # Remove axis ticks self.remove_axes_ticks() # Draw magic self.canvas.draw()
def __init__(self, parent: BaseTab, output_dir: Path, cut_files: List[Path], elements: List[Element], x_units: DepthProfileUnit, line_zero: bool, used_eff: bool, line_scale: bool, systematic_error: float, eff_files_str: str, progress: Optional[ProgressReporter] = None): """Inits widget. Args: parent: a MeasurementTabWidget. output_dir: full path to depth file location cut_files: A list of Cut files. elements: A list of Element objects that are used in depth profile. x_units: Units to be used for x-axis of depth profile. line_zero: A boolean representing if vertical line is drawn at zero. used_eff: A boolean representing if used eff files are shown. line_scale: A boolean representing if horizontal line is drawn at the defined depth scale. systematic_error: A double representing systematic error. """ try: super().__init__() uic.loadUi(gutils.get_ui_dir() / "ui_depth_profile.ui", self) self.parent = parent self.measurement: Measurement = parent.obj self.output_dir = output_dir self.elements = elements self.x_units = x_units self.use_cuts = cut_files self._line_zero_shown = line_zero self._eff_files_shown = used_eff self._eff_files_str = eff_files_str self._line_scale_shown = line_scale self._systematic_error = systematic_error if progress is not None: sub_progress = progress.get_sub_reporter(lambda x: 0.5 * x) else: sub_progress = None depth_files.generate_depth_files(self.use_cuts, self.output_dir, self.measurement, progress=sub_progress) if progress is not None: progress.report(50) # Check for RBS selections. rbs_list = cut_file.get_rbs_selections(self.use_cuts) for rbs in rbs_list: # Search and replace instances of Beam element with scatter # elements. # When loading request, the scatter element is already # replaced. This is essentially done only when creating # a new Depth Profile graph. # TODO seems overly complicated. This stuff should be sorted # before initializing the widget element = Element.from_string(rbs.split(".")[0]) for i, elem in enumerate(elements): if elem == element: elements[i] = rbs_list[rbs] if self._line_scale_shown: _, _, _, profile, _ = self.measurement.get_used_settings() depth_scale = (profile.depth_for_concentration_from, profile.depth_for_concentration_to) else: depth_scale = None if progress is not None: sub_progress = progress.get_sub_reporter( lambda x: 50 + 0.5 * x) else: sub_progress = None self.matplotlib = MatplotlibDepthProfileWidget( self, self.output_dir, self.elements, rbs_list, icon_manager=self.parent.icon_manager, selection_colors=self.measurement.selector.get_colors(), depth_scale=depth_scale, x_units=self.x_units, add_line_zero=self._line_zero_shown, show_eff_files=self._eff_files_shown, used_eff_str=self._eff_files_str, systematic_error=self._systematic_error, progress=sub_progress) except Exception as e: msg = f"Could not create Depth Profile graph: {e}" self.measurement.log_error(msg) if hasattr(self, "matplotlib"): self.matplotlib.delete() finally: if progress is not None: progress.report(100)
def make_depth_profile(self, directory: Path, name: str, serial_number_m: int, sample_folder_name: str, progress=None): """Make depth profile from loaded lines from saved file. Args: directory: A path to depth files directory. name: A string representing measurement's name. serial_number_m: Measurement's serial number. sample_folder_name: Sample's serial number. progress: a ProgressReporter object """ file = Path(directory, DepthProfileWidget.save_file) lines = MeasurementTabWidget._load_file(file) if not lines: return m_name = self.obj.name try: base = Measurement.DIRECTORY_PREFIX # TODO add sample prefix old_folder_prefix = base + "%02d" % serial_number_m new_folder_prefix = base + "%02d" % self.obj.serial_number new_sample_name = "Sample_" + "%02d" % \ self.obj.sample.serial_number + "-" + \ self.obj.sample.name output_dir = self.__confirm_filepath(lines[0].strip(), name, m_name, old_folder_prefix, new_folder_prefix, sample_folder_name, new_sample_name) use_cuts = self.__confirm_filepath(lines[2].strip().split("\t"), name, m_name, old_folder_prefix, new_folder_prefix, sample_folder_name, new_sample_name) elements_string = lines[1].strip().split("\t") elements = [ Element.from_string(element) for element in elements_string ] try: x_unit = DepthProfileUnit(lines[3].strip()) except ValueError: x_unit = DepthProfileUnit.ATOMS_PER_SQUARE_CM line_zero = False line_scale = False systerr = 0.0 if len(lines) == 7: # "Backwards compatibility" line_zero = lines[4].strip() == "True" line_scale = lines[5].strip() == "True" systerr = float(lines[6].strip()) DepthProfileDialog.x_unit = x_unit DepthProfileDialog.checked_cuts[m_name] = set(use_cuts) DepthProfileDialog.line_zero = line_zero DepthProfileDialog.line_scale = line_scale DepthProfileDialog.systerr = systerr self.depth_profile_widget = DepthProfileWidget(self, output_dir, use_cuts, elements, x_unit, line_zero, line_scale, systerr, progress=progress) icon = self.icon_manager.get_icon("depth_profile_icon_2_16.png") self.add_widget(self.depth_profile_widget, icon=icon) except Exception as e: # We do not need duplicate error logs, log in widget instead print(e)
def on_draw(self): """Draw method for matplotlib. """ self.axes.clear() # Clear old stuff # keys = sorted(self.split.keys()) keys = [ item[0] for item in sorted(self.split.items(), key=lambda x: self.__sortt(x[0])) ] for key in keys: cut_file = key.split('.') # TODO provide elements as parameter rather than initializing # them here element_object = Element.from_string(cut_file[0].strip()) element = element_object.symbol isotope = element_object.isotope if key in self.__ignore_elements: continue if self.reference_cut_key and key == self.reference_cut_key: continue # Check RBS selection rbs_string = "" if len(cut_file) == 3: if key + ".cut" in self.__rbs_list: element_object = self.__rbs_list[key + ".cut"] element = element_object.symbol isotope = element_object.isotope rbs_string = "*" else: if key in self.__rbs_list: element_object = self.__rbs_list[key] element = element_object.symbol isotope = element_object.isotope rbs_string = "*" # Get color for selection if isotope is None: isotope = "" if rbs_string == "*": color_string = "{0}{1}{2}".format("RBS_" + isotope, element, cut_file[2]) else: color_string = "{0}{1}{2}".format(isotope, element, cut_file[2]) color = self.selection_colors.get(color_string, "red") # Set label text if len(cut_file) == 3: label = r"$^{" + str(isotope) + "}$" + element + rbs_string else: label = r"$^{" + str(isotope) + "}$" + element + rbs_string \ + "$_{split: " + cut_file[3] + "}$" # Modify data if scaled to 100. data = self.split[key] if self.__scale_mode == 2: n = None for val in data: if val == 0: continue else: n = val break modifier = 100 / n # self.split[key][0] data = [i * modifier for i in data] self.axes.plot(data, color=color, label=label) if self.draw_legend: if not self.__initiated_box: self.fig.tight_layout(pad=0.5) box = self.axes.get_position() self.axes.set_position( [box.x0, box.y0, box.width * 0.9, box.height]) self.__initiated_box = True handles, labels = self.axes.get_legend_handles_labels() leg = self.axes.legend(handles, labels, loc=3, bbox_to_anchor=(1, 0), borderaxespad=0, prop={'size': 12}) for handle in leg.legendHandles: handle.set_linewidth(3.0) # Scale based on values x_min, x_max = self.axes.get_xlim() y_min, y_max = self.axes.get_ylim() # Y-axis scaling option, 0 = 0-max, 1 = min-max if self.y_scale == 0: y_min = 0 # Set limits accordingly self.axes.set_ylim([y_min, y_max]) self.axes.set_xlim([x_min, x_max]) # Scale for log if self.__scale_mode == 1: self.axes.set_yscale('symlog') else: self.axes.set_yscale('linear') # Set axes names if self.__scale_mode == 2: self.axes.set_ylabel("Scaled count") else: self.axes.set_ylabel("Count") self.axes.set_xlabel("Split") # Remove axis ticks self.remove_axes_ticks() self.canvas.draw()