orto 1.6.0__tar.gz → 1.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orto
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: A package to make life easier when performing Orca calculations.
5
5
  Home-page: https://orto.kragskow.group
6
6
  Author: Jon Kragskow
@@ -0,0 +1 @@
1
+ __version__ = '1.7.0'
@@ -776,7 +776,7 @@ def plot_xas_func(uargs):
776
776
  return
777
777
 
778
778
 
779
- def plot_abs_func(uargs):
779
+ def plot_abs_func(uargs, save_data_only=False):
780
780
  '''
781
781
  Wrapper for CLI plot abs call\n\n
782
782
 
@@ -789,6 +789,9 @@ def plot_abs_func(uargs):
789
789
  uargs : argparser object
790
790
  User arguments
791
791
 
792
+ save_data_only : bool, default=False
793
+ If True, saves generated spectrum data to file only.
794
+
792
795
  Returns
793
796
  -------
794
797
  None
@@ -862,7 +865,7 @@ def plot_abs_func(uargs):
862
865
  output_file
863
866
  )
864
867
  elif uargs.intensities == 'semi-classical':
865
- all_datasets = oe.AbsorptionSemiClassicalDipoleExtractor.extract(
868
+ all_datasets = oe.AbsorptionSemiClassicalDipoleExtractor.extract( # noqa
866
869
  output_file
867
870
  )
868
871
 
@@ -872,7 +875,7 @@ def plot_abs_func(uargs):
872
875
  d.AbsorptionData.from_extractor_dataset(
873
876
  dataset,
874
877
  uargs.intensities,
875
- remove_zero_osc=True
878
+ remove_zero_osc=uargs.zero_osc
876
879
  )
877
880
  for dataset in all_datasets
878
881
  ]
@@ -936,30 +939,60 @@ def plot_abs_func(uargs):
936
939
  comment=unique_names[it]
937
940
  )
938
941
 
939
- # Plot absorption spectrum
940
- plotter.plot_absorption_spectrum(
941
- abs_data,
942
- linecolor=colours[it],
943
- stickcolour=colours[it],
944
- osc_style=uargs.osc_style,
945
- normalise=uargs.normalise,
946
- window_title='',
947
- fig=fig,
948
- ax=ax,
949
- oax=oax,
950
- show=False,
951
- save=False,
952
- x_lim=[x_min, x_max],
953
- y_lim=uargs.y_lim,
954
- x_shift=uargs.x_shift[it],
955
- legend=legend
956
- )
942
+ if save_data_only:
943
+ # Save spectrum data to file
944
+ abs_data.save_spectrum_data(
945
+ f'absorption_spectrum_{output_file}.csv',
946
+ comments='Data from {}\nfwhm={}, lineshape={}\nintensities={}'.format( # noqa
947
+ output_file,
948
+ uargs.linewidth,
949
+ uargs.lineshape,
950
+ uargs.intensities
951
+ )
952
+ )
953
+ abs_data.save_transition_data(
954
+ f'transition_data_{output_file}.csv',
955
+ comments='Data from {}\nintensities={}'.format(
956
+ output_file,
957
+ uargs.intensities
958
+ )
959
+ )
960
+ ut.cprint(
961
+ f'Saved absorption data to absorption_spectrum_{output_file}.csv', # noqa
962
+ 'cyan'
963
+ )
964
+ ut.cprint(
965
+ f'Saved absorption spectrum data to transition_data_{output_file}.csv', # noqa
966
+ 'cyan'
967
+ )
968
+ else:
969
+ # Plot absorption spectrum
970
+ plotter.plot_absorption_spectrum(
971
+ abs_data,
972
+ linecolor=colours[it],
973
+ stickcolour=colours[it],
974
+ osc_style=uargs.osc_style,
975
+ normalise=uargs.normalise,
976
+ window_title='',
977
+ fig=fig,
978
+ ax=ax,
979
+ oax=oax,
980
+ show=False,
981
+ save=False,
982
+ x_lim=[x_min, x_max],
983
+ y_lim=uargs.y_lim,
984
+ x_shift=uargs.x_shift[it],
985
+ legend=legend
986
+ )
957
987
 
958
- if _SAVE_CONV[uargs.plot]:
959
- plt.savefig('absorption_spectrum.png', dpi=500)
988
+ if not save_data_only:
989
+ if _SAVE_CONV[uargs.plot]:
990
+ savename = f'absorption_spectrum_{output_file}.png'
991
+ plt.savefig(savename, dpi=500)
992
+ ut.cprint(f'Saved image to {savename}', 'cyan')
960
993
 
961
- if _SHOW_CONV[uargs.plot]:
962
- plt.show()
994
+ if _SHOW_CONV[uargs.plot]:
995
+ plt.show()
963
996
 
964
997
  return
965
998
 
@@ -1880,6 +1913,109 @@ def read_args(arg_list=None):
1880
1913
  )
1881
1914
  )
1882
1915
 
1916
+ gen_abs = gen_parser.add_parser(
1917
+ 'abs',
1918
+ description='Generates absorption spectrum data from TDDFT/CI calculation output', # noqa
1919
+ usage=ut.cstring('orto gen abs <output_file> [options]', 'cyan'), # noqa
1920
+ formatter_class=argparse.RawTextHelpFormatter
1921
+ )
1922
+ gen_abs._positionals.title = 'Mandatory Arguments'
1923
+
1924
+ gen_abs.set_defaults(func=lambda x: plot_abs_func(x, save_data_only=True))
1925
+
1926
+ gen_abs.add_argument(
1927
+ 'output_file',
1928
+ type=pathlib.Path,
1929
+ nargs='+',
1930
+ help='Orca output file name'
1931
+ )
1932
+
1933
+ gen_abs.add_argument(
1934
+ '--intensities',
1935
+ '-i',
1936
+ type=str,
1937
+ choices=['velocity', 'electric', 'semi-classical'],
1938
+ default='electric',
1939
+ help='Type of intensity to plot (orca_mapspc uses electric)'
1940
+ )
1941
+
1942
+ gen_abs.add_argument(
1943
+ '--linewidth',
1944
+ '-lw',
1945
+ type=float,
1946
+ default=1,
1947
+ help=(
1948
+ 'Width of signal (FWHM for Gaussian, Width for Lorentzian),'
1949
+ ' in same unit as x axis'
1950
+ )
1951
+ )
1952
+
1953
+ gen_abs.add_argument(
1954
+ '--lineshape',
1955
+ '-ls',
1956
+ type=str,
1957
+ choices=['gaussian', 'lorentzian'],
1958
+ default='lorentzian',
1959
+ help='Lineshape to use for each signal'
1960
+ )
1961
+
1962
+ gen_abs.add_argument(
1963
+ '--x_unit',
1964
+ type=str,
1965
+ choices=['energy', 'wavelength', 'wavenumber'],
1966
+ default='energy',
1967
+ help='x units to use for spectrum'
1968
+ )
1969
+
1970
+ gen_abs.add_argument(
1971
+ '--x_shift',
1972
+ type=float,
1973
+ default=None,
1974
+ nargs='+',
1975
+ help=(
1976
+ 'Shift spectrum by this amount in x units\n'
1977
+ 'Default: 0.'
1978
+ )
1979
+ )
1980
+
1981
+ gen_abs.add_argument(
1982
+ '--no_trim',
1983
+ action='store_true',
1984
+ default=False,
1985
+ help=(
1986
+ 'Do not trim spectrum to non-zero oscillator strength\n'
1987
+ 'Default: %(default)s'
1988
+ )
1989
+ )
1990
+
1991
+ gen_abs.add_argument(
1992
+ '--x_lim',
1993
+ nargs=2,
1994
+ default=['auto', 'auto'],
1995
+ help='x limits of spectrum'
1996
+ )
1997
+
1998
+ gen_abs.add_argument(
1999
+ '--normalise',
2000
+ '-n',
2001
+ action='store_true',
2002
+ default=False,
2003
+ help=(
2004
+ 'Normalises spectrum to maximum value\n'
2005
+ 'Default: %(default)s'
2006
+ )
2007
+ )
2008
+
2009
+ gen_abs.add_argument(
2010
+ '--zero_osc',
2011
+ default=1E-5,
2012
+ type=float,
2013
+ help=(
2014
+ 'Oscillator strengths below this value are treated as zero\n'
2015
+ 'Default: %(default)s'
2016
+ )
2017
+ )
2018
+
1883
2019
  plot_subprog = all_subparsers.add_parser(
1884
2020
  'plot',
1885
2021
  description='Plot data from orca file',
@@ -1988,16 +2124,6 @@ def read_args(arg_list=None):
1988
2124
  )
1989
2125
  )
1990
2126
 
1991
- plot_abs.add_argument(
1992
- '--no_trim',
1993
- action='store_true',
1994
- default=False,
1995
- help=(
1996
- 'Do not trim spectrum to non-zero oscillator strength\n'
1997
- 'Default: %(default)s'
1998
- )
1999
- )
2000
-
2001
2127
  plot_abs.add_argument(
2002
2128
  '--x_lim',
2003
2129
  nargs=2,
@@ -2023,6 +2149,16 @@ def read_args(arg_list=None):
2023
2149
  )
2024
2150
  )
2025
2151
 
2152
+ plot_abs.add_argument(
2153
+ '--zero_osc',
2154
+ default=1E-5,
2155
+ type=float,
2156
+ help=(
2157
+ 'Oscillator strengths below this value are treated as zero\n'
2158
+ 'Default: %(default)s'
2159
+ )
2160
+ )
2161
+
2026
2162
  plot_xes = plot_parser.add_parser(
2027
2163
  'xes',
2028
2164
  description='Plots XES from CI calculation output',
@@ -18,11 +18,15 @@ class AbsorptionSpectrum():
18
18
  x_unit: str
19
19
  Unit of x values: 'eV', 'nm', or 'cm^-1'
20
20
  x_label: str
21
- Label for x axis using LaTeX formatting
21
+ Label for x axis
22
+ x_label_mathmode: str
23
+ Label for x axis using LaTeX mathmode formatting
22
24
  y_unit: str
23
25
  Unit of y values, e.g. 'cm^-1 mol^-1 L'
24
26
  y_label: str
25
- Label for y axis using LaTeX formatting
27
+ Label for y axis
28
+ y_label_mathmode: str
29
+ Label for y axis using LaTeX mathmode formatting
26
30
  spectrum: NDArray
27
31
  Absorption spectrum values at each point in x_grid
28
32
  fwhm: float
@@ -37,11 +41,15 @@ class AbsorptionSpectrum():
37
41
  x_unit: str
38
42
  Unit of x values: 'eV', 'nm', or 'cm^-1'
39
43
  x_label: str
40
- Label for x axis using LaTeX formatting
44
+ Label for x axis
45
+ x_label_mathmode: str
46
+ Label for x axis using LaTeX mathmode formatting
41
47
  y_unit: str
42
48
  Unit of y values, e.g. 'cm^-1 mol^-1 L'
43
49
  y_label: str
44
- Label for y axis using LaTeX formatting
50
+ Label for y axis
51
+ y_label_mathmode: str
52
+ Label for y axis using LaTeX mathmode formatting
45
53
  y_values: NDArray
46
54
  Absorption spectrum values at each point in x_grid
47
55
  fwhm: float
@@ -50,14 +58,17 @@ class AbsorptionSpectrum():
50
58
  Comment or metadata for the spectrum
51
59
  '''
