Skip to content

Commit 6fcd667

Browse files
authored
ORCA Optimisation Fixes (#812)
When ORCA is used for optimisation, we attempt to get the scaling factor, however the parser was broken. It caused the scaling factor to return 0.002 at wb97x-d3. This PR should rectify it now to be: ``` FREQ: A PROGRAM FOR OPTIMIZING SCALE FACTORS (Version 1) written by Haoyu S. Yu, Lucas J. Fiedler, I.M. Alecu, and Donald G. Truhlar Department of Chemistry and Supercomputing Institute University of Minnesota, Minnesota 55455-0431 CITATIONS: 1. I.M., Alecu, J. Zheng, Y. Zhao, D.G. Truhlar, J. Chem. Theory Comput. 2010, 6, 9, 2872-2887, DOI: 10.1021/ct100326h 2. H.S. Yu, L.J. Fiedler, I.M. Alecu,, D.G. Truhlar, Computer Physics Communications 2017, 210, 132-138, DOI: 10.1016/j.cpc.2016.09.004 Level of theory: wb97xd3/def2tzvp Scale Factor for Zero-Point Energies = 0.973 Scale Factor for Harmonic Frequencies = 0.987 Scale Factor for Fundamental Frequencies = 0.948 (execution time: 00:11:35) You may copy-paste the computed harmonic frequency scaling factor(s) to ARC (under the `freq_dict` in ARC/data/freq_scale_factors.yml): 'wb97xd3/def2tzvp': 0.987, # [4] ``` Also, adjusted the optimisation keywords as they appear to be out of date.
2 parents ab32e19 + 1b00944 commit 6fcd667

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

arc/job/adapters/orca.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,50 @@
3232

3333
logger = get_logger()
3434

35+
ORCA_METHOD_ALIASES = {
36+
'wb97xd3': 'wb97x-d3',
37+
}
38+
39+
40+
def _format_orca_method(method: str) -> str:
41+
"""
42+
Convert ARC method names to ORCA-friendly labels when needed.
43+
"""
44+
if not method:
45+
return method
46+
if method.lower() == 'wb97xd':
47+
logger.warning('ORCA does not support wb97xd; use wb97x or wb97x-d3.')
48+
return ORCA_METHOD_ALIASES.get(method.lower(), method)
49+
50+
51+
def _format_orca_basis_token(token: str) -> str:
52+
"""
53+
Convert def2 basis tokens to ORCA formatting (e.g., def2tzvp -> def2-tzvp).
54+
"""
55+
if not token:
56+
return token
57+
parts = token.split('/')
58+
base = parts[0]
59+
if base.lower().startswith('def2'):
60+
base_rest = base[4:]
61+
if base_rest.startswith('-'):
62+
base_rest = base_rest[1:]
63+
if base_rest:
64+
base = f"def2-{base_rest.lower()}"
65+
if len(parts) > 1:
66+
parts = [base] + [part.lower() for part in parts[1:]]
67+
return '/'.join(parts)
68+
return base
69+
70+
71+
def _format_orca_basis(basis: str) -> str:
72+
"""
73+
Convert basis strings to ORCA-friendly labels where applicable.
74+
"""
75+
if not basis:
76+
return basis
77+
return ' '.join(_format_orca_basis_token(token) for token in basis.split())
78+
3579
default_job_settings, global_ess_settings, input_filenames, output_filenames, servers, submit_filenames = \
3680
settings['default_job_settings'], settings['global_ess_settings'], settings['input_filenames'], \
3781
settings['output_filenames'], settings['servers'], settings['submit_filenames']
@@ -219,13 +263,13 @@ def write_input_file(self) -> None:
219263
'keywords',
220264
]:
221265
input_dict[key] = ''
222-
input_dict['auxiliary_basis'] = self.level.auxiliary_basis or ''
223-
input_dict['basis'] = self.level.basis or ''
266+
input_dict['auxiliary_basis'] = _format_orca_basis(self.level.auxiliary_basis or '')
267+
input_dict['basis'] = _format_orca_basis(self.level.basis or '')
224268
input_dict['charge'] = self.charge
225269
input_dict['cpus'] = self.cpu_cores
226270
input_dict['label'] = self.species_label
227271
input_dict['memory'] = self.input_file_memory
228-
input_dict['method'] = self.level.method
272+
input_dict['method'] = _format_orca_method(self.level.method)
229273
input_dict['multiplicity'] = self.multiplicity
230274
input_dict['xyz'] = xyz_to_str(self.xyz)
231275

