orca-step 2026.6.27__py2.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.
- orca_step/__init__.py +37 -0
- orca_step/__main__.py +16 -0
- orca_step/_version.py +21 -0
- orca_step/data/configuration.txt +16 -0
- orca_step/data/properties.csv +1 -0
- orca_step/data/references.bib +40 -0
- orca_step/energy.py +186 -0
- orca_step/energy_parameters.py +98 -0
- orca_step/energy_step.py +25 -0
- orca_step/installer.py +39 -0
- orca_step/metadata.py +80 -0
- orca_step/optimization.py +114 -0
- orca_step/optimization_parameters.py +30 -0
- orca_step/optimization_step.py +25 -0
- orca_step/orca.py +103 -0
- orca_step/orca_base.py +271 -0
- orca_step/orca_step.py +65 -0
- orca_step/tk_energy.py +90 -0
- orca_step/tk_optimization.py +35 -0
- orca_step/tk_orca.py +63 -0
- orca_step-2026.6.27.dist-info/METADATA +127 -0
- orca_step-2026.6.27.dist-info/RECORD +28 -0
- orca_step-2026.6.27.dist-info/WHEEL +6 -0
- orca_step-2026.6.27.dist-info/entry_points.txt +16 -0
- orca_step-2026.6.27.dist-info/licenses/AUTHORS.rst +5 -0
- orca_step-2026.6.27.dist-info/licenses/LICENSE +31 -0
- orca_step-2026.6.27.dist-info/top_level.txt +1 -0
- orca_step-2026.6.27.dist-info/zip-safe +1 -0
orca_step/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
orca_step
|
|
5
|
+
A SEAMM plug-in for ORCA, with a sub-flowchart of capabilities (Energy,
|
|
6
|
+
Optimization, ...), modeled on the MOPAC and Gaussian steps.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Metadata and the shared base first (others import them).
|
|
10
|
+
from .metadata import metadata # noqa: F401
|
|
11
|
+
from .orca_base import ORCABase # noqa: F401
|
|
12
|
+
|
|
13
|
+
# Sub-step parameters / nodes / Tk / factories.
|
|
14
|
+
from .energy_parameters import EnergyParameters # noqa: F401
|
|
15
|
+
from .energy import Energy # noqa: F401
|
|
16
|
+
from .tk_energy import TkEnergy # noqa: F401
|
|
17
|
+
from .energy_step import EnergyStep # noqa: F401
|
|
18
|
+
|
|
19
|
+
from .optimization_parameters import OptimizationParameters # noqa: F401
|
|
20
|
+
from .optimization import Optimization # noqa: F401
|
|
21
|
+
from .tk_optimization import TkOptimization # noqa: F401
|
|
22
|
+
from .optimization_step import OptimizationStep # noqa: F401
|
|
23
|
+
|
|
24
|
+
# Main node, Tk, and factory.
|
|
25
|
+
from .orca import ORCA # noqa: F401
|
|
26
|
+
from .tk_orca import TkORCA # noqa: F401
|
|
27
|
+
from .orca_step import ORCAStep # noqa: F401
|
|
28
|
+
|
|
29
|
+
# Versioneer
|
|
30
|
+
from ._version import get_versions
|
|
31
|
+
|
|
32
|
+
__author__ = "Paul Saxe"
|
|
33
|
+
__email__ = "psaxe@molssi.org"
|
|
34
|
+
versions = get_versions()
|
|
35
|
+
__version__ = versions["version"]
|
|
36
|
+
__git_revision__ = versions["full-revisionid"]
|
|
37
|
+
del get_versions, versions
|
orca_step/__main__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# !/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""Handle the installation of the ORCA step."""
|
|
5
|
+
|
|
6
|
+
from .installer import Installer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run():
|
|
10
|
+
"""Find and/or register the ORCA executable in seamm.ini."""
|
|
11
|
+
installer = Installer()
|
|
12
|
+
installer.run()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
run()
|
orca_step/_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# This file was generated by 'versioneer.py' (0.18) from
|
|
3
|
+
# revision-control system data, or from the parent directory name of an
|
|
4
|
+
# unpacked source archive. Distribution tarballs contain a pre-generated copy
|
|
5
|
+
# of this file.
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
version_json = '''
|
|
10
|
+
{
|
|
11
|
+
"date": "2026-06-27T21:40:25-0400",
|
|
12
|
+
"dirty": false,
|
|
13
|
+
"error": null,
|
|
14
|
+
"full-revisionid": "fafca4b1d969cf0c6ca3e0ae5c8e21afc8f78b1c",
|
|
15
|
+
"version": "2026.6.27"
|
|
16
|
+
}
|
|
17
|
+
''' # END VERSION_JSON
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_versions():
|
|
21
|
+
return json.loads(version_json)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[orca-step]
|
|
2
|
+
# Path to the ORCA executable, if it is not on the PATH. ORCA must be invoked by
|
|
3
|
+
# its full path so it can find its sub-programs.
|
|
4
|
+
# orca-path =
|
|
5
|
+
|
|
6
|
+
# How many cores/processes ORCA may use (via %pal). Default is serial (1).
|
|
7
|
+
# Parallel ORCA requires a matching OpenMPI runtime (see library-path).
|
|
8
|
+
# ncores = 1
|
|
9
|
+
|
|
10
|
+
# Directory holding ORCA's required OpenMPI libraries (e.g. libmpi.40.dylib),
|
|
11
|
+
# prepended to (DY)LD_LIBRARY_PATH for parallel runs. On this machine the
|
|
12
|
+
# seamm-orca conda environment provides them.
|
|
13
|
+
# library-path =
|
|
14
|
+
|
|
15
|
+
# Maximum number of atoms for which to print detailed per-atom results.
|
|
16
|
+
# max_atoms_to_print = 25
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Property,Type,Units,Description,URL
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
This is an initial library of BibTex references for the orca_step plug-in for SEAMM,
|
|
2
|
+
generated automatically by the cookiecutter. You may wish to edit the first entry to correct some
|
|
3
|
+
of the information.
|
|
4
|
+
|
|
5
|
+
Add other references to the end of the file and add code in the plug-in to cite the references as
|
|
6
|
+
appropriate. This is done with code like this:
|
|
7
|
+
|
|
8
|
+
self.references.cite(
|
|
9
|
+
raw=self._bibliography['doi:10.1002/jcc.21224'],
|
|
10
|
+
alias='packmol',
|
|
11
|
+
module='packmol_step',
|
|
12
|
+
level=1,
|
|
13
|
+
note='The principle PACKMOL citation.'
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
which is typically in the run() method of the plug-in.
|
|
17
|
+
|
|
18
|
+
The base node class in SEAMM automatically reads this list of references into a dictionary in
|
|
19
|
+
self._bibliography with the key being the key of the BibTex entry -- usually the name on the
|
|
20
|
+
first line of the entry after the 'article' or 'misc' (for software) keyword.
|
|
21
|
+
|
|
22
|
+
See the documentation for the Reference Handler for the other arguments to cite().
|
|
23
|
+
|
|
24
|
+
# End of initial comments #
|
|
25
|
+
|
|
26
|
+
# This is the citation for this plug-in, generated and used automatically.
|
|
27
|
+
# You might need to edit the author, address, etc. The month, year and version fields
|
|
28
|
+
# are automatically filled in with information about current version of the plug-in.
|
|
29
|
+
# This is handled in seamm/nody.py, the run() method.
|
|
30
|
+
|
|
31
|
+
@Misc{orca_step,
|
|
32
|
+
author = {Paul Saxe},
|
|
33
|
+
title = {ORCA plug-in for SEAMM},
|
|
34
|
+
month = {$month},
|
|
35
|
+
year = {$year},
|
|
36
|
+
organization = {molssi-seamm},
|
|
37
|
+
url = {https://github.com/molssi-seamm/orca_step },
|
|
38
|
+
address = {USA},
|
|
39
|
+
version = {$version}
|
|
40
|
+
}
|
orca_step/energy.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""An ORCA single-point energy sub-step."""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import pprint # noqa: F401
|
|
7
|
+
|
|
8
|
+
import orca_step
|
|
9
|
+
import seamm
|
|
10
|
+
from seamm_util import ureg, Q_ # noqa: F401
|
|
11
|
+
import seamm_util.printing as printing
|
|
12
|
+
from seamm_util.printing import FormattedText as __
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
job = printing.getPrinter()
|
|
16
|
+
printer = printing.getPrinter("ORCA")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Energy(orca_step.ORCABase):
|
|
20
|
+
"""A single-point energy with ORCA.
|
|
21
|
+
|
|
22
|
+
See Also
|
|
23
|
+
--------
|
|
24
|
+
TkEnergy, EnergyParameters, Optimization
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, flowchart=None, title="Energy", extension=None, logger=logger):
|
|
28
|
+
"""Initialize the Energy sub-step."""
|
|
29
|
+
logger.debug(f"Creating ORCA Energy {self}")
|
|
30
|
+
|
|
31
|
+
super().__init__(
|
|
32
|
+
flowchart=flowchart,
|
|
33
|
+
title=title,
|
|
34
|
+
extension=extension,
|
|
35
|
+
module=__name__,
|
|
36
|
+
logger=logger,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self._calculation = "energy"
|
|
40
|
+
self._metadata = orca_step.metadata
|
|
41
|
+
self.parameters = orca_step.EnergyParameters()
|
|
42
|
+
|
|
43
|
+
def description_text(self, P=None):
|
|
44
|
+
"""Describe what this sub-step will do."""
|
|
45
|
+
if not P:
|
|
46
|
+
P = self.parameters.values_to_dict()
|
|
47
|
+
|
|
48
|
+
use_mc = P["use model chemistry"]
|
|
49
|
+
if not isinstance(use_mc, bool):
|
|
50
|
+
use_mc = use_mc == "yes"
|
|
51
|
+
|
|
52
|
+
if use_mc:
|
|
53
|
+
text = (
|
|
54
|
+
"Single-point energy with ORCA using the model chemistry from a "
|
|
55
|
+
"preceding Model Chemistry step."
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
text = f"Single-point energy with ORCA at {P['method']}/{P['basis']}."
|
|
59
|
+
return self.header + "\n" + __(text, indent=4 * " ").__str__()
|
|
60
|
+
|
|
61
|
+
def keyword_line(self, P):
|
|
62
|
+
"""Build the ORCA '!' keyword line.
|
|
63
|
+
|
|
64
|
+
If ``use model chemistry`` is set, the method (and basis, if given) come
|
|
65
|
+
from the global ``_model_chemistry`` variable defined by a preceding
|
|
66
|
+
Model Chemistry step; ORCA raises a clear error if it cannot provide that
|
|
67
|
+
model chemistry. Otherwise the explicit method/basis parameters are used.
|
|
68
|
+
The auxiliary basis and extra keywords always come from this node.
|
|
69
|
+
"""
|
|
70
|
+
use_mc = P["use model chemistry"]
|
|
71
|
+
if not isinstance(use_mc, bool):
|
|
72
|
+
use_mc = use_mc == "yes"
|
|
73
|
+
|
|
74
|
+
if use_mc:
|
|
75
|
+
method, basis = self._method_basis_from_model_chemistry(P)
|
|
76
|
+
else:
|
|
77
|
+
method, basis = P["method"], P["basis"]
|
|
78
|
+
|
|
79
|
+
keywords = [method, basis]
|
|
80
|
+
aux = P["auxiliary basis"]
|
|
81
|
+
if aux and aux.lower() != "none":
|
|
82
|
+
keywords.append(aux)
|
|
83
|
+
extra = P["extra keywords"].strip()
|
|
84
|
+
if extra:
|
|
85
|
+
keywords.append(extra)
|
|
86
|
+
return " ".join(k for k in keywords if k)
|
|
87
|
+
|
|
88
|
+
def _method_basis_from_model_chemistry(self, P):
|
|
89
|
+
"""Resolve (method, basis) from the global model chemistry, erroring
|
|
90
|
+
clearly if ORCA cannot provide it.
|
|
91
|
+
"""
|
|
92
|
+
if not self.variable_exists("_model_chemistry"):
|
|
93
|
+
raise RuntimeError(
|
|
94
|
+
"'Use the global model chemistry' is selected, but no Model "
|
|
95
|
+
"Chemistry step has defined one (the '_model_chemistry' variable "
|
|
96
|
+
"is not set). Add a Model Chemistry step before this ORCA step, "
|
|
97
|
+
"or turn off 'use model chemistry' and set the method and basis "
|
|
98
|
+
"explicitly."
|
|
99
|
+
)
|
|
100
|
+
mc = self.get_variable("_model_chemistry")
|
|
101
|
+
level = mc.get("level", "") or mc.get("method", "")
|
|
102
|
+
owner = (mc.get("owner") or "").strip()
|
|
103
|
+
mtype = (mc.get("type") or "").strip()
|
|
104
|
+
method = (mc.get("method") or "").strip()
|
|
105
|
+
basis = mc.get("basis") or ""
|
|
106
|
+
|
|
107
|
+
# ORCA can evaluate an ORCA-owned or program-agnostic level of theory,
|
|
108
|
+
# but not another program's (e.g. MOPAC/xTB/LAMMPS) or a non-QC type.
|
|
109
|
+
if owner not in ("", "ORCA"):
|
|
110
|
+
raise RuntimeError(
|
|
111
|
+
f"The model chemistry '{level}' is owned by '{owner}', which "
|
|
112
|
+
"ORCA cannot provide. Choose an ORCA (or program-agnostic) model "
|
|
113
|
+
"chemistry, or set the method explicitly in the ORCA step."
|
|
114
|
+
)
|
|
115
|
+
if mtype.upper() in ("SQM", "FF", "MLFF"):
|
|
116
|
+
raise RuntimeError(
|
|
117
|
+
f"The model chemistry '{level}' is of type '{mtype}', which ORCA "
|
|
118
|
+
"does not provide. ORCA handles HF, DFT, MP2, and coupled-cluster "
|
|
119
|
+
"(QC) methods."
|
|
120
|
+
)
|
|
121
|
+
if method == "":
|
|
122
|
+
raise RuntimeError(
|
|
123
|
+
f"The model chemistry '{level}' does not name a method ORCA can " "use."
|
|
124
|
+
)
|
|
125
|
+
# If the model chemistry omits a basis, fall back to this node's basis.
|
|
126
|
+
if basis == "":
|
|
127
|
+
basis = P["basis"]
|
|
128
|
+
return method, basis
|
|
129
|
+
|
|
130
|
+
def run(self, keywords=None):
|
|
131
|
+
"""Run the single-point energy."""
|
|
132
|
+
next_node = super().run(printer)
|
|
133
|
+
|
|
134
|
+
P = self.parameters.current_values_to_dict(
|
|
135
|
+
context=seamm.flowchart_variables._data
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
printer.important(__(self.description_text(P), indent=self.indent))
|
|
139
|
+
|
|
140
|
+
keyword_line = self.keyword_line(P)
|
|
141
|
+
if keywords:
|
|
142
|
+
keyword_line += " " + " ".join(keywords)
|
|
143
|
+
|
|
144
|
+
data = self.run_orca(keyword_line)
|
|
145
|
+
|
|
146
|
+
self._data = data
|
|
147
|
+
self.analyze(P=P, data=data)
|
|
148
|
+
|
|
149
|
+
return next_node
|
|
150
|
+
|
|
151
|
+
def analyze(self, indent="", P=None, data=None, **kwargs):
|
|
152
|
+
"""Report the energy."""
|
|
153
|
+
if data is None:
|
|
154
|
+
data = getattr(self, "_data", {})
|
|
155
|
+
energy = data.get("energy")
|
|
156
|
+
if energy is None:
|
|
157
|
+
printer.normal(
|
|
158
|
+
__(
|
|
159
|
+
"ORCA finished but no final single-point energy was found.",
|
|
160
|
+
indent=self.indent + 4 * " ",
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
# Store on the configuration as a property.
|
|
166
|
+
_, configuration = self.get_system_configuration(None)
|
|
167
|
+
try:
|
|
168
|
+
configuration.properties.put(
|
|
169
|
+
"total energy#ORCA#{model}",
|
|
170
|
+
energy,
|
|
171
|
+
_type="float",
|
|
172
|
+
units="E_h",
|
|
173
|
+
)
|
|
174
|
+
except Exception as e: # pragma: no cover - property store is best-effort
|
|
175
|
+
logger.debug(f"Could not store energy property: {e}")
|
|
176
|
+
|
|
177
|
+
printer.normal(
|
|
178
|
+
__(
|
|
179
|
+
f"The total energy is {energy:.8f} E_h.",
|
|
180
|
+
indent=self.indent + 4 * " ",
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def cleanup(self):
|
|
185
|
+
"""Nothing to clean up for a single point."""
|
|
186
|
+
pass
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Control parameters for an ORCA single-point energy."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import orca_step
|
|
7
|
+
import seamm
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EnergyParameters(seamm.Parameters):
|
|
13
|
+
"""The control parameters for an ORCA energy calculation.
|
|
14
|
+
|
|
15
|
+
The default path takes the method from a preceding Model Chemistry step; the
|
|
16
|
+
user can turn that off and choose the method and basis explicitly (similar to
|
|
17
|
+
the Gaussian step). The explicit choices are driven by the step's metadata.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
parameters = {
|
|
21
|
+
"use model chemistry": {
|
|
22
|
+
"default": "yes",
|
|
23
|
+
"kind": "boolean",
|
|
24
|
+
"default_units": "",
|
|
25
|
+
"enumeration": ("yes", "no"),
|
|
26
|
+
"format_string": "",
|
|
27
|
+
"description": "Use the global model chemistry:",
|
|
28
|
+
"help_text": (
|
|
29
|
+
"Use the model chemistry defined by a preceding Model Chemistry "
|
|
30
|
+
"step (the '_model_chemistry' variable). If ORCA cannot provide "
|
|
31
|
+
"it, an error is raised. Turn this off to set the method and basis "
|
|
32
|
+
"explicitly below."
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
"method": {
|
|
36
|
+
"default": "DLPNO-CCSD(T)",
|
|
37
|
+
"kind": "enum",
|
|
38
|
+
"default_units": "",
|
|
39
|
+
"enumeration": tuple(orca_step.metadata["methods"].keys()),
|
|
40
|
+
"format_string": "",
|
|
41
|
+
"description": "Method:",
|
|
42
|
+
"help_text": "The ORCA method (an ORCA '!' keyword).",
|
|
43
|
+
},
|
|
44
|
+
"basis": {
|
|
45
|
+
"default": "def2-TZVP",
|
|
46
|
+
"kind": "enum",
|
|
47
|
+
"default_units": "",
|
|
48
|
+
"enumeration": tuple(orca_step.metadata["basis sets"]),
|
|
49
|
+
"format_string": "",
|
|
50
|
+
"description": "Basis set:",
|
|
51
|
+
"help_text": (
|
|
52
|
+
"The orbital basis set. ORCA's built-in name is used; a valid "
|
|
53
|
+
"name may also be typed in. (Basis Set Exchange is a planned "
|
|
54
|
+
"opt-in alternative.)"
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
"auxiliary basis": {
|
|
58
|
+
"default": "AutoAux",
|
|
59
|
+
"kind": "enum",
|
|
60
|
+
"default_units": "",
|
|
61
|
+
"enumeration": tuple(orca_step.metadata["auxiliary basis sets"]),
|
|
62
|
+
"format_string": "",
|
|
63
|
+
"description": "Auxiliary (fitting) basis:",
|
|
64
|
+
"help_text": (
|
|
65
|
+
"The auxiliary/fitting basis. 'AutoAux' generates a fitting basis "
|
|
66
|
+
"automatically and is the robust choice for correlated methods "
|
|
67
|
+
"(DLPNO, MP2). 'none' omits it."
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
"extra keywords": {
|
|
71
|
+
"default": "TightSCF",
|
|
72
|
+
"kind": "string",
|
|
73
|
+
"default_units": "",
|
|
74
|
+
"enumeration": tuple(),
|
|
75
|
+
"format_string": "",
|
|
76
|
+
"description": "Extra keywords:",
|
|
77
|
+
"help_text": (
|
|
78
|
+
"Any additional ORCA '!' keywords to append, e.g. 'TightSCF', "
|
|
79
|
+
"'RIJCOSX', 'Grid5'."
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
"results": {
|
|
83
|
+
"default": {},
|
|
84
|
+
"kind": "dictionary",
|
|
85
|
+
"default_units": "",
|
|
86
|
+
"enumeration": tuple(),
|
|
87
|
+
"format_string": "",
|
|
88
|
+
"description": "results",
|
|
89
|
+
"help_text": "The results to save to variables or in tables.",
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def __init__(self, defaults={}, data=None):
|
|
94
|
+
"""Initialize with the parameters above plus any overrides."""
|
|
95
|
+
logger.debug("EnergyParameters.__init__")
|
|
96
|
+
super().__init__(
|
|
97
|
+
defaults={**EnergyParameters.parameters, **defaults}, data=data
|
|
98
|
+
)
|
orca_step/energy_step.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Stevedore helper for the ORCA Energy sub-step."""
|
|
4
|
+
|
|
5
|
+
import orca_step
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EnergyStep(object):
|
|
9
|
+
my_description = {
|
|
10
|
+
"description": "Single-point energy with ORCA",
|
|
11
|
+
"group": "Calculation",
|
|
12
|
+
"name": "Energy",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def __init__(self, flowchart=None, gui=None):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def description(self):
|
|
19
|
+
return EnergyStep.my_description
|
|
20
|
+
|
|
21
|
+
def create_node(self, flowchart=None, **kwargs):
|
|
22
|
+
return orca_step.Energy(flowchart=flowchart, **kwargs)
|
|
23
|
+
|
|
24
|
+
def create_tk_node(self, canvas=None, **kwargs):
|
|
25
|
+
return orca_step.TkEnergy(canvas=canvas, **kwargs)
|
orca_step/installer.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Installer for the ORCA plug-in.
|
|
4
|
+
|
|
5
|
+
ORCA is licensed software installed manually from https://www.faccts.de / the
|
|
6
|
+
ORCA forum, so this installer locates the existing ``orca`` executable and
|
|
7
|
+
registers its full path in seamm.ini (ORCA needs its full path to launch its
|
|
8
|
+
sub-programs).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import importlib
|
|
12
|
+
import logging
|
|
13
|
+
import shutil
|
|
14
|
+
|
|
15
|
+
import seamm_installer
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Installer(seamm_installer.InstallerBase):
|
|
21
|
+
"""Locate the ORCA executable and register it in seamm.ini."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, logger=logger):
|
|
24
|
+
super().__init__(logger=logger)
|
|
25
|
+
logger.debug("Initializing the ORCA installer object.")
|
|
26
|
+
|
|
27
|
+
self.section = "orca-step"
|
|
28
|
+
self.executables = ["orca"]
|
|
29
|
+
self.resource_path = importlib.resources.files("orca_step") / "data"
|
|
30
|
+
|
|
31
|
+
def exe_version(self, config):
|
|
32
|
+
"""Return the ORCA name and version.
|
|
33
|
+
|
|
34
|
+
ORCA has no stable ``--version`` flag (the version is in the run banner),
|
|
35
|
+
so this just confirms the executable is reachable.
|
|
36
|
+
"""
|
|
37
|
+
code = config.get("code", "orca")
|
|
38
|
+
path = shutil.which(code) or code
|
|
39
|
+
return "ORCA", "unknown" if path is None else "unknown"
|
orca_step/metadata.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Metadata describing ORCA's methods, basis sets, and results.
|
|
4
|
+
|
|
5
|
+
This drives the explicit method/basis GUI (like the Gaussian step) and will also
|
|
6
|
+
back the model-chemistry protocol (``get_model_chemistry_options``) used by the
|
|
7
|
+
Model Chemistry step. It is kept deliberately small for now and will be filled
|
|
8
|
+
out as more of ORCA's capabilities are exposed.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
metadata = {}
|
|
12
|
+
|
|
13
|
+
# Methods offered in the explicit GUI. Each is an ORCA "!" keyword. "type"
|
|
14
|
+
# groups them for the model-chemistry protocol (QC = wavefunction, DFT, etc.).
|
|
15
|
+
metadata["methods"] = {
|
|
16
|
+
"HF": {"type": "HF", "description": "Hartree-Fock"},
|
|
17
|
+
"MP2": {"type": "MP2", "description": "second-order Moller-Plesset (MP2)"},
|
|
18
|
+
"RI-MP2": {"type": "MP2", "description": "RI-MP2 (resolution of identity)"},
|
|
19
|
+
"CCSD(T)": {"type": "QC", "description": "coupled cluster CCSD(T)"},
|
|
20
|
+
"DLPNO-CCSD(T)": {
|
|
21
|
+
"type": "QC",
|
|
22
|
+
"description": "near-linear-scaling DLPNO-CCSD(T)",
|
|
23
|
+
"needs_aux": True,
|
|
24
|
+
},
|
|
25
|
+
"B3LYP": {"type": "DFT", "description": "B3LYP hybrid functional"},
|
|
26
|
+
"PBE": {"type": "DFT", "description": "PBE GGA functional"},
|
|
27
|
+
"PBE0": {"type": "DFT", "description": "PBE0 hybrid functional"},
|
|
28
|
+
"wB97X-D3": {
|
|
29
|
+
"type": "DFT",
|
|
30
|
+
"description": "range-separated wB97X-D3 functional",
|
|
31
|
+
},
|
|
32
|
+
"M06-2X": {"type": "DFT", "description": "M06-2X meta-hybrid functional"},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Curated basis-set families (ORCA's built-in names). Free text is also allowed
|
|
36
|
+
# in the GUI; these are the guided choices.
|
|
37
|
+
metadata["basis sets"] = [
|
|
38
|
+
# Pople
|
|
39
|
+
"6-31G",
|
|
40
|
+
"6-31G*",
|
|
41
|
+
"6-31G**",
|
|
42
|
+
"6-31+G*",
|
|
43
|
+
"6-311G**",
|
|
44
|
+
"6-311++G**",
|
|
45
|
+
# Dunning correlation-consistent
|
|
46
|
+
"cc-pVDZ",
|
|
47
|
+
"cc-pVTZ",
|
|
48
|
+
"cc-pVQZ",
|
|
49
|
+
"aug-cc-pVDZ",
|
|
50
|
+
"aug-cc-pVTZ",
|
|
51
|
+
# Karlsruhe def2
|
|
52
|
+
"def2-SVP",
|
|
53
|
+
"def2-TZVP",
|
|
54
|
+
"def2-TZVPP",
|
|
55
|
+
"def2-QZVP",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
# Auxiliary / fitting basis choices. AutoAux generates a fitting basis for any
|
|
59
|
+
# method/basis and is the robust default for correlated (DLPNO/MP2) methods.
|
|
60
|
+
metadata["auxiliary basis sets"] = [
|
|
61
|
+
"AutoAux",
|
|
62
|
+
"none",
|
|
63
|
+
"def2/J",
|
|
64
|
+
"def2/JK",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
"""Results ORCA can produce. Same recognized fields as the other QM steps."""
|
|
68
|
+
metadata["results"] = {
|
|
69
|
+
"energy": {
|
|
70
|
+
"description": "The total energy",
|
|
71
|
+
"dimensionality": "scalar",
|
|
72
|
+
"property": "total energy#ORCA#{model}",
|
|
73
|
+
"type": "float",
|
|
74
|
+
"units": "E_h",
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Placeholder for the model-chemistry protocol; populated in the Model Chemistry
|
|
79
|
+
# integration phase via get_model_chemistry_options() on ORCAStep.
|
|
80
|
+
metadata["computational models"] = {}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""An ORCA geometry-optimization sub-step.
|
|
4
|
+
|
|
5
|
+
Extends the Energy sub-step by adding the ``Opt`` keyword and, after the run,
|
|
6
|
+
updating the configuration with the optimized geometry.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import pprint # noqa: F401
|
|
12
|
+
|
|
13
|
+
import orca_step
|
|
14
|
+
from .energy import Energy
|
|
15
|
+
import seamm # noqa: F401
|
|
16
|
+
from seamm_util.printing import FormattedText as __
|
|
17
|
+
import seamm_util.printing as printing
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
printer = printing.getPrinter("ORCA")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Optimization(Energy):
|
|
24
|
+
"""A geometry optimization with ORCA.
|
|
25
|
+
|
|
26
|
+
See Also
|
|
27
|
+
--------
|
|
28
|
+
TkOptimization, OptimizationParameters, Energy
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self, flowchart=None, title="Optimization", extension=None, logger=logger
|
|
33
|
+
):
|
|
34
|
+
logger.debug(f"Creating ORCA Optimization {self}")
|
|
35
|
+
super().__init__(
|
|
36
|
+
flowchart=flowchart, title=title, extension=extension, logger=logger
|
|
37
|
+
)
|
|
38
|
+
self._calculation = "optimization"
|
|
39
|
+
self.parameters = orca_step.OptimizationParameters()
|
|
40
|
+
|
|
41
|
+
def description_text(self, P=None):
|
|
42
|
+
if not P:
|
|
43
|
+
P = self.parameters.values_to_dict()
|
|
44
|
+
use_mc = P["use model chemistry"]
|
|
45
|
+
if not isinstance(use_mc, bool):
|
|
46
|
+
use_mc = use_mc == "yes"
|
|
47
|
+
method = "the model chemistry" if use_mc else f"{P['method']}/{P['basis']}"
|
|
48
|
+
conv = P["optimization convergence"]
|
|
49
|
+
text = f"Geometry optimization with ORCA at {method} ({conv})."
|
|
50
|
+
return self.header + "\n" + __(text, indent=4 * " ").__str__()
|
|
51
|
+
|
|
52
|
+
def run(self, keywords=None):
|
|
53
|
+
"""Run the optimization by adding the Opt keyword to the energy run."""
|
|
54
|
+
P = self.parameters.current_values_to_dict(
|
|
55
|
+
context=seamm.flowchart_variables._data
|
|
56
|
+
)
|
|
57
|
+
opt_keywords = [P["optimization convergence"]]
|
|
58
|
+
if keywords:
|
|
59
|
+
opt_keywords += list(keywords)
|
|
60
|
+
return super().run(keywords=opt_keywords)
|
|
61
|
+
|
|
62
|
+
def analyze(self, indent="", P=None, data=None, **kwargs):
|
|
63
|
+
"""Report the energy and update the configuration with the optimized
|
|
64
|
+
geometry, plus check that the optimization converged.
|
|
65
|
+
"""
|
|
66
|
+
super().analyze(indent=indent, P=P, data=data)
|
|
67
|
+
|
|
68
|
+
directory = Path(self.directory)
|
|
69
|
+
|
|
70
|
+
# Convergence check from the output.
|
|
71
|
+
out = directory / "orca.out"
|
|
72
|
+
converged = False
|
|
73
|
+
if out.exists():
|
|
74
|
+
text = out.read_text()
|
|
75
|
+
converged = "THE OPTIMIZATION HAS CONVERGED" in text
|
|
76
|
+
if not converged:
|
|
77
|
+
printer.normal(
|
|
78
|
+
__(
|
|
79
|
+
"Warning: the ORCA geometry optimization did not report "
|
|
80
|
+
"convergence.",
|
|
81
|
+
indent=self.indent + 4 * " ",
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Update the configuration with the optimized geometry (ORCA writes it
|
|
86
|
+
# to orca.xyz).
|
|
87
|
+
xyz = directory / "orca.xyz"
|
|
88
|
+
if xyz.exists():
|
|
89
|
+
coords = self._read_xyz_coordinates(xyz)
|
|
90
|
+
_, configuration = self.get_system_configuration(None)
|
|
91
|
+
if len(coords) == configuration.n_atoms:
|
|
92
|
+
configuration.atoms.set_coordinates(coords, fractionals=False)
|
|
93
|
+
printer.normal(
|
|
94
|
+
__(
|
|
95
|
+
"Updated the structure with the optimized geometry.",
|
|
96
|
+
indent=self.indent + 4 * " ",
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
logger.warning(
|
|
101
|
+
f"orca.xyz has {len(coords)} atoms, configuration has "
|
|
102
|
+
f"{configuration.n_atoms}; not updating geometry."
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def _read_xyz_coordinates(path):
|
|
107
|
+
"""Read [[x, y, z], ...] in Angstrom from a standard .xyz file."""
|
|
108
|
+
lines = path.read_text().splitlines()
|
|
109
|
+
n = int(lines[0].split()[0])
|
|
110
|
+
coords = []
|
|
111
|
+
for line in lines[2 : 2 + n]:
|
|
112
|
+
fields = line.split()
|
|
113
|
+
coords.append([float(fields[1]), float(fields[2]), float(fields[3])])
|
|
114
|
+
return coords
|