orto 1.10.0__py3-none-any.whl → 1.11.0__py3-none-any.whl

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.
orto/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '1.10.0'
1
+ __version__ = '1.11.0'
orto/cli.py CHANGED
@@ -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
 
@@ -254,7 +447,8 @@ def extract_gmatrix_func(uargs, save=True):
254
447
  'total': 'ELECTRONIC G-MATRIX',
255
448
  'L': 'ELECTRONIC G-MATRIX: L contribution',
256
449
  'S': 'ELECTRONIC G-MATRIX: S contribution',
257
- 'eff': 'ELECTRONIC G-MATRIX FROM EFFECTIVE HAMILTONIAN'
450
+ 'eff': 'ELECTRONIC G-MATRIX FROM EFFECTIVE HAMILTONIAN',
451
+ 'dft': 'ELECTRONIC G-MATRIX FROM DFT LINEAR RESPONSE'
258
452
  }
259
453
 
260
454
  title = titles[uargs.type]
@@ -268,7 +462,7 @@ def extract_gmatrix_func(uargs, save=True):
268
462
  style = doc.styles['Normal']
269
463
  font = style.font
270
464
  font.name = 'Arial'
271
- font.size = Pt(9)
465
+ font.size = Pt(12)
272
466
 
273
467
  # For each extracted section, print matrix, vectors, and values
274
468
  for it, data in enumerate(all_data):
@@ -330,7 +524,13 @@ def extract_gmatrix_func(uargs, save=True):
330
524
  cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
331
525
  cell.paragraphs[0].style = 'Normal'
332
526
 
333
- 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
+ )
334
534
 
335
535
  ut.cprint(f'Data written to {out_name}', 'cyan')
336
536
 
@@ -343,13 +543,14 @@ def gen_spden_func(uargs):
343
543
 
344
544
  Parameters
345
545
  ----------
346
- uargs : argparser object
546
+ uargs: argparser object
347
547
  User arguments
348
548
 
349
549
  Returns
350
550
  -------
351
551
  None
352
552
  '''
553
+ from subto.job import SlurmJob
353
554
 
354
555
  # Check orca module
355
556
  if len(uargs.orca_load):
@@ -423,13 +624,15 @@ def gen_trunc_molden_func(uargs):
423
624
 
424
625
  Parameters
425
626
  ----------
426
- uargs : argparser object
627
+ uargs: argparser object
427
628
  User arguments
428
629
 
429
630
  Returns
430
631
  -------
431
632
  None
432
633
  '''
634
+ from mmap import mmap, ACCESS_READ
635
+ from shutil import move as shutilmove
433
636
 
434
637
  # Read molden file in as binary string
435
638
  # and find number of MOs by counting number of
@@ -492,7 +695,7 @@ def gen_job_func(uargs):
492
695
 
493
696
  Parameters
494
697
  ----------
495
- uargs : argparser object
698
+ uargs: argparser object
496
699
  User arguments
497
700
 
498
701
  Returns
@@ -657,7 +860,7 @@ def plot_xes_func(uargs):
657
860
 
658
861
  Parameters
659
862
  ----------
660
- uargs : argparser object
863
+ uargs: argparser object
661
864
  User arguments
662
865
 
663
866
  Returns
@@ -773,7 +976,7 @@ def plot_xas_func(uargs):
773
976
 
774
977
  Parameters
775
978
  ----------
776
- uargs : argparser object
979
+ uargs: argparser object
777
980
  User arguments
778
981
 
779
982
  Returns
@@ -882,10 +1085,10 @@ def plot_abs_func(uargs, save_data_only=False):
882
1085
 
883
1086
  Parameters
884
1087
  ----------
885
- uargs : argparser object
1088
+ uargs: argparser object
886
1089
  User arguments
887
1090
 
888
- save_data_only : bool, default=False
1091
+ save_data_only: bool, default=False
889
1092
  If True, saves generated spectrum data to file only.
890
1093
 
891
1094
  Returns
@@ -897,6 +1100,7 @@ def plot_abs_func(uargs, save_data_only=False):
897
1100
  import matplotlib.colors as mcolors
898
1101
  from . import plotter
899
1102
  from . import extractor as oe
1103
+ from . import data as d
900
1104
 
901
1105
  # Change matplotlib font size to be larger
902
1106
  mpl.rcParams.update({'font.size': 12})
@@ -1106,14 +1310,13 @@ def plot_ir_func(uargs):
1106
1310
 
1107
1311
  Parameters
1108
1312
  ----------
1109
- uargs : argparser object
1313
+ uargs: argparser object
1110
1314
  User arguments
1111
1315
 
1112
1316
  Returns
1113
1317
  -------
1114
1318
  None
