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.

179 lines
7.0 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  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.5, '626.082 nm', color = '#db2929', fontsize = 16, fontweight = 'bold')
  72. #ax.text(red_J - 0.49, red_wavenumber * 0.42, '$(\\Gamma = 2\\pi\\times 136 ~ \mathrm{kHz})$', fontsize = 12, color = '#db2929')
  73. #draw red transition arrow
  74. ax.annotate('',
  75. xy=(red_J, red_wavenumber),
  76. xytext=(gs_J, gs_wavenumber),
  77. arrowprops=dict(color='#db2929', width=1.5),
  78. horizontalalignment='right',
  79. verticalalignment='top')
  80. #write electronic configuration for triplet excited state
  81. #ax.text(red_J + 0.35, red_wavenumber + 200, '$6s6p(^3P_1)$', fontsize = 10)
  82. #draw guide line for triplet excited state
  83. plt.axhline(y=red_wavenumber, color='m', linestyle='--', linewidth=1, alpha=0.5)
  84. #write wavelength of red transition
  85. ax.text(blue_J - 1.8, blue_wavenumber * 0.4, '421.291 nm', color = '#2630ea', fontsize = 16, fontweight = 'bold')
  86. #ax.text(blue_J - 1.85, blue_wavenumber * 0.4, '$(\\Gamma = 2\\pi\\times$', fontsize = 12, color = '#2630ea') #$(\\Gamma = 2\\pi\\times 32.2 ~ \mathrm{MHz})$
  87. #ax.text(blue_J - 1.5, blue_wavenumber * 0.35, '$32.2 ~ \mathrm{MHz})$', fontsize = 12, color = '#2630ea')
  88. #draw blue transition arrow
  89. ax.annotate('',
  90. xy=(blue_J, blue_wavenumber),
  91. xytext=(gs_J, gs_wavenumber),
  92. arrowprops=dict(color='#2630ea', width=3.5),
  93. horizontalalignment='right',
  94. verticalalignment='top')
  95. #write electronic configuration for singlet excited state
  96. #ax.text(blue_J + 0.35, blue_wavenumber + 200, '$6s6p(^1P_1)$', fontsize = 10)
  97. #draw guide line for singlet excited state
  98. plt.axhline(y=blue_wavenumber, color='m', linestyle='--', linewidth=1, alpha=0.5)
  99. #figure options
  100. f.canvas.draw()
  101. plt.xlabel('J', fontsize=20, fontweight = 'bold')
  102. #plt.ylabel('$\\tilde{v}~(cm^{-1})$', fontsize=16)
  103. plt.ylabel('Wavelength (nm)', fontsize=20, fontweight = 'bold')
  104. plot_handle.set_xticks(range(min_J-1, max_J+2))
  105. ax.get_xticklabels()[0].set_visible(False)
  106. ax.get_xticklabels()[-1].set_visible(False)
  107. ax.get_xticklines()[0].set_visible(False)
  108. ax.get_xticklines()[-2].set_visible(False)
  109. ax.set_xticklabels(ax.get_xticks(), weight='bold')
  110. yticklabels = [item.get_text() for item in ax.get_yticklabels()]
  111. yticklabels = ['' if item.startswith('') or item.startswith('0') else item for item in yticklabels]
  112. yticks = [float(item) if item != '' else 0.0 for item in yticklabels]
  113. new_yticks = np.arange(min(yticks), max_wavenumber, 4000)
  114. plot_handle.set_yticks(new_yticks)
  115. new_yticklabels = [round(1e7/item) if item != 0 else item for item in new_yticks]
  116. ax.set_yticklabels(new_yticklabels, weight='bold')
  117. ax.get_yticklabels()[0].set_visible(False)
  118. #ax.get_yticklabels()[-1].set_visible(False)
  119. ax.get_yticklines()[0].set_visible(False)
  120. #ax.get_yticklines()[-2].set_visible(False)
  121. plt.tick_params(axis='both', which='major', labelsize=16)
  122. #f.tight_layout()
  123. #plt.show()
  124. f.savefig(Path(home_path + os.sep + 'result.pdf'), format='pdf', bbox_inches = "tight")
  125. if __name__ == '__main__':
  126. min_J = 8
  127. max_J = 9
  128. #max_wavenumber = 24500.0
  129. max_wavenumber = 28500.0
  130. #Ground State: [Xe]4f^{10} 6s^2(^5I_8)
  131. gs_J = 8
  132. gs_wavenumber = 0.0
  133. #626 nm transition GS ---> [Xe]4f^{10}(^5I_8) 6s6p(^3P_1)(8,1)_9
  134. red_J = 9
  135. red_wavenumber = 15972.35
  136. #421 nm transition GS ---> [Xe]4f^{10}(^5I_8) 6s6p(^1P_1)(8,1)_9
  137. blue_J = 9
  138. blue_wavenumber = 23736.610
  139. NIST_Dy_level_data_filename= 'Dylevels.txt'
  140. home_path = str(Path(__file__).parent.resolve())
  141. dataframe = parse_NIST_data(home_path + os.sep + NIST_Dy_level_data_filename, min_J, max_J, max_wavenumber)
  142. 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)