52
60
 
53
- def __init__(self, x_grid: NDArray, x_unit: str, x_label: str, y_unit: str,
54
- y_label: str, y_values: NDArray, fwhm: float,
61
+ def __init__(self, x_grid: NDArray, x_unit: str, x_label: str,
62
+ x_label_mathmode: str, y_unit: str, y_label: str,
63
+ y_label_mathmode: str, y_values: NDArray, fwhm: float,
55
64
  comment: str = '') -> None:
56
65
  self.x_grid = x_grid
57
66
  self.x_unit = x_unit
58
67
  self.x_label = x_label
68
+ self.x_label_mathmode = x_label_mathmode
59
69
  self.y_unit = y_unit
60
70
  self.y_label = y_label
71
+ self.y_label_mathmode = y_label_mathmode
61
72
  self.fwhm = fwhm
62
73
  self.y_values = y_values
63
74
  self.comment = comment
@@ -172,7 +183,7 @@ class AbsorptionData():
172
183
 
173
184
  @classmethod
174
185
  def from_extractor(cls, extractor: oe.AbsorptionExtractor,
175
- remove_zero_osc: bool = True) -> list['AbsorptionData']: # noqa
186
+ remove_zero_osc: float = 1E-4) -> list['AbsorptionData']: # noqa
176
187
  '''
177
188
  Creates AbsorptionData object from data extractor
178
189
 
@@ -180,8 +191,8 @@ class AbsorptionData():
180
191
  ----------
181
192
  extractor: Extractor
182
193
  Data extractor object containing transition data
183
- remove_zero_osc: bool
184
- If True, removes transitions with zero oscillator strength
194
+ remove_zero_osc: float, default=1E-4
195
+ Remove transitions with oscillator strength below this value
185
196
 
186
197
  Returns
187
198
  -------
@@ -204,7 +215,7 @@ class AbsorptionData():
204
215
  def from_extractor_dataset(cls,
205
216
  dataset: dict[str, list[int | float]],
206
217
  operator: str,
207
- remove_zero_osc: bool = False) -> 'AbsorptionData': # noqa
218
+ remove_zero_osc: float = 1E-4) -> 'AbsorptionData': # noqa
208
219
  '''
