orto 1.10.1__tar.gz → 1.11.1__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.10.1
3
+ Version: 1.11.1
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
@@ -14,7 +14,7 @@ Requires-Python: >=3.10
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: numpy>=2.1.2
17
- Requires-Dist: xyz_py>=5.13.1
17
+ Requires-Dist: xyz_py>=5.19.2
18
18
  Requires-Dist: matplotlib>=3.9.2
19
19
  Requires-Dist: extto>=1.0.1
20
20
  Requires-Dist: pandas>=2.2.3
@@ -0,0 +1 @@
1
+ __version__ = '1.11.1'
@@ -1,6 +1,4 @@
1
1
  import argparse
2
- import xyz_py as xyzp
3
- from xyz_py.atomic import elements as atomic_elements
4
2
  import sys
5
3
  import pathlib
6
4
  import os
@@ -9,14 +7,11 @@ import subprocess
9
7
  import csv
10
8
  import numpy as np
11
9
  import re
12
- from mmap import mmap, ACCESS_READ
13
- from shutil import move as shutilmove
14
- from subto.job import SlurmJob
10
+ from xyz_py.atomic import elements as atomic_elements
15
11
 
16
12
  from . import job
17
13
  from . import utils as ut
18
14
  from . import constants as cst
19
- from . import data as d
20
15
  from .exceptions import DataNotFoundError, DataFormattingError
21
16
 
