Skip to content

Commit

Permalink
Generalize
Browse files Browse the repository at this point in the history
  • Loading branch information
edan-bainglass committed Jan 4, 2025
1 parent 6a822bf commit d6aa79c
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 462 deletions.
221 changes: 112 additions & 109 deletions src/aiidalab_qe/app/result/components/summary/model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from __future__ import annotations

import json
from pathlib import Path

import traitlets as tl
from importlib_resources import files
from jinja2 import Environment
Expand Down Expand Up @@ -64,33 +67,25 @@ def generate_report_html(self):
"""Read from the builder parameters and generate a html for reporting
the inputs for the `QeAppWorkChain`.
"""

def _fmt_yes_no(truthy):
return "yes" if truthy else "no"

env = Environment()
env.filters.update(
{
"fmt_yes_no": _fmt_yes_no,
}
)
template = (
files(templates)
.joinpath(f"workflow_{DEFAULT['summary_format']}_summary.jinja")
.read_text()
)
style = files(styles).joinpath("style.css").read_text()
template = files(templates).joinpath("workflow_summary.jinja").read_text()
summary_format = DEFAULT["summary_format"]
style = files(styles).joinpath(f"summary/{summary_format}.css").read_text()
parameters = self._generate_report_parameters()
report = {key: value for key, value in parameters.items() if value is not None}

return env.from_string(template).render(style=style, **report)
schema = json.load(Path(__file__).parent.joinpath("schema.json").open())
return env.from_string(template).render(
style=style,
report=report,
schema=schema,
format=summary_format,
)

def generate_report_text(self, report_dict):
"""Generate a text for reporting the inputs for the `QeAppWorkChain`
:param report_dict: dictionary generated by the `generate_report_dict` function.
"""

