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

JdftxInputGenerator works from added BaseJdftxSet.yaml #5

Merged
merged 1 commit into from
Aug 6, 2024
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
2 changes: 1 addition & 1 deletion src/atomate2/jdftx/emmet/core/jdftx/calculation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Core definitions of a QChem calculation document."""
"""Core definitions of a JDFTx calculation document."""

# mypy: ignore-errors

Expand Down
236 changes: 236 additions & 0 deletions src/atomate2/jdftx/emmet/core/jdftx/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# mypy: ignore-errors

""" Core definition of a Q-Chem Task Document """
from typing import Any, Dict, List, Optional, Callable

from pydantic import BaseModel, Field
from pymatgen.core.structure import Molecule

from emmet.core.structure import MoleculeMetadata
from emmet.core.task import BaseTaskDocument
from emmet.core.utils import ValueEnum
from emmet.core.qchem.calc_types import (
LevelOfTheory,
CalcType,
TaskType,
calc_type,
level_of_theory,
task_type,
solvent,
lot_solvent_string,
)


__author__ = "Evan Spotte-Smith <ewcspottesmith@lbl.gov>"


class JDFTxStatus(ValueEnum):
"""
JDFTx Calculation State
"""

SUCCESS = "successful"
FAILED = "unsuccessful"


class OutputSummary(BaseModel):
"""
Summary of an output for a Q-Chem calculation
"""

initial_molecule: Optional[Molecule] = Field(
None, description="Input Molecule object"
)
optimized_molecule: Optional[Molecule] = Field(
None, description="Optimized Molecule object"
)

final_energy: Optional[float] = Field(
None, description="Final electronic energy for the calculation (units: Hartree)"
)
enthalpy: Optional[float] = Field(
None, description="Total enthalpy of the molecule (units: kcal/mol)"
)
entropy: Optional[float] = Field(
None, description="Total entropy of the molecule (units: cal/mol-K"
)

mulliken: Optional[List[Any]] = Field(
None, description="Mulliken atomic partial charges and partial spins"
)
resp: Optional[List[float]] = Field(
None,
description="Restrained Electrostatic Potential (RESP) atomic partial charges",
)
nbo: Optional[Dict[str, Any]] = Field(
None, description="Natural Bonding Orbital (NBO) output"
)

frequencies: Optional[List[float]] = Field(
None, description="Vibrational frequencies of the molecule (units: cm^-1)"
)

def as_dict(self) -> Dict[str, Any]:
return {
"@module": self.__class__.__module__,
"@class": self.__class__.__name__,
"initial_molecule": self.initial_molecule,
"optimized_molecule": self.optimized_molecule,
"final_energy": self.final_energy,
"enthalpy": self.enthalpy,
"entropy": self.entropy,
"mulliken": self.mulliken,
"resp": self.resp,
"nbo": self.nbo,
"frequencies": self.frequencies,
}


class TaskDocument(BaseTaskDocument, MoleculeMetadata):
"""
Definition of a Q-Chem task document
"""

calc_code: str = "Q-Chem"
completed: bool = True

is_valid: bool = Field(
True, description="Whether this task document passed validation or not"
)
state: Optional[QChemStatus] = Field(None, description="State of this calculation")

cputime: Optional[float] = Field(None, description="The system CPU time in seconds")
walltime: Optional[float] = Field(
None, description="The real elapsed time in seconds"
)

calcs_reversed: List[Dict] = Field(
[], description="The 'raw' calculation docs used to assembled this task"
)

orig: Dict[str, Any] = Field(
{}, description="Summary of the original Q-Chem inputs"
)
output: OutputSummary = Field(OutputSummary())

critic2: Optional[Dict[str, Any]] = Field(
None, description="Output from Critic2 critical point analysis code"
)
custom_smd: Optional[str] = Field(
None, description="Parameter string for SMD implicit solvent model"
)

special_run_type: Optional[str] = Field(
None, description="Special workflow name (if applicable)"
)

smiles: Optional[str] = Field(
None,
description="Simplified molecular-input line-entry system (SMILES) string for the molecule involved "
"in this calculation.",
)

species_hash: Optional[str] = Field(
None,
description="Weisfeiler Lehman (WL) graph hash using the atom species as the graph "
"node attribute.",
)
coord_hash: Optional[str] = Field(
None,
description="Weisfeiler Lehman (WL) graph hash using the atom coordinates as the graph "
"node attribute.",
)

# TODO - type of `tags` field seems to differ among task databases
# sometimes List, sometimes Dict
# left as Any here to ensure tags don't cause validation to fail.
tags: Optional[Any] = Field(None, description="Metadata tags")

warnings: Optional[Dict[str, bool]] = Field(
None, description="Any warnings related to this task document"
)

@property
def level_of_theory(self) -> LevelOfTheory:
return level_of_theory(self.orig)

@property
def solvent(self) -> str:
return solvent(self.orig, custom_smd=self.custom_smd)

@property
def lot_solvent(self) -> str:
return lot_solvent_string(self.orig, custom_smd=self.custom_smd)

@property
def task_type(self) -> TaskType:
return task_type(self.orig, special_run_type=self.special_run_type)

@property
def calc_type(self) -> CalcType:
return calc_type(self.special_run_type, self.orig)

@property
def entry(self) -> Dict[str, Any]:
if self.output.optimized_molecule is not None:
mol = self.output.optimized_molecule
else:
mol = self.output.initial_molecule

if self.charge is None:
charge = int(mol.charge)
else:
charge = int(self.charge)

if self.spin_multiplicity is None:
spin = mol.spin_multiplicity
else:
spin = self.spin_multiplicity

entry_dict = {
"entry_id": self.task_id,
"task_id": self.task_id,
"charge": charge,
"spin_multiplicity": spin,
"level_of_theory": self.level_of_theory,
"solvent": self.solvent,
"lot_solvent": self.lot_solvent,
"custom_smd": self.custom_smd,
"task_type": self.task_type,
"calc_type": self.calc_type,
"molecule": mol,
"composition": mol.composition,
"formula": mol.composition.alphabetical_formula,
"energy": self.output.final_energy,
"output": self.output.as_dict(),
"critic2": self.critic2,
"orig": self.orig,
"tags": self.tags,
"last_updated": self.last_updated,
"species_hash": self.species_hash,
"coord_hash": self.coord_hash,
}

return entry_dict


def filter_task_type(
entries: List[Dict[str, Any]],
task_type: TaskType,
sort_by: Optional[Callable] = None,
) -> List[Dict[str, Any]]:
"""
Filter (and sort) TaskDocument entries based on task type