@@ -241,9 +285,9 @@ def write_input_file(self) -> None:
241285
input_dict['method_class'] = 'KS'
242286
# DFT grid must be the same for both opt and freq
243287
if self.fine:
244-
self.add_to_args(val='Grid6 NoFinalGrid', key1='keyword')
288+
self.add_to_args(val='defgrid3', key1='keyword')
245289
else:
246-
self.add_to_args(val='Grid5 NoFinalGrid', key1='keyword')
290+
self.add_to_args(val='defgrid2', key1='keyword')
247291
elif self.level.method_type == 'wavefunction':
248292
input_dict['method_class'] = 'HF'
249293
if 'dlpno' in self.level.method:

arc/job/adapters/orca_test.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
import unittest
1313

1414
from arc.common import ARC_PATH
15-
from arc.job.adapters.orca import OrcaAdapter
15+
from arc.job.adapters.orca import (OrcaAdapter,
16+
_format_orca_basis,
17+
_format_orca_basis_token,
18+
_format_orca_method,
19+
)
1620
from arc.level import Level
1721
from arc.settings.settings import input_filenames, output_filenames
1822
from arc.species import ARCSpecies
@@ -173,6 +177,28 @@ def test_write_input_file_with_CPCM_solvation(self):
173177
"""
174178
self.assertEqual(content_3, job_3_expected_input_file)
175179

180+
def test_format_orca_method(self):
181+
"""Test ORCA method formatting helper."""
182+
self.assertEqual(_format_orca_method('wb97xd3'), 'wb97x-d3')
183+
self.assertEqual(_format_orca_method('wb97xd'), 'wb97xd')
184+
self.assertEqual(_format_orca_method('B3LYP'), 'B3LYP')
185+
186+
def test_format_orca_basis_token(self):
187+
"""Test ORCA basis token formatting helper."""
188+
self.assertEqual(_format_orca_basis_token('def2tzvp'), 'def2-tzvp')
189+
self.assertEqual(_format_orca_basis_token('def2-TZVP'), 'def2-tzvp')
190+
self.assertEqual(_format_orca_basis_token('def2tzvp/c'), 'def2-tzvp/c')
191+
self.assertEqual(_format_orca_basis_token('def2-TZVP/C'), 'def2-tzvp/c')
192+
self.assertEqual(_format_orca_basis_token('cc-pvtz'), 'cc-pvtz')
193+
194+
def test_format_orca_basis(self):
195+
"""Test ORCA basis formatting helper."""
196+
self.assertEqual(_format_orca_basis('def2tzvp'), 'def2-tzvp')
197+
self.assertEqual(_format_orca_basis('def2-TZVP'), 'def2-tzvp')
198+
self.assertEqual(_format_orca_basis('def2tzvp/c'), 'def2-tzvp/c')
199+
self.assertEqual(_format_orca_basis('def2tzvp def2tzvp/c'),
200+
'def2-tzvp def2-tzvp/c')
201+
176202
def test_set_files(self):
177203
"""Test setting files"""
178204
job_1_files_to_upload = [{'file_name': 'submit.sub',

arc/parser/adapters/orca.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,11 @@ def parse_zpe_correction(self) -> Optional[float]:
224224
if 'Zero point energy' in line:
225225
# Example: Zero point energy ... 0.025410 Eh
226226
try:
227-
zpe = float(line.split()[-2])
227+
parts = line.split()
228+
if 'Eh' in parts:
229+
zpe = float(parts[parts.index('Eh') - 1])
230+
else:
231+
zpe = float(parts[-2])
228232
break
229233
except (ValueError, IndexError):
230234
continue

0 commit comments

Comments
 (0)