209
220
  Creates AbsorptionData object from data extractor
210
221
 
@@ -214,8 +225,8 @@ class AbsorptionData():
214
225
  Dataset from orto AbsorptionExtractor.data
215
226
  operator: str
216
227
  Type of operator used to calculate transitions
217
- remove_zero_osc: bool
218
- If True, removes transitions with zero oscillator strength
228
+ remove_zero_osc: float, default=1E-4
229
+ Remove transitions with oscillator strength below this value
219
230
 
220
231
  Returns
221
232
  -------
@@ -228,16 +239,21 @@ class AbsorptionData():
228
239
 
229
240
  # Remove transitions with zero oscillator strength from
230
241
  # beginning and end of spectrum
231
- if remove_zero_osc:
232
- osc_strengths = np.array(osc_strengths)
233
- # Find first non-zero oscillator strength
234
- first_nonzero = np.where(osc_strengths > 1E-4)[0][0]
235
- # Find last non-zero oscillator strength
236
- last_nonzero = np.where(osc_strengths > 1E-4)[0][-1]
242
+ osc_strengths = np.array(osc_strengths)
243
+ # Find first non-zero oscillator strength
244
+ try:
245
+ first_nonzero = np.where(osc_strengths > remove_zero_osc)[0][0]
246
+ except IndexError:
247
+ raise DataFormattingError(
248
+ 'No transitions with oscillator strength above '
249
+ f'{remove_zero_osc} found.'
250
+ )
251
+ # Find last non-zero oscillator strength
252
+ last_nonzero = np.where(osc_strengths > remove_zero_osc)[0][-1]
237
253
 