:param entries: List of TaskDocument entry dicts
:param TaskType: TaskType to accept
:param sort_by: Function used to sort (default None)
:return: Filtered (sorted) list of entries
"""

filtered = [f for f in entries if f["task_type"] == task_type]

if sort_by is not None:
return sorted(filtered, key=sort_by)
else:
return filtered
7 changes: 3 additions & 4 deletions src/atomate2/jdftx/emmet/jdftx_tasks.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# mypy: ignore-errors

""" Core definition of a Q-Chem Task Document """
""" Core definition of a JDFTx Task Document """
from typing import Any, Dict, List, Optional
import logging
import re
from collections import OrderedDict
from pydantic import BaseModel, Field
from custodian.qchem.jobs import QCJob
from pymatgen.core.structure import Molecule
from pymatgen.io.qchem.inputs import QCInput
from atomate2.jdftx.io.inputs import JdftxInput
from monty.serialization import loadfn
from typing import Type, TypeVar, Union
from emmet.core.structure import MoleculeMetadata
Expand All @@ -20,7 +19,7 @@
)
from emmet.core.qchem.calculation import Calculation, CalculationInput

from emmet.core.qchem.task import QChemStatus
from atomate2.jdftx.emmet.core.jdftx.task import JDFTxStatus


__author__ = (
Expand Down
48 changes: 48 additions & 0 deletions src/atomate2/jdftx/io/CO.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#Testing JDFTx input file for CO molecule. Intended to test input parsers

lattice \
18.897261 0.000000 0.000000 \
0.000000 18.897261 0.000000 \
0.000000 0.000000 18.897261

dump-name $VAR
initial-state $VAR
elec-ex-corr gga
van-der-waals D3
elec-cutoff 20 100
elec-n-bands 15
kpoint-folding 1 1 1
electronic-minimize nIterations 100 energyDiffThreshold 1e-07
elec-smearing Fermi 0.001
spintype z-spin
core-overlap-check none
converge-empty-states yes
latt-move-scale 0 0 0
symmetries none
fluid LinearPCM
pcm-variant CANDLE
fluid-solvent H2O
fluid-cation Na+ 0.5
fluid-anion F- 0.5
vibrations useConstraints no rotationSym no
dump End Dtot
dump End BoundCharge
dump End State
dump End Forces
dump End Ecomponents
dump End VfluidTot
dump End ElecDensity
dump End KEdensity
dump End EigStats
dump End BandEigs
dump End DOS

coords-type Cartesian
ion O -0.235981 -0.237621 2.242580 1
ion C -0.011521 -0.011600 0.109935 1

ion-species GBRV_v1.5/$ID_pbe_v1.uspp

coulomb-interaction Periodic
dump End Forces
dump End Ecomponents
6 changes: 3 additions & 3 deletions src/atomate2/jdftx/io/JDFTXInfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
from pymatgen.util.io_utils import clean_lines
from pymatgen.core import Structure

from generic_tags import flatten_list
from JDFTXInfile_master_format import get_tag_object
from JDFTXInfile_master_format import MASTER_TAG_LIST, __TAG_LIST__, __WANNIER_TAGS__, __PHONON_TAGS__
from .generic_tags import flatten_list
from .JDFTXInfile_master_format import get_tag_object
from .JDFTXInfile_master_format import MASTER_TAG_LIST, __TAG_LIST__, __WANNIER_TAGS__, __PHONON_TAGS__

if TYPE_CHECKING:
from typing import Any
Expand Down
2 changes: 1 addition & 1 deletion src/atomate2/jdftx/io/JDFTXInfile_master_format.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from copy import deepcopy

from generic_tags import BoolTag, StrTag, IntTag, FloatTag, TagContainer, MultiformatTag
from .generic_tags import BoolTag, StrTag, IntTag, FloatTag, TagContainer, MultiformatTag


#simple dictionaries deepcopied multiple times into MASTER_TAG_LIST later for different tags
Expand Down
Empty file.
10 changes: 6 additions & 4 deletions src/atomate2/jdftx/io/example-read.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

import sys
import numpy as np
import pathlib

from JDFTXInfile import JDFTXInfile
from atomate2.jdftx.io.JDFTXInfile import JDFTXInfile


#read file example
filename = 'input-simple1.in'
p = pathlib.Path(__file__)
filename = p.parents[0] / pathlib.Path("input-simple1.in")
jin1 = JDFTXInfile.from_file(filename)
print(jin1)
jin1.write_file('test-write.in')
# jin1.write_file('test-write.in')
print('===============================================================')


Expand Down Expand Up @@ -42,7 +44,7 @@
'elec-n-bands': 20,
'elec-ex-corr': 'gga-PBE',
'dump-name': 'jdft.$VAR',
'dump': {'freq': 'End', 'var': 'State'},
'dump': {'freq': 'End', 'var': 'State'}, # TODO add support for dump lists
}
jin2 = JDFTXInfile.from_dict(water_tagdict)
print(jin2)
Expand Down
6 changes: 4 additions & 2 deletions src/atomate2/jdftx/io/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from pymatgen.core import Molecule, Structure
from pymatgen.io.core import InputFile

from .utils import lower_and_check_unique, read_pattern, read_table_pattern
#TODO functions are currently not implemented in utils. Remove these?
# from .utils import lower_and_check_unique, read_pattern, read_table_pattern

if TYPE_CHECKING:
from pathlib import Path
Expand Down Expand Up @@ -56,9 +57,10 @@ def __init__(
svp: dict | None = None,
pcm_nonels: dict | None = None,
):
#TODO update docustring for JDFTx
"""
Args:
molecule (pymatgen Molecule object, list of Molecule objects, or "read"):
structure (pymatgen Structure object or "read"):
Input molecule(s). molecule can be set as a pymatgen Molecule object, a list of such
Molecule objects, or as the string "read". "read" can be used in multi_job QChem input
files where the molecule is read in from the previous calculation.
Expand Down
Loading