22
17
  _SHOW_CONV = {
@@ -40,9 +35,9 @@ def extract_coords_func(uargs, save=True):
40
35
 
41
36
  Parameters
42
37
  ----------
43
- args : argparser object
38
+ uargs: argparser object
44
39
  command line arguments
45
- save : bool, default=True
40
+ save: bool, default=True
46
41
  If True, saves data to file. If False, prints to stdout.
47
42
 
48
43
  Returns
@@ -50,6 +45,7 @@ def extract_coords_func(uargs, save=True):
50
45
  None
51
46
  '''
52
47
  from . import extractor as oe
48
+ import xyz_py as xyzp
53
49
 
54
50
  # Open file and extract coordinates
55
51
  labels, coords = oe.get_coords(
@@ -120,7 +116,7 @@ def extract_sf_energies_func(uargs):
120
116
  style = doc.styles['Normal']
121
117
  font = style.font
122
118
  font.name = 'Arial'
123
- font.size = Pt(9)
119
+ font.size = Pt(12)
124
120
 
125
121
  # For each extracted section, print matrix, vectors, and values
126
122
  for it, data in enumerate(all_data):
@@ -154,7 +150,13 @@ def extract_sf_energies_func(uargs):
154
150
  cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
155
151
  cell.paragraphs[0].style = 'Normal'
156
152
 
157
- doc.save(out_name)
153
+ try:
154
+ doc.save(out_name)
155
+ except PermissionError:
156
+ ut.red_exit(
157
+ f'Cannot write to {out_name}\n'
158
+ 'is the file open somewhere else?'
159
+ )
158
160
 
159
161
  ut.cprint(f'Data written to {out_name}', 'cyan')
160
162
 
@@ -191,15 +193,205 @@ def extract_so_energies_func(uargs):
191
193
  return
192
194
 
193
195
 
196
+ def extract_hyperfine_func(uargs, save=True):
197
+ '''
198
+ Wrapper for cli call to extract hyperfine
199
+
200
+ Parameters
201
+ ----------
202
+ uargs: argparser object
203
+ command line arguments
204
+ save: bool, default=True
205
+ If True, saves data to file, else prints to stdout.
206
+ '''
207
+
208
+ from . import extractor as oe
209
+ from docx import Document
210
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
211
+ from docx.enum.table import WD_ALIGN_VERTICAL
212
+ from docx.shared import Pt
213
+
214
+ try:
215
+ all_data = oe.HyperfineExtractor.extract(uargs.output_file)
216
+ except (DataFormattingError, ValueError) as e:
217
+ ut.red_exit(str(e))
218
+
219
+ if not save:
220
+ for data in all_data:
221
+ for key, val in data.items():
222
+ print(f'{key}:')
223
+ print(val)
224
+ sys.exit(0)
225
+
226
+ if uargs.output_format == 'txt':
227
+ out_name = f'{uargs.output_file.stem}_hyperfine.txt'
228
+ with open(out_name, 'w') as f:
229
+ f.write(f'Data from {uargs.output_file}\n')
230
+ f.write('All values are in MHz')
231
+ for data in all_data:
232
+ if len(all_data) > 1:
233
+ f.write('=================\n')
234
+ for key, val in data.items():
235
+ f.write(f'{key}:\n')
236
+ f.write(str(val).replace('[', '').replace(']', ''))
237
+ f.write('\n')
238
+
239
+ if uargs.output_format == 'docx':
240
+ out_name = f'{uargs.output_file.stem}_hyperfine.docx'
241
+
242
+ title = 'Hyperfine coupling data from DFT'
243
+ title += (f'Data from {uargs.output_file}\n')
244
+
245
+ # Create document
246
+ doc = Document()
247
+
248
+ doc.add_heading(title, 0)
249
+
250
+ # Add style
251
+ style = doc.styles['Normal']
252
+ font = style.font
253
+ font.name = 'Arial'
254
+ font.size = Pt(12)
255
+
256
+ # For each extracted section, print all data
257
+ for data in all_data:
258
+ if len(all_data) > 1:
259
+ doc.add_paragraph(
260
+ f'Nucleus: {data['nucleus']}, Isotope: {data['isotope']}'
261
+ )
262
+
263
+ # Full matrix
264
+ matrix = doc.add_table(rows=5, cols=3)
265
+
266
+ matrix.cell(0, 0).merge(
267
+ matrix.cell(0, 1)
268
+ ).merge(
269
+ matrix.cell(0, 2)
270
+ )
271
+
272
+ matrix.cell(0, 0).text = 'Full Hyperfine Tensor / MHz'
273
+
274
+ matrix.cell(1, 0).text = '{:.4f}'.format(data['matrix'][0, 0])
275
+ matrix.cell(2, 0).text = '{:.4f}'.format(data['matrix'][0, 1])
276
+ matrix.cell(3, 0).text = '{:.4f}'.format(data['matrix'][0, 2])
277
+
278
+ matrix.cell(1, 1).text = '{:.4f}'.format(data['matrix'][1, 0])
279
+ matrix.cell(2, 1).text = '{:.4f}'.format(data['matrix'][1, 1])
280
+ matrix.cell(3, 1).text = '{:.4f}'.format(data['matrix'][1, 2])
281
+
282
+ matrix.cell(1, 2).text = '{:.4f}'.format(data['matrix'][2, 0])
283
+ matrix.cell(2, 2).text = '{:.4f}'.format(data['matrix'][2, 1])
284
+ matrix.cell(3, 2).text = '{:.4f}'.format(data['matrix'][2, 2])
285
+
286
+ matrix.cell(4, 0).text = 'Isotropic / MHz'
287
+
288
+ # Merge three cells for isotropic value
289
+ matrix.cell(4, 1).merge(
290
+ matrix.cell(4, 2)
291
+ )
292
+ matrix.cell(4, 1).text = f'{data['iso']:.4f}'
293
+
294
+ doc.add_paragraph('\n')
295
+
296
+ for row in matrix.rows:
297
+ for cell in row.cells:
298
+ cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # noqa
299
+ cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
300
+ cell.paragraphs[0].style = 'Normal'
301
+
302
+ # g values and g vectors
303
+ eigs = doc.add_table(rows=4, cols=4)
304
+ eigs.cell(0, 1).merge(eigs.cell(0, 1)).merge(eigs.cell(0, 2)).merge(eigs.cell(0, 3)) # noqa
305
+
306
+ eigs.cell(0, 0).text = 'Values / MHz'
307
+ eigs.cell(0, 1).text = 'Vectors'
308
+
309
+ eigs.cell(1, 0).text = '{:.4f}'.format(data['values'][0])
310
+ eigs.cell(2, 0).text = '{:.4f}'.format(data['values'][1])
311
+ eigs.cell(3, 0).text = '{:.4f}'.format(data['values'][2])
312
+
313
+ eigs.cell(1, 1).text = '{:.4f}'.format(data['vectors'][0, 0])
314
+ eigs.cell(2, 1).text = '{:.4f}'.format(data['vectors'][0, 1])
315
+ eigs.cell(3, 1).text = '{:.4f}'.format(data['vectors'][0, 2])
316
+
317
+ eigs.cell(1, 2).text = '{:.4f}'.format(data['vectors'][1, 0])
318
+ eigs.cell(2, 2).text = '{:.4f}'.format(data['vectors'][1, 1])
319
+ eigs.cell(3, 2).text = '{:.4f}'.format(data['vectors'][1, 2])
320
+
321
+ eigs.cell(1, 3).text = '{:.4f}'.format(data['vectors'][2, 0])
322
+ eigs.cell(2, 3).text = '{:.4f}'.format(data['vectors'][2, 1])
323
+ eigs.cell(3, 3).text = '{:.4f}'.format(data['vectors'][2, 2])
324
+
325
+ for row in eigs.rows:
326
+ for cell in row.cells:
327
+ cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # noqa
328
+ cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
329
+ cell.paragraphs[0].style = 'Normal'
330
+ doc.add_paragraph('\n')
331
+
332
+ components = doc.add_table(rows=5, cols=5)
333
+ components.cell(1, 0).text = 'Fermi Contact'
334
+ components.cell(2, 0).text = 'Spin Dipole'
335
+ components.cell(3, 0).text = 'Diamagnetic (Gauge Corr.)'
336
+ components.cell(4, 0).text = 'Spin-Orbit'
337
+
338
+ components.cell(0, 1).text = 'x / MHz'
339
+ components.cell(0, 2).text = 'y / MHz'
340
+ components.cell(0, 3).text = 'z / MHz'
341
+ components.cell(0, 4).text = 'Average / MHz'
342
+
343
+ if len(data['fc']):
344
+ components.cell(1, 1).text = f'{data['fc'][0]:.4f}'
345
+ components.cell(1, 2).text = f'{data['fc'][1]:.4f}'
346
+ components.cell(1, 3).text = f'{data['fc'][2]:.4f}'
347
+ components.cell(1, 4).text = f'{np.mean(data['fc']):.4f}'
348
+ if len(data['sd']):
349
+ components.cell(2, 1).text = f'{data['sd'][0]:.4f}'
350
+ components.cell(2, 2).text = f'{data['sd'][1]:.4f}'
351
+ components.cell(2, 3).text = f'{data['sd'][2]:.4f}'
352
+ components.cell(2, 4).text = f'{np.mean(data['sd']):.4f}'
353
+ if len(data['dia']):
354
+ components.cell(3, 1).text = f'{data['dia'][0]:.4f}'
355
+ components.cell(3, 2).text = f'{data['dia'][1]:.4f}'
356
+ components.cell(3, 3).text = f'{data['dia'][2]:.4f}'
357
+ components.cell(3, 4).text = f'{np.mean(data['dia']):.4f}'
358
+ if len(data['orb']):
359
+ components.cell(4, 1).text = f'{data['orb'][0]:.4f}'
360
+ components.cell(4, 2).text = f'{data['orb'][1]:.4f}'
361
+ components.cell(4, 3).text = f'{data['orb'][2]:.4f}'
362
+ components.cell(4, 4).text = f'{np.mean(data['orb']):.4f}'
363
+
364
+ for tab in [matrix, eigs, components]:
365
+ for row in tab.rows:
366
+ for cell in row.cells:
367
+ cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # noqa
368
+ cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
369
+ cell.paragraphs[0].style = 'Normal'
370
+
371
+ doc.add_page_break()
372
+
373
+ try:
374
+ doc.save(out_name)
375
+ except PermissionError:
376
+ ut.red_exit(
377
+ f'Cannot write to {out_name}\n'
378
+ 'is the file open somewhere else?'
379
+ )
380
+
381
+ ut.cprint(f'Data written to {out_name}', 'cyan')
382
+
383
+ return
384
+
385
+
194
386
  def extract_gmatrix_func(uargs, save=True):
195
387
  '''
196
388
  Wrapper for cli call to extract gmatrix
197
389
 
198
390
  Parameters
199
391
  ----------
200
- args : argparser object
392
+ uargs: argparser object
201
393
  command line arguments
202
- save : bool, default=True
394
+ save: bool, default=True
203
395
  If True, saves data to file. If False, prints to stdout.
204
396
 
205
397
  Returns
@@ -220,6 +412,7 @@ def extract_gmatrix_func(uargs, save=True):
220
412
  'dft': oe.GMatrixDFTExtractor
221
413
  }
