Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standard structure handling and cleaned up output #35

Merged
merged 3 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
=======
History
=======
2023.11.10 -- Standard structure handling and cleaned up output
* Switched to standard structure handling and naming, giving consistent options
across SEAMM.
* Corrected issues with the model name in the properties.
* Generally cleaned up the output, mainly indentation.

2023.11.8 -- Bugfix: Fermi level being an array caused problems
* The Fermi level in DFTB+ is a vector with 1 or 2 elements, depending whether the
calculation is spin-polarized. DFTB+ can handle different Fermi levels, but it is
Expand Down
9 changes: 9 additions & 0 deletions dftbplus_step/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ def is_runable(self):
"""Indicate whether this not runs or just adds input."""
return True

@property
def model(self):
"""The model (chemistry) used to obtain results."""
return self.parent.model

@model.setter
def model(self, value):
self.parent.model = value

def band_structure(
self, input_path, sym_points, sym_names, Efermi=[0.0, 0.0], DOS=None
):
Expand Down
12 changes: 7 additions & 5 deletions dftbplus_step/choose_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import seamm_util.printing as printing
from seamm_util.printing import FormattedText as __

from .base import DftbBase

logger = logging.getLogger(__name__)
job = printing.getPrinter()
printer = printing.getPrinter("DFTB+")


class ChooseParameters(seamm.Node):
class ChooseParameters(DftbBase):
def __init__(
self, flowchart=None, title="Choose Parameters", extension=None, logger=logger
):
Expand Down Expand Up @@ -88,7 +90,7 @@ def get_input(self):
PP[key] = "{:~P}".format(PP[key])

self.description = []
self.description.append(__(self.description_text(PP), **PP, indent=self.indent))
self.description.append(__(self.description_text(PP), **PP, indent=4 * " "))

# The parameter data
parameter_set = P["dataset"]
Expand All @@ -113,7 +115,7 @@ def get_input(self):
potentials = metadata[model]["potentials"]
pairs = dataset["potential pairs"]

self.parent._hamiltonian = parameter_set
self.model = parameter_set.replace(" - ", "/")

if P["subset"] == "none" or P["subset"] == "":
subset = None
Expand All @@ -122,7 +124,7 @@ def get_input(self):
if " - " not in subset:
# From a variable
subset = f"{model} - {subset}"
self.parent._hamiltonian += " + " + subset.split(" - ")[1]
self.model += "+" + subset.split(" - ")[1]
subset = datasets[subset]
subpairs = subset["potential pairs"]

Expand Down Expand Up @@ -216,7 +218,7 @@ def get_input(self):
)
elif model == "xTB":
# Broadcast to the parent so that other substeps can use
self.parent._hamiltonian = P["dataset"]
self.model = P["dataset"].replace(" - ", "/")
self.parent._dataset = dataset
self.parent._subset = None