report_string = (
"All calculations are performed within the density-functional "
"theory formalism as implemented in the Quantum ESPRESSO code. "
Expand Down Expand Up @@ -168,122 +163,130 @@ def _generate_report_parameters(self):
# drop support for old ui parameters
if "workchain" not in ui_parameters:
return {}

initial_structure = qeapp_wc.inputs.structure
basic = ui_parameters["workchain"]
advanced = ui_parameters["advanced"]
ctime = qeapp_wc.ctime
mtime = qeapp_wc.mtime

report = {
"pk": qeapp_wc.pk,
"uuid": str(qeapp_wc.uuid),
"label": qeapp_wc.label,
"description": qeapp_wc.description,
"creation_time": format_time(qeapp_wc.ctime),
"creation_time_relative": relative_time(qeapp_wc.ctime),
"modification_time": format_time(qeapp_wc.mtime),
"modification_time_relative": relative_time(qeapp_wc.mtime),
"structure_pk": initial_structure.pk,
"structure_uuid": initial_structure.uuid,
"formula": initial_structure.get_formula(),
"num_atoms": len(initial_structure.sites),
"space_group": "{} ({})".format(
*initial_structure.get_pymatgen().get_space_group_info()
),
"cell_lengths": "{:.3f} {:.3f} {:.3f}".format(
*initial_structure.cell_lengths
),
"cell_angles": "{:.0f} {:.0f} {:.0f}".format(
*initial_structure.cell_angles
),
"relaxed": None
if ui_parameters["workchain"]["relax_type"] == "none"
else ui_parameters["workchain"]["relax_type"],
"relax_method": ui_parameters["workchain"]["relax_type"],
"electronic_type": ui_parameters["workchain"]["electronic_type"],
"material_magnetic": ui_parameters["workchain"]["spin_type"],
"protocol": ui_parameters["workchain"]["protocol"],
"initial_magnetic_moments": ui_parameters["advanced"][
"initial_magnetic_moments"
],
"properties": ui_parameters["workchain"]["properties"],
"workflow_properties": {
"pk": qeapp_wc.pk,
"uuid": str(qeapp_wc.uuid),
"label": qeapp_wc.label,
"description": qeapp_wc.description,
"creation_time": f"{format_time(ctime)} ({relative_time(ctime)})",
"modification_time": f"{format_time(mtime)} ({relative_time(mtime)})",
},
"initial_structure_properties": {
"structure_pk": initial_structure.pk,
"structure_uuid": initial_structure.uuid,
"formula": initial_structure.get_formula(),
"num_atoms": len(initial_structure.sites),
"space_group": "{} ({})".format(
*initial_structure.get_pymatgen().get_space_group_info()
),
"cell_lengths": "{:.3f} {:.3f} {:.3f}".format(
*initial_structure.cell_lengths
),
"cell_angles": "{:.0f} {:.0f} {:.0f}".format(
*initial_structure.cell_angles
),
},
"basic_settings": {
"relaxed": "off"
if basic["relax_type"] == "none"
else basic["relax_type"],
"protocol": basic["protocol"],
"spin_type": "off" if basic["spin_type"] == "none" else "on",
"electronic_type": basic["electronic_type"],
"periodicity": PERIODICITY_MAPPING.get(
qeapp_wc.inputs.structure.pbc, "xyz"
),
},
"advanced_settings": {},
}
#
report.update(
{
"bands_computed": "bands" in ui_parameters["workchain"]["properties"],
"pdos_computed": "pdos" in ui_parameters["workchain"]["properties"],
}
)
# update pseudo family information to report
pseudo_family = ui_parameters["advanced"].get("pseudo_family")

pseudo_family = advanced.get("pseudo_family")
pseudo_family_info = pseudo_family.split("/")
pseudo_library = pseudo_family_info[0]
pseudo_version = pseudo_family_info[1]
functional = pseudo_family_info[2]
if pseudo_library == "SSSP":
pseudo_protocol = pseudo_family_info[3]
elif pseudo_library == "PseudoDojo":
pseudo_protocol = pseudo_family_info[4]
report.update(
{
"pseudo_family": pseudo_family,
"pseudo_library": pseudo_library,
"pseudo_version": pseudo_family_info[1],
"functional": functional,
"pseudo_protocol": pseudo_protocol,
"pseudo_link": PSEUDO_LINK_MAP[pseudo_library],
"functional_link": FUNCTIONAL_LINK_MAP[functional],
}
)
report["advanced_settings"] |= {
"functional": {
"url": FUNCTIONAL_LINK_MAP[functional],
"value": functional,
},
"pseudo_library": {
"url": PSEUDO_LINK_MAP[pseudo_library],
"value": f"{pseudo_library} {pseudo_protocol} v{pseudo_version}",
},
}

# Extract the pw calculation parameters from the workchain's inputs
# energy_cutoff is same for all pw calculations when pseudopotentials are fixed
# as well as the smearing settings (semaring and degauss) and scf kpoints distance
# as well as the smearing settings (smearing and degauss) and scf kpoints distance
# read from the first pw calculation of relax workflow.
# It is safe then to extract these parameters from the first pw calculation, since the
# builder is anyway set with subworkchain inputs even it is not run which controlled by
# the properties inputs.
pw_parameters = qeapp_wc.inputs.relax.base.pw.parameters.get_dict()
energy_cutoff_wfc = pw_parameters["SYSTEM"]["ecutwfc"]
energy_cutoff_rho = pw_parameters["SYSTEM"]["ecutrho"]
occupation = pw_parameters["SYSTEM"]["occupations"]
scf_kpoints_distance = (
qeapp_wc.inputs.relax.base.kpoints_distance.base.attributes.get("value")
)
report.update(
{
"energy_cutoff_wfc": energy_cutoff_wfc,
"energy_cutoff_rho": energy_cutoff_rho,
"occupation_type": occupation,
"scf_kpoints_distance": scf_kpoints_distance,
}
)
relax = qeapp_wc.inputs.relax.base
pw_parameters = relax.pw.parameters.get_dict()
system = pw_parameters["SYSTEM"]
occupation = system["occupations"]
report["advanced_settings"] |= {
"energy_cutoff_wfc": system["ecutwfc"],
"energy_cutoff_rho": system["ecutrho"],
"occupation_type": occupation,
}
if occupation == "smearing":
report["degauss"] = pw_parameters["SYSTEM"]["degauss"]
report["smearing"] = pw_parameters["SYSTEM"]["smearing"]
report["tot_charge"] = pw_parameters["SYSTEM"].get("tot_charge", 0.0)
report["vdw_corr"] = VDW_CORRECTION_VERSION.get(
pw_parameters["SYSTEM"].get("dftd3_version"),
pw_parameters["SYSTEM"].get("vdw_corr", "none"),
report["advanced_settings"] |= {
"smearing": system["smearing"],
"degauss": system["degauss"],
}
report["advanced_settings"]["scf_kpoints_distance"] = (
relax.kpoints_distance.base.attributes.get("value")
)
report["periodicity"] = PERIODICITY_MAPPING.get(
qeapp_wc.inputs.structure.pbc, "xyz"
if "bands" in qeapp_wc.inputs:
report["advanced_settings"]["bands_kpoints_distance"] = (
PwBandsWorkChain.get_protocol_inputs(
basic["protocol"]
)["bands_kpoints_distance"]
)
if "pdos" in qeapp_wc.inputs:
report["advanced_settings"]["nscf_kpoints_distance"] = (
qeapp_wc.inputs.pdos.nscf.kpoints_distance.base.attributes.get("value")
)

vdw_corr = VDW_CORRECTION_VERSION.get(
system.get("dftd3_version"),
system.get("vdw_corr", "none"),
)
report["advanced_settings"] |= {
"tot_charge": system.get("tot_charge", 0.0),
"vdw_corr": "off" if vdw_corr == "none" else vdw_corr,
}

# Spin-Orbit coupling
report["spin_orbit"] = pw_parameters["SYSTEM"].get("lspinorb", False)
if basic["spin_type"] == "collinear":
if tot_magnetization := system.get("tot_magnetization", False):
report["advanced_settings"]["tot_magnetization"] = tot_magnetization
else:
report["advanced_settings"]["initial_magnetic_moments"] = advanced[
"initial_magnetic_moments"
]

if hubbard_dict := ui_parameters["advanced"].pop("hubbard_parameters", None):
hubbard_parameters = hubbard_dict["hubbard_u"]
report["hubbard_u"] = hubbard_parameters
report["tot_magnetization"] = pw_parameters["SYSTEM"].get(
"tot_magnetization", False
)
report["advanced_settings"]["hubbard_u"] = hubbard_parameters

# hard code bands and pdos
if "bands" in qeapp_wc.inputs:
report["bands_kpoints_distance"] = PwBandsWorkChain.get_protocol_inputs(
report["protocol"]
)["bands_kpoints_distance"]
spin_orbit = system.get("lspinorb", False)
report["advanced_settings"]["spin_orbit"] = "on" if spin_orbit else "off"

if "pdos" in qeapp_wc.inputs:
report["nscf_kpoints_distance"] = (
qeapp_wc.inputs.pdos.nscf.kpoints_distance.base.attributes.get("value")
)
return report

@staticmethod
Expand Down
Loading

0 comments on commit d6aa79c

Please sign in to comment.