222
414
 
415
+ # Check for linear response hyperfines
223
416
  if oe.EPRNMRDetector(uargs.output_file):
224
417
  uargs.type = 'dft'
225
418
 
@@ -269,7 +462,7 @@ def extract_gmatrix_func(uargs, save=True):
269
462
  style = doc.styles['Normal']
270
463
  font = style.font
271
464
  font.name = 'Arial'
272
- font.size = Pt(9)
465
+ font.size = Pt(12)
273
466
 
274
467
  # For each extracted section, print matrix, vectors, and values
275
468
  for it, data in enumerate(all_data):
@@ -331,7 +524,13 @@ def extract_gmatrix_func(uargs, save=True):
331
524
  cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
332
525
  cell.paragraphs[0].style = 'Normal'
333
526
 
334
- doc.save(out_name)
527
+ try:
528
+ doc.save(out_name)
529
+ except PermissionError:
530
+ ut.red_exit(
531
+ f'Cannot write to {out_name}\n'
532
+ 'is the file open somewhere else?'
533
+ )
335
534
 
336
535
  ut.cprint(f'Data written to {out_name}', 'cyan')
337
536
 
@@ -344,13 +543,14 @@ def gen_spden_func(uargs):
344
543
 
345
544
  Parameters
346
545
  ----------
347
- uargs : argparser object
546
+ uargs: argparser object
348
547
  User arguments
349
548
 
350
549
  Returns
351
550
  -------
352
551
  None
353
552
  '''
553
+ from subto.job import SlurmJob
354
554
 
355
555
  # Check orca module
356
556
  if len(uargs.orca_load):
@@ -424,13 +624,15 @@ def gen_trunc_molden_func(uargs):
424
624
 
425
625
  Parameters
426
626
  ----------
427
- uargs : argparser object
627
+ uargs: argparser object
428
628
  User arguments
429
629
 
430
630
  Returns
431
631
  -------
432
632
  None
433
633
  '''
634
+ from mmap import mmap, ACCESS_READ
635
+ from shutil import move as shutilmove
434
636
 
435
637
  # Read molden file in as binary string
436
638
  # and find number of MOs by counting number of
@@ -493,7 +695,7 @@ def gen_job_func(uargs):
493
695
 
494
696
  Parameters
495
697
  ----------
496
- uargs : argparser object
698
+ uargs: argparser object
497
699
  User arguments
498
700
 
499
701
  Returns
@@ -658,7 +860,7 @@ def plot_xes_func(uargs):
658
860
 
659
861
  Parameters
660
862
  ----------
661
- uargs : argparser object
863
+ uargs: argparser object
662
864
  User arguments
663
865
 
664
866
  Returns
@@ -774,7 +976,7 @@ def plot_xas_func(uargs):
774
976
 
775
977
  Parameters
776
978
  ----------
777
- uargs : argparser object
979
+ uargs: argparser object
778
980
  User arguments
779
981
 
780
982
  Returns
@@ -838,6 +1040,7 @@ def plot_xas_func(uargs):
838
1040
 
839
1041
  # Remove transitions with zero oscillator strength from
840
1042
  # beginning and end of spectrum
1043
+ print(data['fosc'], x_values)
841
1044
  if not uargs.no_trim:
842
1045
  # Find first non-zero oscillator strength
843
1046
  first_nonzero = np.where(data['fosc'] > 1E-6)[0][0]
@@ -883,10 +1086,10 @@ def plot_abs_func(uargs, save_data_only=False):
883
1086
 
884
1087
  Parameters
885
1088
  ----------
886
- uargs : argparser object
1089
+ uargs: argparser object
887
1090
  User arguments
888
1091
 
889
- save_data_only : bool, default=False
1092
+ save_data_only: bool, default=False
890
1093
  If True, saves generated spectrum data to file only.
891
1094
 
892
1095
  Returns
@@ -898,6 +1101,7 @@ def plot_abs_func(uargs, save_data_only=False):
898
1101
  import matplotlib.colors as mcolors
899
1102
  from . import plotter
900
1103
  from . import extractor as oe
1104
+ from . import data as d
901
1105
 
902
1106
  # Change matplotlib font size to be larger
903
1107
  mpl.rcParams.update({'font.size': 12})
@@ -1107,14 +1311,13 @@ def plot_ir_func(uargs):
1107
1311
 
1108
1312
  Parameters
1109
1313
  ----------
1110
- uargs : argparser object
1314
+ uargs: argparser object
1111
1315
  User arguments
