Conversation
| Computational cost | ||
| ------------------ | ||
|
|
||
| Medium: benchmark involves only single-point calculations, but for 36,718 slab-bulk pairs. |
There was a problem hiding this comment.
it would be good to give a rough indication of time e.g. hours on gpu and minutes on gpu?
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
| from ase.io import read |
There was a problem hiding this comment.
| from ase.io import read | |
| from ase.io import read, write | |
| from tqdm import tqdm |
| / "cleavage_energy" | ||
| ) | ||
|
|
||
| results = {} |
There was a problem hiding this comment.
| results = {} | |
| write_dir = OUT_PATH / model_name | |
| write_dir.mkdir(parents=True, exist_ok=True) |
|
|
||
| results = {} | ||
|
|
||
| for mpid_dir in sorted(d for d in data_dir.iterdir() if d.is_dir()): |
There was a problem hiding this comment.
| for mpid_dir in sorted(d for d in data_dir.iterdir() if d.is_dir()): | |
| idx = 0 | |
| for mpid_dir in tqdm(sorted(d for d in data_dir.iterdir() if d.is_dir())): |
| unique_id = slab.info["unique_id"] | ||
| results[unique_id] = { | ||
| "slab_energy": slab_energy, | ||
| "bulk_energy": bulk_energy, | ||
| "area_slab": float(slab.info["area_slab"]), | ||
| "thickness_ratio": float(slab.info["thickness_ratio"]), | ||
| "ref_cleavage_energy": float(slab.info["ref_cleavage_energy"]), | ||
| "mpid": slab.info["mpid"], | ||
| "miller": slab.info["miller"], | ||
| "term": int(slab.info["term"]), | ||
| } | ||
|
|
||
| OUT_PATH.mkdir(parents=True, exist_ok=True) | ||
| output_file = OUT_PATH / f"{model_name}.json" | ||
| with open(output_file, "w", encoding="utf-8") as f: | ||
| json.dump(results, f) |
There was a problem hiding this comment.
| unique_id = slab.info["unique_id"] | |
| results[unique_id] = { | |
| "slab_energy": slab_energy, | |
| "bulk_energy": bulk_energy, | |
| "area_slab": float(slab.info["area_slab"]), | |
| "thickness_ratio": float(slab.info["thickness_ratio"]), | |
| "ref_cleavage_energy": float(slab.info["ref_cleavage_energy"]), | |
| "mpid": slab.info["mpid"], | |
| "miller": slab.info["miller"], | |
| "term": int(slab.info["term"]), | |
| } | |
| OUT_PATH.mkdir(parents=True, exist_ok=True) | |
| output_file = OUT_PATH / f"{model_name}.json" | |
| with open(output_file, "w", encoding="utf-8") as f: | |
| json.dump(results, f) | |
| slab.info.update( | |
| { | |
| "slab_energy": slab_energy, | |
| "bulk_energy": bulk_energy, | |
| "area_slab": float(slab.info["area_slab"]), | |
| "thickness_ratio": float(slab.info["thickness_ratio"]), | |
| "ref_cleavage_energy": float(slab.info["ref_cleavage_energy"]), | |
| "mpid": slab.info["mpid"], | |
| "miller": slab.info["miller"], | |
| "term": int(slab.info["term"]), | |
| } | |
| ) | |
| write(write_dir / f"{idx}.xyz", slab, format="extxyz") | |
| idx += 1 |
|
Hey @vue1999, thanks for the PR and its looking super good. A few things:
Let me know if you've got any ideas or are unsure about any of my suggestions, thanks! |
| RMSE: | ||
| good: 0.0 | ||
| bad: 10.0 | ||
| unit: meV/A^2 | ||
| tooltip: Root Mean Squared Error of cleavage energies | ||
| level_of_theory: PBE |
There was a problem hiding this comment.
| RMSE: | |
| good: 0.0 | |
| bad: 10.0 | |
| unit: meV/A^2 | |
| tooltip: Root Mean Squared Error of cleavage energies | |
| level_of_theory: PBE |
| from ml_peg.analysis.utils.decorators import build_table, plot_parity | ||
| from ml_peg.analysis.utils.utils import load_metrics_config, mae |
There was a problem hiding this comment.
| from ml_peg.analysis.utils.decorators import build_table, plot_parity | |
| from ml_peg.analysis.utils.utils import load_metrics_config, mae | |
| from ml_peg.analysis.utils.decorators import build_table, plot_density_scatter | |
| from ml_peg.analysis.utils.utils import ( | |
| load_metrics_config, | |
| mae, | |
| write_density_trajectories, | |
| ) |
|
|
||
| def _load_model_results(model_name: str) -> dict | None: | ||
| """ | ||
| Load the JSON energy results for a model, or None if absent. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| model_name | ||
| Name of the model whose results file to load. | ||
|
|
||
| Returns | ||
| ------- | ||
| dict | None | ||
| Parsed JSON results dictionary, or None if the file does not exist. | ||
| """ | ||
| result_file = CALC_PATH / f"{model_name}.json" | ||
| if not result_file.exists(): | ||
| return None | ||
| with open(result_file, encoding="utf-8") as f: | ||
| return json.load(f) | ||
|
|
||
|
|
||
| def system_names() -> list[str]: | ||
| """ | ||
| Get list of system identifiers from calc outputs. | ||
|
|
||
| Returns | ||
| ------- | ||
| list[str] | ||
| Sorted list of unique_id values from the first model with results. | ||
| """ | ||
| for model_name in MODELS: | ||
| data = _load_model_results(model_name) | ||
| if data is not None: | ||
| return sorted(data.keys()) | ||
| return [] | ||
|
|
There was a problem hiding this comment.
| def _load_model_results(model_name: str) -> dict | None: | |
| """ | |
| Load the JSON energy results for a model, or None if absent. | |
| Parameters | |
| ---------- | |
| model_name | |
| Name of the model whose results file to load. | |
| Returns | |
| ------- | |
| dict | None | |
| Parsed JSON results dictionary, or None if the file does not exist. | |
| """ | |
| result_file = CALC_PATH / f"{model_name}.json" | |
| if not result_file.exists(): | |
| return None | |
| with open(result_file, encoding="utf-8") as f: | |
| return json.load(f) | |
| def system_names() -> list[str]: | |
| """ | |
| Get list of system identifiers from calc outputs. | |
| Returns | |
| ------- | |
| list[str] | |
| Sorted list of unique_id values from the first model with results. | |
| """ | |
| for model_name in MODELS: | |
| data = _load_model_results(model_name) | |
| if data is not None: | |
| return sorted(data.keys()) | |
| return [] |
| @plot_parity( | ||
| filename=OUT_PATH / "figure_cleavage_energies.json", | ||
| title="Cleavage Energies", | ||
| x_label="Predicted cleavage energy / meV/\u00c5\u00b2", | ||
| y_label="Reference cleavage energy / meV/\u00c5\u00b2", | ||
| hoverdata={ | ||
| "System": system_names(), | ||
| }, | ||
| ) | ||
| def cleavage_energies() -> dict[str, list]: |
There was a problem hiding this comment.
| @plot_parity( | |
| filename=OUT_PATH / "figure_cleavage_energies.json", | |
| title="Cleavage Energies", | |
| x_label="Predicted cleavage energy / meV/\u00c5\u00b2", | |
| y_label="Reference cleavage energy / meV/\u00c5\u00b2", | |
| hoverdata={ | |
| "System": system_names(), | |
| }, | |
| ) | |
| def cleavage_energies() -> dict[str, list]: | |
| def cleavage_energies() -> dict[str, dict[str, list]]: |
| dict[str, list] | ||
| Dictionary of reference and predicted cleavage energies in meV/A^2. |
There was a problem hiding this comment.
| dict[str, list] | |
| Dictionary of reference and predicted cleavage energies in meV/A^2. | |
| dict[str, dict[str, list]] | |
| Dictionary of model names to ``{"ref": [...], "pred": [...]}`` in meV/A^2. |
| results = {"ref": []} | {mlip: [] for mlip in MODELS} | ||
| canonical_ids = None |
There was a problem hiding this comment.
| results = {"ref": []} | {mlip: [] for mlip in MODELS} | |
| canonical_ids = None | |
| results = {mlip: {"ref": [], "pred": []} for mlip in MODELS} | |
| ref_stored = False | |
| stored_ref = [] |
| data = _load_model_results(model_name) | ||
| if data is None: |
There was a problem hiding this comment.
| data = _load_model_results(model_name) | |
| if data is None: | |
| model_dir = CALC_PATH / model_name | |
| if not model_dir.exists(): |
|
|
||
| from ml_peg.app import APP_ROOT | ||
| from ml_peg.app.base_app import BaseApp | ||
| from ml_peg.app.utils.load import read_plot |
There was a problem hiding this comment.
| from ml_peg.app.utils.load import read_plot | |
| from ml_peg.app.utils.build_callbacks import plot_from_table_cell, struct_from_scatter | |
| from ml_peg.app.utils.load import collect_traj_assets, read_density_plot_for_model |
| """Cleavage energy benchmark app layout and callbacks.""" | ||
|
|
||
| def register_callbacks(self) -> None: | ||
| """No interactive callbacks needed.""" |
There was a problem hiding this comment.
| """No interactive callbacks needed.""" | |
| """Register callbacks to app.""" | |
| density_plots: dict[str, dict] = {} | |
| for model in MODELS: | |
| density_graph = read_density_plot_for_model( | |
| filename=DATA_PATH / "figure_cleavage_energies.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", | |
| cell_to_plot=density_plots, | |
| ) | |
| struct_trajs = collect_traj_assets( | |
| data_path=DATA_PATH, | |
| assets_prefix="assets/surfaces/cleavage_energy", | |
| models=MODELS, | |
| traj_dirname="density_traj", | |
| suffix=".extxyz", | |
| ) | |
| for model in struct_trajs: | |
| struct_from_scatter( | |
| scatter_id=f"{BENCHMARK_NAME}-{model}-density", | |
| struct_id=f"{BENCHMARK_NAME}-struct-placeholder", | |
| structs=struct_trajs[model], | |
| mode="traj", | |
| ) |
| scatter = read_plot( | ||
| DATA_PATH / "figure_cleavage_energies.json", | ||
| id=f"{BENCHMARK_NAME}-figure", | ||
| ) |
There was a problem hiding this comment.
| scatter = read_plot( | |
| DATA_PATH / "figure_cleavage_energies.json", | |
| id=f"{BENCHMARK_NAME}-figure", | |
| ) |
| docs_url=DOCS_URL, | ||
| table_path=DATA_PATH / "cleavage_energy_metrics_table.json", | ||
| extra_components=[ | ||
| Div(scatter, style={"marginTop": "20px"}), |
There was a problem hiding this comment.
| Div(scatter, style={"marginTop": "20px"}), | |
| Div(id=f"{BENCHMARK_NAME}-figure-placeholder"), | |
| Div(id=f"{BENCHMARK_NAME}-struct-placeholder"), |
| full_app = Dash( | ||
| __name__, | ||
| assets_folder=DATA_PATH.parent.parent, | ||
| suppress_callback_exceptions=True, |
There was a problem hiding this comment.
| suppress_callback_exceptions=True, |
Pre-review checklist for PR author
PR author must check the checkboxes below when creating the PR.
Summary
Adding a benchmark to evaluate the accuracy of predicting cleavage energies of crystalline surfaces.
Linked issue
Resolves #403
Progress
Testing
MACE omat, ORB v3
New decorators/callbacks
no