238
- # Trim data
239
- energies = energies[first_nonzero:last_nonzero + 1]
240
- osc_strengths = osc_strengths[first_nonzero:last_nonzero + 1]
254
+ # Trim data
255
+ energies = energies[first_nonzero:last_nonzero + 1]
256
+ osc_strengths = osc_strengths[first_nonzero:last_nonzero + 1]
241
257
 
242
258
  return cls(energies, osc_strengths, operator)
243
259
 
@@ -287,20 +303,24 @@ class AbsorptionData():
287
303
  if x_type == 'energy':
288
304
  x_unit = 'eV'
289
305
  x_label = 'Energy (eV)'
306
+ x_label_mathmode = 'Energy (eV)'
290
307
  x_vals = self.energies
291
308
  elif x_type == 'wavelength':
292
309
  x_unit = 'nm'
293
310
  x_label = 'Wavelength (nm)'
311
+ x_label_mathmode = 'Wavelength (nm)'
294
312
  x_vals = self.wavelengths
295
313
  elif x_type == 'wavenumber':
296
314
  x_unit = 'cm^-1'
297
- x_label = r'Wavenumber (cm$\mathregular{^{-1}}$)'
315
+ x_label = r'Wavenumber (cm⁻¹)'
316
+ x_label_mathmode = r'Wavenumber (cm$\mathregular{^{-1}}$)'
298
317
  x_vals = self.wavenumbers
