orto 1.8.2__tar.gz → 1.9.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.8.2
3
+ Version: 1.9.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.9.0'
@@ -11,6 +11,7 @@ import numpy as np
11
11
  import re
12
12
  from mmap import mmap, ACCESS_READ
13
13
  from shutil import move as shutilmove
14
+ from subto.job import SlurmJob
14
15
 
15
16
  from . import job
16
17
  from . import utils as ut
@@ -329,6 +330,86 @@ def extract_gmatrix_func(uargs, save=True):
329
330
  return
330
331
 
331
332
 
333
+ def gen_spden_func(uargs):
334
+ '''
335
+ Wrapper for CLI gen spin density call
336
+
337
+ Parameters
338
+ ----------
339
+ uargs : argparser object
340
+ User arguments
341
+
342
+ Returns
343
+ -------
344
+ None
345
+ '''
346
+
347
+ # Check orca module
348
+ if len(uargs.orca_load):
349
+ orca_load_val = copy.copy(uargs.orca_load)
350
+ elif os.getenv('orto_orca_load'):
351
+ try:
352
+ if len(os.getenv('orto_orca_load')):
353
+ orca_load_val = os.getenv('orto_orca_load')
354
+ except ValueError:
355
+ ut.red_exit(
356
+ (
357
+ 'Error in orto_orca_load environment variable'
358
+ )
359
+ )
360
+ else:
361
+ ut.red_exit(
362
+ (
363
+ 'Missing orto_orca_load environment variable or '
364
+ '--orca_load argument'
365
+ )
366
+ )
367
+
368
+ # Create string of command numbers for orca plot to follow
369
+ command_nums = '1\\n3\\ny\\n4'
370
+ command_nums += f'\\n{uargs.n_pts:d}'
371
+ command_nums += '\\n5\\n7\\n11\\n12\\n'
372
+
373
+ # Generate job file for orca_plot
374
+ spin_job = SlurmJob(
375
+ pathlib.Path(f'{uargs.gbw_file.stem}_spin_density_job.sh')
376
+ )
377
+
378
+ # Create content of job
379
+ spin_job.content_block = ''
380
+
381
+ spin_job.content_block += '# Load orca module\n'
382
+ spin_job.content_block += f'module load {orca_load_val}\n'
383
+ spin_job.content_block += '# Run orca_plot to generate spin density cube file\n' # noqa
384
+ spin_job.content_block += f'time mpirun -np {uargs.n_procs:d} $(which orca_plot_mpi) {uargs.gbw_file.name} -i aa <<< $\'{command_nums}\'\n\n' # noqa
385
+
386
+ # Set job name
387
+ spin_job.job_name = f'{uargs.gbw_file.stem}_spin_density'
388
+
389
+ spin_job.ntasks_per_node = str(uargs.n_procs)
390
+ spin_job.mem_per_cpu = str(uargs.memory)
391
+
392
+ spin_job.error = f'{uargs.gbw_file.stem}_spin_density.%j.e'
393
+ spin_job.output = f'{uargs.gbw_file.stem}_spin_density.%j.o'
394
+
395
+ # Write job script
396
+ # with submitter configuration options specified
397
+ spin_job.write_script(True)
398
+
399
+ # Submit to queue
400
+ if not uargs.no_sub:
401
+ subprocess.call(
402
+ 'cd {}; {} "{}"; cd ../'.format(
403
+ uargs.gbw_file.parents[0],
404
+ spin_job.SUBMIT_COMMAND,
405
+ spin_job.file_path
406
+ ),
407
+ shell=True
408
+ )
409
+
410
+ return
411
+
412
+
332
413
  def gen_trunc_molden_func(uargs):
