Scripts to produce publication-ready figures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
7.4 KiB

  1. import os, csv
  2. from pathlib import Path
  3. import numpy as np
  4. import pandas as pd
  5. import seaborn as sns
  6. import matplotlib.pyplot as plt
  7. sns.set_theme(style="ticks")
  8. def parse_NIST_data(path, min_J, max_J, max_wavenumber):
  9. data_list = []
  10. with open(path) as f:
  11. reader = csv.reader(f, delimiter="\t")
  12. data_list = list(reader)
  13. #collect data
  14. Parity = np.zeros(len(data_list))
  15. J = np.zeros(len(data_list))
  16. Wavenumber = np.zeros(len(data_list))
  17. for i in range(1, len(data_list)):
  18. try:
  19. tmp = data_list[:][i]
  20. if not tmp[0] == '' and tmp[0].find('*') != -1:
  21. Parity[i] = 1
  22. elif not tmp[0] == '':
  23. Parity[i] = 0
  24. J[i] = int(tmp[1])
  25. Wavenumber[i] = float(tmp[3])
  26. except ValueError:
  27. J[i] = np.nan
  28. Wavenumber[i] = np.nan
  29. remove_idxs = []
  30. for i in range(1, len(data_list)):
  31. p = Parity[i]
  32. j = J[i]
  33. wn = Wavenumber[i]
  34. if np.isnan(p) or np.isnan(j) or np.isnan(wn):
  35. remove_idxs.append(i)
  36. Parity = np.delete(Parity, remove_idxs)
  37. J = np.delete(J, remove_idxs)
  38. Wavenumber = np.delete(Wavenumber, remove_idxs)
  39. #sort data
  40. sorting_indices = np.argsort(J)
  41. Parity = Parity[sorting_indices]
  42. J = J[sorting_indices]
  43. Wavenumber = Wavenumber[sorting_indices]
  44. # splice data to within user-defined range of Js
  45. splice_idx_start = np.where(J==min_J)[0][0]
  46. splice_idx_stop = len(J) - 1 - np.where(J[::-1]==max_J)[0][0]
  47. Parity = Parity[splice_idx_start:splice_idx_stop]
  48. J = J[splice_idx_start:splice_idx_stop]
  49. Wavenumber = Wavenumber[splice_idx_start:splice_idx_stop]
  50. # splice data to within user-defined range of Wavenumbers
  51. splice_idxs = [i for i in range(len(Wavenumber)) if Wavenumber[i] > max_wavenumber]
  52. Parity = [ele for idx, ele in enumerate(Parity) if idx not in splice_idxs]
  53. J = [ele for idx, ele in enumerate(J) if idx not in splice_idxs]
  54. Wavenumber = [ele for idx, ele in enumerate(Wavenumber) if idx not in splice_idxs]
  55. # Create a Pandas data frame with the data
  56. dataset = pd.DataFrame(np.array(list(zip(Parity, J, Wavenumber))), columns=['Parity', 'J', 'Wavenumber'])
  57. return dataset
  58. def plot_level_structure_with_red_and_blue_transitions(*args, **kwargs):
  59. #start plotting
  60. f, ax = plt.subplots(figsize=(4.5, 7))
  61. #plt.subplots_adjust(top=0.973, bottom=0.121, left=0.244, right=0.959, hspace=0.2, wspace=0.2)
  62. named_colors = ['r', 'm']
  63. Red_Blue_colors = ['#ab162a', '#cf5246', '#eb9172', '#fac8af', '#faeae1', '#e6eff4', '#bbdaea', '#7bb6d6', '#3c8abe', '#1e61a5']
  64. #draw levels
  65. plot_handle = sns.scatterplot(x='J', y='Wavenumber', data = dataframe, s=2000, hue = 'Parity', palette = sns.color_palette(named_colors), marker = '_', linewidth=1.5, legend=False)
  66. #write electronic configuration for GS
  67. #ax.text(gs_J + 0.15, gs_wavenumber + 400, '$6s^2$')
  68. #draw guide line for GS
  69. #plt.axhline(y=gs_wavenumber, color='m', linestyle='--', linewidth=1, alpha=0.5)
  70. #write wavelength of red transition
  71. ax.text(red_J - 0.4, red_wavenumber * 0.54 - 500, '626.082 nm', color = '#db2929', fontsize = 17, fontweight = 'bold')
  72. #ax.text(red_J - 0.43, red_wavenumber * 0.38 - 500 , '$(\\Gamma = 2\\pi\\times 136$'+ '\n' + '$~ \mathrm{kHz})$', fontsize = 14, color = '#db2929')
  73. ax.text(red_J - 0.325, red_wavenumber * 0.45 - 500 , '$(136~ \mathrm{kHz})$', fontsize = 16, color = '#db2929')
  74. #draw red transition arrow
  75. ax.annotate('',
  76. xy=(red_J, red_wavenumber),
  77. xytext=(gs_J, gs_wavenumber),
  78. arrowprops=dict(color='#db2929', width=1.5),
  79. horizontalalignment='right',
  80. verticalalignment='top')
  81. #write electronic configuration for triplet excited state
  82. #ax.text(red_J + 0.35, red_wavenumber + 200, '$6s6p(^3P_1)$', fontsize = 10)
  83. #draw guide line for triplet excited state
  84. plt.axhline(y=red_wavenumber, color='m', linestyle='--', linewidth=1, alpha=0.5)
  85. #write wavelength of red transition
  86. ax.text(blue_J - 1.8, blue_wavenumber * 0.44, '421.291 nm', color = '#2630ea', fontsize = 17, fontweight = 'bold')
  87. # ax.text(blue_J - 1.85, blue_wavenumber * 0.33, '$(\\Gamma = 2\\pi\\times 32.2$'+ '\n' + '$~ \mathrm{MHz})$', fontsize = 14, color = '#2630ea') #$(\\Gamma = 2\\pi\\times 32.2 ~ \mathrm{MHz})$
  88. ax.text(blue_J - 1.775, blue_wavenumber * 0.38, '$(32.2~ \mathrm{MHz})$', fontsize = 16, color = '#2630ea') #$(\\Gamma = 2\\pi\\times 32.2 ~ \mathrm{MHz})$
  89. #ax.text(blue_J - 1.5, blue_wavenumber * 0.3, '$32.2 ~ \mathrm{MHz})$', fontsize = 10, color = '#2630ea')
  90. #draw blue transition arrow
  91. ax.annotate('',
  92. xy=(blue_J, blue_wavenumber),
  93. xytext=(gs_J, gs_wavenumber),
  94. arrowprops=dict(color='#2630ea', width=3.5),
  95. horizontalalignment='right',
  96. verticalalignment='top')
  97. #write electronic configuration for singlet excited state
  98. #ax.text(blue_J + 0.35, blue_wavenumber + 200, '$6s6p(^1P_1)$', fontsize = 10)
  99. #draw guide line for singlet excited state
  100. plt.axhline(y=blue_wavenumber, color='m', linestyle='--', linewidth=1, alpha=0.5)
  101. #figure options
  102. f.canvas.draw()
  103. plt.xlabel('J', fontsize=20, fontweight = 'bold')
  104. #plt.ylabel('$\\tilde{v}~(cm^{-1})$', fontsize=16)
  105. plt.ylabel('Wavelength (nm)', fontsize=20, fontweight = 'bold')
  106. plot_handle.set_xticks(range(min_J-1, max_J+2))
  107. ax.get_xticklabels()[0].set_visible(False)
  108. ax.get_xticklabels()[-1].set_visible(False)
  109. ax.get_xticklines()[0].set_visible(False)
  110. ax.get_xticklines()[-2].set_visible(False)
  111. ax.set_xticklabels(ax.get_xticks(), weight='bold')
  112. yticklabels = [item.get_text() for item in ax.get_yticklabels()]
  113. yticklabels = ['' if item.startswith('') or item.startswith('0') else item for item in yticklabels]
  114. yticks = [float(item) if item != '' else 0.0 for item in yticklabels]
  115. new_yticks = np.arange(min(yticks), max_wavenumber, 4000)
  116. plot_handle.set_yticks(new_yticks)
  117. new_yticklabels = [round(1e7/item) if item != 0 else item for item in new_yticks]
  118. ax.set_yticklabels(new_yticklabels, weight='bold')
  119. ax.get_yticklabels()[0].set_visible(False)
  120. #ax.get_yticklabels()[-1].set_visible(False)
  121. ax.get_yticklines()[0].set_visible(False)
  122. #ax.get_yticklines()[-2].set_visible(False)
  123. plt.tick_params(axis='both', which='major', labelsize=16)
  124. #f.tight_layout()
  125. #plt.show()
  126. f.savefig(Path(home_path + os.sep + 'result.pdf'), format='pdf', bbox_inches = "tight")
  127. if __name__ == '__main__':
  128. min_J = 8
  129. max_J = 9
  130. #max_wavenumber = 24500.0
  131. max_wavenumber = 28500.0
  132. #Ground State: [Xe]4f^{10} 6s^2(^5I_8)
  133. gs_J = 8
  134. gs_wavenumber = 0.0
  135. #626 nm transition GS ---> [Xe]4f^{10}(^5I_8) 6s6p(^3P_1)(8,1)_9
  136. red_J = 9
  137. red_wavenumber = 15972.35
  138. #421 nm transition GS ---> [Xe]4f^{10}(^5I_8) 6s6p(^1P_1)(8,1)_9
  139. blue_J = 9
  140. blue_wavenumber = 23736.610
  141. NIST_Dy_level_data_filename= 'Dylevels.txt'
  142. home_path = str(Path(__file__).parent.resolve())
  143. dataframe = parse_NIST_data(home_path + os.sep + NIST_Dy_level_data_filename, min_J, max_J, max_wavenumber)
  144. plot_level_structure_with_red_and_blue_transitions(dataframe, gs_J, gs_wavenumber, red_J, red_wavenumber, blue_J, blue_wavenumber, max_wavenumber, home_path)