Skip to content

Commit

Permalink
Refactoring transferibility verification workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
unkcpz committed Jun 13, 2024
1 parent 94f2623 commit d5c3c88
Show file tree
Hide file tree
Showing 26 changed files with 3,666 additions and 475 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ aiida-sssp-workflow = "aiida_sssp_workflow.cli:cmd_root"
"sssp_workflow.birch_murnaghan_fit" = "aiida_sssp_workflow.calculations.birch_murnaghan_fit:birch_murnaghan_fit"

[project.entry-points."aiida.workflows"]
"sssp_workflow.measure_precision" = "aiida_sssp_workflow.workflows.measure.precision:PrecisionMeasureWorkChain"
"sssp_workflow.measure.transferability" = "aiida_sssp_workflow.workflows.measure.transferability:EOSTransferabilityWorkChain"
"sssp_workflow.measure_bands" = "aiida_sssp_workflow.workflows.measure.bands:BandsMeasureWorkChain"
"sssp_workflow.convergence.caching" = "aiida_sssp_workflow.workflows.convergence.caching:_CachingConvergenceWorkChain"
"sssp_workflow.convergence.eos" = "aiida_sssp_workflow.workflows.convergence.eos:ConvergenceEOSWorkChain"
Expand Down
2,755 changes: 2,755 additions & 0 deletions src/aiida_sssp_workflow/statics/upf/O.nc.pbe.z_6.oncvpsp3.dojo.v0.4.1-std.upf

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions src/aiida_sssp_workflow/utils/pseudo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from pydantic import BaseModel
from importlib import resources
from enum import Enum

from pseudo_parser.upf_parser import parse
from aiida.plugins import DataFactory

UpfData = DataFactory("pseudo.upf")


class CurateType(Enum):
SSSP = "sssp"
NC = "nc"


class PseudoInfo(BaseModel):
Expand Down Expand Up @@ -43,3 +53,86 @@ def parse_std_filename(filename: str, extension: str = "upf") -> PseudoInfo:
type=type,
z_valence=int(num_of_valence),
)


def compute_total_nelectrons(configuration, pseudos):
"""Compute the number of electrons of oxide configurations with pseudos
This function is limited to only computer the total number of electrons of oxides.
"""
if len(pseudos) != 2:
raise ValueError(
f"There are {len(pseudos)} != 2 pseudos, we expect for binary oxides."
)

z_O = None
z_X = None
for e, p in pseudos.items():
if e == "O":
z_O = p.z_valence
else:
z_X = p.z_valence

if z_O is None or z_X is None:
raise ValueError(
"Either `O` or `X` pseudos not read properly to get number of valence electrons."
)

if configuration == "XO":
return z_X + z_O

elif configuration == "XO2":
return z_X + z_O * 2

elif configuration == "XO3":
return z_X + z_O * 3

elif configuration == "X2O":
return z_X * 2 + z_O

elif configuration == "X2O3":
return z_X * 4 + z_O * 6

elif configuration == "X2O5":
return z_X * 4 + z_O * 10
else:
raise ValueError(
f"Cannot compute the number electrons of configuration {configuration}."
)


def get_pseudo_O(curate_type: CurateType | str = CurateType.SSSP):
"""Return pseudo of oxygen for oxides"""
match curate_type:
case CurateType.SSSP:
import_path = resources.path(
"aiida_sssp_workflow.statics.upf", "O.paw.pbe.z_6.ld1.psl.v0.1.upf"
)
with import_path as psp_path, open(psp_path, "rb") as stream:
pseudo = UpfData(stream)
ecutwfc, ecutrho = 70.0, 560.0
case CurateType.NC:
import_path = resources.path(
"aiida_sssp_workflow.statics.upf",
"O.nc.pbe.z_6.oncvpsp3.dojo.v0.4.1-std.upf",
)
with import_path as psp_path, open(psp_path, "rb") as stream:
pseudo = UpfData(stream)
ecutwfc, ecutrho = 80.0, 320.0
case _:
raise ValueError(f"Unknown curate_type = {curate_type}")