1112
1316
 
1113
1317
  Returns
1114
1318
  -------
1115
1319
  None
1116
1320
  '''
1117
- import matplotlib.pyplot as plt
1118
1321
  import matplotlib as mpl
1119
1322
  from . import plotter
1120
1323
  from . import extractor as oe
@@ -1152,7 +1355,7 @@ def distort_func(uargs):
1152
1355
 
1153
1356
  Parameters
1154
1357
  ----------
1155
- args : argparser object
1358
+ uargs: argparser object
1156
1359
  command line arguments
1157
1360
 
1158
1361
  Returns
@@ -1160,6 +1363,7 @@ def distort_func(uargs):
1160
1363
  None
1161
1364
 
1162
1365
  '''
1366
+ import xyz_py as xyzp
1163
1367
  from . import extractor as oe
1164
1368
 
1165
1369
  # Open file and extract coordinates
@@ -1198,9 +1402,9 @@ def extract_orbs_func(uargs, save=True) -> None:
1198
1402
 
1199
1403
  Parameters
1200
1404
  ----------
1201
- args : argparser object
1405
+ uargs: argparser object
1202
1406
  command line arguments
1203
- save : bool, default=True
1407
+ save: bool, default=True
1204
1408
  If True, saves data to file. If False, prints to stdout.
1205
1409
 
1206
1410
  Returns
@@ -1337,7 +1541,7 @@ def extract_orbs_func(uargs, save=True) -> None:
1337
1541
  total = 0.
1338
1542
  for row, val in mo.items():
1339
1543
  if val > uargs.threshold and row[1] in uargs.elements:
1340
- _output += f' {row[1]+str(row[0]):5} {row[2]:5} : {val:>5.1f} %\n' # noqa
1544
+ _output += f' {row[1]+str(row[0]):5} {row[2]:5}: {val:>5.1f} %\n' # noqa
1341
1545
  total += val
1342
1546
  if len(_output):
1343
1547
  print(f'MO #{mo_num} (Occ={occupancies[mo_num]}, E={energies[mo_num]: .5f}):') # noqa
@@ -1374,9 +1578,9 @@ def extract_freq_func(uargs, save=True):
1374
1578
 
1375
1579
  Parameters
1376
1580
  ----------
1377
- args : argparser object
1581
+ uargs: argparser object
1378
1582
  command line arguments
1379
- save : bool, default=True
1583
+ save: bool, default=True
1380
1584
  If True, saves data to file. If False, prints to stdout.
1381
1585
 
1382
1586
  Returns
@@ -1425,14 +1629,15 @@ def extract_pop_func(uargs, save=True) -> None:
1425
1629
 
1426
1630
  Parameters
1427
1631
  ----------
1428
- uargs : argparser object
1632
+ uargs: argparser object
1429
1633
  User arguments
1430
- save : bool, default=False
1634
+ save: bool, default=False
1431
1635
  If True, saves data to file. If False, prints to stdout.
1432
1636
  Returns
1433
1637
  -------
1434
1638
  None