1115
1319
  '''
1116
- import matplotlib.pyplot as plt
1117
1320
  import matplotlib as mpl
1118
1321
  from . import plotter
1119
1322
  from . import extractor as oe
@@ -1151,7 +1354,7 @@ def distort_func(uargs):
1151
1354
 
1152
1355
  Parameters
1153
1356
  ----------
1154
- args : argparser object
1357
+ uargs: argparser object
1155
1358
  command line arguments
1156
1359
 
1157
1360
  Returns
@@ -1159,6 +1362,7 @@ def distort_func(uargs):
1159
1362
  None
1160
1363
 
1161
1364
  '''
1365
+ import xyz_py as xyzp
1162
1366
  from . import extractor as oe
1163
1367
 
1164
1368
  # Open file and extract coordinates
@@ -1197,9 +1401,9 @@ def extract_orbs_func(uargs, save=True) -> None:
1197
1401
 
1198
1402
  Parameters
1199
1403
  ----------
1200
- args : argparser object
1404
+ uargs: argparser object
1201
1405
  command line arguments
1202
- save : bool, default=True
1406
+ save: bool, default=True
1203
1407
  If True, saves data to file. If False, prints to stdout.
1204
1408
 
1205
1409
  Returns
@@ -1336,7 +1540,7 @@ def extract_orbs_func(uargs, save=True) -> None:
1336
1540
  total = 0.
1337
1541
  for row, val in mo.items():
1338
1542
  if val > uargs.threshold and row[1] in uargs.elements:
1339
- _output += f' {row[1]+str(row[0]):5} {row[2]:5} : {val:>5.1f} %\n' # noqa
1543
+ _output += f' {row[1]+str(row[0]):5} {row[2]:5}: {val:>5.1f} %\n' # noqa
1340
1544
  total += val
1341
1545
  if len(_output):
1342
1546
  print(f'MO #{mo_num} (Occ={occupancies[mo_num]}, E={energies[mo_num]: .5f}):') # noqa
@@ -1373,9 +1577,9 @@ def extract_freq_func(uargs, save=True):
1373
1577
 
1374
1578
  Parameters
1375
1579
  ----------
1376
- args : argparser object
1580
+ uargs: argparser object
1377
1581
  command line arguments
1378
- save : bool, default=True
1582
+ save: bool, default=True
1379
1583
  If True, saves data to file. If False, prints to stdout.
1380
1584
 
1381
1585
  Returns
@@ -1424,14 +1628,15 @@ def extract_pop_func(uargs, save=True) -> None:
1424
1628
 
1425
1629
  Parameters
1426
1630
  ----------
1427
- uargs : argparser object
1631
+ uargs: argparser object
1428
1632
  User arguments
1429
- save : bool, default=False
1633
+ save: bool, default=False
1430
1634
  If True, saves data to file. If False, prints to stdout.
1431
1635
  Returns
1432
1636
  -------
1433
1637
  None