333
414
  '''
334
415
  Wrapper for CLI gen truncmolden call
@@ -527,6 +608,14 @@ def gen_job_func(uargs):
527
608
  else:
528
609
  nbo_module = None
529
610
 
611
+ # Check structure in input file or xyz file
612
+ # unless skip_structure flag is set
613
+ if not uargs.skip_structure:
614
+ try:
615
+ inp.check_structure(oj.input_file)
616
+ except (DataNotFoundError, DataFormattingError) as e:
617
+ ut.red_exit(str(e))
618
+
530
619
  # Set SLURM error and output file names
531
620
  config['error'] = 'slurm.%j.e'
532
621
  config['output'] = 'slurm.%j.o'
@@ -1641,6 +1730,7 @@ def read_args(arg_list=None):
1641
1730
 
1642
1731
  extract_coords = extract_parser.add_parser(
1643
1732
  'coords',
1733
+ aliases=['coord'],
1644
1734
  description='Extracts coordinates from Orca output file',
1645
1735
  formatter_class=argparse.RawTextHelpFormatter,
1646
1736
  usage=ut.cstring('orto extract coords <output_file> [options]', 'cyan')
@@ -1810,6 +1900,71 @@ def read_args(arg_list=None):
1810
1900
  )
1811
1901
  )
1812
1902
 
1903
+ gen_spden = gen_parser.add_parser(
1904
+ 'spdens',
1905
+ aliases=['spin_density', 'spden'],
1906
+ description='Generate spin density cube file from gbw file', # noqa
1907
+ usage=ut.cstring(
1908
+ 'orto gen spdens <gbw_file> <n_procs> [options]',
1909
+ 'cyan'
1910
+ ),
1911
+ formatter_class=argparse.RawTextHelpFormatter
1912
+ )
1913
+ gen_spden._positionals.title = 'Mandatory Arguments'
1914
+ gen_spden.set_defaults(func=gen_spden_func)
1915
+
1916
+ gen_spden.add_argument(
1917
+ 'gbw_file',
1918
+ type=pathlib.Path,
1919
+ help='Orca gbw file name'
1920
+ )
1921
+
1922
+ gen_spden.add_argument(
1923
+ 'n_procs',
1924
+ type=int,
1925
+ help='Number of processors/cores used in calculation',
1926
+ default=1
1927
+ )
1928
+
1929
+ gen_spden.add_argument(
1930
+ '--n_pts',
1931
+ '-n',
1932
+ type=int,
1933
+ default=100,
1934
+ help=(
1935
+ 'Number of points in each dimension of cube file\n'
1936
+ 'Default: %(default)s'
1937
+ )
1938
+ )
1939
+
1940
+ gen_spden.add_argument(
1941
+ '--memory',
1942
+ '-mem',
1943
+ type=int,
1944
+ default=500,
1945
+ help=(
1946
+ 'Per-core Memory to use in MB\n'
1947
+ 'Default: %(default)s'
1948
+ )
1949
+ )
1950
+
1951
+ gen_spden.add_argument(
1952
+ '-om',
1953
+ '--orca_load',
1954
+ type=str,
1955
+ default='',
1956
+ help='Orca environment module (overrides ORTO_ORCA_LOAD envvar)'
1957
+ )
1958
+
1959
+ gen_spden.add_argument(
1960
+ '--no_sub',
1961
+ '-ns',
1962
+ action='store_true',
1963
+ help=(
1964
+ 'Disables submission of job to queue'
1965
+ )
1966
+ )
1967
+
1813
1968
  gen_job = gen_parser.add_parser(
1814
1969
  'job',
1815
1970
  description=(
@@ -1880,6 +2035,15 @@ def read_args(arg_list=None):
1880
2035
  )
1881
2036
  )
1882
2037
 
2038
+ gen_job.add_argument(
2039
+ '--skip_structure',
2040
+ '-ss',
2041
+ action='store_true',
2042
+ help=(
2043
+ 'Disables checking of structure in input file or xyz file'
2044
+ )
2045
+ )
2046
+
1883
2047
  gen_job.add_argument(
1884
2048
  '-om',
1885
2049
  '--orca_load',
@@ -2347,6 +2511,7 @@ def read_args(arg_list=None):
2347
2511
 
2348
2512
  plot_ailft = plot_parser.add_parser(
2349
2513
  'ailft_orbs',
2514
+ aliases=['ailft_orb'],
2350
2515
  description='Plots AI-LFT orbital energies from output file',
2351
2516
  usage=ut.cstring(
2352
2517
  'orto plot ailft_orbs <output_file> [options]',
@@ -2204,6 +2204,85 @@ class XYZInputExtractor(extto.LineExtractor):
2204
2204
  return _ext.data
2205
2205
 
2206
2206
 
2207
+ class StructureInputExtractor(extto.BetweenExtractor):
2208
+ '''
2209
+ Extracts structure from xyz file
2210
+ '''
2211
+
2212
+ # Regex Start Pattern
2213
+ START_PATTERN = rb'(?<=\*xyz )'
2214
+
2215
+ # Regex End Pattern
2216
+ END_PATTERN = rb'(?=\*)'
2217
+
2218
+ @property
2219
+ def data(self) -> tuple[list[str], NDArray]:
2220
+ '''
2221
+ list of atom labels and ndarray of coordinates (N, 3)
2222
+ '''
2223
+ return self._data
2224
+
2225
+ @staticmethod
2226
+ def _process_block(block: str) -> tuple[list[str], NDArray]:
2227
+ '''
2228
+ Converts single block into data entries described in self.data
2229
+
2230
+ Parameters
2231
+ ----------
2232
+ block: str
2233
+ String block extracted from file
2234
+
2235
+ Returns
2236
+ -------
2237
+ tuple[list[str], NDArray]
2238
+ '''
2239
+
2240
+ # Get labels and coordinates
2241
+ all_info = re.findall(
2242
+ r'([A-Za-z]+\s*-?\d\.\d*\s+-?\d\.\d*\s+-?\d\.\d*)',
2243
+ block
2244
+ )
2245
+
2246
+ labels = re.findall(
2247
+ r'([A-Za-z]+)',
2248
+ block
2249
+ )
2250
+
2251
+ coords = np.asarray([
2252
+ [float(v) for v in val.split()[1:]]
2253
+ for val in all_info
2254
+ ])
2255
+
2256
+ if len(labels) != coords.shape[0]:
2257
+ raise DataFormattingError(
2258
+ 'Number of atom labels does not match number of coordinates'
2259
+ )
2260
+
2261
+ data = (labels, coords)
2262
+
2263
+ return data
2264
+
2265
+ @classmethod
2266
+ def extract(cls, file_name: str | pathlib.Path) -> tuple[list[str], NDArray]: # noqa
2267
+ '''
2268
+ Convenience method which instantiates class, extracts blocks, and
2269
+ returns processed datasets
2270
+
2271
+ Parameters
2272
+ ----------
2273
+ file_name: str | pathlib.Path
2274
+ File to parse
2275
+
2276
+ Returns
2277
+ -------
2278
+ tuple[list[str], NDArray]
2279
+ Each entry contains processed data, as defined in cls.data
2280
+ '''
2281
+ _ext = cls()
2282
+ _ext(file_name, process=True)
2283
+ return _ext.data
2284
+
2285
+
2207
2286
  class IntInputExtractor(XYZInputExtractor):
2208
2287
  '''