1435
1639
  '''
1640
+ import xyz_py as xyzp
1436
1641
  from . import extractor as oe
1437
1642
 
1438
1643
  if uargs.flavour in ['loewdin', 'lowdin']:
@@ -1486,7 +1691,7 @@ def extract_pop_func(uargs, save=True) -> None:
1486
1691
  _chg = sum([datum[0][labels[ind]] for ind in entity])
1487
1692
  _spin = sum([datum[1][labels[ind]] for ind in entity])
1488
1693
  ut.cprint(
1489
- f'{entity_name} : {_chg:.4f} {_spin:.4f}',
1694
+ f'{entity_name}: {_chg:.4f} {_spin:.4f}',
1490
1695
  'cyan'
1491
1696
  )
1492
1697
 
@@ -1499,7 +1704,7 @@ def plot_susc_func(uargs) -> None:
1499
1704
 
1500
1705
  Parameters
1501
1706
  ----------
1502
- args : argparser object
1707
+ uargs: argparser object
1503
1708
  command line arguments
1504
1709
 
1505
1710
  Returns
@@ -1611,7 +1816,7 @@ def plot_ailft_func(uargs) -> None:
1611
1816
 
1612
1817
  Parameters
1613
1818
  ----------
1614
- args : argparser object
1819
+ uargs: argparser object
1615
1820
  command line arguments
1616
1821
 
1617
1822
  Returns
@@ -1697,7 +1902,7 @@ def read_args(arg_list=None):
1697
1902
 
1698
1903
  Parameters
1699
1904
  ----------
1700
- args : argparser object
1905
+ uargs: argparser object
1701
1906
  command line arguments
1702
1907
 
1703
1908
  Returns
@@ -1792,6 +1997,32 @@ def read_args(arg_list=None):
1792
1997
  help='Number of frequencies to print, default is all'
1793
1998
  )
1794
1999
 
2000
+ extract_hyperfine = extract_parser.add_parser(
2001
+ 'hyperfine',
2002
+ description='Extracts hyperfine couplings from Orca output file',
2003
+ usage=ut.cstring(
2004
+ 'orto extract hyperfine <output_file> [options]',
2005
+ 'cyan'
2006
+ ),
2007
+ formatter_class=argparse.RawTextHelpFormatter
2008
+ )
2009
+ extract_hyperfine._positionals.title = 'Mandatory Arguments'
2010
+ extract_hyperfine.set_defaults(func=extract_hyperfine_func)
2011
+
2012
+ extract_hyperfine.add_argument(
2013
+ 'output_file',
2014
+ type=pathlib.Path,
2015
+ help='Path to/Name of Orca output file containing HYPERFINE section'
2016
+ )
2017
+
2018
+ extract_hyperfine.add_argument(
2019
+ '--output_format',
2020
+ type=str,
2021
+ help='Format of outputted data file',
2022
+ choices=['txt', 'docx'],
2023
+ default='txt'
2024
+ )
2025
+
1795
2026
  extract_gmatrix = extract_parser.add_parser(
1796
2027
  'gmatrix',
1797
2028
  description='Extracts coordinates from Orca output file',
@@ -1804,14 +2035,14 @@ def read_args(arg_list=None):
1804
2035
  extract_gmatrix.add_argument(
1805
2036
  'output_file',
1806
2037
  type=pathlib.Path,
1807
- help='Orca output file name containing G-MATRIX block'
2038
+ help='Path to/Name of Orca output file containing G-MATRIX block'
1808
2039
  )
1809
2040
 
1810
2041
  extract_gmatrix.add_argument(
1811
2042
  '--type',
1812
2043
  type=str,
1813
- help='Which G-MATRIX block to extract.',
1814
- choices=['total', 'S', 'L', 'eff'],
2044
+ help='Which G-MATRIX block to extract - if DFT then this option is redundant', # noqa
2045
+ choices=['total', 'S', 'L', 'eff', 'dft'],
1815
2046
  default='total'
1816
2047
  )
1817
2048
 
@@ -2187,7 +2418,7 @@ def read_args(arg_list=None):
2187
2418
 
2188
2419
  gen_abs.add_argument(
2189
2420
  '--zero_osc',
2190
- default=1E-5,
2421
+ default=1E-7,
2191
2422
  type=float,
2192
2423
  help=(
2193
2424
  'Oscillator strengths below this value are treated as zero\n'
@@ -2352,7 +2583,7 @@ def read_args(arg_list=None):
2352
2583
 
2353
2584
  plot_abs.add_argument(
2354
2585
  '--zero_osc',
2355
- default=1E-5,
2586
+ default=1E-7,
2356
2587
  type=float,
2357
2588
  help=(
2358
2589
  'Oscillator strengths below this value are treated as zero\n'
@@ -2382,6 +2613,16 @@ def read_args(arg_list=None):
2382
2613
  )
2383
2614
  )
2384
2615
 
2616
+ plot_abs.add_argument(
2617
+ '--no_trim',
2618
+ action='store_true',
2619
+ default=False,
2620
+ help=(
2621
+ 'Do not trim spectrum to non-zero oscillator strength\n'
2622
+ 'Default: %(default)s'
2623
+ )
2624
+ )
2625
+
2385
2626
  plot_xes = plot_parser.add_parser(
2386
2627
  'xes',
2387
2628
  description='Plots XES from CI calculation output',
@@ -2753,9 +2994,30 @@ def read_args(arg_list=None):
2753
2994
  # If argument list is empty then call help function
2754
2995
  print_subprog.set_defaults(func=lambda _: print_subprog.print_help())
2755
2996
 
2997
+ print_hyperfine = print_parser.add_parser(
2998
+ 'hyperfine',
2999
+ description='Prints hyperfine couplings from Orca output file',
3000
+ usage=ut.cstring(
3001
+ 'orto print hyperfine <output_file> [options]',
3002
+ 'cyan'
3003
+ ),
3004
+ formatter_class=argparse.RawTextHelpFormatter
3005
+ )
3006
+ print_hyperfine._positionals.title = 'Mandatory Arguments'
3007
+
3008
+ print_hyperfine.set_defaults(
3009
+ func=lambda x: extract_hyperfine_func(x, save=False)
3010
+ )
3011
+
3012
+ print_hyperfine.add_argument(
3013
+ 'output_file',
3014
+ type=pathlib.Path,
3015
+ help='Path to/Name of Orca output file containing HYPERFINE section'
3016
+ )
3017
+
2756
3018
  print_gmatrix = print_parser.add_parser(
2757
3019
  'gmatrix',
2758
- description='Extracts g matrix from Orca output file',
3020
+ description='Prints g matrix from Orca output file',
2759
3021
  usage=ut.cstring('orto print gmatrix <output_file> [options]', 'cyan'),
2760
3022
  formatter_class=argparse.RawTextHelpFormatter
2761
3023
  )
@@ -2768,7 +3030,7 @@ def read_args(arg_list=None):
2768
3030
  print_gmatrix.add_argument(
2769
3031
  'output_file',
2770
3032
  type=pathlib.Path,
2771
- help='Orca output file name containing G-MATRIX block'
3033
+ help='Path to/Name of Orca output file containing G-MATRIX block'
2772
3034
  )
2773
3035
 
2774
3036
  print_gmatrix.add_argument(
@@ -2781,7 +3043,7 @@ def read_args(arg_list=None):
2781
3043
 
2782
3044
  print_freq = print_parser.add_parser(
2783
3045
  'freq',
2784
- description='Prints frequencies',
3046
+ description='Prints frequencies from Orca output file',
2785
3047
  usage=ut.cstring('orto print freq <output_file> [options]', 'cyan'),
2786
3048
  formatter_class=argparse.RawTextHelpFormatter
2787
3049
  )
@@ -2792,7 +3054,7 @@ def read_args(arg_list=None):
2792
3054
  print_freq.add_argument(
2793
3055
  'output_file',
2794
3056
  type=pathlib.Path,
2795
- help='Orca output file name - must contain Frequencies section'
3057
+ help='Path to/Name of Orca output file containing FREQUENCIES section'
2796
3058
  )
2797
3059
 
2798
3060
  print_freq.add_argument(
@@ -2819,8 +3081,8 @@ def read_args(arg_list=None):
2819
3081
  'output_file',
2820
3082
  type=pathlib.Path,
2821
3083
  help=(
2822
- 'Orca output file name\n'
2823
- 'File must contain one of the following sections\n'
3084
+ 'Path to/Name of Orca output file containing\n'
3085
+ 'one of the following sections\n'
2824
3086
  ' LOEWDIN ORBITAL-COMPOSITIONS\n'
2825
3087
  ' LOEWDIN REDUCED ORBITAL POPULATIONS PER MO\n'
2826
3088
  ' LOEWDIN ORBITAL POPULATIONS PER MO\n'
@@ -236,7 +236,7 @@ class AbsorptionData():
236
236
  def from_extractor_dataset(cls,
237
237
  dataset: dict[str, list[int | float]],
238
238
  operator: str,
239
- remove_zero_osc: float = 1E-4) -> 'AbsorptionData': # noqa
239
+ remove_zero_osc: float = 1E-7) -> 'AbsorptionData': # noqa
240
240
  '''
