diff --git a/.gitignore b/.gitignore
index 10eb59c0..cc5cbc82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -782,3 +782,5 @@ examples/ex1-ase/
/*.psp8
/test-1/
/test-2/
+*.pt
+/README.html
diff --git a/README.md b/README.md
index bcbdda33..cf8e4767 100644
--- a/README.md
+++ b/README.md
@@ -4,11 +4,27 @@
[![Unit tests](https://github.com/SPARC-X/SPARC-X-API/actions/workflows/installation_test.yml/badge.svg)](https://github.com/SPARC-X/SPARC-X-API/actions/workflows/installation_test.yml)
[![JSON-API](https://raw.githubusercontent.com/SPARC-X/SPARC-X-API/badges/badges/api_version.svg)](https://raw.githubusercontent.com/SPARC-X/SPARC-X-API/badges/badges/api_version.svg)
+# Table of Contents
+- [Installation](#installation)
+- [Setting Up Environment](#setting-up-the-environment)
+- [Basic Usage](#basic-usage-of-the-python-api)
+- [Advanced Usage: Socket Interface](#advanced-usage-sparc-x-api-as-a-socket-interface)
+- [Troubleshooting](#troubleshooting)
+- [Advanced Topics](#advanced-topics)
+- [Support and Contribution](#how-to-contribute)
+
`SPARC-X-API` is an [ASE](https://wiki.fysik.dtu.dk/ase/)-compatible Python API for the density functional theory (DFT) code [SPARC](https://github.com/SPARC-X/SPARC). It offers:
1. ASE-compatible I/O format for SPARC files
-2. A JSON API interfacing with SPARC's C-code for parameter validation and conversion
-3. A comprehensive calculator interface for SPARC.
+2. A JSON Schema interfacing with SPARC's C-code for parameter validation and conversion
+3. A comprehensive calculator interface for SPARC with socket-communication support.
+
+
+[Fig. 1](#fig-1-schematic-drawing-for-the-architecture-of-the-sparc-x-api-package) provides an overlook of the components of `SPARC-X-API` and its relation with the SPARC C-code.
+
+#### Fig. 1 Schematic drawing for the architecture of the `SPARC-X-API` package
+![scheme-sparc-api-outlook](doc/img/scheme_api_architecture.png)
+
## Installation:
@@ -53,7 +69,7 @@ To utilize the API for drive SPARC calculations, please
following the [SPARC manual](https://github.com/SPARC-X/SPARC) for
compilation and installation of the SPARC DFT code itself.
-## Post-installation check
+### Post-installation check
We recommend the users to run a simple test after installation and setup:
@@ -206,9 +222,9 @@ To learn more about the JSON schema design, please refer to [Advanced
Topics](doc/advanced_topics.md).
-### 3. Calculator interface
+### 3. Calculator interface (File-IO mode)
-`SPARC-X-API` offers a calculator interface that aligns with many
+`SPARC-X-API` offers a calculator interface based on file I/O that aligns with many
other ASE calculators. If you've worked with ASE modules like `Vasp`,
`QuantumEspresso`, or `GPAW`, you'll find this package intuitive,
as shown in the following examples:
@@ -277,7 +293,7 @@ opt.run(fmax=0.02)
### 4. Command-line tools
-`SPARC-X-API` provides a simple command wrapper `sparc-ase` to add
+A simple command wrapper `sparc-ase` is provided to add
support of SPARC file formats to the `ase` cli tools. Simple
replace `ase [subcommand] [args]` with `sparc-ase [subcommand] [args]`
to access your SPARC bundle files as you would use for other file
@@ -286,9 +302,10 @@ for the visualization of atomistic structures. Depending on the
bundle's contents, this could display individual atoms or multiple
images.
-Below is a screenshot showing the usage of `sparc-ase gui` to visualize a
+[Fig. 2](#fig-2-a-screenshot-of-the-sparc-ase-program) is a screenshot showing the usage of `sparc-ase gui` to visualize a
short [MD trajectory](tests/outputs/NH3_sort_lbfgs_opt.sparc).
+#### Fig 2. A screenshot of the `sparc-ase` program
### 5. Parameters and units used in `SPARC-X-API`
@@ -362,7 +379,7 @@ calc = SPARC(xc="PBE", kpts=(9, 9, 9), h=0.25, directory="sparc-calc.sparc", con
*Disclaimer: The socket communication feature in SPARC and SPARC-X-API are experimental and subject to changes until the release of v2.0 SPARC-X-API.*
### Overview
-Experienced users can now harness the power of SPARC and SPARC-X-API's
+Experienced users can harness the power of SPARC and SPARC-X-API's
socket communication layer to build efficient and flexible
computational workflows. By integrating a socket communication
interface directly into SPARC, users can significantly reduce the
@@ -370,7 +387,14 @@ overhead typically associated with file I/O during calculation
restarts. This feature is particularly beneficial for tasks involving
repetitive operations like structural optimization and saddle point
searches, where traditional file-based communication can become a
-bottleneck.
+bottleneck. The underlying software architecture is shown in [Fig. 3](#fig-3-sparc-electronic-calculations-with-socket-communication-across-hybrid-computing-platforms):
+
+#### Fig. 3. SPARC electronic calculations with socket communication across hybrid computing platforms
+
+![scheme-sparc-socket](doc/img/scheme_socket_hetero.png)
+
+
+
**Requirements**: the SPARC binary must be manually compiled from the source
code with [socket
@@ -389,15 +413,18 @@ adheres to the [i-PI protocol](https://github.com/i-pi/i-pi)
standard. Specifically, we implement the original i-PI protocol within
the SPARC C-source code, while the python SPARC-X-API uses a
backward-compatible protocol based on i-PI. The dual-mode design is aimed for both low-level and
-high-level interfacing of the DFT codes, providing the following features:
+high-level interfacing of the DFT codes, providing the following features as shown in [Fig. 4](#fig-4-overview-of-the-sparc-protocol-as-an-extension-to-the-standard-i-pi-protocol):
+
+#### Fig. 4. Overview of the SPARC protocol as an extension to the standard i-PI protocol.
+![scheme-sparc-protocol](doc/img/scheme_sparc_protocol.png)
+
+Based on the scenarios, the socket communication layer can be accessed via the following approaches as shown in [Fig. 5](#fig-5-different-ways-of-using-sparcs-socket-mode):
-1. **Unified Interface**: A consistent interface for both client and server codes, simplifying user interaction across different modes.
-2. **Versatile Operation Modes:** Supports various modes (Local-only, Client, Server) to cater to diverse computational needs.
-3. **Seamless Calculation Restart:** Automates the restarting process on the client side, enhancing user convenience.
+#### Fig. 5. Different ways of using SPARC's socket mode.
+![scheme-sparc-modes](doc/img/scheme-SPARC-socket-modes.png)
-Based on the user scenarios, the socket communication layer can be accessed via the following approaches:
-1. **SPARC binary only**
+1. **SPARC binary only** ([Fig. 5](#fig-5-different-ways-of-using-sparcs-socket-mode) **a**)
SPARC binary with socket support can be readily coupled with any i-PI compatible socker server, such as
`ase.calculators.socketio.SocketIOCalculator`, for example
@@ -419,7 +446,7 @@ Based on the user scenarios, the socket communication layer can be accessed via
to be run on a single computer system.
-2. **Local-only Mode**
+2. **Local-only Mode** ([Fig. 5](#fig-5-different-ways-of-using-sparcs-socket-mode) **b**)
Ideal for standalone calculations, this mode simulates a conventional calculator while benefiting from socket-based efficiency.
@@ -429,7 +456,7 @@ Based on the user scenarios, the socket communication layer can be accessed via
```
For most users we recommend using this mode when performing a calculation on a single HPC node.
-3. **Client (Relay) Mode**
+3. **Client (Relay) Mode** ([Fig. 5](#fig-5-different-ways-of-using-sparcs-socket-mode) **c**)
In this mode, the `sparc.SPARC` calculator servers as a passive
client which listens to a remote i-PI-compatible server. When
@@ -457,7 +484,7 @@ Based on the user scenarios, the socket communication layer can be accessed via
automatically determine if it is necessary to restart the SPARC
subprocess.
-4. **Server Mode**
+4. **Server Mode** ([Fig. 5](#fig-5-different-ways-of-using-sparcs-socket-mode) **d**)
Paired with the client mode in (3), SPARC-X-API can be run as a
socket server, isolated from the node that performs the
@@ -482,7 +509,8 @@ Based on the user scenarios, the socket communication layer can be accessed via
### (In-progress) Controlling SPARC routines from socket interface
-Our SPARC socket protocol designs allows bidirectional control of
+As shown in [Fig. 4](#fig-4-overview-of-the-sparc-protocol-as-an-extension-to-the-standard-i-pi-protocol),
+the SPARC socket protocol designs allows bidirectional control of
internal SPARC routines. Local- or server-mode `sparc.SPARC`
calculators can communicate with the SPARC binary via functions like
`set_params`. This can be useful for applications like on-the-fly
diff --git a/doc/img/scheme-SPARC-socke-modes.svg b/doc/img/scheme-SPARC-socke-modes.svg
new file mode 100644
index 00000000..45147876
--- /dev/null
+++ b/doc/img/scheme-SPARC-socke-modes.svg
@@ -0,0 +1,867 @@
+
+
+
+
diff --git a/doc/img/scheme-SPARC-socket-modes.png b/doc/img/scheme-SPARC-socket-modes.png
new file mode 100644
index 00000000..a3e47ede
Binary files /dev/null and b/doc/img/scheme-SPARC-socket-modes.png differ
diff --git a/doc/img/scheme_api_architecture.png b/doc/img/scheme_api_architecture.png
new file mode 100644
index 00000000..4e575334
Binary files /dev/null and b/doc/img/scheme_api_architecture.png differ
diff --git a/doc/img/scheme_api_architecture.svg b/doc/img/scheme_api_architecture.svg
new file mode 100644
index 00000000..e456aa1c
--- /dev/null
+++ b/doc/img/scheme_api_architecture.svg
@@ -0,0 +1,675 @@
+
+
+
+
diff --git a/doc/img/scheme_socket_hetero.png b/doc/img/scheme_socket_hetero.png
new file mode 100644
index 00000000..9a33a890
Binary files /dev/null and b/doc/img/scheme_socket_hetero.png differ
diff --git a/doc/img/scheme_socket_hetero.svg b/doc/img/scheme_socket_hetero.svg
new file mode 100644
index 00000000..031001f9
--- /dev/null
+++ b/doc/img/scheme_socket_hetero.svg
@@ -0,0 +1,1493 @@
+
+
+
+
diff --git a/doc/img/scheme_sparc_protocol.png b/doc/img/scheme_sparc_protocol.png
new file mode 100644
index 00000000..4df1fb8a
Binary files /dev/null and b/doc/img/scheme_sparc_protocol.png differ
diff --git a/doc/img/scheme_sparc_protocol.svg b/doc/img/scheme_sparc_protocol.svg
new file mode 100644
index 00000000..0507ee32
--- /dev/null
+++ b/doc/img/scheme_sparc_protocol.svg
@@ -0,0 +1,1181 @@
+
+
+
+
diff --git a/examples/active_learning/example_finetuna_minimal.py b/examples/active_learning/example_finetuna_minimal.py
new file mode 100644
index 00000000..ab74a70f
--- /dev/null
+++ b/examples/active_learning/example_finetuna_minimal.py
@@ -0,0 +1,93 @@
+"""A minimal example combining active learning library like Finetuna with SPARC
+
+usage
+First download the checkpoint from the url https://dl.fbaipublicfiles.com/opencatalystproject/models/2021_08/s2ef/gemnet_t_direct_h512_all.pt
+
+python example_finetuna_minimal.py
+"""
+import torch
+import os
+import yaml
+from pathlib import Path
+from ase.io.trajectory import Trajectory
+from ase.optimize import BFGS
+from ase.constraints import FixAtoms
+from finetuna.ml_potentials.finetuner_ensemble_calc import FinetunerEnsembleCalc
+import ase
+from ase.cluster.cubic import FaceCenteredCubic
+from finetuna.online_learner.online_learner import OnlineLearner
+import argparse
+from sparc.calculator import SPARC
+
+from ase.build import molecule
+
+cpu = not torch.cuda.is_available()
+curdir = Path(__file__).parent
+config_file = curdir / "ft_config_gemnet_gpu.yml"
+with open(config_file, "r") as fd:
+ configs = yaml.load(fd, Loader=yaml.FullLoader)
+
+checkpoint = os.environ.get("CHECKPOINT_PATH", None)
+if checkpoint is None:
+ # Use default (relative path)
+ checkpoint = curdir / configs["ocp"]["checkpoint_path_list"][0]
+checkpoint = Path(checkpoint)
+
+if not checkpoint.is_file():
+ raise FileNotFoundError("Cannot found the model checkpoint file!")
+
+finetuner = configs["finetuner"]
+finetuner[0].update(cpu=cpu)
+learner = configs["learner"]
+
+ml_potential = FinetunerEnsembleCalc(
+ checkpoint_paths=[checkpoint],
+ mlp_params=finetuner,
+)
+
+
+<<<<<<< HEAD
+# init_molecule = molecule("H2O", pbc=False, cell=[8, 8, 8])
+# init_molecule.center()
+# init_molecule.rattle()
+
+
+surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
+layers = [1, 2, 1]
+lc = 3.61000
+
+# init_atoms = molecule("CH4", pbc=True, cell=[8, 8, 8])
+# init_atoms.constraints = [FixAtoms([0])]
+# init_atoms.center()
+# init_atoms.rattle(0.05)
+
+init_atoms = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)
+init_atoms.cell = [12, 12, 12]
+init_atoms.center()
+init_atoms.pbc = False
+init_atoms.rattle(0.05)
+
+
+sparc_params = {"xc": "pbe", "h": 0.13}
+# with SPARC(directory="pure_BFGS", **sparc_params) as calc:
+# atoms = init_atoms.copy()
+# atoms.calc = calc
+# dyn = BFGS(atoms, maxstep=0.2, trajectory="pure_bfgs.traj")
+# dyn.run(fmax=0.03)
+
+
+with SPARC(directory="online_coldstart", **sparc_params) as parent_calc:
+ atoms = init_atoms.copy()
+=======
+init_molecule = molecule("H2O", pbc=False, cell=[8, 8, 8])
+init_molecule.center()
+init_molecule.rattle()
+
+sparc_params = {"directory": curdir, "xc": "pbe", "h": 0.22}
+with SPARC(**sparc_params) as parent_calc:
+>>>>>>> f703ec5 (use smaller mesh size)
+ onlinecalc = OnlineLearner(learner, [], ml_potential, parent_calc)
+ atoms.calc = onlinecalc
+ dyn = BFGS(atoms,
+ maxstep=0.2)
+ dyn.run(fmax=0.03)
diff --git a/examples/active_learning/ft_config_gemnet_gpu.yml b/examples/active_learning/ft_config_gemnet_gpu.yml
new file mode 100644
index 00000000..e2bafef3
--- /dev/null
+++ b/examples/active_learning/ft_config_gemnet_gpu.yml
@@ -0,0 +1,73 @@
+finetuner:
+- cpu: false
+ optim:
+ batch_size: 1
+ break_below_lr: 1.0e-07
+ checkpoint_every: 100000
+ eps: 1.0e-08
+ eval_every: 1
+ factor: 0.9
+ force_coefficient: 100
+ lr_initial: 0.0003
+ max_epochs: 400
+ num_workers: 0
+ optimizer_params:
+ eps: 1.0e-08
+ weight_decay: 0
+ patience: 3
+ # print_loss_and_lr: true
+ scheduler_loss: train
+ weight_decay: 0
+ task:
+ primary_metric: loss
+ tuner:
+ num_threads: 8
+ unfreeze_blocks:
+ - out_blocks.3.seq_forces
+ - out_blocks.3.scale_rbf_F
+ - out_blocks.3.dense_rbf_F
+ - out_blocks.3.out_forces
+ - out_blocks.2.seq_forces
+ - out_blocks.2.scale_rbf_F
+ - out_blocks.2.dense_rbf_F
+ - out_blocks.2.out_forces
+ - out_blocks.1.seq_forces
+ - out_blocks.1.scale_rbf_F
+ - out_blocks.1.dense_rbf_F
+ - out_blocks.1.out_forces
+learner:
+ dyn_avg_steps: 15
+ dyn_uncertain_tol: 1000000 # Dynamic uncertainty tolerance
+ fmax_verify_threshold: 0.03 # Fmax threshold for calling VASP single point calculation
+ initial_points_to_keep: []
+ # logger:
+ # pca_quantify: true # Log PCA on wandb
+ # uncertainty_quantify: false
+ num_initial_points: 1 # Number of VASP calls at the beginning of the relaxation
+ partial_fit: true
+ query_every_n_steps: 30 # K-steps querying strategy: query every 100 steps
+ stat_uncertain_tol: 1000000 # Static uncertainty tolerance
+ tolerance_selection: min
+ # valset_system_id: '1498818'
+ # wandb_init:
+ # entity: ulissi-group
+ # project: project
+ # group: group
+ # name: name
+ # notes: notes
+ # wandb_log: false # Wandb disabled
+# optional_config:
+# links:
+# traj: /path/to/vasp/reference/traj
+ocp:
+ model_class_list:
+ - gemnet
+ checkpoint_path_list:
+ - "gemnet_t_direct_h512_all.pt"
+# relaxation:
+# # fmax: 0.03
+# max_parent_calls: null
+# maxstep: 0.2
+# replay_method: parent_only
+# steps: null
+# trajname: oal_relaxation.traj
diff --git a/sparc/calculator.py b/sparc/calculator.py
index c8faf82b..2b6bfec1 100644
--- a/sparc/calculator.py
+++ b/sparc/calculator.py
@@ -276,14 +276,14 @@ def ensure_socket(self):
def __enter__(self):
"""Reset upon entering the context."""
- IOContext.__enter__()
+ IOContext.__enter__(self)
self.reset()
self.close()
return self
def __exit__(self, type, value, traceback):
"""Exiting the context manager and reset process"""
- IOContext.__exit__(type, value, traceback)
+ IOContext.__exit__(self, type, value, traceback)
self.close()
return
@@ -770,13 +770,14 @@ def execute(self):
def close(self, keep_out_socket=False):
"""Close the socket communication, the SPARC process etc"""
+ if not self.use_socket:
+ return
if self.in_socket is not None:
self.in_socket.close()
if (self.out_socket is not None) and (not keep_out_socket):
self.out_socket.close()
- # import pdb; pdb.set_trace()
# In most cases if in_socket is closed, the SPARC process should also exit
if self.process:
with time_limit(5):