return pseudo, ecutwfc, ecutrho


# XXX: should do the same as pseudo_O when using for LAN-Nitride band structure calculation.
# Depend on the target curate library type we use the corresponding N pseudos.
def get_pseudo_N():
"""Return pseudo of nitrogen for lanthanide nitrides"""
import_path = resources.path(
"aiida_sssp_workflow.statics.upf", "N.us.pbe.z_5.ld1.psl.v0.1.upf"
)
with import_path as psp_path, open(psp_path, "rb") as stream:
pseudo_N = UpfData(stream)

return pseudo_N, 55.0, 330.0
8 changes: 6 additions & 2 deletions src/aiida_sssp_workflow/utils/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
UNARIE_CONFIGURATIONS = ["BCC", "FCC", "SC", "DC"]
ACWF_CONFIGURATIONS = OXIDE_CONFIGURATIONS + UNARIE_CONFIGURATIONS

VALID_CONFIGURATIONS = (
ACWF_CONFIGURATIONS # FIXME: should also have GS for bands and LANN
)


@calcfunction
def get_default_configuration(element: orm.Str, property: orm.Str) -> orm.Str:
Expand Down Expand Up @@ -40,7 +44,7 @@ def _get_default_configuration(element: str, property: str) -> str:

@calcfunction
def get_standard_structure(
element: orm.Str, configuration=orm.Str
element: orm.Str, configuration: orm.Str
) -> orm.StructureData:
try:
ase_structure = _get_standard_structure(element.value, configuration.value)
Expand All @@ -59,7 +63,7 @@ def get_standard_structure(
return structure


def _get_standard_structure(element: str, configuration=str) -> Atoms:
def _get_standard_structure(element: str, configuration: str) -> Atoms:
"""
Create an ASE structure from property and configuration and element.
Expand Down
26 changes: 0 additions & 26 deletions src/aiida_sssp_workflow/workflows/common.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import importlib
from typing import Optional

from aiida import orm
from aiida.plugins import DataFactory

UpfData = DataFactory("pseudo.upf")


def get_extra_parameters_for_lanthanides(element, nbnd) -> dict:
Expand Down Expand Up @@ -35,28 +31,6 @@ def get_extra_parameters_for_lanthanides(element, nbnd) -> dict:
return extra_parameters


def get_pseudo_N():
"""Return pseudo of nitrogen for lanthanide nitrides"""
import_path = importlib.resources.path(
"aiida_sssp_workflow.statics.upf", "N.us.z_5.ld1.psl.v0.1.upf"
)
with import_path as psp_path, open(psp_path, "rb") as stream:
pseudo_N = UpfData(stream)

return pseudo_N


def get_pseudo_O():
"""Return pseudo of oxygen for oxides"""
import_path = importlib.resources.path(
"aiida_sssp_workflow.statics.upf", "O.paw.z_6.ld1.psl.v0.1.upf"
)
with import_path as psp_path, open(psp_path, "rb") as stream:
pseudo_O = UpfData(stream)

return pseudo_O


def clean_workdir(node: orm.CalcJobNode) -> Optional[int]:
"""clean remote workdir of nonmenon calcjob"""
# I have to do only clean nonmenon calcjob since I regard it as a bug that
Expand Down
4 changes: 2 additions & 2 deletions src/aiida_sssp_workflow/workflows/convergence/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def define(cls, spec):
)

spec.output(
"convergence_report",
"report",
valid_type=orm.Dict,
required=True,
help="The output report of convergence verification, it is a dict contains the full information of convergence test, the mapping of cutoffs to the UUID of the evaluation workchain etc.",
Expand Down Expand Up @@ -413,7 +413,7 @@ def inspect_convergence(self):
raise e
else:
self.out(
"convergence_report",
"report",
orm.Dict(dict=validated_report.model_dump()).store(),
)

Expand Down
2 changes: 1 addition & 1 deletion src/aiida_sssp_workflow/workflows/evaluate/_metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


class MetricWorkChain(SelfCleanWorkChain):
"""WorkChain calculate the bands for certain pseudopotential"""
"""WorkChain running EOS workflow and compute nu/delta values"""

@classmethod
def define(cls, spec):
Expand Down
42 changes: 18 additions & 24 deletions src/aiida_sssp_workflow/workflows/measure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@
class _BaseMeasureWorkChain(SelfCleanWorkChain):
"""Base Measure Workchain since bands measure and precision measure share same input ports"""

# ECUT for oxygen, remember to update this if the oxygen pseudo is changed
# Currently the oxygen pseudo is `O.paw.z_6.ld1.psl.v0.1.upf`
_O_ECUTWFC = 70.0
_O_ECUTRHO = 560.0

# ECUT for nitrogen, remember to update this if the nitrogen pseudo is changed
# Currently the nitrogen pseudo is `N.us.z_5.ld1.psl.v0.1.upf`
_N_ECUTWFC = 55.0
_N_ECUTRHO = 330.0

@classmethod
def define(cls, spec):
"""Define the process specification."""
Expand All @@ -33,31 +23,35 @@ def define(cls, spec):
help='The `pw.x` code use for the `PwCalculation`.')
spec.input('pseudo', valid_type=UpfData, required=True,
help='Pseudopotential to be verified')
spec.input('oxygen_pseudo', valid_type=UpfData, required=False)
spec.input('oxygen_ecutwfc', valid_type=orm.Float, required=False)
spec.input('oxygen_ecutrho', valid_type=orm.Float, required=False)
spec.input('oxygen_pseudo', valid_type=UpfData, required=True)
spec.input('oxygen_ecutwfc', valid_type=orm.Float, required=True)
spec.input('oxygen_ecutrho', valid_type=orm.Float, required=True)
spec.input('protocol', valid_type=orm.Str, required=True,
help='The protocol which define input calculation parameters.')
spec.input('wavefunction_cutoff', valid_type=orm.Float, required=True, help='The wavefunction cutoff.')
spec.input('configurations', valid_type=orm.List, required=False)
spec.input('wavefunction_cutoff', valid_type=orm.Float, required=True, help='The wavefunction cutoff.')
spec.input('charge_density_cutoff', valid_type=orm.Float, required=True, help='The charge density cutoff.')
spec.input('options', valid_type=orm.Dict, required=True,
help='Optional `options` to use for the `PwCalculations`.')
spec.input('parallelization', valid_type=orm.Dict, required=True,
help='Parallelization options for the `PwCalculations`.')
spec.input(
"parallelization",
valid_type=orm.Dict,
required=False,
help="The parallelization settings for the `PwCalculation`.",
)
spec.input(
"mpi_options",
valid_type=orm.Dict,
required=False,
help="The MPI options for the `PwCalculation`.",
)

def _get_pw_cutoff(
self, structure: orm.StructureData, ecutwfc: float, ecutrho: float
):
"""Get cutoff pair, if strcture contains oxygen or nitrogen, need
to use the max between pseudo cutoff and the O/N cutoff.
"""
if "oxygen_pseudo" in self.inputs:
o_ecutwfc = self.inputs.oxygen_ecutwfc.value
o_ecutrho = self.inputs.oxygen_ecutrho.value
else:
o_ecutwfc = self._O_ECUTWFC
o_ecutrho = self._O_ECUTRHO
o_ecutwfc = self.inputs.oxygen_ecutwfc.value
o_ecutrho = self.inputs.oxygen_ecutrho.value

elements = set(structure.get_symbols_set())
if "O" in elements:
Expand Down
Loading

0 comments on commit d5c3c88

Please sign in to comment.