From 49757cb65f5fc913a99c2a20e44e3573854ab4c9 Mon Sep 17 00:00:00 2001 From: Domantas Kuryla Date: Sat, 20 Dec 2025 19:59:06 +0000 Subject: [PATCH 1/8] Add NCIA HB300SPXx10 calculation script --- .../ncia_hb300spxx10/.dvc/.gitignore | 3 + .../ncia_hb300spxx10/.dvc/config | 0 .../ncia_hb300spxx10/.dvcignore | 3 + .../ncia_hb300spxx10/calc_ncia_hb300spxx10.py | 159 ++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore create mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config create mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore create mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore new file mode 100644 index 000000000..528f30c71 --- /dev/null +++ b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore @@ -0,0 +1,3 @@ +/config.local +/tmp +/cache diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config new file mode 100644 index 000000000..e69de29bb diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore new file mode 100644 index 000000000..519730552 --- /dev/null +++ b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore @@ -0,0 +1,3 @@ +# Add patterns of files dvc should ignore, which could improve +# the performance. Learn more at +# https://dvc.org/doc/user-guide/dvcignore diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py new file mode 100644 index 000000000..8b152c5a6 --- /dev/null +++ b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py @@ -0,0 +1,159 @@ +""" +Compute the NCIA HB300SPXx100. + +Journal of Chemical Theory and Computation 2020 16 (10), 6305-6316. +""" + +from __future__ import annotations + +from pathlib import Path + +from ase import units +from ase.io import read, write +import mlipx +from mlipx.abc import NodeWithCalculator +from tqdm import tqdm +import zntrack + +from ml_peg.calcs.utils.utils import chdir, download_s3_data +from ml_peg.models.get_models import load_models +from ml_peg.models.models import current_models + +MODELS = load_models(current_models) + +KCAL_TO_EV = units.kcal / units.mol +EV_TO_KCAL = 1 / KCAL_TO_EV + +OUT_PATH = Path(__file__).parent / "outputs" + + +class NCIAHB300SPXx10Benchmark(zntrack.Node): + """Benchmarking NCIA_HB300SPXx10 ionic hydrogen bonds benchmark dataset.""" + + model: NodeWithCalculator = zntrack.deps() + model_name: str = zntrack.params() + + def get_ref_energies(self, data_path): + """ + Get reference energies. + + Parameters + ---------- + data_path + Path to data. + """ + self.ref_energies = {} + with open(data_path / "NCIA_HB300SPXx10_benchmark.txt") as lines: + for i, line in enumerate(lines): + if i < 3: + continue + items = line.strip().split() + label = items[0] + ref_energy = float(items[1]) * KCAL_TO_EV + self.ref_energies[label] = ref_energy + + @staticmethod + def get_monomers(atoms): + """ + Get ASE atoms objects of the monomers. + + Parameters + ---------- + atoms + ASE atoms object of the structure. + + Returns + ------- + tuple[ASE.Atoms, ASE.Atoms] + Tuple containing the two monomers. + """ + if isinstance(atoms.info["selection_a"], str): + a_ids = [int(id) for id in atoms.info["selection_a"].split("-")] + a_ids[0] -= 1 + else: + a_ids = [int(atoms.info["selection_a"]) - 1, int(atoms.info["selection_a"])] + + if isinstance(atoms.info["selection_b"], str): + b_ids = [int(id) for id in atoms.info["selection_b"].split("-")] + b_ids[0] -= 1 + else: + b_ids = [int(atoms.info["selection_b"]) - 1, int(atoms.info["selection_b"])] + + atoms_a = atoms[a_ids[0] : a_ids[1]] + atoms_b = atoms[b_ids[0] : b_ids[1]] + assert len(atoms_a) + len(atoms_b) == len(atoms) + + atoms_a.info["charge"] = int(atoms.info["charge_a"]) + atoms_a.info["spin"] = 1 + + atoms_b.info["charge"] = int(atoms.info["charge_b"]) + atoms_b.info["spin"] = 1 + return (atoms_a, atoms_b) + + def run(self): + """Run new benchmark.""" + # Read in data and attach calculator + data_path = ( + download_s3_data( + filename="NCIA_HB300SPXx10.zip", + key="inputs/non_covalent_interactions/NCIA_HB300SPXx10/NCIA_HB300SPXx10.zip", + ) + / "NCIA_HB300SPXx10" + ) + self.get_ref_energies(data_path) + + calc = self.model.get_calculator() + + for label, ref_energy in tqdm(self.ref_energies.items()): + xyz_fname = f"{label}.xyz" + atoms = read(data_path / "geometries" / xyz_fname) + atoms_a, atoms_b = self.get_monomers(atoms) + atoms.info["spin"] = 1 + atoms.info["charge"] = int(atoms_a.info["charge"] + atoms_b.info["charge"]) + atoms.calc = calc + atoms_a.calc = calc + atoms_b.calc = calc + + atoms.info["model_int_energy"] = ( + atoms.get_potential_energy() + - atoms_a.get_potential_energy() + - atoms_b.get_potential_energy() + ) + atoms.info["ref_int_energy"] = ref_energy + atoms.calc = None + + write_dir = OUT_PATH / self.model_name + write_dir.mkdir(parents=True, exist_ok=True) + write(write_dir / f"{label}.xyz", atoms) + + +def build_project(repro: bool = False) -> None: + """ + Build mlipx project. + + Parameters + ---------- + repro + Whether to call dvc repro -f after building. + """ + project = mlipx.Project() + benchmark_node_dict = {} + + for model_name, model in MODELS.items(): + with project.group(model_name): + benchmark = NCIAHB300SPXx10Benchmark( + model=model, + model_name=model_name, + ) + benchmark_node_dict[model_name] = benchmark + + if repro: + with chdir(Path(__file__).parent): + project.repro(build=True, force=True) + else: + project.build() + + +def test_ncia_hb300spxx10(): + """Run NCIA_HB300SPXx10 barriers benchmark via pytest.""" + build_project(repro=True) From d50efa93a22da1e5f67cf971c9ff07201f7920bd Mon Sep 17 00:00:00 2001 From: Domantas Kuryla Date: Sun, 21 Dec 2025 21:00:01 +0000 Subject: [PATCH 2/8] Add NCIA_HB300SPXx10 analysis files --- .../analyse_ncia_hb300spxx10.py | 177 ++++++++++++++++++ .../ncia_hb300spxx10/metrics.yml | 13 ++ 2 files changed, 190 insertions(+) create mode 100644 ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py create mode 100644 ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py new file mode 100644 index 000000000..173d3a046 --- /dev/null +++ b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py @@ -0,0 +1,177 @@ +"""Analyse NCIA_HB300SPXx10 benchmark.""" + +from __future__ import annotations + +from pathlib import Path + +from ase import units +from ase.io import read, write +import pytest + +from ml_peg.analysis.utils.decorators import build_table, plot_parity +from ml_peg.analysis.utils.utils import ( + build_d3_name_map, + load_metrics_config, + mae, + rmse, +) +from ml_peg.app import APP_ROOT +from ml_peg.calcs import CALCS_ROOT +from ml_peg.models.get_models import load_models +from ml_peg.models.models import current_models + +MODELS = load_models(current_models) +D3_MODEL_NAMES = build_d3_name_map(MODELS) + +KCAL_TO_EV = units.kcal / units.mol +EV_TO_KCAL = 1 / KCAL_TO_EV +CALC_PATH = CALCS_ROOT / "non_covalent_interactions" / "ncia_hb300spxx10" / "outputs" +OUT_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "ncia_hb300spxx10" + +METRICS_CONFIG_PATH = Path(__file__).with_name("metrics.yml") +DEFAULT_THRESHOLDS, DEFAULT_TOOLTIPS, DEFAULT_WEIGHTS = load_metrics_config( + METRICS_CONFIG_PATH +) + + +def labels() -> list: + """ + Get list of system names. + + Returns + ------- + list + List of all system names. + """ + for model in MODELS: + labels_list = [path.stem for path in (CALC_PATH / model).glob("*.xyz")] + break + return labels_list + + +@pytest.fixture +@plot_parity( + filename=OUT_PATH / "figure_ncia_hb300spxx10.json", + title="Interaction energies", + x_label="Predicted energy / eV", + y_label="Reference energy / eV", + hoverdata={ + "Labels": labels(), + }, +) +def interaction_energies() -> dict[str, list]: + """ + Get interaction energies for all systems. + + Returns + ------- + dict[str, list] + Dictionary of all reference and predicted interaction energies. + """ + results = {"ref": []} | {mlip: [] for mlip in MODELS} + + ref_stored = False + + for model_name in MODELS: + for label in labels(): + atoms = read(CALC_PATH / model_name / f"{label}.xyz") + if not ref_stored: + results["ref"].append(atoms.info["ref_int_energy"]) + + results[model_name].append(atoms.info["model_int_energy"]) + + # Write structures for app + structs_dir = OUT_PATH / model_name + structs_dir.mkdir(parents=True, exist_ok=True) + write(structs_dir / f"{label}.xyz", atoms) + + ref_stored = True + return results + + +@pytest.fixture +def get_mae(interaction_energies) -> dict[str, float]: + """ + Get mean absolute error for energies. + + Parameters + ---------- + interaction_energies + Dictionary of reference and predicted energies. + + Returns + ------- + dict[str, float] + Dictionary of predicted energy errors for all models. + """ + results = {} + for model_name in MODELS: + results[model_name] = mae( + interaction_energies["ref"], interaction_energies[model_name] + ) + return results + + +@pytest.fixture +def get_rmse(interaction_energies) -> dict[str, float]: + """ + Get root mean square error for energies. + + Parameters + ---------- + interaction_energies + Dictionary of reference and predicted energies. + + Returns + ------- + dict[str, float] + Dictionary of predicted energy errors for all models. + """ + results = {} + for model_name in MODELS: + results[model_name] = rmse( + interaction_energies["ref"], interaction_energies[model_name] + ) + return results + + +@pytest.fixture +@build_table( + filename=OUT_PATH / "ncia_hb300spxx10_metrics_table.json", + metric_tooltips=DEFAULT_TOOLTIPS, + thresholds=DEFAULT_THRESHOLDS, + mlip_name_map=D3_MODEL_NAMES, +) +def metrics(get_mae: dict[str, float], get_rmse: dict[str, float]) -> dict[str, dict]: + """ + Get all metrics. + + Parameters + ---------- + get_mae + Mean absolute errors for all models. + + get_rmse + Root Mean Square Error for all models. + + Returns + ------- + dict[str, dict] + Metric names and values for all models. + """ + return { + "MAE": get_mae, + "RMSE": get_rmse, + } + + +def test_ncia_hb300spxx10(metrics: dict[str, dict]) -> None: + """ + Run ncia_hb300spxx10 test. + + Parameters + ---------- + metrics + All new benchmark metric names and dictionary of values for each model. + """ + return diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml new file mode 100644 index 000000000..cbfb7e7a2 --- /dev/null +++ b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml @@ -0,0 +1,13 @@ +metrics: + MAE: + good: 0.0 + bad: 0.5 + unit: eV + tooltip: Mean Absolute Error for all systems + level_of_theory: CCSD(T) + RMSE: + good: 0.0 + bad: 0.5 + unit: eV + tooltip: Root Mean Square Error for all systems + level_of_theory: CCSD(T) From 651f7af5b9cab31890d04609cb9954d730db0637 Mon Sep 17 00:00:00 2001 From: Domantas Kuryla Date: Mon, 29 Dec 2025 20:14:55 +0000 Subject: [PATCH 3/8] Add D3 calc --- .../ncia_hb300spxx10/calc_ncia_hb300spxx10.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py index 8b152c5a6..c2dfa8eec 100644 --- a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py +++ b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py @@ -103,6 +103,8 @@ def run(self): self.get_ref_energies(data_path) calc = self.model.get_calculator() + # Add D3 calculator for this test + calc = self.model.add_d3_calculator(calc) for label, ref_energy in tqdm(self.ref_energies.items()): xyz_fname = f"{label}.xyz" From f2bb95c298efda14dd073e138eea8df53b9ce740 Mon Sep 17 00:00:00 2001 From: joehart2001 Date: Tue, 6 Jan 2026 16:01:01 +0000 Subject: [PATCH 4/8] app and only MAE --- .../analyse_ncia_hb300spxx10.py | 38 +------ .../ncia_hb300spxx10/metrics.yml | 6 -- .../ncia_hb300spxx10/app_ncia_hb300spxx10.py | 99 +++++++++++++++++++ 3 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py index 173d3a046..9edf1f793 100644 --- a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py +++ b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py @@ -9,12 +9,7 @@ import pytest from ml_peg.analysis.utils.decorators import build_table, plot_parity -from ml_peg.analysis.utils.utils import ( - build_d3_name_map, - load_metrics_config, - mae, - rmse, -) +from ml_peg.analysis.utils.utils import build_d3_name_map, load_metrics_config, mae from ml_peg.app import APP_ROOT from ml_peg.calcs import CALCS_ROOT from ml_peg.models.get_models import load_models @@ -44,7 +39,7 @@ def labels() -> list: List of all system names. """ for model in MODELS: - labels_list = [path.stem for path in (CALC_PATH / model).glob("*.xyz")] + labels_list = sorted([path.stem for path in (CALC_PATH / model).glob("*.xyz")]) break return labels_list @@ -112,29 +107,6 @@ def get_mae(interaction_energies) -> dict[str, float]: return results -@pytest.fixture -def get_rmse(interaction_energies) -> dict[str, float]: - """ - Get root mean square error for energies. - - Parameters - ---------- - interaction_energies - Dictionary of reference and predicted energies. - - Returns - ------- - dict[str, float] - Dictionary of predicted energy errors for all models. - """ - results = {} - for model_name in MODELS: - results[model_name] = rmse( - interaction_energies["ref"], interaction_energies[model_name] - ) - return results - - @pytest.fixture @build_table( filename=OUT_PATH / "ncia_hb300spxx10_metrics_table.json", @@ -142,7 +114,7 @@ def get_rmse(interaction_energies) -> dict[str, float]: thresholds=DEFAULT_THRESHOLDS, mlip_name_map=D3_MODEL_NAMES, ) -def metrics(get_mae: dict[str, float], get_rmse: dict[str, float]) -> dict[str, dict]: +def metrics(get_mae: dict[str, float]) -> dict[str, dict]: """ Get all metrics. @@ -151,9 +123,6 @@ def metrics(get_mae: dict[str, float], get_rmse: dict[str, float]) -> dict[str, get_mae Mean absolute errors for all models. - get_rmse - Root Mean Square Error for all models. - Returns ------- dict[str, dict] @@ -161,7 +130,6 @@ def metrics(get_mae: dict[str, float], get_rmse: dict[str, float]) -> dict[str, """ return { "MAE": get_mae, - "RMSE": get_rmse, } diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml index cbfb7e7a2..8118e8238 100644 --- a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml +++ b/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml @@ -5,9 +5,3 @@ metrics: unit: eV tooltip: Mean Absolute Error for all systems level_of_theory: CCSD(T) - RMSE: - good: 0.0 - bad: 0.5 - unit: eV - tooltip: Root Mean Square Error for all systems - level_of_theory: CCSD(T) diff --git a/ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py b/ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py new file mode 100644 index 000000000..c047db96d --- /dev/null +++ b/ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py @@ -0,0 +1,99 @@ +"""Run NCIA_HB300SPXx10 app.""" + +from __future__ import annotations + +from dash import Dash +from dash.html import Div + +from ml_peg.app import APP_ROOT +from ml_peg.app.base_app import BaseApp +from ml_peg.app.utils.build_callbacks import ( + plot_from_table_column, + struct_from_scatter, +) +from ml_peg.app.utils.load import read_plot +from ml_peg.models.get_models import get_model_names +from ml_peg.models.models import current_models + +MODELS = get_model_names(current_models) +BENCHMARK_NAME = "NCIA_HB300SPXx10" +DOCS_URL = ( + "https://ddmms.github.io/ml-peg/user_guide/benchmarks/" + "non_covalent_interactions.html#ncia-hb300spxx10" +) +DATA_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "ncia_hb300spxx10" + + +class NCIANHB300SPXx10App(BaseApp): + """NCIA_HB300SPXx10 benchmark app layout and callbacks.""" + + def register_callbacks(self) -> None: + """Register callbacks to app.""" + scatter = read_plot( + DATA_PATH / "figure_ncia_hb300spxx10.json", + id=f"{BENCHMARK_NAME}-figure", + ) + + model_dir = DATA_PATH / MODELS[0] + if model_dir.exists(): + labels = sorted([f.stem for f in model_dir.glob("*.xyz")]) + structs = [ + ( + "assets/non_covalent_interactions/ncia_hb300spxx10/" + f"{MODELS[0]}/{label}.xyz" + ) + for label in labels + ] + else: + structs = [] + + plot_from_table_column( + table_id=self.table_id, + plot_id=f"{BENCHMARK_NAME}-figure-placeholder", + column_to_plot={"MAE": scatter}, + ) + + struct_from_scatter( + scatter_id=f"{BENCHMARK_NAME}-figure", + struct_id=f"{BENCHMARK_NAME}-struct-placeholder", + structs=structs, + mode="struct", + ) + + +def get_app() -> NCIANHB300SPXx10App: + """ + Get NCIA_HB300SPXx10 benchmark app layout and callback registration. + + Returns + ------- + NCIANHB300SPXx10App + Benchmark layout and callback registration. + """ + return NCIANHB300SPXx10App( + name=BENCHMARK_NAME, + description=( + "Performance in predicting ionic hydrogen bond interaction energies " + "for the NCIA HB300SPXx10 dataset (ionic dimers from HB300). " + "Reference data from CCSD(T) calculations." + ), + docs_url=DOCS_URL, + table_path=DATA_PATH / "ncia_hb300spxx10_metrics_table.json", + extra_components=[ + Div(id=f"{BENCHMARK_NAME}-figure-placeholder"), + Div(id=f"{BENCHMARK_NAME}-struct-placeholder"), + ], + ) + + +if __name__ == "__main__": + # Create Dash app + full_app = Dash(__name__, assets_folder=DATA_PATH.parent.parent) + + # Construct layout and register callbacks + benchmark_app = get_app() + full_app.layout = benchmark_app.layout + benchmark_app.register_callbacks() + + # Run app + full_app.run(port=8057, debug=True) From 39e3ee72b93db51d37e87dbd680ff6ff6ad8bba1 Mon Sep 17 00:00:00 2001 From: ElliottKasoar <45317199+ElliottKasoar@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:41:25 +0000 Subject: [PATCH 5/8] Remove mlipx --- .../ncia_hb300spxx10/.dvc/.gitignore | 3 - .../ncia_hb300spxx10/.dvc/config | 0 .../ncia_hb300spxx10/.dvcignore | 3 - .../ncia_hb300spxx10/calc_ncia_hb300spxx10.py | 240 ++++++++---------- 4 files changed, 110 insertions(+), 136 deletions(-) delete mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore delete mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config delete mode 100644 ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore deleted file mode 100644 index 528f30c71..000000000 --- a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/config.local -/tmp -/cache diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvc/config deleted file mode 100644 index e69de29bb..000000000 diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore deleted file mode 100644 index 519730552..000000000 --- a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/.dvcignore +++ /dev/null @@ -1,3 +0,0 @@ -# Add patterns of files dvc should ignore, which could improve -# the performance. Learn more at -# https://dvc.org/doc/user-guide/dvcignore diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py index c2dfa8eec..13ae44b02 100644 --- a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py +++ b/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py @@ -7,155 +7,135 @@ from __future__ import annotations from pathlib import Path +from typing import Any -from ase import units +from ase import Atoms, units from ase.io import read, write -import mlipx -from mlipx.abc import NodeWithCalculator +import pytest from tqdm import tqdm -import zntrack -from ml_peg.calcs.utils.utils import chdir, download_s3_data +from ml_peg.calcs.utils.utils import download_s3_data from ml_peg.models.get_models import load_models from ml_peg.models.models import current_models MODELS = load_models(current_models) KCAL_TO_EV = units.kcal / units.mol -EV_TO_KCAL = 1 / KCAL_TO_EV OUT_PATH = Path(__file__).parent / "outputs" -class NCIAHB300SPXx10Benchmark(zntrack.Node): - """Benchmarking NCIA_HB300SPXx10 ionic hydrogen bonds benchmark dataset.""" - - model: NodeWithCalculator = zntrack.deps() - model_name: str = zntrack.params() - - def get_ref_energies(self, data_path): - """ - Get reference energies. - - Parameters - ---------- - data_path - Path to data. - """ - self.ref_energies = {} - with open(data_path / "NCIA_HB300SPXx10_benchmark.txt") as lines: - for i, line in enumerate(lines): - if i < 3: - continue - items = line.strip().split() - label = items[0] - ref_energy = float(items[1]) * KCAL_TO_EV - self.ref_energies[label] = ref_energy - - @staticmethod - def get_monomers(atoms): - """ - Get ASE atoms objects of the monomers. - - Parameters - ---------- - atoms - ASE atoms object of the structure. - - Returns - ------- - tuple[ASE.Atoms, ASE.Atoms] - Tuple containing the two monomers. - """ - if isinstance(atoms.info["selection_a"], str): - a_ids = [int(id) for id in atoms.info["selection_a"].split("-")] - a_ids[0] -= 1 - else: - a_ids = [int(atoms.info["selection_a"]) - 1, int(atoms.info["selection_a"])] - - if isinstance(atoms.info["selection_b"], str): - b_ids = [int(id) for id in atoms.info["selection_b"].split("-")] - b_ids[0] -= 1 - else: - b_ids = [int(atoms.info["selection_b"]) - 1, int(atoms.info["selection_b"])] - - atoms_a = atoms[a_ids[0] : a_ids[1]] - atoms_b = atoms[b_ids[0] : b_ids[1]] - assert len(atoms_a) + len(atoms_b) == len(atoms) - - atoms_a.info["charge"] = int(atoms.info["charge_a"]) - atoms_a.info["spin"] = 1 - - atoms_b.info["charge"] = int(atoms.info["charge_b"]) - atoms_b.info["spin"] = 1 - return (atoms_a, atoms_b) - - def run(self): - """Run new benchmark.""" - # Read in data and attach calculator - data_path = ( - download_s3_data( - filename="NCIA_HB300SPXx10.zip", - key="inputs/non_covalent_interactions/NCIA_HB300SPXx10/NCIA_HB300SPXx10.zip", - ) - / "NCIA_HB300SPXx10" - ) - self.get_ref_energies(data_path) - - calc = self.model.get_calculator() - # Add D3 calculator for this test - calc = self.model.add_d3_calculator(calc) - - for label, ref_energy in tqdm(self.ref_energies.items()): - xyz_fname = f"{label}.xyz" - atoms = read(data_path / "geometries" / xyz_fname) - atoms_a, atoms_b = self.get_monomers(atoms) - atoms.info["spin"] = 1 - atoms.info["charge"] = int(atoms_a.info["charge"] + atoms_b.info["charge"]) - atoms.calc = calc - atoms_a.calc = calc - atoms_b.calc = calc - - atoms.info["model_int_energy"] = ( - atoms.get_potential_energy() - - atoms_a.get_potential_energy() - - atoms_b.get_potential_energy() - ) - atoms.info["ref_int_energy"] = ref_energy - atoms.calc = None - - write_dir = OUT_PATH / self.model_name - write_dir.mkdir(parents=True, exist_ok=True) - write(write_dir / f"{label}.xyz", atoms) - - -def build_project(repro: bool = False) -> None: +def get_ref_energies(data_path: Path) -> dict[str, float]: """ - Build mlipx project. + Get reference energies. Parameters ---------- - repro - Whether to call dvc repro -f after building. + data_path + Path to data. + + Returns + ------- + dict[str, float] + Loaded reference energies. + """ + ref_energies = {} + + with open(data_path / "NCIA_HB300SPXx10_benchmark.txt") as lines: + for i, line in enumerate(lines): + if i < 3: + continue + items = line.strip().split() + label = items[0] + ref_energy = float(items[1]) * KCAL_TO_EV + ref_energies[label] = ref_energy + + return ref_energies + + +def get_monomers(atoms: Atoms) -> tuple[Atoms, Atoms]: """ - project = mlipx.Project() - benchmark_node_dict = {} - - for model_name, model in MODELS.items(): - with project.group(model_name): - benchmark = NCIAHB300SPXx10Benchmark( - model=model, - model_name=model_name, - ) - benchmark_node_dict[model_name] = benchmark - - if repro: - with chdir(Path(__file__).parent): - project.repro(build=True, force=True) + Get ASE atoms objects of the monomers. + + Parameters + ---------- + atoms + ASE atoms object of the structure. + + Returns + ------- + tuple[Atoms, Atoms] + Tuple containing the two monomers. + """ + if isinstance(atoms.info["selection_a"], str): + a_ids = [int(id) for id in atoms.info["selection_a"].split("-")] + a_ids[0] -= 1 else: - project.build() + a_ids = [int(atoms.info["selection_a"]) - 1, int(atoms.info["selection_a"])] + if isinstance(atoms.info["selection_b"], str): + b_ids = [int(id) for id in atoms.info["selection_b"].split("-")] + b_ids[0] -= 1 + else: + b_ids = [int(atoms.info["selection_b"]) - 1, int(atoms.info["selection_b"])] + + atoms_a = atoms[a_ids[0] : a_ids[1]] + atoms_b = atoms[b_ids[0] : b_ids[1]] + assert len(atoms_a) + len(atoms_b) == len(atoms) + + atoms_a.info["charge"] = int(atoms.info["charge_a"]) + atoms_a.info["spin"] = 1 + + atoms_b.info["charge"] = int(atoms.info["charge_b"]) + atoms_b.info["spin"] = 1 + return (atoms_a, atoms_b) + + +@pytest.mark.parametrize("mlip", MODELS.items()) +def test_ncia_hb300spxx10(mlip: tuple[str, Any]) -> None: + """ + Run NCIA HB300SPXx10 barriers benchmark. + + Parameters + ---------- + mlip + Name of model use and model to get calculator. + """ + model_name, model = mlip + calc = model.get_calculator() + + # Read in data and attach calculator + data_path = ( + download_s3_data( + filename="NCIA_HB300SPXx10.zip", + key="inputs/non_covalent_interactions/NCIA_HB300SPXx10/NCIA_HB300SPXx10.zip", + ) + / "NCIA_HB300SPXx10" + ) + ref_energies = get_ref_energies(data_path) + + calc = model.get_calculator() + # Add D3 calculator for this test + calc = model.add_d3_calculator(calc) + + for label, ref_energy in tqdm(ref_energies.items()): + xyz_fname = f"{label}.xyz" + atoms = read(data_path / "geometries" / xyz_fname) + atoms_a, atoms_b = get_monomers(atoms) + atoms.info["spin"] = 1 + atoms.info["charge"] = int(atoms_a.info["charge"] + atoms_b.info["charge"]) + atoms.calc = calc + atoms_a.calc = calc + atoms_b.calc = calc + + atoms.info["model_int_energy"] = ( + atoms.get_potential_energy() + - atoms_a.get_potential_energy() + - atoms_b.get_potential_energy() + ) + atoms.info["ref_int_energy"] = ref_energy + atoms.calc = None -def test_ncia_hb300spxx10(): - """Run NCIA_HB300SPXx10 barriers benchmark via pytest.""" - build_project(repro=True) + write_dir = OUT_PATH / model_name + write_dir.mkdir(parents=True, exist_ok=True) + write(write_dir / f"{label}.xyz", atoms) From 6d7eef6655f2ec0df7276b4dd60fcafcde4bbefa Mon Sep 17 00:00:00 2001 From: ElliottKasoar <45317199+ElliottKasoar@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:45:26 +0000 Subject: [PATCH 6/8] Rename benchmark --- .../analyse_NCIA_HB300SPXx10.py} | 8 ++++---- .../metrics.yml | 0 .../app_NCIA_HB300SPXx10.py} | 12 ++++++------ .../calc_NCIA_HB300SPXx10.py} | 0 4 files changed, 10 insertions(+), 10 deletions(-) rename ml_peg/analysis/non_covalent_interactions/{ncia_hb300spxx10/analyse_ncia_hb300spxx10.py => NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py} (93%) rename ml_peg/analysis/non_covalent_interactions/{ncia_hb300spxx10 => NCIA_HB300SPXx10}/metrics.yml (100%) rename ml_peg/app/non_covalent_interactions/{ncia_hb300spxx10/app_ncia_hb300spxx10.py => NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py} (88%) rename ml_peg/calcs/non_covalent_interactions/{ncia_hb300spxx10/calc_ncia_hb300spxx10.py => NCIA_HB300SPXx10/calc_NCIA_HB300SPXx10.py} (100%) diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py similarity index 93% rename from ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py rename to ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py index 9edf1f793..75ee6a8b9 100644 --- a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/analyse_ncia_hb300spxx10.py +++ b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py @@ -1,4 +1,4 @@ -"""Analyse NCIA_HB300SPXx10 benchmark.""" +"""Analyse NCIA HB300SPXx10 benchmark.""" from __future__ import annotations @@ -20,8 +20,8 @@ KCAL_TO_EV = units.kcal / units.mol EV_TO_KCAL = 1 / KCAL_TO_EV -CALC_PATH = CALCS_ROOT / "non_covalent_interactions" / "ncia_hb300spxx10" / "outputs" -OUT_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "ncia_hb300spxx10" +CALC_PATH = CALCS_ROOT / "non_covalent_interactions" / "NCIA_HB300SPXx10" / "outputs" +OUT_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "NCIA_HB300SPXx10" METRICS_CONFIG_PATH = Path(__file__).with_name("metrics.yml") DEFAULT_THRESHOLDS, DEFAULT_TOOLTIPS, DEFAULT_WEIGHTS = load_metrics_config( @@ -135,7 +135,7 @@ def metrics(get_mae: dict[str, float]) -> dict[str, dict]: def test_ncia_hb300spxx10(metrics: dict[str, dict]) -> None: """ - Run ncia_hb300spxx10 test. + Run NCIA HB300SPXx10 test. Parameters ---------- diff --git a/ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml similarity index 100% rename from ml_peg/analysis/non_covalent_interactions/ncia_hb300spxx10/metrics.yml rename to ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml diff --git a/ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py b/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py similarity index 88% rename from ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py rename to ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py index c047db96d..6d3d69c2d 100644 --- a/ml_peg/app/non_covalent_interactions/ncia_hb300spxx10/app_ncia_hb300spxx10.py +++ b/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py @@ -1,4 +1,4 @@ -"""Run NCIA_HB300SPXx10 app.""" +"""Run NCIA HB300SPXx10 app.""" from __future__ import annotations @@ -16,16 +16,16 @@ from ml_peg.models.models import current_models MODELS = get_model_names(current_models) -BENCHMARK_NAME = "NCIA_HB300SPXx10" +BENCHMARK_NAME = "NCIA HB300SPXx10" DOCS_URL = ( "https://ddmms.github.io/ml-peg/user_guide/benchmarks/" "non_covalent_interactions.html#ncia-hb300spxx10" ) -DATA_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "ncia_hb300spxx10" +DATA_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "NCIA_HB300SPXx10" class NCIANHB300SPXx10App(BaseApp): - """NCIA_HB300SPXx10 benchmark app layout and callbacks.""" + """NCIA HB300SPXx10 benchmark app layout and callbacks.""" def register_callbacks(self) -> None: """Register callbacks to app.""" @@ -39,7 +39,7 @@ def register_callbacks(self) -> None: labels = sorted([f.stem for f in model_dir.glob("*.xyz")]) structs = [ ( - "assets/non_covalent_interactions/ncia_hb300spxx10/" + "assets/non_covalent_interactions/NCIA_HB300SPXx10/" f"{MODELS[0]}/{label}.xyz" ) for label in labels @@ -63,7 +63,7 @@ def register_callbacks(self) -> None: def get_app() -> NCIANHB300SPXx10App: """ - Get NCIA_HB300SPXx10 benchmark app layout and callback registration. + Get NCIA HB300SPXx10 benchmark app layout and callback registration. Returns ------- diff --git a/ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py b/ml_peg/calcs/non_covalent_interactions/NCIA_HB300SPXx10/calc_NCIA_HB300SPXx10.py similarity index 100% rename from ml_peg/calcs/non_covalent_interactions/ncia_hb300spxx10/calc_ncia_hb300spxx10.py rename to ml_peg/calcs/non_covalent_interactions/NCIA_HB300SPXx10/calc_NCIA_HB300SPXx10.py From aa72180d46006ec9e33da574b5e8dd1408c24a96 Mon Sep 17 00:00:00 2001 From: ElliottKasoar <45317199+ElliottKasoar@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:48:58 +0000 Subject: [PATCH 7/8] Tidy and use kcal/mol --- .../NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py | 11 +++++------ .../NCIA_HB300SPXx10/metrics.yml | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py index 75ee6a8b9..b05758f23 100644 --- a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py +++ b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py @@ -18,8 +18,7 @@ MODELS = load_models(current_models) D3_MODEL_NAMES = build_d3_name_map(MODELS) -KCAL_TO_EV = units.kcal / units.mol -EV_TO_KCAL = 1 / KCAL_TO_EV +EV_TO_KCAL = units.mol / units.kcal CALC_PATH = CALCS_ROOT / "non_covalent_interactions" / "NCIA_HB300SPXx10" / "outputs" OUT_PATH = APP_ROOT / "data" / "non_covalent_interactions" / "NCIA_HB300SPXx10" @@ -48,8 +47,8 @@ def labels() -> list: @plot_parity( filename=OUT_PATH / "figure_ncia_hb300spxx10.json", title="Interaction energies", - x_label="Predicted energy / eV", - y_label="Reference energy / eV", + x_label="Predicted energy / kcal/mol", + y_label="Reference energy / kcal/mol", hoverdata={ "Labels": labels(), }, @@ -71,9 +70,9 @@ def interaction_energies() -> dict[str, list]: for label in labels(): atoms = read(CALC_PATH / model_name / f"{label}.xyz") if not ref_stored: - results["ref"].append(atoms.info["ref_int_energy"]) + results["ref"].append(atoms.info["ref_int_energy"] * EV_TO_KCAL) - results[model_name].append(atoms.info["model_int_energy"]) + results[model_name].append(atoms.info["model_int_energy"] * EV_TO_KCAL) # Write structures for app structs_dir = OUT_PATH / model_name diff --git a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml index 8118e8238..45388b831 100644 --- a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml +++ b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/metrics.yml @@ -1,7 +1,7 @@ metrics: MAE: good: 0.0 - bad: 0.5 - unit: eV + bad: 10 + unit: kcal/mol tooltip: Mean Absolute Error for all systems level_of_theory: CCSD(T) From 1134fbe24389d05c0ddcade2591b6bb1bbcdffb0 Mon Sep 17 00:00:00 2001 From: joehart2001 Date: Wed, 4 Feb 2026 14:53:13 +0000 Subject: [PATCH 8/8] add density plot --- .../analyse_NCIA_HB300SPXx10.py | 55 +++++++++++++++---- .../NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py | 47 +++++----------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py index b05758f23..fc62b9669 100644 --- a/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py +++ b/ml_peg/analysis/non_covalent_interactions/NCIA_HB300SPXx10/analyse_NCIA_HB300SPXx10.py @@ -8,7 +8,10 @@ from ase.io import read, write import pytest -from ml_peg.analysis.utils.decorators import build_table, plot_parity +from ml_peg.analysis.utils.decorators import ( + build_table, + plot_density_scatter, +) from ml_peg.analysis.utils.utils import build_d3_name_map, load_metrics_config, mae from ml_peg.app import APP_ROOT from ml_peg.calcs import CALCS_ROOT @@ -44,15 +47,6 @@ def labels() -> list: @pytest.fixture -@plot_parity( - filename=OUT_PATH / "figure_ncia_hb300spxx10.json", - title="Interaction energies", - x_label="Predicted energy / kcal/mol", - y_label="Reference energy / kcal/mol", - hoverdata={ - "Labels": labels(), - }, -) def interaction_energies() -> dict[str, list]: """ Get interaction energies for all systems. @@ -83,6 +77,40 @@ def interaction_energies() -> dict[str, list]: return results +@pytest.fixture +@plot_density_scatter( + filename=OUT_PATH / "figure_ncia_hb300spxx10_density.json", + title="Interaction energy density plot", + x_label="Reference energy / kcal/mol", + y_label="Predicted energy / kcal/mol", + annotation_metadata={"system_count": "Systems"}, +) +def interaction_density(interaction_energies: dict[str, list]) -> dict[str, dict]: + """ + Build density scatter inputs for interaction energies. + + Parameters + ---------- + interaction_energies + Reference and predicted interaction energies per model. + + Returns + ------- + dict[str, dict] + Mapping of model names to density-plot payloads. + """ + ref_vals = interaction_energies["ref"] + density_inputs: dict[str, dict] = {} + for model_name in MODELS: + preds = interaction_energies.get(model_name, []) + density_inputs[model_name] = { + "ref": ref_vals, + "pred": preds, + "meta": {"system_count": len([val for val in preds if val is not None])}, + } + return density_inputs + + @pytest.fixture def get_mae(interaction_energies) -> dict[str, float]: """ @@ -132,7 +160,10 @@ def metrics(get_mae: dict[str, float]) -> dict[str, dict]: } -def test_ncia_hb300spxx10(metrics: dict[str, dict]) -> None: +def test_ncia_hb300spxx10( + metrics: dict[str, dict], + interaction_density: dict[str, dict], +) -> None: """ Run NCIA HB300SPXx10 test. @@ -140,5 +171,7 @@ def test_ncia_hb300spxx10(metrics: dict[str, dict]) -> None: ---------- metrics All new benchmark metric names and dictionary of values for each model. + interaction_density + Density-scatter inputs for all models (drives saved plots). """ return diff --git a/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py b/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py index 6d3d69c2d..2b3288dec 100644 --- a/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py +++ b/ml_peg/app/non_covalent_interactions/NCIA_HB300SPXx10/app_NCIA_HB300SPXx10.py @@ -7,11 +7,8 @@ from ml_peg.app import APP_ROOT from ml_peg.app.base_app import BaseApp -from ml_peg.app.utils.build_callbacks import ( - plot_from_table_column, - struct_from_scatter, -) -from ml_peg.app.utils.load import read_plot +from ml_peg.app.utils.build_callbacks import plot_from_table_cell +from ml_peg.app.utils.load import read_density_plot_for_model from ml_peg.models.get_models import get_model_names from ml_peg.models.models import current_models @@ -29,35 +26,20 @@ class NCIANHB300SPXx10App(BaseApp): def register_callbacks(self) -> None: """Register callbacks to app.""" - scatter = read_plot( - DATA_PATH / "figure_ncia_hb300spxx10.json", - id=f"{BENCHMARK_NAME}-figure", - ) - - model_dir = DATA_PATH / MODELS[0] - if model_dir.exists(): - labels = sorted([f.stem for f in model_dir.glob("*.xyz")]) - structs = [ - ( - "assets/non_covalent_interactions/NCIA_HB300SPXx10/" - f"{MODELS[0]}/{label}.xyz" - ) - for label in labels - ] - else: - structs = [] - - plot_from_table_column( + density_plots: dict[str, dict] = {} + for model in MODELS: + density_graph = read_density_plot_for_model( + filename=DATA_PATH / "figure_ncia_hb300spxx10_density.json", + model=model, + id=f"{BENCHMARK_NAME}-{model}-density", + ) + if density_graph is not None: + density_plots[model] = {"MAE": density_graph} + + plot_from_table_cell( table_id=self.table_id, plot_id=f"{BENCHMARK_NAME}-figure-placeholder", - column_to_plot={"MAE": scatter}, - ) - - struct_from_scatter( - scatter_id=f"{BENCHMARK_NAME}-figure", - struct_id=f"{BENCHMARK_NAME}-struct-placeholder", - structs=structs, - mode="struct", + cell_to_plot=density_plots, ) @@ -81,7 +63,6 @@ def get_app() -> NCIANHB300SPXx10App: table_path=DATA_PATH / "ncia_hb300spxx10_metrics_table.json", extra_components=[ Div(id=f"{BENCHMARK_NAME}-figure-placeholder"), - Div(id=f"{BENCHMARK_NAME}-struct-placeholder"), ], )