Example #1
0
    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")))
Example #2
0
    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())
Example #3
0
    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())
Example #4
0
    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)
Example #5
0
    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()
Example #6
0
 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())
Example #7
0
 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())
Example #8
0
 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))
Example #9
0
    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)
Example #10
0
    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)
Example #11
0
    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)))
Example #12
0
    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)]
Example #14
0
 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)
Example #15
0
    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))
Example #16
0
    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))
Example #17
0
    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"))
Example #18
0
    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())
Example #20
0
    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)
Example #21
0
    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)
Example #23
0
    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")
Example #24
0
    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))
Example #25
0
 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())
Example #26
0
    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)
Example #27
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()
Example #28
0
    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)
Example #29
0
 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)
Example #30
0
    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()