241
241
  Creates AbsorptionData object from data extractor
242
242
 
@@ -246,7 +246,7 @@ class AbsorptionData():
246
246
  Dataset from orto AbsorptionExtractor.data
247
247
  operator: str
248
248
  Type of operator used to calculate transitions
249
- remove_zero_osc: float, default=1E-4
249
+ remove_zero_osc: float, default=1E-7
250
250
  Remove transitions with oscillator strength below this value
251
251
 
252
252
  Returns
@@ -261,6 +261,7 @@ class AbsorptionData():
261
261
  # Remove transitions with zero oscillator strength from
262
262
  # beginning and end of spectrum
263
263
  osc_strengths = np.array(osc_strengths)
264
+
264
265
  # Find first non-zero oscillator strength
265
266
  try:
266
267
  first_nonzero = np.where(osc_strengths > remove_zero_osc)[0][0]
@@ -16,7 +16,7 @@ from . import constants as const
16
16
 
17
17
  def EPRNMRDetector(file_name: str | pathlib.Path) -> bool:
18
18
  '''
19
- Detects if Orca output file is from EPR/NMR calculation
19
+ Detects if Orca output file contains an EPRNMR section
20
20
  '''
21
21
  with open(file_name, 'rb') as f:
22
22
  file_content = f.read()
@@ -29,7 +29,7 @@ def EPRNMRDetector(file_name: str | pathlib.Path) -> bool:
29
29
 
30
30
  class OrcaVersionExtractor(extto.LineExtractor):
31
31
  '''
32
- Extracts Orca version from output file
32
+ Extracts Orca version from Orca output file
33
33
  '''
34
34
 
35
35
  # Regex Start Pattern
@@ -235,7 +235,8 @@ class SusceptibilityExtractor(extto.BetweenExtractor):
235
235
 
236
236
  class ExchangeCouplingExtractor(extto.BetweenExtractor):
237
237
  '''
238
- Extracts Exchange Coupling Constants and information (J) from output file
238
+ Extracts Exchange Coupling Constants and information (J) from Orca \n
239
+ output file
239
240
  '''
240
241
 
241
242
  # Regex Start Pattern
@@ -342,7 +343,7 @@ class ExchangeCouplingExtractor(extto.BetweenExtractor):
342
343
 
343
344
  class AILFTOrbEnergyExtractor(extto.BetweenExtractor):
344
345
  '''
345
- Extracts AI-LFT orbital energies from output file
346
+ Extracts AI-LFT orbital energies from Orca output file
346
347
  '''
347
348
  # Regex Start Pattern
348
349
  START_PATTERN = rb'(?<=The ligand field one electron eigenfunctions:\s-{41})' # noqa
@@ -437,7 +438,7 @@ class AILFTOrbEnergyExtractor(extto.BetweenExtractor):
437
438
  class FrequencyExtractor(extto.BetweenExtractor):
438
439
  '''
439
440
  Extracts Vibrational mode energies, eigenvectors, intensities, \n
440
- and irreps from output file
441
+ and irreps from Orca output file
441
442
  '''
442
443
 
443
444
  # Regex Start Pattern
@@ -665,7 +666,7 @@ class FrequencyExtractor(extto.BetweenExtractor):
665
666
 
666
667
  class LoewdinPopulationExtractor(extto.BetweenExtractor):
667
668
  '''
668
- Extracts Loewdin Population Analysis Section from output file
669
+ Extracts Loewdin Population Analysis Section from Orca output file
669
670
  '''
670
671
 
671
672
  # Regex Start Pattern
@@ -746,7 +747,7 @@ class MullikenPopulationExtractorDensities(LoewdinPopulationExtractor):
746
747
  '''
747
748
  Extracts Mulliken Population Analysis Section with \n
748
749
  MULLIKEN ATOMIC CHARGES AND SPIN DENSITIES header\n
749
- from output file
750
+ from Orca output file`
750
751
  '''
751
752
 
752
753
  # Regex Start Pattern
@@ -759,8 +760,8 @@ class MullikenPopulationExtractorDensities(LoewdinPopulationExtractor):
759
760
  class MullikenPopulationExtractorPopulations(LoewdinPopulationExtractor):
760
761
  '''
761
762
  Extracts Mulliken Population Analysis Section with \n
762
- MULLIKEN ATOMIC CHARGES AND SPIN POPULATIONS header
763
- from output file
763
+ MULLIKEN ATOMIC CHARGES AND SPIN POPULATIONS header\n
764
+ from Orca output file
764
765
  '''
765
766
 
766
767
  # Regex Start Pattern
@@ -772,13 +773,13 @@ class MullikenPopulationExtractorPopulations(LoewdinPopulationExtractor):
772
773
 
773
774
  class MullikenPopulationExtractor(MullikenPopulationExtractorDensities):
774
775
  '''
775
- Extracts Mulliken Population Analysis Section from output file
776
+ Extracts Mulliken Population Analysis Section from Orca output file
776
777
  '''
777
778
 
778
779
 
779
780
  class LoewdinCompositionExtractor(extto.BetweenExtractor):
