Skip to content

Commit

Permalink
Handle space/point-group correctly (#1055)
Browse files Browse the repository at this point in the history
This PR uses pymatgen symmetry analyzer classes to extract symmetry group information from the structure for use in the summary.
  • Loading branch information
edan-bainglass authored Jan 6, 2025
1 parent 1df8f54 commit 94f23c7
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 16 deletions.
64 changes: 48 additions & 16 deletions src/aiidalab_qe/app/result/components/summary/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def _generate_report_parameters(self):
return {}

inputs = qeapp_wc.inputs
initial_structure = inputs.structure
structure: orm.StructureData = inputs.structure
basic = ui_parameters["workchain"]
advanced = ui_parameters["advanced"]
ctime = qeapp_wc.ctime
Expand All @@ -176,29 +176,32 @@ def _generate_report_parameters(self):
"creation_time": f"{format_time(ctime)} ({relative_time(ctime)})",
"modification_time": f"{format_time(mtime)} ({relative_time(mtime)})",
},
}

report |= {
"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
),
},
"structure_pk": structure.pk,
"structure_uuid": structure.uuid,
"formula": structure.get_formula(),
"num_atoms": len(structure.sites),
}
}

report["initial_structure_properties"] |= {
**self._get_symmetry_group_info(structure),
"cell_lengths": "{:.3f} {:.3f} {:.3f}".format(*structure.cell_lengths),
"cell_angles": "{:.0f} {:.0f} {:.0f}".format(*structure.cell_angles),
}

report |= {
"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(initial_structure.pbc, "xyz"),
"periodicity": PERIODICITY_MAPPING.get(structure.pbc, "xyz"),
},
"advanced_settings": {},
}
Expand Down Expand Up @@ -282,6 +285,35 @@ def _generate_report_parameters(self):

return report

def _get_symmetry_group_info(self, structure: orm.StructureData) -> dict:
# HACK the use of the clone for non-molecular systems is due to a rigid
# condition in AiiDA < 2.6 that only considers 3D systems as pymatgen
# `Structure` objects (`Molecule` otherwise). Once AiiDAlab is updated with
# AiiDA 2.6 throughout, we can fall back to using `StructureData.get_pymatgen`
# (which this method mimics) to obtain the correct pymatgen object
# (`Molecule` for 0D systems | `Structure` otherwise)
if any(structure.pbc):
return self._get_pymatgen_structure(structure)
return self._get_pymatgen_molecule(structure)

@staticmethod
def _get_pymatgen_structure(structure: orm.StructureData) -> dict:
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

clone = structure.clone()
clone.pbc = (True, True, True)
analyzer = SpacegroupAnalyzer(structure=clone.get_pymatgen_structure())
symbol = analyzer.get_space_group_symbol()
number = analyzer.get_space_group_number()
return {"space_group": f"{symbol} ({number})"}

@staticmethod
def _get_pymatgen_molecule(structure: orm.StructureData) -> dict:
from pymatgen.symmetry.analyzer import PointGroupAnalyzer

analyzer = PointGroupAnalyzer(mol=structure.get_pymatgen_molecule())
return {"point_group": analyzer.get_pointgroup()}

@staticmethod
def _get_final_calcjob(node: orm.WorkChainNode) -> orm.CalcJobNode | None:
"""Get the final calculation job node called by a workchain node.
Expand Down
5 changes: 5 additions & 0 deletions src/aiidalab_qe/app/result/components/summary/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
"type": "text",
"description": "The space group of the structure"
},
"point_group": {
"title": "Point group",
"type": "text",
"description": "The point group of the molecule"
},
"cell_lengths": {
"title": "Cell lengths in Å",
"type": "text",
Expand Down
30 changes: 30 additions & 0 deletions tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from bs4 import BeautifulSoup

from aiidalab_qe.app.main import App
Expand Down Expand Up @@ -78,6 +79,35 @@ def test_summary_report_advanced_settings(data_regression, generate_qeapp_workch
assert moments["Si"] == 0.1


@pytest.mark.parametrize(
("pbc", "symmetry_key"),
[
[(False, False, False), "point_group"], # 0D
[(True, False, False), "space_group"], # 1D
[(True, True, False), "space_group"], # 2D
[(True, True, True), "space_group"], # 3D
],
)
def test_summary_report_symmetry_group(
generate_qeapp_workchain,
generate_structure_data,
pbc,
symmetry_key,
):
"""Test summary report includes correct symmetry group for all system dimension."""

system = generate_structure_data("silicon", pbc=pbc)
workchain = generate_qeapp_workchain(
structure=system,
run_bands=False,
relax_type="none",
)
model = WorkChainSummaryModel()
model.process_uuid = workchain.node.uuid
report_parameters = model._generate_report_parameters()
assert symmetry_key in report_parameters["initial_structure_properties"]


def test_summary_view(generate_qeapp_workchain):
"""Test the report html can be properly generated."""
workchain = generate_qeapp_workchain()
Expand Down

0 comments on commit 94f23c7

Please sign in to comment.