diff --git a/pyproject.toml b/pyproject.toml index 5841189..30ac101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,8 +61,6 @@ testpaths = "tests" addopts = "--cov -v" filterwarnings = 'ignore:datetime.datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning' # 3rd party - - [tool.box] builder = "rye" app_entry = "rimsschemedrawer.app:run_gui" diff --git a/src/rimsschemedrawer/gui.py b/src/rimsschemedrawer/gui.py index de951ec..8357486 100644 --- a/src/rimsschemedrawer/gui.py +++ b/src/rimsschemedrawer/gui.py @@ -72,6 +72,7 @@ def __init__(self): self.cmb_lasers = None self.chk_lowlying = [] self.chk_forbidden = [] + self.chk_laststep = None # settings line edits self.edt_sett_plttitle = QtWidgets.QLineEdit() @@ -251,6 +252,16 @@ def init_ui(self): "Check this box to mark the\n" "transition as forbidden." ) + # draw last step to IP + self.chk_laststep = QtWidgets.QCheckBox("Draw last step to IP?") + self.chk_laststep.setToolTip( + "Check this box to draw the last step, if it is below the IP, to the IP.\n" + "The wavelength will then be labeled as an upper limit `<` and no level\n" + "information will be printed. This option has no influence if the last\n" + "step specified above is already above the IP!" + ) + layout.addWidget(self.chk_laststep, 4 + len(self.lbl_steps), 0, 1, 2) + # name the labels self.set_label_names() @@ -267,10 +278,10 @@ def init_ui(self): # Set the elements element_lbl = QtWidgets.QLabel("Element") - layout.addWidget(element_lbl, 4 + len(self.lbl_steps), 0, 1, 1) + layout.addWidget(element_lbl, 5 + len(self.lbl_steps), 0, 1, 1) cmb_element = QtWidgets.QComboBox() cmb_element.addItems(ut.get_elements()) - layout.addWidget(cmb_element, 4 + len(self.lbl_steps), 1, 1, 1) + layout.addWidget(cmb_element, 5 + len(self.lbl_steps), 1, 1, 1) cmb_element.setToolTip("Select the element to set the IP.") cmb_element.currentIndexChanged.connect(lambda x: self.set_ip(x)) cmb_element.setCurrentIndex(0) @@ -279,7 +290,7 @@ def init_ui(self): # Laser selection laser_lbls = QtWidgets.QLabel("Lasers") - layout.addWidget(laser_lbls, 5 + len(self.lbl_steps), 0, 1, 1) + layout.addWidget(laser_lbls, 6 + len(self.lbl_steps), 0, 1, 1) cmb_lasers = QtWidgets.QComboBox() cmb_lasers.addItems(ut.LASERS) cmb_lasers.setToolTip( @@ -289,7 +300,7 @@ def init_ui(self): "to the RIMS database website " "and its entry will be saved to the configuration file." ) - layout.addWidget(cmb_lasers, 5 + len(self.lbl_steps), 1, 1, 1) + layout.addWidget(cmb_lasers, 6 + len(self.lbl_steps), 1, 1, 1) self.cmb_lasers = cmb_lasers # set sizes and validators of boxes defined outside loop @@ -740,22 +751,37 @@ def load_config(self, **kwargs): self.edt_gslevel.setText(str(config_parser.gs_level)) self.edt_gsterm.setText(config_parser.gs_term_no_formatting) + + # get the data to fill from + if config_parser.sett_unit_nm: + value_list = config_parser.step_nm + else: + value_list = config_parser.step_levels + step_terms_no_formatting = config_parser.step_terms_no_formatting + transition_strengths = config_parser.transition_strengths + is_low_lying = config_parser.is_low_lying + step_forbidden = config_parser.step_forbidden + + # cut last index off if we are dealing with last_step_to_ip defined + if config_parser.last_step_to_ip_mode: + value_list = value_list[:-1] + step_terms_no_formatting = step_terms_no_formatting[:-1] + transition_strengths = transition_strengths[:-1] + is_low_lying = is_low_lying[:-1] + step_forbidden = step_forbidden[:-1] + for it in range( len(self.edt_level) ): # only loop through as many entries as there are try: - if config_parser.sett_unit_nm: - value_list = config_parser.step_nm - else: - value_list = config_parser.step_levels self.edt_level[it].setText(str(value_list[it])) - self.edt_term[it].setText(config_parser.step_terms_no_formatting[it]) + self.edt_term[it].setText(step_terms_no_formatting[it]) - trans_strength = config_parser.transition_strengths[it] + trans_strength = transition_strengths[it] if trans_strength > 0: self.edt_transition_strengths[it].setText(str(trans_strength)) - self.chk_lowlying[it].setChecked(config_parser.is_low_lying[it]) - self.chk_forbidden[it].setChecked(config_parser.step_forbidden[it]) + self.chk_lowlying[it].setChecked(is_low_lying[it]) + self.chk_forbidden[it].setChecked(step_forbidden[it]) except IndexError: self.edt_level[it].setText("") self.edt_term[it].setText("") @@ -763,6 +789,9 @@ def load_config(self, **kwargs): self.chk_lowlying[it].setChecked(False) self.chk_forbidden[it].setChecked(False) + # Last step to IP + self.chk_laststep.setChecked(config_parser.last_step_to_ip) + # IP level self.cmb_element.setCurrentText(config_parser.element) if config_parser.element_guessed: @@ -850,6 +879,7 @@ def write_json(self) -> dict: savedict["scheme"]["ip_term"] = self.edt_ipterm.text() savedict["scheme"]["element"] = self.cmb_element.currentText() savedict["scheme"]["lasers"] = self.cmb_lasers.currentText() + savedict["scheme"]["last_step_to_ip"] = self.chk_laststep.isChecked() # save the settings savedict["settings"]["fig_width"] = self.edt_sett_figwidth.text() diff --git a/src/rimsschemedrawer/json_parser.py b/src/rimsschemedrawer/json_parser.py index 436e374..ebd6ab1 100644 --- a/src/rimsschemedrawer/json_parser.py +++ b/src/rimsschemedrawer/json_parser.py @@ -21,6 +21,8 @@ def __init__(self, data: Dict): self.data = data self._element_guessed = False + self._last_step_to_ip_mode = False + self._parse_data_scheme() self._parse_data_settings() @@ -81,6 +83,16 @@ def lasers(self) -> str: """Get the types of lasers used in this scheme (defaults to Ti:Sa).""" return self._lasers + @property + def last_step_to_ip(self) -> bool: + """Get the last step to the ionization potential setup in file.""" + return self._last_step_to_ip + + @property + def last_step_to_ip_mode(self) -> bool: + """Get if we are actually in last step to IP mode?""" + return self._last_step_to_ip_mode + @property def number_of_levels(self) -> int: """Get the number of steps in the scheme.""" @@ -223,6 +235,8 @@ def scheme_table(self, prec: int = 3, prec_strength: int = 1) -> Tuple[List, Lis Table: All entries are formatted as strings! + fixme: Modifications if in last step to IP mode + :param prec: Precision for the wavelength and steps. :param prec_strength: Precision for the transition strength. @@ -365,6 +379,12 @@ def _parse_data_scheme(self): except KeyError: self._lasers = ut.LASERS[0] + # get last step to IP + last_step_to_ip_default = ut.DEFAULT_SETTINGS["scheme"]["last_step_to_ip"] + self._last_step_to_ip = self.data["scheme"].get( + "last_step_to_ip", last_step_to_ip_default + ) + # Get the step levels and save them as cm-1 (transform if in nm) step_levels = [] idx = 0 @@ -430,6 +450,19 @@ def _parse_data_scheme(self): self._step_levels_cm[it] - self._step_levels_cm[it - 1] ) + # adjust scheme if last_step_to_ip and set mode if required + if self._last_step_to_ip and self._step_levels_cm[-1] < self._ip_level: + self._last_step_to_ip_mode = True + # append the last step + self._step_levels_cm = np.append(self._step_levels_cm, self._ip_level) + self._steps_nm = np.append( + self._steps_nm, ut.cm_2_to_nm(self._ip_level - self._step_levels_cm[-2]) + ) + self._forbidden = np.append(self._forbidden, False) + self._low_lying = np.append(self._low_lying, False) + self._transition_strength = np.append(self._transition_strength, 0) + self._step_term = np.append(self._step_term, "") + def _parse_data_key(self, key: str, dtype: type, default: any) -> np.ndarray: """Parse a key from the data and return values with the correct type. diff --git a/src/rimsschemedrawer/plotter.py b/src/rimsschemedrawer/plotter.py index 840efcc..4e19897 100644 --- a/src/rimsschemedrawer/plotter.py +++ b/src/rimsschemedrawer/plotter.py @@ -227,6 +227,13 @@ def _plotit(self): xvalplot = firstarrowxmfl else: xvalplot = xval + # face color for arrow + fc_col = col + if ( + self.config_parser.last_step_to_ip_mode + and it == len(lambda_steps) - 1 + ): + fc_col = "None" # now plot the arrow self._axes.arrow( xvalplot, @@ -234,7 +241,7 @@ def _plotit(self): 0, wstp, width=sett_arr, - fc=col, + fc=fc_col, ec=col, length_includes_head=True, head_width=sett_arr_head, @@ -254,14 +261,15 @@ def _plotit(self): ) # draw a little solid line for the last/end state - if it == len(lambda_steps) - 1: - self._axes.hlines( - tstp, - xmin=xval - 0.5, - xmax=xval + 0.5, - linestyle="solid", - color=self.colmain, - ) + if not self.config_parser.last_step_to_ip_mode: + if it == len(lambda_steps) - 1: + self._axes.hlines( + tstp, + xmin=xval - 0.5, + xmax=xval + 0.5, + linestyle="solid", + color=self.colmain, + ) # alignment of labels if xval <= 5.0: @@ -276,6 +284,11 @@ def _plotit(self): if not forbidden_steps[it] or show_forbidden_trans == "x-out": # wavelength text and transition strength lambdastr = f"{lambda_steps[it]:.{prec_lambda}f}$\\,$nm" + if ( + self.config_parser.last_step_to_ip_mode + and it == len(lambda_steps) - 1 + ): + lambdastr = f"<{lambdastr}" if ( show_trans_strength and (tmp_strength := transition_strengths_steps[it]) != 0 @@ -309,6 +322,7 @@ def _plotit(self): ) # level text + # fixme: only do this if we are not in the last step to IP mode levelstr = f"{tstp:.{prec_level}f}$\\,$cm$^{{-1}}$" if term_symb[it] is not None: levelstr += f"{lbreak}{term_symb[it]}" @@ -318,15 +332,20 @@ def _plotit(self): else: leveltextypos = tstp - 0.01 * totwavenumber_photons leveltextvaalign = "top" - self._axes.text( - xloc_levelstr, - leveltextypos, - levelstr, - color=self.colmain, - ha=halignlev, - va=leveltextvaalign, - size=fsz_labels, - ) + + if ( + not self.config_parser.last_step_to_ip_mode + or it != len(lambda_steps) - 1 + ): + self._axes.text( + xloc_levelstr, + leveltextypos, + levelstr, + color=self.colmain, + ha=halignlev, + va=leveltextvaalign, + size=fsz_labels, + ) # update yval_bott yval_bott = transition_steps[it] @@ -348,7 +367,7 @@ def _plotit(self): color=self.colmain, ) - for it in range(len(wavenumber_es)): + for it in range(len(wavenumber_es)): # these are never steps to IP col = ut.color_wavelength(lambda_step_es[it], self.darkmode) # values for spacing and distance xval = firstarrowxmfl + x_spacing_es + it * x_spacing_es diff --git a/src/rimsschemedrawer/utils.py b/src/rimsschemedrawer/utils.py index 144ff40..7037fae 100644 --- a/src/rimsschemedrawer/utils.py +++ b/src/rimsschemedrawer/utils.py @@ -30,6 +30,7 @@ "scheme": { "gs_term": "", "ip_term": "", + "last_step_to_ip": False, }, } diff --git a/tests/data/draw_last_false.json b/tests/data/draw_last_false.json new file mode 100644 index 0000000..ccdce3c --- /dev/null +++ b/tests/data/draw_last_false.json @@ -0,0 +1,67 @@ +{ + "scheme": { + "element": "Fe", + "gs_level": "0.0", + "gs_term": "5D4", + "ip_term": "", + "lasers": "Ti:Sa", + "last_step_to_ip": true, + "step_forbidden0": false, + "step_forbidden1": false, + "step_forbidden2": false, + "step_forbidden3": false, + "step_forbidden4": false, + "step_forbidden5": false, + "step_forbidden6": false, + "step_level0": "26140.196", + "step_level1": "50530.829", + "step_level2": "64126.024", + "step_level3": "", + "step_level4": "", + "step_level5": "", + "step_level6": "", + "step_lowlying0": false, + "step_lowlying1": false, + "step_lowlying2": false, + "step_lowlying3": false, + "step_lowlying4": false, + "step_lowlying5": false, + "step_lowlying6": false, + "step_term0": "5D3", + "step_term1": "5D3", + "step_term2": "", + "step_term3": "", + "step_term4": "", + "step_term5": "", + "step_term6": "", + "trans_strength0": "", + "trans_strength1": "", + "trans_strength2": "", + "trans_strength3": "", + "trans_strength4": "", + "trans_strength5": "", + "trans_strength6": "", + "unit": "cm-1" + }, + "settings": { + "arrow_head_width": "0.6", + "arrow_width": "0.2", + "fig_height": "8.0", + "fig_width": "5.0", + "fs_axes": "11.0", + "fs_axes_labels": "11.0", + "fs_labels": "11.0", + "fs_title": "14.0", + "headspace": "2500.0", + "ip_label_pos": "Bottom", + "line_breaks": true, + "plot_style": "light", + "plot_title": "", + "prec_level": "0", + "prec_wavelength": "3", + "show_cm-1_axis": true, + "show_eV_axis": true, + "show_forbidden_transitions": "x-out", + "show_transition_strength": true + } +} diff --git a/tests/data/draw_last_true.json b/tests/data/draw_last_true.json new file mode 100644 index 0000000..50fb10d --- /dev/null +++ b/tests/data/draw_last_true.json @@ -0,0 +1,67 @@ +{ + "scheme": { + "element": "Fe", + "gs_level": "0.0", + "gs_term": "5D4", + "ip_term": "", + "lasers": "Ti:Sa", + "last_step_to_ip": true, + "step_forbidden0": false, + "step_forbidden1": false, + "step_forbidden2": false, + "step_forbidden3": false, + "step_forbidden4": false, + "step_forbidden5": false, + "step_forbidden6": false, + "step_level0": "26140.196", + "step_level1": "50530.829", + "step_level2": "", + "step_level3": "", + "step_level4": "", + "step_level5": "", + "step_level6": "", + "step_lowlying0": false, + "step_lowlying1": false, + "step_lowlying2": false, + "step_lowlying3": false, + "step_lowlying4": false, + "step_lowlying5": false, + "step_lowlying6": false, + "step_term0": "5D3", + "step_term1": "5D3", + "step_term2": "", + "step_term3": "", + "step_term4": "", + "step_term5": "", + "step_term6": "", + "trans_strength0": "", + "trans_strength1": "", + "trans_strength2": "", + "trans_strength3": "", + "trans_strength4": "", + "trans_strength5": "", + "trans_strength6": "", + "unit": "cm-1" + }, + "settings": { + "arrow_head_width": "0.6", + "arrow_width": "0.2", + "fig_height": "8.0", + "fig_width": "5.0", + "fs_axes": "11.0", + "fs_axes_labels": "11.0", + "fs_labels": "11.0", + "fs_title": "14.0", + "headspace": "2500.0", + "ip_label_pos": "Bottom", + "line_breaks": true, + "plot_style": "light", + "plot_title": "", + "prec_level": "0", + "prec_wavelength": "3", + "show_cm-1_axis": true, + "show_eV_axis": true, + "show_forbidden_transitions": "x-out", + "show_transition_strength": true + } +} diff --git a/tests/test_json_parser.py b/tests/test_json_parser.py index e961191..4af4e92 100644 --- a/tests/test_json_parser.py +++ b/tests/test_json_parser.py @@ -18,6 +18,8 @@ def test_config_parser(data_path, fname): parser = jp.ConfigParser(data) assert parser.lasers == "Ti:Sa" + assert not parser.last_step_to_ip_mode + assert not parser.last_step_to_ip @pytest.mark.parametrize( @@ -118,3 +120,18 @@ def test_json_reader_new(data_path, tmp_path): assert isinstance(data_in, dict) assert "scheme" in data_in.keys() assert "settings" in data_in.keys() + + +@pytest.mark.parametrize("selector", [True, False]) +def test_json_reader_last_step_to_ip(data_path, selector): + """Check if last step to IP is correctly parsed.""" + if selector: + fin = data_path.joinpath("draw_last_true.json") + else: + fin = data_path.joinpath("draw_last_false.json") + data = jp.json_reader(fin) + + parser = jp.ConfigParser(data) + + assert parser.last_step_to_ip + assert parser.last_step_to_ip_mode is selector