780
781
  '''
781
- Extracts Loewdin Orbital-Compositions Section from output file\n
782
+ Extracts Loewdin Orbital-Compositions Section from Orca output file
782
783
  '''
783
784
 
784
785
  # Regex Start Pattern
@@ -2434,9 +2435,176 @@ class SimpleInputExtractor(extto.LineExtractor):
2434
2435
  return _ext.data
2435
2436
 
2436
2437
 
2438
+ class HyperfineExtractor(extto.BetweenExtractor):
2439
+ '''
2440
+ Extracts Hyperfine coupling tensor and associated information from Orca\n
2441
+ output file
2442
+ '''
2443
+
2444
+ # Regex Start Pattern
2445
+ START_PATTERN = rb'(?<=-{59}\s Nucleus)'
2446
+
2447
+ # Regex End Pattern
2448
+ END_PATTERN = rb'(?=The A matrix conforms to the)'
2449
+
2450
+ @property
2451
+ def data(self) -> dict[str, NDArray]:
2452
+ '''
2453
+ Hyperfine data:\n
2454
+ A dictionary with keys:\n
2455
+ nucleus\n
2456
+ element\n
2457
+ isotope\n
2458
+ matrix\n
2459
+ values\n
2460
+ vectors\n
2461
+ iso\n
2462
+ fc\n
2463
+ sd\n
2464
+ orb\n
2465
+ dia\n
2466
+ All values are ndarray of floats and have units of MHz
2467
+ '''
2468
+ return self._data
2469
+
2470
+ @staticmethod
2471
+ def _process_block(block: str) -> dict[str, NDArray]:
2472
+ '''
2473
+ Converts single block into data entries described in self.data
2474
+
2475
+ Parameters
2476
+ ----------
2477
+ block: str
2478
+ String block extracted from file
2479
+
2480
+ Returns
2481
+ -------
2482
+ dict[str, NDArray]
2483
+ '''
2484
+
2485
+ def float_triple(list_str: str) -> list[float]:
2486
+ '''
2487
+ Converts string form of three element list to list of floats
2488
+
2489
+ list_str: str
2490
+ String of three float values separated by spaces
2491
+
2492
+ Returns
2493
+ -------
2494
+ list[float]
2495
+ floats in list
2496
+ '''
2497
+
2498
+ if list_str is None:
2499
+ list_float = []
2500
+ else:
2501
+ list_float = [
2502
+ float(val)
2503
+ for val in list_str.split()
2504
+ ]
2505
+
2506
+ return list_float
2507
+
2508
+ # Get Nucleus
2509
+ nucleus = block.split(':')[0].lstrip().rstrip()
2510
+ element = ''.join(
2511
+ [
2512
+ letter
2513
+ for letter in nucleus
2514
+ if letter not in '0123456789'
2515
+ ]
2516
+ )
2517
+
2518
+ isotope = int(re.findall(r'Isotope\=\s+(\d+)', block)[0])
2519
+
2520
+ # Get Matrix
2521
+ _matrix = re.findall(
2522
+ r'(\s-?\d+\.\d{4}\s+-?\d+\.\d{4}\s+-?\d+\.\d{4})',
2523
+ block
2524
+ )[:3]
2525
+
2526
+ _fc = re.findall(
2527
+ r'A\(FC\)\s+(-?\d+.\d{4}\s+-?\d+.\d{4}\s+-?\d+.\d{4})',
2528
+ block
2529
+ )
2530
+ fc = float_triple(_fc[0])
2531
+
2532
+ _sd = re.findall(
2533
+ r'A\(SD\)\s+(-?\d+.\d{4}\s+-?\d+.\d{4}\s+-?\d+.\d{4})',
2534
+ block
2535
+ )
2536
+ sd = float_triple(_sd[0])
2537
+
2538
+ _orb = re.findall(
2539
+ r'A\(ORB\)\s+(-?\d+.\d{4}\s+-?\d+.\d{4}\s+-?\d+.\d{4})',
2540
+ block
2541
+ )
2542
+ orb = float_triple(_orb[0])
2543
+
2544
+ _dia = re.findall(
2545
+ r'A\(DIA\)\s+(-?\d+.\d{4}\s+-?\d+.\d{4}\s+-?\d+.\d{4})',
2546
+ block
2547
+ )
2548
+ dia = float_triple(_dia[0])
2549
+
2550
+ # Convert to matrix of floats
2551
+ matrix = np.asarray([
2552
+ [float(v) for v in val.split()]
2553
+ for val in _matrix
2554
+ ])
2555
+
2556
+ if matrix.shape != (3, 3):
2557
+ raise DataFormattingError(
2558
+ 'Hyperfine tensor is not the correct shape (3x3)'
2559
+ )
2560
+
2561
+ # Calculate isotropic hyperfine coupling
2562
+ iso = np.trace(matrix) / 3.
2563
+
2564
+ # Diagonalise tensor
2565
+ vals, vecs = la.eigh(matrix)
2566
+
2567
+ # Future - pseudocontact/dipolar?
2568
+ data = {
2569
+ 'nucleus': nucleus,
2570
+ 'element': element,
2571
+ 'isotope': isotope,
2572
+ 'matrix': matrix,
2573
+ 'values': vals,
2574
+ 'vectors': vecs,
2575
+ 'iso': iso,
2576
+ 'fc': fc,
2577
+ 'sd': sd,
2578
+ 'dia': dia,
2579
+ 'orb': orb,
2580
+ }
2581
+
2582
+ return data
2583
+
2584
+ @classmethod
2585
+ def extract(cls, file_name: str | pathlib.Path) -> list[dict[str, NDArray]]: # noqa
2586
+ '''
2587
+ Convenience method which instantiates class, extracts blocks, and
2588
+ returns processed datasets
2589
+
2590
+ Parameters
2591
+ ----------
2592
+ file_name: str | pathlib.Path
2593
+ File to parse
2594
+
2595
+ Returns
2596
+ -------
2597
+ list[dict[str, NDArray]]
2598
+ Each entry contains processed data, as defined in cls.data
2599
+ '''
2600
+ _ext = cls()
2601
+ _ext(file_name, process=True)
2602
+ return _ext.data
2603
+
2604
+
2437
2605
  class GMatrixDFTExtractor(extto.BetweenExtractor):