Expand Down
3 changes: 1 addition & 2 deletions dftbplus_step/dftbplus.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ def __init__(
# Data to pass between substeps
self._dataset = None # SLAKO dataset used
self._subset = None # SLAKO modifier dataset applied to dataset
self._hamiltonian = None # String name of the Hamiltonian
self._reference_energies = None # Reference energies per element.
self._reference_energy = None # for calculating energy of formation
self._steps = None # The nodes for the steps run so far.
Expand Down Expand Up @@ -363,7 +362,7 @@ def description_text(self, P=None):
text = self.header + "\n\n"
while node is not None:
try:
text += __(node.description_text(), indent=3 * " ").__str__()
text += __(node.description_text(), indent=4 * " ").__str__()
except Exception as e:
print(
"Error describing dftbplus flowchart: {} in {}".format(
Expand Down
18 changes: 9 additions & 9 deletions dftbplus_step/energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ def get_input(self):

# Set up the description.
self.description = []
self.description.append(__(self.description_text(PP), **PP, indent=self.indent))
self.description.append(__(self.description_text(PP), **PP, indent=4 * " "))

# Determine the input and as we do so, replace any default values
# in PP so that we print what is actually done

dataset = self.parent._dataset
parameter_set = self.parent._hamiltonian
model, parameter_set_name = parameter_set.split(" - ", 1)
parameter_set = self.model
model, parameter_set_name = parameter_set.split("/", 1)

if model == "DFTB":
if "defaults" in dataset:
Expand Down Expand Up @@ -407,9 +407,9 @@ def get_input(self):
# First check if we have shell resolved constants or not
spin_constants = hamiltonian["SpinConstants"] = {}
symbols = sorted([*set(atoms.symbols)])
dataset_name = self.parent._hamiltonian
dataset_name = self.model
# e.g. "DFTB - mio"
key = dataset_name.split(" - ")[1]
key = dataset_name.split("/")[1]
if key in spin_constant_data:
constants = spin_constant_data[key]
else:
Expand Down Expand Up @@ -496,7 +496,7 @@ def get_input(self):
__(
f"The mesh for the Brillouin zone integration is {na} x {nb} x {nc}"
f" with offsets of {oa}, {ob}, and {oc}",
indent=self.indent + 4 * " ",
indent=4 * " ",
)
)

Expand Down Expand Up @@ -711,11 +711,11 @@ def analyze(self, indent="", data={}, out=[]):
except Exception as e:
text += f"There was an error making the plots: {str(e)}"
else:
text += f"Orbital plots not supported for {self.parent._hamiltonian}"
text += f"Orbital plots not supported for {self.model}"

text = str(__(text, **data, indent=self.indent + 4 * " "))
text = str(__(text, **data, indent=8 * " "))
text += "\n\n"
text += textwrap.indent("\n".join(text_lines), self.indent + 7 * " ")
text += textwrap.indent("\n".join(text_lines), 12 * " ")

printer.normal(text)

Expand Down
99 changes: 44 additions & 55 deletions dftbplus_step/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ def description_text(self, P=None):
f" A maximum of {P['MaxSteps']} steps will be used."
)

if self.model is None:
kwargs = {}
else:
kwargs = {"Hamiltonian": self.model}
text += seamm.standard_parameters.structure_handling_description(P, **kwargs)

return (
self.header
+ "\n"
Expand All @@ -85,7 +91,7 @@ def get_input(self):
PP[key] = "{:~P}".format(PP[key])

self.description = []
self.description.append(__(self.description_text(PP), **PP, indent=self.indent))
self.description.append(__(self.description_text(PP), **PP, indent=4 * " "))

_, configuration = self.get_system_configuration(None)

Expand Down Expand Up @@ -136,26 +142,39 @@ def analyze(self, indent="", data={}, out=[]):
context=seamm.flowchart_variables._data
)

# Read the detailed output file to get the number of iterations
directory = Path(self.directory)
path = directory / "detailed.out"
lines = iter(path.read_text().splitlines())
data["nsteps"] = "unknown number of"
data["ediff"] = "unknown"
data["scc error"] = None
for line in lines:
if "Geometry optimization step:" in line:
data["nsteps"] = line.split()[3]
if "Diff electronic" in line:
tmp = next(lines).split()
data["ediff"] = float(tmp[2])

# Print the key results

text += (
"The geometry optimization converged in {nsteps} steps. "
"The last change in energy was {ediff:.6} Eh"
)
if P["SCC"] == "Yes" and data["scc error"] is not None:
text += " and the error in the charges of {scc error:.6}."
else:
text += "."

# Update the structure
if "final structure" in data:
sdata = data["final structure"]

system, starting_configuration = self.get_system_configuration(None)
periodicity = starting_configuration.periodicity
if (
"structure handling" in P
and P["structure handling"] == "Create a new configuration"
):
configuration = system.create_configuration(
periodicity=periodicity,
atomset=starting_configuration.atomset,
bondset=starting_configuration.bondset,
cell_id=starting_configuration.cell_id,
)
else:
configuration = starting_configuration
_, starting_configuration = self.get_system_configuration()
system, configuration = self.get_system_configuration(P)

if periodicity == 3:
if starting_configuration.periodicity == 3:
(
lattice_in,
fractionals_in,
Expand Down Expand Up @@ -190,45 +209,15 @@ def analyze(self, indent="", data={}, out=[]):
)

# And the name of the configuration.
if "configuration name" in P:
if P["configuration name"] == "optimized with <Hamiltonian>":
hamiltonian = self.parent._hamiltonian
configuration.name = f"optimized with {hamiltonian}"
elif P["configuration name"] == "keep current name":
pass
elif P["configuration name"] == "use SMILES string":
configuration.name = configuration.smiles
elif P["configuration name"] == "use Canonical SMILES string":
configuration.name = configuration.canonical_smiles
elif P["configuration name"] == "use configuration number":
configuration.name = str(configuration.n_configurations)

# Read the detailed output file to get the number of iterations
directory = Path(self.directory)
path = directory / "detailed.out"
lines = iter(path.read_text().splitlines())
data["nsteps"] = "unknown number of"
data["ediff"] = "unknown"
data["scc error"] = None
for line in lines:
if "Geometry optimization step:" in line:
data["nsteps"] = line.split()[3]
if "Diff electronic" in line:
tmp = next(lines).split()
data["ediff"] = float(tmp[2])

# Print the key results

text += (
"The geometry optimization converged in {nsteps} steps. "
"The last change in energy was {ediff:.6} Eh"
)
if P["SCC"] == "Yes" and data["scc error"] is not None:
text += " and the error in the charges of {scc error:.6}."
else:
text += "."

printer.normal(__(text, **data, indent=self.indent + 4 * " "))
text += seamm.standard_parameters.set_names(
system,
configuration,
P,
_first=True,
Hamiltonian=self.model,
)

printer.normal(__(text, **data, indent=8 * " "))

printer.normal("\n")

Expand Down
57 changes: 24 additions & 33 deletions dftbplus_step/optimization_parameters.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
"""Global control parameters for DFTB+
"""
import logging

import dftbplus_step
import logging
import seamm

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -159,47 +160,33 @@ class OptimizationParameters(dftbplus_step.EnergyParameters):
"description": "Alpha update on reset:",
"help_text": "The factor for the update the alpha parameter on reset.",
},
# Put in the configuration handling options needed
"structure handling": {
"default": "Create a new configuration",
"kind": "enum",
"default_units": "",
"enumeration": (
"Overwrite the current configuration",
"Create a new configuration",
),
"format_string": "s",
"description": "Configuration handling:",
"help_text": (
"Whether to overwrite the current configuration, or create a new "
"configuration or system and configuration for the new structure"
),
},
"configuration name": {
"default": "optimized with <Hamiltonian>",
"kind": "string",
"default_units": "",
"enumeration": (
"optimized with <Hamiltonian>",
"keep current name",
"use SMILES string",
"use Canonical SMILES string",
"use configuration number",
),
"format_string": "s",
"description": "Configuration name:",
"help_text": "The name for the new configuration",
},
}

def __init__(self, defaults={}, data=None):
"""Initialize the instance, by default from the default
parameters given in the class"""

super().__init__(
defaults={**OptimizationParameters.parameters, **defaults}, data=data
defaults={
**OptimizationParameters.parameters,
**seamm.standard_parameters.structure_handling_parameters,
**defaults,
},
data=data,
)

# Do any local editing of defaults
tmp = self["structure handling"]
tmp.description = "Structure handling:"

tmp = self["system name"]
tmp._data["enumeration"] = (*tmp.enumeration, "optimized with {Hamiltonian}")
tmp.default = "keep current name"

tmp = self["configuration name"]
tmp._data["enumeration"] = ["optimized with {Hamiltonian}", *tmp.enumeration]
tmp.default = "optimized with {Hamiltonian}"

def update(self, data):
"""Update values from a dict

Expand All @@ -220,4 +207,8 @@ def update(self, data):
if key in data:
del data[key]

for key in ("system name", "configuration name"):
if key in data and data[key] == "optimized with <Hamiltonian>":
data[key] = "optimized with {Hamiltonian}"

super().update(data)
Loading