2209
2288
  Extracts *int line of an input file
@@ -1,6 +1,7 @@
1
1
  from pathlib import Path
2
2
  import re
3
3
  import numpy as np
4
+ import numpy.linalg as la
4
5
  import copy
5
6
  import xyz_py as xyzp
6
7
 
@@ -304,6 +305,77 @@ def check_xyz(file_name: str | Path, skip_check) -> None:
304
305
  return
305
306
 
306
307
 
308
+ def check_structure(file_name: str | Path, threshold: float = 0.1) -> None:
309
+ '''
310
+ Checks that no atoms of the structure are overlapping.\n
311
+
312
+ Parameters
313
+ ----------
314
+ file_name: str | Path
315
+ Orca input file as either name or Path object
316
+ threshold: float
317
+ Minimum allowed distance between atoms in Angstroms
318
+
319
+ Returns
320
+ -------
321
+ None
322
+
323
+ Raises
324
+ ------
325
+ DataFormattingError
326
+ If any atoms are overlapping
327
+ '''
328
+
329
+ # Get xyz file name and check it exists and is formatted correctly
330
+ try:
331
+ xyz_file = oe.XYZFileInputExtractor.extract(file_name)
332
+ except DataNotFoundError:
333
+ xyz_file = []
334
+
335
+ try:
336
+ xyzline = oe.XYZInputExtractor.extract(file_name)
337
+ except DataNotFoundError:
338
+ xyzline = []
339
+
340
+ if not len(xyz_file) and not len(xyzline):
341
+ ut.red_exit(
342
+ 'Error: missing or incorrect *xyzfile or *xyz line in input'
343
+ )
344
+
345
+ if len(xyz_file):
346
+ # Load structure from xyz file
347
+ xyz_file = Path(xyz_file[0])
348
+ labels, coords = xyzp.load_xyz(xyz_file)
349
+ else:
350
+ # Extract structure from input file
351
+ labels, coords = oe.StructureInputExtractor.extract(file_name)[0]
352
+
353
+ # Compute pairwise differences using broadcasting
354
+ # Broadcast to shape (N, N, 3)
355
+ # from (N, 1, 3) and (1, N, 3)
356
+ diffs = coords[:, np.newaxis, :] - coords[np.newaxis, :, :]
357
+
358
+ # Compute Euclidean distances
359
+ distances = la.norm(diffs, axis=-1) # shape (N, N)
360
+
361
+ # Add large value to diagonal to avoid zero self-distances
362
+ distances += np.diag([100]*len(labels))
363
+ # Find index of zero distances
364
+ zero_dist = np.where(
365
+ distances < threshold
366
+ )
367
+ # and report
368
+ if len(zero_dist[0]):
369
+ raise DataFormattingError(
370
+ 'Error: Overlapping atoms detected in structure'
371
+ ' at atom numbers:\n' + ', '.join(
372
+ str(i+1) for i in zero_dist[0]
373
+ )
374
+ )
375
+
376
+ return
377
+
378
+
307
379
  def check_moinp_moread(file_name: str | Path) -> None:
308
380
  '''
309
381
  Checks if MORead and/or MOInp are present in the input file.\n
@@ -29,7 +29,7 @@ class OrcaJob():
29
29
  Orca output file name. Default is same as input with extension \n
30
30
  replaced by .out
31
31
  job_file: pathlib.Path
32
- Submission script name. Default is same as input with extension \n
32
+ Submission script file path. Default is same as input with extension \n
33
33
  replaced by scheduler job file extension e.g. '.slm'
34
34
  job_name: str
35
35
  Job name. Default is same as input without extension
@@ -99,6 +99,9 @@ class OrcaJob():
99
99
 
100
100
  @property
101
101
  def job_file(self) -> pathlib.Path:
102
+ '''
103
+ The submission script file path
104
+ '''
102
105
  return self._job_file
103
106
 
104
107
  @job_file.setter
@@ -164,7 +167,7 @@ class OrcaJob():
164
167
  verbose: bool, default True
165
168
  If True, jobscript location is written to screen
166
169
  **kwargs
167
- The keyword arguments are passed to `subto.job.Job` constructor
170
+ Keyword arguments passed to `subto.job.Job` constructor
168
171
  '''
169
172
 
170
173
  if len(self.load):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orto
3
- Version: 1.8.2
3
+ Version: 1.9.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.8.2'
11
+ __version__ = '1.9.0'
12
12
 
13
13
  setuptools.setup(
14
14
  name='orto',
@@ -1 +0,0 @@
1
- __version__ = '1.8.2'
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