From 4a39f6c66257cbb226a5c82becd59f7952b10dc1 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Sun, 3 Dec 2023 10:06:19 +0100 Subject: [PATCH 1/2] Add CLI for analyze element result in a bar figure --- aiida_sssp_workflow/cli/inspect.py | 162 +++++++++++++++++++++++++++++ aiida_sssp_workflow/utils.py | 26 +++++ 2 files changed, 188 insertions(+) diff --git a/aiida_sssp_workflow/cli/inspect.py b/aiida_sssp_workflow/cli/inspect.py index 19ffd542..a212cf9d 100644 --- a/aiida_sssp_workflow/cli/inspect.py +++ b/aiida_sssp_workflow/cli/inspect.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """CLI to inspect the results of the workflow""" import json +import random from pathlib import Path import click @@ -14,6 +15,100 @@ from aiida_sssp_workflow.utils import HIGH_DUAL_ELEMENTS, get_protocol +def lighten_color(color, amount=0.5): + """ + Lightens the given color by multiplying (1-luminosity) by the given amount. + Input can be matplotlib color string, hex string, or RGB tuple. + REF from https://stackoverflow.com/questions/37765197/darken-or-lighten-a-color-in-matplotlib + + Examples: + >> lighten_color('g', 0.3) + >> lighten_color('#F034A3', 0.6) + >> lighten_color((.3,.55,.1), 0.5) + """ + import colorsys + + import matplotlib.colors as mc + + try: + c = mc.cnames[color] + except Exception: + c = color + c = colorsys.rgb_to_hls(*mc.to_rgb(c)) + return colorsys.hls_to_rgb(c[0], 1 - amount * (1 - c[1]), c[2]) + + +def cmap(pseudo_info: dict) -> str: + """Return RGB string of color for given pseudo info + Hardcoded at the momment. + """ + if pseudo_info["family"] == "sg15" and pseudo_info["version"] == "v0": + return "#000000" + + if pseudo_info["family"] == "gbrv": + return "#4682B4" + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "us" + and pseudo_info["version"] == "v1.0.0-high" + ): + return "#F50E02" + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "us" + and pseudo_info["version"] == "v1.0.0-low" + ): + return lighten_color("#F50E02") + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "paw" + and pseudo_info["version"] == "v1.0.0-high" + ): + return "#008B00" + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "paw" + and pseudo_info["version"] == "v1.0.0-low" + ): + return lighten_color("#008B00") + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "paw" + and "v0." in pseudo_info["version"] + ): + return "#FF00FF" + + if ( + pseudo_info["family"] == "psl" + and pseudo_info["type"] == "us" + and "v0." in pseudo_info["version"] + ): + return lighten_color("#FF00FF") + + if pseudo_info["family"] == "dojo" and pseudo_info["version"] == "v4-str": + return "#F9A501" + + if pseudo_info["family"] == "dojo" and pseudo_info["version"] == "v4-std": + return lighten_color("#F9A501") + + if pseudo_info["family"] == "jth" and pseudo_info["version"] == "v1.1-str": + return "#00C5ED" + + if pseudo_info["family"] == "jth" and pseudo_info["version"] == "v1.1-std": + return lighten_color("#00C5ED") + + # TODO: more mapping + # if a unknow type generate random color based on ascii sum + ascn = sum([ord(c) for c in pseudo_info["representive_label"]]) + random.seed(ascn) + return f"#{random.randint(0, 16777215):06x}" + + def birch_murnaghan(V, E0, V0, B0, B01): """ Return the energy for given volume (V - it can be a vector) according to @@ -130,6 +225,73 @@ def convergence_plot( ax.set_title(title, fontsize=8) +@cmd_root.command("analyze") +@click.argument("group") # The group to inspect, name or pk of the group +@click.option("--output", "-o", default="output", help="The output file name") +def analyze(group, output): + """Render the plot for the given pseudos and measure type.""" + from aiida_sssp_workflow.utils import ACWF_CONFIGURATIONS, parse_label + + # landscape mode + fig, ax = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=100) + + group_node = orm.load_group(group) + + # Get all nodes in the output group (EOS workflows) + group_node_query = ( + orm.QueryBuilder() + .append(orm.Group, filters={"id": group_node.id}, tag="groups") + .append(orm.Node, project="*", with_group="groups") + ) + + nodes_lst = group_node_query.all(flat=True) + + for i, node in enumerate(nodes_lst): + width = 0.6 / len(nodes_lst) + pseudo_info = parse_label(node.base.extras.all["label"].split(" ")[-1]) + + # TODO assert element should not change + element = node.outputs.pseudo_info.get_dict()["element"] + + y_nu = [] + for configuration in ACWF_CONFIGURATIONS: + res = node.outputs.measure.precision[configuration]["output_parameters"] + + nu = res["rel_errors_vec_length"] + y_nu.append(nu) + + x = np.arange(len(ACWF_CONFIGURATIONS)) + + ax.bar( + x + width * i, + y_nu, + width, + color=cmap(pseudo_info), + edgecolor="black", + linewidth=1, + label=pseudo_info["representive_label"], + ) + ax.set_title(f"X={element}") + + ax.axhline(y=0.1, linestyle="--", color="green", label="~0.1 excellent") + ax.axhline(y=0.33, linestyle="--", color="red", label="~0.33 good") + ax.legend(loc="upper left", prop={"size": 10}) + ax.set_ylabel("ν -facto") + ax.set_ylim([0, 0.6]) + ax.set_xticks(range(len(ACWF_CONFIGURATIONS))) + ax.set_xticklabels(ACWF_CONFIGURATIONS) + + # fig to pdf + fig.tight_layout() + fig.suptitle( + f"Comparison on {element}", + fontsize=10, + ) + fig.subplots_adjust(top=0.92) + fpath = Path.cwd() / f"{output}_{element}_fight.pdf" + fig.savefig(fpath.name, bbox_inches="tight") + + @cmd_root.command("inspect") @click.argument("node") # The node to inspect, uuid or pk @click.option("--output", "-o", default="output", help="The output file name") diff --git a/aiida_sssp_workflow/utils.py b/aiida_sssp_workflow/utils.py index aaa0813f..5c7fa4f9 100644 --- a/aiida_sssp_workflow/utils.py +++ b/aiida_sssp_workflow/utils.py @@ -96,6 +96,7 @@ def get_protocol(category, name=None): OXIDE_CONFIGURATIONS = ["XO", "XO2", "XO3", "X2O", "X2O3", "X2O5"] UNARIE_CONFIGURATIONS = ["BCC", "FCC", "SC", "Diamond"] +ACWF_CONFIGURATIONS = OXIDE_CONFIGURATIONS + UNARIE_CONFIGURATIONS def update_dict(d, u): @@ -230,6 +231,31 @@ def get_standard_structure( return structure +def parse_label(label): + """parse standard pseudo label to dict of pseudo info""" + element, type, z, tool, family, *version = label.split(".") + version = ".".join(version) + + if type == "nc": + full_type = "NC" + if type == "us": + full_type = "Ultrasoft" + if type == "paw": + full_type = "PAW" + + return { + "element": element, + "type": type, + "z": z, + "tool": tool, + "family": family, + "version": version, + "representive_label": f"{z}|{full_type}|{family}|{tool}|{version}", + "concise_label": f"{z}|{type}|{family}|{version}", + "full_label": f"{element}|{z}|{full_type}|{family}|{tool}|{version}", + } + + def parse_upf(upf_content: str) -> dict: """ From 17d2118f1258c215de68a08d4074c0138922351b Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Wed, 6 Dec 2023 11:08:23 +0100 Subject: [PATCH 2/2] Add eiger computers configure --- aiida_sssp_workflow/cli/inspect.py | 29 ++++++++++++------- ...aml => computer-setup-eiger-mr32-mem.yaml} | 2 +- setup-yaml/computer-setup-eiger-mr33-mem.yaml | 22 ++++++++++++++ ...ger-mem.yaml => ph7.0-eiger-mr32-mem.yaml} | 4 +-- setup-yaml/ph7.0-eiger-mr33-mem.yaml | 12 ++++++++ ...ger-mem.yaml => pw7.0-eiger-mr32-mem.yaml} | 4 +-- setup-yaml/pw7.0-eiger-mr33-mem.yaml | 12 ++++++++ 7 files changed, 69 insertions(+), 16 deletions(-) rename setup-yaml/{computer-setup-eiger-mem.yaml => computer-setup-eiger-mr32-mem.yaml} (92%) create mode 100644 setup-yaml/computer-setup-eiger-mr33-mem.yaml rename setup-yaml/{ph7.0-eiger-mem.yaml => ph7.0-eiger-mr32-mem.yaml} (63%) create mode 100644 setup-yaml/ph7.0-eiger-mr33-mem.yaml rename setup-yaml/{pw7.0-eiger-mem.yaml => pw7.0-eiger-mr32-mem.yaml} (63%) create mode 100644 setup-yaml/pw7.0-eiger-mr33-mem.yaml diff --git a/aiida_sssp_workflow/cli/inspect.py b/aiida_sssp_workflow/cli/inspect.py index a212cf9d..09079a61 100644 --- a/aiida_sssp_workflow/cli/inspect.py +++ b/aiida_sssp_workflow/cli/inspect.py @@ -245,9 +245,9 @@ def analyze(group, output): ) nodes_lst = group_node_query.all(flat=True) + width = 0.6 / len(nodes_lst) for i, node in enumerate(nodes_lst): - width = 0.6 / len(nodes_lst) pseudo_info = parse_label(node.base.extras.all["label"].split(" ")[-1]) # TODO assert element should not change @@ -273,20 +273,27 @@ def analyze(group, output): ) ax.set_title(f"X={element}") - ax.axhline(y=0.1, linestyle="--", color="green", label="~0.1 excellent") - ax.axhline(y=0.33, linestyle="--", color="red", label="~0.33 good") - ax.legend(loc="upper left", prop={"size": 10}) - ax.set_ylabel("ν -facto") - ax.set_ylim([0, 0.6]) - ax.set_xticks(range(len(ACWF_CONFIGURATIONS))) + d = 0.01 # offset for the text + + ax.axhline(y=0.1, linestyle="--", color="green") + ax.text(0 - d, 0.1 + d, "0.1 excellent agreement", color="black") + ax.axhline(y=0.33, linestyle="--", color="red") + ax.text(0 - d, 0.33 + d, "0.33 good agreement", color="black") + ax.legend(loc="upper right", prop={"size": 10}) + ax.set_ylabel("ν -factor") + ax.set_ylim([0, 0.5]) + + xticks_shift = len(nodes_lst) * width / 2 + xticks = [i + xticks_shift for i in range(len(ACWF_CONFIGURATIONS))] + ax.set_xticks(xticks) ax.set_xticklabels(ACWF_CONFIGURATIONS) # fig to pdf fig.tight_layout() - fig.suptitle( - f"Comparison on {element}", - fontsize=10, - ) + # fig.suptitle( + # f"Comparison on {element}", + # fontsize=10, + # ) fig.subplots_adjust(top=0.92) fpath = Path.cwd() / f"{output}_{element}_fight.pdf" fig.savefig(fpath.name, bbox_inches="tight") diff --git a/setup-yaml/computer-setup-eiger-mem.yaml b/setup-yaml/computer-setup-eiger-mr32-mem.yaml similarity index 92% rename from setup-yaml/computer-setup-eiger-mem.yaml rename to setup-yaml/computer-setup-eiger-mr32-mem.yaml index b17cb494..6d77c6da 100644 --- a/setup-yaml/computer-setup-eiger-mem.yaml +++ b/setup-yaml/computer-setup-eiger-mr32-mem.yaml @@ -4,7 +4,7 @@ hostname: "eiger.cscs.ch" description: Eiger is the production partition on Alps, the HPE Cray EX Supercomputer large mem nodes. transport: "core.ssh" scheduler: "core.slurm" -work_dir: "/scratch/e1000/{username}/aiida/" +work_dir: "/capstor/scratch/cscs/{username}/aiida/" shebang: "#!/bin/bash" mpirun_command: "srun -n {tot_num_mpiprocs}" mpiprocs_per_machine: 128 diff --git a/setup-yaml/computer-setup-eiger-mr33-mem.yaml b/setup-yaml/computer-setup-eiger-mr33-mem.yaml new file mode 100644 index 00000000..91dd3558 --- /dev/null +++ b/setup-yaml/computer-setup-eiger-mr33-mem.yaml @@ -0,0 +1,22 @@ +--- +label: "eiger-mc-mr33-mem" +hostname: "eiger.cscs.ch" +description: Eiger is the production partition on Alps, the HPE Cray EX Supercomputer large mem nodes. +transport: "core.ssh" +scheduler: "core.slurm" +work_dir: "/capstor/scratch/cscs/{username}/aiida/" +shebang: "#!/bin/bash" +mpirun_command: "srun -n {tot_num_mpiprocs}" +mpiprocs_per_machine: 128 +prepend_text: | + ### computer prepend_text start ### + #SBATCH --partition=normal + #SBATCH --account=mr33 + #SBATCH --constraint=mc + #SBATCH --hint=nomultithread + #SBATCH --mem=497G + + export OMP_PROC_BIND=close + export OMP_PLACES=cores + ### computer prepend_text end ### +append_text: " " diff --git a/setup-yaml/ph7.0-eiger-mem.yaml b/setup-yaml/ph7.0-eiger-mr32-mem.yaml similarity index 63% rename from setup-yaml/ph7.0-eiger-mem.yaml rename to setup-yaml/ph7.0-eiger-mr32-mem.yaml index 68f71833..a4c10699 100644 --- a/setup-yaml/ph7.0-eiger-mem.yaml +++ b/setup-yaml/ph7.0-eiger-mr32-mem.yaml @@ -3,9 +3,9 @@ label: "ph-7.0" description: Quantum ESPRESSO ph.x v7.0 Compiled with CpeIntel v21.12 default_calc_job_plugin: "quantumespresso.ph" computer: "eiger-mc-mr32-mem" -filepath_executable: "/apps/eiger/UES/jenkins/1.4.0/software/QuantumESPRESSO/7.0-cpeIntel-21.12/bin/ph.x" +filepath_executable: "/capstor/apps/cscs/eiger/easybuild/software/QuantumESPRESSO/7.0-cpeIntel-22.05/bin/ph.x" prepend_text: | - module load cpeIntel/21.12 + module load cray/22.05 cpeIntel/22.05 module load QuantumESPRESSO/7.0 export OMP_NUM_THREADS=1 diff --git a/setup-yaml/ph7.0-eiger-mr33-mem.yaml b/setup-yaml/ph7.0-eiger-mr33-mem.yaml new file mode 100644 index 00000000..b72719c9 --- /dev/null +++ b/setup-yaml/ph7.0-eiger-mr33-mem.yaml @@ -0,0 +1,12 @@ +--- +label: "ph-7.0" +description: Quantum ESPRESSO ph.x v7.0 Compiled with CpeIntel v21.12 +default_calc_job_plugin: "quantumespresso.ph" +computer: "eiger-mc-mr33-mem" +filepath_executable: "/capstor/apps/cscs/eiger/easybuild/software/QuantumESPRESSO/7.0-cpeIntel-22.05/bin/ph.x" +prepend_text: | + module load cray/22.05 cpeIntel/22.05 + module load QuantumESPRESSO/7.0 + + export OMP_NUM_THREADS=1 +append_text: " " diff --git a/setup-yaml/pw7.0-eiger-mem.yaml b/setup-yaml/pw7.0-eiger-mr32-mem.yaml similarity index 63% rename from setup-yaml/pw7.0-eiger-mem.yaml rename to setup-yaml/pw7.0-eiger-mr32-mem.yaml index bf9acf01..98e6ed9e 100644 --- a/setup-yaml/pw7.0-eiger-mem.yaml +++ b/setup-yaml/pw7.0-eiger-mr32-mem.yaml @@ -3,9 +3,9 @@ label: "pw-7.0" description: Quantum ESPRESSO pw.x v7.0 Compiled with CpeIntel v21.12 default_calc_job_plugin: "quantumespresso.pw" computer: "eiger-mc-mr32-mem" -filepath_executable: "/apps/eiger/UES/jenkins/1.4.0/software/QuantumESPRESSO/7.0-cpeIntel-21.12/bin/pw.x" +filepath_executable: "/capstor/apps/cscs/eiger/easybuild/software/QuantumESPRESSO/7.0-cpeIntel-22.05/bin/pw.x" prepend_text: | - module load cpeIntel/21.12 + module load cray/22.05 cpeIntel/22.05 module load QuantumESPRESSO/7.0 export OMP_NUM_THREADS=1 diff --git a/setup-yaml/pw7.0-eiger-mr33-mem.yaml b/setup-yaml/pw7.0-eiger-mr33-mem.yaml new file mode 100644 index 00000000..a83e9eb1 --- /dev/null +++ b/setup-yaml/pw7.0-eiger-mr33-mem.yaml @@ -0,0 +1,12 @@ +--- +label: "pw-7.0" +description: Quantum ESPRESSO pw.x v7.0 Compiled with CpeIntel v21.12 +default_calc_job_plugin: "quantumespresso.pw" +computer: "eiger-mc-mr33-mem" +filepath_executable: "/capstor/apps/cscs/eiger/easybuild/software/QuantumESPRESSO/7.0-cpeIntel-22.05/bin/pw.x" +prepend_text: | + module load cray/22.05 cpeIntel/22.05 + module load QuantumESPRESSO/7.0 + + export OMP_NUM_THREADS=1 +append_text: " "