Source code for psi4_step.optimization
# -*- coding: utf-8 -*-
"""Setup and run Psi4"""
import logging
from pathlib import Path
import psi4_step
import seamm
import seamm.data
from seamm_util import units_class
import seamm_util.printing as printing
from seamm_util.printing import FormattedText as __
logger = logging.getLogger(__name__)
job = printing.getPrinter()
printer = printing.getPrinter("psi4")
[docs]
class Optimization(psi4_step.Energy):
def __init__(
self,
flowchart=None,
title="Optimization",
extension=None,
logger=logger,
):
"""Initialize the node"""
logger.debug("Creating Optimization {}".format(self))
super().__init__(flowchart=flowchart, title=title, extension=extension)
self._calculation = "optimization"
self._model = None
self._metadata = psi4_step.metadata
self.parameters = psi4_step.OptimizationParameters()
self.description = "A geometry optimization"
[docs]
def description_text(
self,
P=None,
calculation_type="Geometry optimization",
configuration=None,
):
"""Prepare information about what this node will do"""
if not P:
P = self.parameters.values_to_dict()
text = super().description_text(
P=P, calculation_type=calculation_type, configuration=configuration
)
added = "\nThe geometry optimization will use the {optimization method} "
if P["max geometry steps"] == "default":
added += "method, using the default maximum number of steps, which"
added += " is based on the system size."
else:
added += "method, with no more than {max geometry steps} steps."
if P["geometry convergence"] == "Custom":
added += " The convergence criterion is"
else:
added += " The convergence criterion is '{geometry convergence}'."
if P["recalc hessian"] != "never":
added += " The Hessian will be recalculated every {recalc hessian}"
added += " steps. Note that calculating the second derivatives is "
added += "quite expensive!"
return text + "\n" + __(added, **P, indent=4 * " ").__str__()
[docs]
def get_input(self, calculation_type="optimize"):
"""Get the input for an optimization calculation for Psi4"""
_, configuration = self.get_system_configuration()
# Create the directory
directory = Path(self.directory)
directory.mkdir(parents=True, exist_ok=True)
# Check for atoms and bypass optimization
if configuration.n_atoms == 1:
return super().get_input()
# references = self.parent.references
P = self.parameters.current_values_to_dict(
context=seamm.flowchart_variables._data
)
# Have to fix formatting for printing...
PP = dict(P)
for key in PP:
if isinstance(PP[key], units_class):
PP[key] = "{:~P}".format(PP[key])
self.description = []
self.description.append(
__(
self.description_text(PP, configuration=configuration),
**PP,
indent=self.indent,
)
)
lines = []
lines.append("")
lines.append("#" * 80)
lines.append(f"# {self.header}")
lines.append("#" * 80)
lines.append("")
lines.append("set opt_type min")
if P["optimization method"] in psi4_step.optimization_methods:
opt_method = psi4_step.optimization_methods[P["optimization method"]]
else:
opt_method = P["optimization method"]
lines.append(f"set step_type {opt_method}")
lines.append(f"set opt_coordinates {P['coordinates']}")
max_steps = P["max geometry steps"]
if max_steps == "default":
max_steps = 6 * configuration.n_atoms
if "nAtoms" in max_steps:
n_atoms = configuration.n_atoms
max_steps = max_steps.replace("nAtoms", str(n_atoms))
max_steps = eval(max_steps)
lines.append(f"set geom_maxiter {max_steps}")
if P["geometry convergence"] == "Custom":
pass
else:
if P["geometry convergence"] in psi4_step.optimization_convergence:
convergence = psi4_step.optimization_convergence[
P["geometry convergence"]
]
else:
convergence = P["geometry convergence"]
lines.append(f"set g_convergence {convergence}")
if P["recalc hessian"] == "every step":
lines.append("set full_hess_every 1")
elif P["recalc hessian"] == "at beginning":
lines.append("set full_hess_every 0")
elif P["recalc hessian"] == "never":
lines.append("set full_hess_every -1")
else:
lines.append(f"set full_hess_every {P['recalc hessian']}")
lines.append(f"set hess_update {P['hessian update']}")
lines.append("")
# Add in the input from the energy part of things
lines.append(super().get_input(calculation_type=calculation_type))
return "\n".join(lines)