From 8e4344d3ba2d2dc3c4e3ec9b1c5d6caeec23de12 Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Fri, 10 Nov 2023 05:38:20 -0500 Subject: [PATCH 1/3] Switched to standard handling of configurations. --- dftbplus_step/optimization.py | 99 +++++++++++------------- dftbplus_step/optimization_parameters.py | 57 ++++++-------- 2 files changed, 68 insertions(+), 88 deletions(-) diff --git a/dftbplus_step/optimization.py b/dftbplus_step/optimization.py index e676ea4..a07b863 100644 --- a/dftbplus_step/optimization.py +++ b/dftbplus_step/optimization.py @@ -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" @@ -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) @@ -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, @@ -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 = 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") diff --git a/dftbplus_step/optimization_parameters.py b/dftbplus_step/optimization_parameters.py index 89774be..7daa280 100644 --- a/dftbplus_step/optimization_parameters.py +++ b/dftbplus_step/optimization_parameters.py @@ -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__) @@ -159,37 +160,6 @@ 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 ", - "kind": "string", - "default_units": "", - "enumeration": ( - "optimized with ", - "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): @@ -197,9 +167,26 @@ def __init__(self, defaults={}, data=None): 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 @@ -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 ": + data[key] = "optimized with {Hamiltonian}" + super().update(data) From ecf77718b5dac723bc5490d5cfaf4746cb5e6f8d Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Fri, 10 Nov 2023 05:38:41 -0500 Subject: [PATCH 2/3] Cleaned up printing and model name in properties. --- dftbplus_step/base.py | 9 +++++++++ dftbplus_step/choose_parameters.py | 12 +++++++----- dftbplus_step/dftbplus.py | 3 +-- dftbplus_step/energy.py | 18 +++++++++--------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/dftbplus_step/base.py b/dftbplus_step/base.py index a510da6..0b58b21 100644 --- a/dftbplus_step/base.py +++ b/dftbplus_step/base.py @@ -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 ): diff --git a/dftbplus_step/choose_parameters.py b/dftbplus_step/choose_parameters.py index 1f7252e..297c396 100644 --- a/dftbplus_step/choose_parameters.py +++ b/dftbplus_step/choose_parameters.py @@ -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 ): @@ -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"] @@ -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 @@ -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"] @@ -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 diff --git a/dftbplus_step/dftbplus.py b/dftbplus_step/dftbplus.py index 50b99b1..5ffa130 100644 --- a/dftbplus_step/dftbplus.py +++ b/dftbplus_step/dftbplus.py @@ -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. @@ -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( diff --git a/dftbplus_step/energy.py b/dftbplus_step/energy.py index 74a56ed..813d798 100644 --- a/dftbplus_step/energy.py +++ b/dftbplus_step/energy.py @@ -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: @@ -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: @@ -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 * " ", ) ) @@ -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) From 2bf9a093b05b87abc09a24f5c5238c03980c530c Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Fri, 10 Nov 2023 05:40:32 -0500 Subject: [PATCH 3/3] Preparing for release --- HISTORY.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index e66cab3..87497c4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -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