299
318
  else:
300
319
  raise DataFormattingError(f'Invalid x_type: {x_type}')
301
320
 
302
321
  y_unit = 'cm mol^-1 eV^-1'
303
- y_label = r'$\epsilon$ (cm$^\mathregular{-1}$ mol$^\mathregular{-1}$ L)' # noqa
322
+ y_label = r'ε (cm⁻¹ mol⁻¹ L))'
323
+ y_label_mathmode = r'$\epsilon$ (cm$^\mathregular{-1}$ mol$^\mathregular{-1}$ L)' # noqa
304
324
 
305
325
  # Create grid for spectrum
306
326
  x_grid = np.linspace(x_min, x_max, num_points)
@@ -339,8 +359,10 @@ class AbsorptionData():
339
359
  x_grid=x_grid,
340
360
  x_unit=x_unit,
341
361
  x_label=x_label,
362
+ x_label_mathmode=x_label_mathmode,
342
363
  y_unit=y_unit,
343
364
  y_label=y_label,
365
+ y_label_mathmode=y_label_mathmode,
344
366
  y_values=y_vals,
345
367
  fwhm=fwhm,
346
368
  comment=comment
@@ -349,3 +371,81 @@ class AbsorptionData():
349
371
  self.spectrum = spectrum
350
372
 
351
373
  return