2438
2606
  '''
2439
- Extracts DFT ELECTRONIC G-MATRIX block from output file
2607
+ Extracts DFT ELECTRONIC G-MATRIX block from Orca output file
2440
2608
  '''
2441
2609
 
2442
2610
  # Regex Start Pattern
@@ -2530,7 +2698,7 @@ class GMatrixDFTExtractor(extto.BetweenExtractor):
2530
2698
 
2531
2699
  class GMatrixExtractor(extto.BetweenExtractor):
2532
2700
  '''
2533
- Extracts ELECTRONIC G-MATRIX block from output file
2701
+ Extracts ELECTRONIC G-MATRIX block from Orca output file
2534
2702
  '''
2535
2703
 
2536
2704
  # Regex Start Pattern
@@ -2620,8 +2788,8 @@ class GMatrixExtractor(extto.BetweenExtractor):
2620
2788
 
2621
2789
  class GMatrixEffectiveExtractor(GMatrixExtractor):
2622
2790
  '''
2623
- Extracts ELECTRONIC G-MATRIX FROM EFFECTIVE HAMILTONIAN block from output\n
2624
- file
2791
+ Extracts ELECTRONIC G-MATRIX FROM EFFECTIVE HAMILTONIAN block from Orca\n
2792
+ output file
2625
2793
  '''
2626
2794
  # Regex Start Pattern
2627
2795
  START_PATTERN = rb'(?<=ELECTRONIC G-MATRIX FROM EFFECTIVE HAMILTONIAN\s[\S\s]{46}\s)' # noqa
@@ -2707,7 +2875,8 @@ class GMatrixEffectiveExtractor(GMatrixExtractor):
2707
2875
 
2708
2876
  class GMatrixLExtractor(GMatrixExtractor):
2709
2877
  '''
2710
- Extracts ELECTRONIC G-MATRIX: L contribution block from output file
2878
+ Extracts ELECTRONIC G-MATRIX: L contribution block from Orca\n
2879
+ output file
2711
2880
  '''
2712
2881
  # Regex Start Pattern
2713
2882
  START_PATTERN = rb'(?<=ELECTRONIC G-MATRIX: L contribution\s[\S\s]{47}\s)'
@@ -2715,7 +2884,8 @@ class GMatrixLExtractor(GMatrixExtractor):
2715
2884
 
2716
2885
  class GMatrixSExtractor(GMatrixExtractor):
2717
2886
  '''
2718
- Extracts ELECTRONIC G-MATRIX: S contribution block from output file
2887
+ Extracts ELECTRONIC G-MATRIX: S contribution block from Orca\n
2888
+ output file
2719
2889
  '''
2720
2890
  # Regex Start Pattern
2721
2891
  START_PATTERN = rb'(?<=ELECTRONIC G-MATRIX: S contribution\s[\S\s]{47}\s)'
@@ -2723,7 +2893,7 @@ class GMatrixSExtractor(GMatrixExtractor):
2723
2893
 
2724
2894
  class SpinFreeEnergyExtractor(extto.BetweenExtractor):
2725
2895
  '''
2726
- Extracts Spin-Free TRANSITION ENERGIES block from output file
2896
+ Extracts Spin-Free TRANSITION ENERGIES block from Orca output file
2727
2897
  '''
2728
2898
 
2729
2899
  # Regex Start Pattern
@@ -2814,7 +2984,7 @@ class SpinFreeEnergyExtractor(extto.BetweenExtractor):
2814
2984
 
2815
2985
  class SpinOrbitEnergyExtractor(extto.BetweenExtractor):
2816
2986
  '''
2817
- Extracts Spin-Orbit Energies block from output file
2987
+ Extracts Spin-Orbit Energies block from Orca output file
2818
2988
  '''
2819
2989
 
2820
2990
  # Regex Start Pattern
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orto
3
- Version: 1.10.1
3
+ Version: 1.11.1
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
@@ -14,7 +14,7 @@ Requires-Python: >=3.10
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: numpy>=2.1.2
17
- Requires-Dist: xyz_py>=5.13.1
17
+ Requires-Dist: xyz_py>=5.19.2
18
18
  Requires-Dist: matplotlib>=3.9.2
19
19
  Requires-Dist: extto>=1.0.1
20
20
  Requires-Dist: pandas>=2.2.3
@@ -1,5 +1,5 @@
1
1
  numpy>=2.1.2
2
- xyz_py>=5.13.1
2
+ xyz_py>=5.19.2
3
3
  matplotlib>=3.9.2
4
4
  extto>=1.0.1
5
5
  pandas>=2.2.3
@@ -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.10.1'
11
+ __version__ = '1.11.1'
12
12
 
13
13
  setuptools.setup(
14
14
  name='orto',
@@ -33,7 +33,7 @@ setuptools.setup(
33
33
  python_requires='>=3.10',
34
34
  install_requires=[
35
35
  'numpy>=2.1.2',
36
- 'xyz_py>=5.13.1',
36
+ 'xyz_py>=5.19.2',
37
37
  'matplotlib>=3.9.2',
38
38
  'extto>=1.0.1',
39
39
  'pandas>=2.2.3',
@@ -1 +0,0 @@
1
- __version__ = '1.10.1'
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