1434
1638
  '''
1639
+ import xyz_py as xyzp
1435
1640
  from . import extractor as oe
1436
1641
 
1437
1642
  if uargs.flavour in ['loewdin', 'lowdin']:
@@ -1485,7 +1690,7 @@ def extract_pop_func(uargs, save=True) -> None:
1485
1690
  _chg = sum([datum[0][labels[ind]] for ind in entity])
1486
1691
  _spin = sum([datum[1][labels[ind]] for ind in entity])
1487
1692
  ut.cprint(
1488
- f'{entity_name} : {_chg:.4f} {_spin:.4f}',
1693
+ f'{entity_name}: {_chg:.4f} {_spin:.4f}',
1489
1694
  'cyan'
1490
1695
  )
1491
1696
 
@@ -1498,7 +1703,7 @@ def plot_susc_func(uargs) -> None:
1498
1703
 
1499
1704
  Parameters
1500
1705
  ----------
1501
- args : argparser object
1706
+ uargs: argparser object
1502
1707
  command line arguments
1503
1708
 
1504
1709
  Returns
@@ -1610,7 +1815,7 @@ def plot_ailft_func(uargs) -> None:
1610
1815
 
1611
1816
  Parameters
1612
1817
  ----------
1613
- args : argparser object
1818
+ uargs: argparser object
1614
1819
  command line arguments
1615
1820
 
1616
1821
  Returns
@@ -1696,7 +1901,7 @@ def read_args(arg_list=None):
1696
1901
 
1697
1902
  Parameters
1698
1903
  ----------
1699
- args : argparser object
1904
+ uargs: argparser object
1700
1905
  command line arguments
1701
1906
 
1702
1907
  Returns
@@ -1791,6 +1996,32 @@ def read_args(arg_list=None):
1791
1996
  help='Number of frequencies to print, default is all'
1792
1997
  )
1793
1998
 
1999
+ extract_hyperfine = extract_parser.add_parser(
2000
+ 'hyperfine',
2001
+ description='Extracts hyperfine couplings from Orca output file',
2002
+ usage=ut.cstring(
2003
+ 'orto extract hyperfine <output_file> [options]',
2004
+ 'cyan'
2005
+ ),
2006
+ formatter_class=argparse.RawTextHelpFormatter
2007
+ )
2008
+ extract_hyperfine._positionals.title = 'Mandatory Arguments'
2009
+ extract_hyperfine.set_defaults(func=extract_hyperfine_func)
2010
+
2011
+ extract_hyperfine.add_argument(
2012
+ 'output_file',
2013
+ type=pathlib.Path,
2014
+ help='Path to/Name of Orca output file containing HYPERFINE section'
2015
+ )
2016
+
2017
+ extract_hyperfine.add_argument(
2018
+ '--output_format',
2019
+ type=str,
2020
+ help='Format of outputted data file',
2021
+ choices=['txt', 'docx'],
2022
+ default='txt'
2023
+ )
2024
+
1794
2025
  extract_gmatrix = extract_parser.add_parser(
1795
2026
  'gmatrix',
1796
2027
  description='Extracts coordinates from Orca output file',
@@ -1803,14 +2034,14 @@ def read_args(arg_list=None):
1803
2034
  extract_gmatrix.add_argument(
1804
2035
  'output_file',
1805
2036
  type=pathlib.Path,
1806
- help='Orca output file name containing G-MATRIX block'
2037
+ help='Path to/Name of Orca output file containing G-MATRIX block'
1807
2038
  )
1808
2039
 
1809
2040
  extract_gmatrix.add_argument(
1810
2041
  '--type',
1811
2042
  type=str,
1812
- help='Which G-MATRIX block to extract.',
1813
- choices=['total', 'S', 'L', 'eff'],
2043
+ help='Which G-MATRIX block to extract - if DFT then this option is redundant', # noqa
2044
+ choices=['total', 'S', 'L', 'eff', 'dft'],
1814
2045
  default='total'
1815
2046
  )
1816
2047
 
@@ -2752,9 +2983,30 @@ def read_args(arg_list=None):
2752
2983
  # If argument list is empty then call help function
2753
2984
  print_subprog.set_defaults(func=lambda _: print_subprog.print_help())
2754
2985
 
2986
+ print_hyperfine = print_parser.add_parser(
2987
+ 'hyperfine',
2988
+ description='Prints hyperfine couplings from Orca output file',
2989
+ usage=ut.cstring(
2990
+ 'orto print hyperfine <output_file> [options]',
2991
+ 'cyan'
2992
+ ),
2993
+ formatter_class=argparse.RawTextHelpFormatter
2994
+ )
2995
+ print_hyperfine._positionals.title = 'Mandatory Arguments'
2996
+
2997
+ print_hyperfine.set_defaults(
2998
+ func=lambda x: extract_hyperfine_func(x, save=False)
2999
+ )
3000
+
3001
+ print_hyperfine.add_argument(
3002
+ 'output_file',
3003
+ type=pathlib.Path,
3004
+ help='Path to/Name of Orca output file containing HYPERFINE section'
3005
+ )
3006
+
2755
3007
  print_gmatrix = print_parser.add_parser(
2756
3008
  'gmatrix',
2757
- description='Extracts g matrix from Orca output file',
3009
+ description='Prints g matrix from Orca output file',
2758
3010
  usage=ut.cstring('orto print gmatrix <output_file> [options]', 'cyan'),
2759
3011
  formatter_class=argparse.RawTextHelpFormatter
2760
3012
  )
@@ -2767,7 +3019,7 @@ def read_args(arg_list=None):
2767
3019
  print_gmatrix.add_argument(
2768
3020
  'output_file',
2769
3021
  type=pathlib.Path,
2770
- help='Orca output file name containing G-MATRIX block'
3022
+ help='Path to/Name of Orca output file containing G-MATRIX block'
2771
3023
  )
2772
3024
 
2773
3025
  print_gmatrix.add_argument(
@@ -2780,7 +3032,7 @@ def read_args(arg_list=None):
2780
3032
 
2781
3033
  print_freq = print_parser.add_parser(
2782
3034
  'freq',
2783
- description='Prints frequencies',
3035
+ description='Prints frequencies from Orca output file',
2784
3036
  usage=ut.cstring('orto print freq <output_file> [options]', 'cyan'),
2785
3037
  formatter_class=argparse.RawTextHelpFormatter
2786
3038
  )
@@ -2791,7 +3043,7 @@ def read_args(arg_list=None):
2791
3043
  print_freq.add_argument(
2792
3044
  'output_file',
2793
3045
  type=pathlib.Path,
2794
- help='Orca output file name - must contain Frequencies section'
3046
+ help='Path to/Name of Orca output file containing FREQUENCIES section'
2795
3047
  )
2796
3048
 
2797
3049
  print_freq.add_argument(
@@ -2818,8 +3070,8 @@ def read_args(arg_list=None):
2818
3070
  'output_file',
2819
3071
  type=pathlib.Path,
2820
3072
  help=(
2821
- 'Orca output file name\n'
2822
- 'File must contain one of the following sections\n'
3073
+ 'Path to/Name of Orca output file containing\n'
3074
+ 'one of the following sections\n'
2823
3075
  ' LOEWDIN ORBITAL-COMPOSITIONS\n'
2824
3076
  ' LOEWDIN REDUCED ORBITAL POPULATIONS PER MO\n'
2825
3077
  ' LOEWDIN ORBITAL POPULATIONS PER MO\n'
orto/extractor.py CHANGED
@@ -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.0
3
+ Version: 1.11.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
@@ -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,17 +1,17 @@
1
1
  orto/__init__.py,sha256=IedlltYr3qYZxChNUdz62qogXA9Pos_MUvXdGXqAa0E,41
2
- orto/__version__.py,sha256=ZXE8njb1SRoMJuadNA6KRGRdfAFsM49XLsgr_fSOXts,23
3
- orto/cli.py,sha256=XBeO5dTWaZP_mnNA5IR2eZBcnhHEtwW6IuFtLgUPMkA,86309
2
+ orto/__version__.py,sha256=glAkwZsi0Hbv-R1Jbe-2qECv-6AWHNGvlRS_cgbvCTU,23
3
+ orto/cli.py,sha256=l470BgtTLUD2vtWNdMuoY7A1zcW7lzCGBJO-n1YI9_Q,95935
4
4
  orto/constants.py,sha256=anxaiTykO8Q_CXliR7zuOAdnXZrQ2-C4ndaviyl7kGc,419
5
5
  orto/data.py,sha256=960LHFeG7l626X_WA-8YxvG2toNAsgNvLo86OoYAmBY,14910
6
6
  orto/exceptions.py,sha256=D7oNeAEGeJNt5thzt6PaCn5FY6JcbJOWUE1N1LVhhuE,159
7
- orto/extractor.py,sha256=oqRdDWLaVPIwB4aZ5nrITWexx60hKn0zghACmymwVfM,80618
7
+ orto/extractor.py,sha256=GKCFU7oSawYCYjOizER8e6HSp4bOsyqOxYcTPQHwdjM,84923
8
8
  orto/input.py,sha256=uUQV6A-8D0GZpRoY1rKK_aUPmk9kVVTnMzTHuisP5t4,11888
9
9
  orto/job.py,sha256=tDiz9omFwinoYJamgz66MZez0Ee9HMhuCIAeHMhXRF8,5916
10
10
  orto/plotter.py,sha256=_t9bow6sUMoRAvD6gVFGJTc-_ifW7RmeR0JAPWK_lm4,17799
11
11
  orto/utils.py,sha256=GcMZ9uebOSnkPQT_U5O0X49LUtTu8YpXZxEsNKgXNTY,9838
12
- orto-1.10.0.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
13
- orto-1.10.0.dist-info/METADATA,sha256=2qdWW5pyurXxTVD-6GXUSvuxZhLJ2BTbkGUtDdPJVoQ,1185
14
- orto-1.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- orto-1.10.0.dist-info/entry_points.txt,sha256=HXenCglMp_03JkN34pK2phkjXK9CFcXTGHKv5QaVY8I,39
16
- orto-1.10.0.dist-info/top_level.txt,sha256=hQ-z28gTN_FZ2B5Kiwxr_9cUTcCoib9W5HjbkceDXw4,5
17
- orto-1.10.0.dist-info/RECORD,,
12
+ orto-1.11.0.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
13
+ orto-1.11.0.dist-info/METADATA,sha256=CrxR7SnLc7EfAUvA26nct7SyVxCtFbwiJVw2oy4jNg0,1185
14
+ orto-1.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ orto-1.11.0.dist-info/entry_points.txt,sha256=HXenCglMp_03JkN34pK2phkjXK9CFcXTGHKv5QaVY8I,39
16
+ orto-1.11.0.dist-info/top_level.txt,sha256=hQ-z28gTN_FZ2B5Kiwxr_9cUTcCoib9W5HjbkceDXw4,5
17
+ orto-1.11.0.dist-info/RECORD,,
File without changes