374
+
375
+ def save_spectrum_data(self, filename: str, comments: str = '') -> None:
376
+ '''
377
+ Saves absorption spectrum data to CSV file
378
+
379
+ Parameters
380
+ ----------
381
+ filename: str
382
+ Name of output CSV file
383
+ comments: str
384
+ Comments to add at the top of the file
385
+
386
+ Returns
387
+ -------
388
+ None
389
+ '''
390
+
391
+ if self.spectrum is None:
392
+ raise ValueError(
393
+ 'Spectrum has not been generated yet\n'
394
+ 'Call generate_spectrum() first.'
395
+ )
396
+
397
+ # Select x values based on x_type
398
+ if self.spectrum.x_label.split()[0].lower() == 'energy':
399
+ x_vals = self.spectrum.x_grid
400
+ elif self.spectrum.x_label.split()[0].lower() == 'wavelength':
401
+ x_vals = const.EV_TO_NM / self.spectrum.x_grid
402
+ elif self.spectrum.x_label.split()[0].lower() == 'wavenumber':
403
+ x_vals = self.spectrum.x_grid * const.EV_TO_WAVENUMBER
404
+
405
+ if len(comments):
406
+ header = comments + f'\n{self.spectrum.x_label}, ε (cm⁻¹ mol⁻¹ L))'
407
+ else:
408
+ header = f'{self.spectrum.x_label}, ε (cm⁻¹ mol⁻¹ L))'
409
+
410
+ # Save to CSV
411
+ np.savetxt(
412
+ filename,
413
+ np.column_stack((x_vals, self.spectrum.y_values)),
414
+ delimiter=',',
415
+ header=header,
416
+ comments='#',
417
+ )
418
+
419
+ def save_transition_data(self, filename: str, comments: str = '') -> None:
420
+ '''
421
+ Saves transition data to CSV file
422
+
423
+ Parameters
424
+ ----------
425
+ filename: str
426
+ Name of output CSV file
427
+ comments: str
428
+ Comments to add at the top of the file
429
+
430
+ Returns
431
+ -------
432
+ None
433
+ '''
434
+
435
+ if len(comments):
436
+ header = comments + '\nEnergy (eV),Wavelength (nm),Wavenumber (cm⁻¹),Oscillator Strength' # noqa
437
+ else:
438
+ header = 'Energy (eV),Wavelength (nm),Wavenumber (cm⁻¹),Oscillator Strength' # noqa
439
+
440
+ np.savetxt(
441
+ filename,
442
+ np.column_stack((
443
+ self.energies,
444
+ self.wavelengths,
445
+ self.wavenumbers,
446
+ self.osc_strengths
447
+ )),
448
+ delimiter=',',
449
+ header=header,
450
+ comments='#',
451
+ )
@@ -115,20 +115,20 @@ def plot_absorption_spectrum(abs_data: data.AbsorptionData,
115
115
  else:
116
116
  _y_values = abs_data.spectrum.y_values
117
117
  _osc = abs_data.osc_strengths
118
- ax.set_ylabel(abs_data.spectrum.y_label)
118
+ ax.set_ylabel(abs_data.spectrum.y_label_mathmode)
119
119
 
120
- if abs_data.spectrum.x_label == 'Wavelength (nm)':
120
+ if abs_data.spectrum.x_label.split()[0].lower() == 'wavelength':
121
121
  ax.invert_xaxis()
122
122
  stick_x_values = [
123
123
  wavelength + x_shift
124
124
  for wavelength in abs_data.wavelengths
125
125
  ]
126
- elif abs_data.spectrum.x_label == 'Energy (eV)':
126
+ elif abs_data.spectrum.x_label.split()[0].lower() == 'energy':
127
127
  stick_x_values = [
128
128
  energy + x_shift
129
129
  for energy in abs_data.energies
130
130
  ]
131
- elif abs_data.spectrum.x_label == r'Wavenumber (cm$\mathregular{^{-1}}$)':
131
+ elif abs_data.spectrum.x_label.split()[0].lower() == r'wavenumber':
132
132
  stick_x_values = [
133
133
  wavenumber + x_shift
134
134
  for wavenumber in abs_data.wavenumbers
@@ -199,7 +199,7 @@ def plot_absorption_spectrum(abs_data: data.AbsorptionData,
199
199
  ax.xaxis.set_minor_locator(AutoMinorLocator())
200
200
  ax.yaxis.set_minor_locator(AutoMinorLocator())
201
201
 
202
- ax.set_xlabel(abs_data.spectrum.x_label)
202
+ ax.set_xlabel(abs_data.spectrum.x_label_mathmode)
203
203
 
204
204
  fig.tight_layout()
205
205
 
@@ -396,6 +396,7 @@ def find_unique_substring(names):
396
396
  break
397
397
  return unique_names
398
398
 
399
+
399
400
  def is_floatable(string: str) -> bool:
400
401
  '''
401
402
  Checks if string can be converted to float
@@ -415,4 +416,4 @@ def is_floatable(string: str) -> bool:
415
416
  float(string)
416
417
  return True
417
418
  except ValueError:
418
- return False
419
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orto
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: A package to make life easier when performing Orca calculations.
5
5
  Home-page: https://orto.kragskow.group
6
6
  Author: Jon Kragskow
@@ -8,7 +8,7 @@ Please see the `orto` documentation for more details.
8
8
 
9
9
  # DO NOT EDIT THIS NUMBER!
10
10
  # IT IS AUTOMATICALLY CHANGED BY python-semantic-release
11
- __version__ = '1.6.0'
11
+ __version__ = '1.7.0'
12
12
 
13
13
  setuptools.setup(
14
14
  name='orto',
@@ -1 +0,0 @@
1
- __version__ = '1.6.0'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes