diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..0262eb8 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,28 @@ +name: Documentation +on: + push: + branches: + - main +permissions: + contents: read + pages: write + id-token: write +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/configure-pages@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: pip install zensical + - run: zensical build --clean + - uses: actions/upload-pages-artifact@v4 + with: + path: site + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/.gitignore b/.gitignore index ee6c946..0d3c304 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ Thumbs.db # Sockets (shouldn't be committed but just in case) /tmp/ipi_* + +# zensical +site/ diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..386d55a --- /dev/null +++ b/docs/api.md @@ -0,0 +1,215 @@ +# API Reference + +## RootstockCalculator + +The main interface to Rootstock is the `RootstockCalculator` class, an ASE-compatible calculator. + +### Basic Usage + +```python +from ase.build import bulk +from rootstock import RootstockCalculator + +atoms = bulk("Cu", "fcc", a=3.6) * (5, 5, 5) + +with RootstockCalculator( + cluster="della", + model="mace", + checkpoint="medium", + device="cuda", +) as calc: + atoms.calc = calc + energy = atoms.get_potential_energy() + forces = atoms.get_forces() + stress = atoms.get_stress() +``` + +### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `cluster` | `str` | Yes* | Cluster name (e.g., `"della"`, `"sophia"`) | +| `root` | `str` | Yes* | Custom root path instead of a known cluster | +| `model` | `str` | Yes | MLIP family: `"mace"`, `"chgnet"`, `"uma"`, `"tensornet"` | +| `checkpoint` | `str` | No | Specific model weights (uses environment default if omitted) | +| `device` | `str` | No | `"cuda"` (default) or `"cpu"` | + +*Either `cluster` or `root` must be provided, but not both. + +### Examples + +```python +# Using a known cluster with explicit checkpoint +RootstockCalculator(cluster="della", model="mace", checkpoint="medium") + +# Using a known cluster with default checkpoint +RootstockCalculator(cluster="della", model="uma") + +# Using a custom root path +RootstockCalculator(root="/scratch/gpfs/specific/install/path/rootstock", model="mace") +``` + +### Context Manager + +`RootstockCalculator` should be used as a context manager to ensure proper cleanup of the worker subprocess: + +```python +with RootstockCalculator(...) as calc: + # Use the calculator + atoms.calc = calc + energy = atoms.get_potential_energy() +# Worker process is automatically terminated when exiting the context +``` + +!!! tip "Manual Cleanup" + If you cannot use a context manager, call `calc.close()` manually to terminate the worker process. + +## Available Models + +Available models vary by cluster and change as new environments are added. See the [Example Configs](clusters.md) page for current deployments on each cluster. + +### Model Reference + +| Model | Environment | Default Checkpoint | Other Checkpoints | +|-------|-------------|-------------------|-------------------| +| `mace` | mace_env | `medium` | `small`, `large` | +| `chgnet` | chgnet_env | (pretrained) | — | +| `uma` | uma_env | `uma-s-1p1` | — | +| `tensornet` | tensornet_env | `TensorNet-MatPES-PBE-v2025.1-PES` | Other MatGL models | + +### Checking Available Models + +To see available models on your cluster, check the [Example Configs](clusters.md) page or run: + +```bash +rootstock status +``` + +This command displays installed environments, their checkpoints, and cache sizes. + +## CLI Reference + +The Rootstock CLI provides commands for both administrators (setting up clusters) and users (querying available environments). + +### User Commands + +Commands users can run to explore available environments: + +#### `rootstock status` + +Display installation status, including all installed environments and cache usage. + +```bash +rootstock status +``` + +#### `rootstock list` + +List all registered environments in the shared environments directory. + +```bash +rootstock list +``` + +#### `rootstock resolve` + +Look up the root directory for a known cluster. + +```bash +# Human-readable output +rootstock resolve --cluster della + +# JSON output +rootstock resolve --cluster della --json +``` + +### Administrator Commands + +Commands for cluster administrators to set up and manage Rootstock installations: + +#### `rootstock init` + +Interactive setup wizard for creating a new Rootstock installation. Prompts for: + +- Root directory path +- API credentials for dashboard integration (optional) +- Maintainer information + +```bash +rootstock init + +# Skip directory creation +rootstock init --skip-dirs + +# Skip manifest initialization +rootstock init --skip-manifest +``` + +#### `rootstock new-env` + +Create a new environment template file with the required PEP 723 structure. + +```bash +# Create template in current directory +rootstock new-env mace + +# Specify output path +rootstock new-env mace -o ./environments/mace_env.py + +# Overwrite existing file +rootstock new-env mace --force +``` + +#### `rootstock install` + +Install environment(s) from a file, directory, or rebuild by name. + +```bash +# Install from a single file +rootstock install ./mace_env.py --models small,medium + +# Install all environments from a directory +rootstock install ./environments/ + +# Rebuild an existing environment +rootstock install mace_env --force + +# Install without pushing manifest to backend +rootstock install mace_env.py --no-push +``` + +Options: + +- `--root `: Specify root directory (or use `$ROOTSTOCK_ROOT`) +- `--models `: Comma-separated list of models to pre-download +- `--force`: Update registration and rebuild if environment exists +- `--verbose`, `-v`: Verbose output +- `--no-push`: Skip pushing manifest to backend + +#### `rootstock serve` + +Start a worker process for an external i-PI server (advanced usage). + +```bash +rootstock serve mace \ + --socket /tmp/ipi_socket \ + --checkpoint medium \ + --device cuda +``` + +#### `rootstock manifest` + +Manage the installation manifest that tracks environment state. + +```bash +# Show current manifest +rootstock manifest show +rootstock manifest show --json + +# Push manifest to dashboard +rootstock manifest push + +# Initialize new manifest +rootstock manifest init --cluster della +rootstock manifest init --cluster della --force +``` diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..331279e --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,77 @@ +# Architecture + +## Overview + +When you create a `RootstockCalculator`, Rootstock spawns a subprocess that runs the MLIP in its own pre-built virtual environment. The main process and worker communicate over a Unix domain socket using the [i-PI protocol](http://ipi-code.org/). All communication occurs on a single node with no remote network calls. + +``` +Your script (on cluster node) Worker subprocess ++-------------------------+ +-----------------------------+ +| RootstockCalculator | | Pre-built MLIP environment | +| (ASE-compatible) | | | +| | | | +| server.py (i-PI server) |<-------->| worker.py (i-PI client) | +| - sends positions | Unix | - receives positions | +| - receives forces | socket | - calculates forces | ++-------------------------+ +-----------------------------+ +``` + +## Design Benefits + +This design eliminates environment conflicts when experimenting with different MLIPs or using multiple MLIPs in a single workflow: + +- **No environment conflicts**: Each MLIP runs in isolation with its exact required dependencies +- **One-line model swapping**: Change `model="mace"` to `model="uma"` without reinstalling anything +- **Multi-model workflows**: Use multiple MLIPs in the same script (sequentially) +- **Clean user environments**: Users only install the lightweight `rootstock` package + +## Tradeoffs + +The architecture introduces minimal overhead due to inter-process communication: + +- **~4% overhead** on an 864-atom system +- Communication occurs via Unix domain socket (fast, local-only) +- Positions and forces are serialized using the i-PI protocol + +For most use cases, this overhead is negligible compared to MLIP forward pass time. + +## Directory Structure + +After setup, the Rootstock root directory looks like this: + +``` +{root}/ +├── .python/ # uv-managed Python interpreters +├── environments/ # Environment source files (*.py with PEP 723 metadata) +│ ├── mace_env.py +│ ├── chgnet_env.py +│ ├── uma_env.py +│ └── tensornet_env.py +├── envs/ # Pre-built virtual environments +│ ├── mace_env/ +│ │ ├── bin/python +│ │ ├── lib/python3.11/site-packages/ +│ │ └── env_source.py +│ └── ... +├── home/ # Redirected HOME for not-well-behaved libraries +│ ├── .cache/fairchem/ +│ └── .matgl/ +└── cache/ # XDG_CACHE_HOME and HF_HOME for well-behaved libraries + ├── mace/ + └── huggingface/ +``` + +### Why the `home/` Directory? + +Some ML libraries (FAIRChem, MatGL) ignore `XDG_CACHE_HOME` and write to `~/.cache/` unconditionally. Rootstock redirects `HOME` during environment builds and worker runtime to ensure model weights are stored in the shared directory rather than in individual user home directories. + +## i-PI Protocol + +Rootstock uses the [i-PI protocol](http://ipi-code.org/) for communication between the main process and worker: + +1. Main process sends atomic positions and cell parameters +2. Worker receives positions and runs the MLIP forward pass +3. Worker sends back energy, forces, and stress +4. Main process receives results and returns them to ASE + +The protocol is text-based and designed for interoperability between simulation codes. diff --git a/docs/cluster-setup.md b/docs/cluster-setup.md new file mode 100644 index 0000000..9dc8126 --- /dev/null +++ b/docs/cluster-setup.md @@ -0,0 +1,195 @@ +# Setting Up a New Cluster + +This guide is for administrators setting up Rootstock on a new cluster. Run all commands below **on the cluster itself** after SSH access is established. Write access to a shared filesystem location visible to users is required. + +## Prerequisites + +- SSH access to the cluster +- Write access to a shared filesystem location +- Python 3.10 or later +- `uv` package manager (Rootstock uses it internally) + +## Step 1: Install Rootstock + +On a login node: + +```bash +pip install rootstock +``` + +## Step 2: Initialize the Rootstock Directory + +Choose a location on a shared filesystem where other users have access: + +```bash +# Choose a shared directory path +# Example: /scratch/shared/rootstock +``` + +Then run the initialization command: + +```bash +rootstock init +``` + +This will interactively prompt you for: + +| Setting | Description | +|---------|-------------| +| **root** | The shared directory path (e.g., `/scratch/shared/rootstock`) | +| **api_key / api_secret** | Optional credentials for pushing the cluster manifest to the dashboard | +| **maintainer name / email** | Identifies the maintainer for this installation | + +!!! tip "Dashboard Integration" + Contact a Rootstock maintainer if you want your cluster to appear on the [Example Configs](clusters.md) page. The API credentials are [Modal Proxy Auth Tokens](https://modal.com/docs/guide/webhook-proxy-auth). + +## Step 3: Install Environments + +Still on the login node: + +```bash +# Install individual environments +rootstock install mace_env.py --models small,medium +rootstock install chgnet_env.py +rootstock install uma_env.py --models uma-s-1p1 +rootstock install tensornet_env.py + +# Or point it at a directory with multiple environments +rootstock install ./environments/ + +# Verify everything is set up +rootstock status +``` + +Each `rootstock install` command performs the following: + +1. Creates an isolated virtual environment under `{root}/envs/` +2. Installs MLIP dependencies +3. Pre-downloads model weights (when `--models` is specified) + +This process can take several minutes per environment, depending on the MLIP and network conditions. + +!!! note "Finding Environment Files" + See the [Example Configs](clusters.md) page for environment files that are known to work — you can use these as a starting point for your cluster. + Some minor tweaks may be required depending on site specific requirements. + +## Step 4: Register with the Dashboard (Optional) + +If you configured API credentials during `rootstock init`, the manifest is pushed automatically when you install or update environments. + +### Managing the Manifest + +The manifest tracks the state of your Rootstock installation and is used by the dashboard to display available environments. You can manage it with the following commands: + +#### View Current Manifest + +```bash +# Display the manifest in human-readable format +rootstock manifest show + +# Output as JSON +rootstock manifest show --json +``` + +#### Push Manifest to Dashboard + +If the automatic push failed (e.g., due to network issues), you can manually retry: + +```bash +rootstock manifest push +``` + +#### Initialize a New Manifest + +To create or reinitialize a manifest for a cluster: + +```bash +# Create a new manifest +rootstock manifest init --cluster della + +# Overwrite existing manifest +rootstock manifest init --cluster della --force + +# Skip automatic push to backend +rootstock manifest init --cluster della --no-push +``` + +## Verifying the Installation + +After setup, verify that everything works: + +```bash +# Check status +rootstock status + +# List all environments +rootstock list +``` + +## Directory Structure + +After setup, the Rootstock root directory will look like this: + +``` +{root}/ +├── .python/ # uv-managed Python interpreters +├── environments/ # Environment source files (*.py with PEP 723 metadata) +│ ├── mace_env.py +│ ├── chgnet_env.py +│ ├── uma_env.py +│ └── tensornet_env.py +├── envs/ # Pre-built virtual environments +│ ├── mace_env/ +│ │ ├── bin/python +│ │ ├── lib/python3.11/site-packages/ +│ │ └── env_source.py +│ └── ... +├── home/ # Redirected HOME for not-well-behaved libraries +│ ├── .cache/fairchem/ +│ └── .matgl/ +└── cache/ # XDG_CACHE_HOME and HF_HOME for well-behaved libraries + ├── mace/ + └── huggingface/ +``` + +## Updating Environments + +To update an environment with new dependencies or model weights: + +```bash +# Rebuild with new models +rootstock install mace_env.py --models small,medium,large --force + +# Push updated manifest +rootstock manifest push +``` + +## Troubleshooting + +### Environment build fails + +Check that you have: + +- Sufficient disk space in `{root}/` +- Network access for downloading packages and model weights +- Correct Python version (3.10+) + +### Users can't access environments + +Verify permissions: + +```bash +# Environments should be readable by all users +ls -la {root}/envs/ + +# Model weights in cache should also be readable +ls -la {root}/cache/ +``` + +### Dashboard push fails + +Check your API credentials and network connectivity, then retry the push: + +```bash +rootstock manifest push +``` diff --git a/docs/clusters.md b/docs/clusters.md new file mode 100644 index 0000000..2fb2588 --- /dev/null +++ b/docs/clusters.md @@ -0,0 +1,168 @@ +# Example Configs + +View live environment configurations from clusters where Rootstock is currently deployed. The configurations below are fetched from the dashboard API and show the exact environments, dependencies, and checkpoints available on each cluster. + +Use these as a starting point when setting up your own cluster. + + + +
+

Loading cluster configurations...

+
+ + diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..5e580e7 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,53 @@ +# Development + +## Local Development Setup + +```bash +git clone https://github.com/Garden-AI/rootstock.git +cd rootstock +uv venv && source .venv/bin/activate +uv pip install -e ".[dev]" +``` + +## Code Quality + +Run linting before committing: + +```bash +ruff check rootstock/ +ruff format rootstock/ +``` + +## Project Structure + +``` +rootstock/ +├── rootstock/ +│ ├── __init__.py +│ ├── calculator.py # ASE Calculator interface +│ ├── server.py # Spawns worker subprocess, manages socket lifecycle +│ ├── worker.py # i-PI client state machine +│ ├── environment.py # Pre-built environment management +│ ├── clusters.py # Cluster registry and known environments +│ └── cli.py # CLI commands (init, install, status, list, etc.) +├── environments/ # Example environment files +├── lammps/ # LAMMPS fix source files +├── tests/ +└── docs/ +``` + +## Running Tests + +```bash +pytest tests/ +``` + +## Get Involved + +Rootstock is an early-stage project. We welcome feedback, bug reports, and collaborators. If you are interested in: + +- Deploying Rootstock on your cluster +- Contributing environment files for new MLIPs +- Using Rootstock for a research project + +Contact Will Engler at [willengler@uchicago.edu](mailto:willengler@uchicago.edu). diff --git a/docs/environments.md b/docs/environments.md new file mode 100644 index 0000000..f17f1b2 --- /dev/null +++ b/docs/environments.md @@ -0,0 +1,246 @@ +# Writing Environment Files + +Each MLIP is defined by a small Python file with [PEP 723](https://peps.python.org/pep-0723/) inline metadata specifying its dependencies and a `setup()` function that returns an ASE calculator. + +## Creating a New Environment + +Use the `rootstock new-env` command to scaffold a new environment file from a template. + +### Generating a Template + +```bash +# Create a new environment file +rootstock new-env mace + +# Specify custom output path +rootstock new-env mace -o ./environments/mace_env.py + +# Overwrite existing file +rootstock new-env mace --force +``` + +This generates a template file `mace_env.py` with the required structure: + +```python +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# +# ] +# /// +""" +MACE environment for Rootstock. + +TODO: Add description of this environment. +""" + + +def setup(model: str | None = None, device: str = "cuda"): + """ + Load a calculator. + + Args: + model: Model identifier or checkpoint name. + device: PyTorch device string (e.g., "cuda", "cuda:0", "cpu"). + + Returns: + ASE-compatible calculator. + """ + raise NotImplementedError("TODO: Implement setup()") +``` + +After generating the template, fill in the dependencies and implement the `setup()` function as described below. + +## Basic Structure + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = ["mace-torch>=0.3.14", "ase>=3.22", "torch>=2.0,<2.10"] +# /// + +def setup(model: str, device: str = "cuda"): + from mace.calculators import mace_mp + return mace_mp(model=model, device=device, default_dtype="float32") +``` + +## How It Works + +1. **PEP 723 metadata**: The `# /// script` block defines Python version requirements and dependencies +2. **`setup()` function**: Called once when a worker starts. The returned calculator is reused for all calculations +3. **Environment building**: Rootstock uses `uv` to create an isolated virtual environment from the specified dependencies + +## Required Elements + +### PEP 723 Metadata Block + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "mace-torch>=0.3.14", +# "ase>=3.22", +# "torch>=2.0,<2.10" +# ] +# /// +``` + +- `requires-python`: Minimum Python version +- `dependencies`: List of pip-installable packages with version constraints + +### The `setup()` Function + +```python +def setup(model: str, device: str = "cuda"): + # Import the MLIP library + from mace.calculators import mace_mp + + # Create and return an ASE calculator + return mace_mp(model=model, device=device, default_dtype="float32") +``` + +**Parameters:** + +- `model` (str): Checkpoint or model name passed by the user +- `device` (str): PyTorch device (`"cuda"` or `"cpu"`) + +**Returns:** + +An ASE-compatible calculator object. + +## Examples + +### MACE + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = ["mace-torch>=0.3.14", "ase>=3.22", "torch>=2.0,<2.10"] +# /// + +def setup(model: str, device: str = "cuda"): + from mace.calculators import mace_mp + return mace_mp(model=model, device=device, default_dtype="float32") +``` + +### CHGNet + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = ["chgnet>=0.3.0", "ase>=3.22", "torch>=2.0"] +# /// + +def setup(model: str = "pretrained", device: str = "cuda"): + from chgnet.model import CHGNetCalculator + return CHGNetCalculator(use_device=device) +``` + +### UMA (FAIRChem) + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "fairchem-core>=1.0.0", +# "ase>=3.22", +# "torch>=2.0" +# ] +# /// + +def setup(model: str = "uma-s-1p1", device: str = "cuda"): + from fairchem.core import OCPCalculator + return OCPCalculator(checkpoint_path=model, device=device) +``` + +### TensorNet (MatGL) + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = ["matgl>=1.0.0", "ase>=3.22", "torch>=2.0"] +# /// + +def setup(model: str = "TensorNet-MatPES-PBE-v2025.1-PES", device: str = "cuda"): + import matgl + from matgl.ext.ase import MatGLCalculator + + potential = matgl.load_model(model) + return MatGLCalculator(potential, device=device) +``` + +## Best Practices + +### Pin dependency versions + +Use version constraints to ensure reproducible builds: + +```python +# Good: pinned versions +# dependencies = ["mace-torch>=0.3.14,<0.4", "torch>=2.0,<2.10"] + +# Avoid: unpinned versions +# dependencies = ["mace-torch", "torch"] +``` + +### Handle model loading errors gracefully + +```python +def setup(model: str, device: str = "cuda"): + from mace.calculators import mace_mp + + try: + return mace_mp(model=model, device=device, default_dtype="float32") + except Exception as e: + raise RuntimeError(f"Failed to load MACE model '{model}': {e}") +``` + +### Document available checkpoints + +Add a comment listing known-good checkpoints: + +```python +# /// script +# requires-python = ">=3.10" +# dependencies = ["mace-torch>=0.3.14", "ase>=3.22", "torch>=2.0,<2.10"] +# /// +# +# Available checkpoints: small, medium, large + +def setup(model: str, device: str = "cuda"): + from mace.calculators import mace_mp + return mace_mp(model=model, device=device, default_dtype="float32") +``` + +## Testing Your Environment + +After creating an environment file, install it and verify the build: + +```bash +# Install the environment +rootstock install my_env.py --models default_checkpoint + +# Verify it was built successfully +rootstock status +``` + +To test the calculator, create a simple Python script: + +```python +from ase.build import bulk +from rootstock import RootstockCalculator + +atoms = bulk("Cu", "fcc", a=3.6) + +with RootstockCalculator( + root="/path/to/rootstock", + model="my_env", + checkpoint="default_checkpoint", + device="cuda", +) as calc: + atoms.calc = calc + energy = atoms.get_potential_energy() + forces = atoms.get_forces() + print(f"Energy: {energy:.4f} eV") + print(f"Forces shape: {forces.shape}") +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ae6c1a1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,75 @@ +# Rootstock + +Rootstock makes it easy to use machine-learned interatomic potentials (MLIPs) on HPC clusters. Researchers can use multiple MLIPs (MACE, CHGNet, UMA, TensorNet, and others) with ASE or LAMMPS without managing conflicting Python environments. + +Rootstock provides an [ASE](https://wiki.fysik.dtu.dk/ase/)-compatible calculator that runs each MLIP in an isolated, pre-built Python environment. Swapping models is a one-line change, even when MLIPs require different Python or library versions. Rootstock also integrates with [LAMMPS](https://www.lammps.org/) through a `fix` command for any supported MLIP. + +## Status + +Rootstock is **early-stage software under active development.** It is currently deployed on two HPC clusters: + +- **Della** — Princeton Research Computing +- **Sophia** — Argonne Leadership Computing Facility (ALCF) + +We are seeking additional clusters and early users to help shape the tool. If you are interested in deploying Rootstock on your cluster or using it for a specific project, contact Will Engler at [willengler@uchicago.edu](mailto:willengler@uchicago.edu). + +## Quick Start + +Rootstock is designed for HPC clusters where it has been set up by a system maintainer. The code below runs in your normal Python environment — inside a SLURM job script, an interactive session, or a Jupyter notebook. Rootstock handles MLIP environment isolation automatically. + +```python +from ase.build import bulk +from rootstock import RootstockCalculator + +atoms = bulk("Cu", "fcc", a=3.6) * (5, 5, 5) + +with RootstockCalculator( + cluster="della", + model="mace", + checkpoint="medium", + device="cuda", +) as calc: + atoms.calc = calc + print(atoms.get_potential_energy()) + print(atoms.get_forces()) +``` + +Changing `model="mace"` to `model="uma"` or `model="tensornet"` swaps the underlying potential. + +## Next Steps + +
+ +- :material-download:{ .lg .middle } **Installation** + + --- + + Install the lightweight `rootstock` package in your environment. + + [:octicons-arrow-right-24: Installation](installation.md) + +- :material-api:{ .lg .middle } **API Reference** + + --- + + Learn about the `RootstockCalculator` API and available models. + + [:octicons-arrow-right-24: API](api.md) + +- :material-earth:{ .lg .middle } **Example Configs** + + --- + + See example environment configurations from deployed clusters. + + [:octicons-arrow-right-24: Example Configs](clusters.md) + +- :material-server:{ .lg .middle } **Cluster Setup** + + --- + + Set up Rootstock on a new HPC cluster. + + [:octicons-arrow-right-24: Cluster Setup](cluster-setup.md) + +
diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..bd54107 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,47 @@ +# Installation + +Users install only the lightweight `rootstock` package. The heavy ML dependencies (PyTorch, MACE, FAIRChem, etc.) live in the pre-built environments on the cluster. + +## Requirements + +- Python 3.10 or later +- Access to a supported HPC cluster (Della, Sophia, or a custom installation) + +## Install with pip + +```bash +pip install rootstock +``` + +## Install with uv + +```bash +uv pip install rootstock +``` + +## Verify Installation + +After installation, verify that Rootstock is working: + +```python +from rootstock import RootstockCalculator + +# Verify import works +print(f"Rootstock installed successfully") +``` + +You can also check available clusters from the command line: + +```bash +rootstock resolve --cluster della --json +``` + +## What Gets Installed + +The `rootstock` package is intentionally minimal. It includes: + +- The `RootstockCalculator` ASE-compatible calculator +- The i-PI protocol client/server implementation +- CLI tools for cluster administrators + +Heavy dependencies (PyTorch, CUDA, MACE, CHGNet, FAIRChem, etc.) are **not** installed on your system. They live in pre-built virtual environments managed by cluster administrators. diff --git a/docs/lammps.md b/docs/lammps.md new file mode 100644 index 0000000..2820f5e --- /dev/null +++ b/docs/lammps.md @@ -0,0 +1,112 @@ +# LAMMPS Integration + +!!! warning "Experimental" + LAMMPS integration is experimental and has not been tested as thoroughly as the ASE integration yet. If you try it and run into issues, please reach out. + +Rootstock includes a native LAMMPS `fix` that auto-spawns a worker subprocess, giving LAMMPS users access to any Rootstock-managed MLIP. + +## Quick Start + +Add one line to your LAMMPS input script: + +```lammps +fix mlip all rootstock cluster della model mace checkpoint medium device cuda elements Cu +``` + +The fix handles worker lifecycle, socket communication, and cleanup automatically. Virial information is passed through, so barostats (`npt`, `nph`) work correctly. Energy is accessible via `f_mlip` in thermo output. + +## Building the Fix + +The fix ships as two files (`fix_rootstock.h`, `fix_rootstock.cpp`) with no dependencies beyond the C++ standard library and POSIX sockets. Copy them into your LAMMPS `src/` directory and rebuild: + +```bash +./lammps/install.sh /path/to/lammps/src +cd /path/to/lammps/build +cmake ../cmake [your usual flags] +make -j 4 +``` + +Rootstock must also be installed and on `PATH` so the fix can call `rootstock resolve` and `rootstock serve`: + +```bash +pip install rootstock +``` + +## Fix Syntax + +```lammps +fix rootstock cluster model \ + checkpoint device [timeout ] elements ... +``` + +### Parameters + +| Keyword | Required | Default | Description | +|---------|----------|---------|-------------| +| `cluster` | yes | — | Cluster name (e.g., `della`) | +| `model` | yes | — | MLIP family: `mace`, `chgnet`, `uma`, `tensornet` | +| `checkpoint` | no | `default` | Model weights (e.g., `medium`, `uma-s-1p1`) | +| `device` | no | `cuda` | `cuda` or `cpu` | +| `timeout` | no | `120` | Seconds to wait for worker startup | +| `elements` | yes | — | Element symbols mapping atom types (must be last) | + +## Example Input Script + +```lammps +units metal +atom_style atomic +boundary p p p + +# Create or read your structure +lattice fcc 3.6 +region box block 0 5 0 5 0 5 +create_box 1 box +create_atoms 1 box +mass 1 63.546 # Cu + +# Use pair_style zero - the fix provides all forces +pair_style zero 6.0 +pair_coeff * * + +# Rootstock fix +fix mlip all rootstock cluster della model mace checkpoint medium device cuda elements Cu + +# Thermo output - energy accessible via f_mlip +thermo_style custom step temp pe f_mlip press +thermo 100 + +# Run dynamics +velocity all create 300.0 12345 +fix nve all nve +run 1000 +``` + +## Important Notes + +- **Requires `units metal`**: The fix checks this at startup and will error if a different unit system is used. +- **Use `pair_style zero`**: The fix provides all interatomic forces, so you need a placeholder pair style. +- **Single-node only**: The worker sees all atoms and computes its own neighborhoods. MPI-parallel LAMMPS runs are not yet supported. +- **Element order matters**: The `elements` keyword must be last, and element symbols must map to LAMMPS atom types in order (type 1 = first element, type 2 = second element, etc.). + +## Accessing Energy + +The fix stores the potential energy computed by the MLIP. Access it in thermo output: + +```lammps +thermo_style custom step temp pe f_mlip press +``` + +Or use it in a variable: + +```lammps +variable mlip_energy equal f_mlip +``` + +## Barostat Support + +Virial information is correctly passed through, so NPT and NPH ensembles work: + +```lammps +fix mlip all rootstock cluster della model mace checkpoint medium device cuda elements Cu +fix npt all npt temp 300 300 0.1 iso 1.0 1.0 1.0 +``` diff --git a/pyproject.toml b/pyproject.toml index 7e7a815..f4dd101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ dev = [ "pytest>=7.0", "ruff>=0.1", + "zensical>=0.0.27", ] modal = [ "modal>=0.56", diff --git a/uv.lock b/uv.lock index 691214c..91a2b2f 100644 --- a/uv.lock +++ b/uv.lock @@ -445,6 +445,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] +[[package]] +name = "deepmerge" +version = "2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/3a/b0ba594708f1ad0bc735884b3ad854d3ca3bdc1d741e56e40bbda6263499/deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20", size = 19890, upload-time = "2024-08-30T05:31:50.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/82/e5d2c1c67d19841e9edc74954c827444ae826978499bde3dfc1d007c8c11/deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00", size = 13475, upload-time = "2024-08-30T05:31:48.659Z" }, +] + [[package]] name = "e3nn" version = "0.4.4" @@ -1016,6 +1025,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/53/7d7b1de354a43e11cb19b370f81833f078f931aff4555b24e869013eea1a/mace_torch-0.3.14-py3-none-any.whl", hash = "sha256:9a7a00a79b3fa3448a13961e2a4af616ff10466fa193aeb4579dc83be9da1b00", size = 237087, upload-time = "2025-08-06T17:30:11.377Z" }, ] +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -2148,6 +2166,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "10.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/63/06673d1eb6d8f83c0ea1f677d770e12565fb516928b4109c9e2055656a9e/pymdown_extensions-10.21.tar.gz", hash = "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5", size = 853363, upload-time = "2026-02-15T20:44:06.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl", hash = "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f", size = 268877, upload-time = "2026-02-15T20:44:05.464Z" }, +] + [[package]] name = "pyparsing" version = "3.3.1" @@ -2281,7 +2312,7 @@ wheels = [ [[package]] name = "rootstock" -version = "0.6.1" +version = "0.7.0" source = { editable = "." } dependencies = [ { name = "ase" }, @@ -2295,6 +2326,7 @@ dependencies = [ dev = [ { name = "pytest" }, { name = "ruff" }, + { name = "zensical" }, ] mace = [ { name = "mace-torch" }, @@ -2315,6 +2347,7 @@ requires-dist = [ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1" }, { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, { name = "torch", marker = "extra == 'mace'", specifier = ">=2.0" }, + { name = "zensical", marker = "extra == 'dev'", specifier = ">=0.0.27" }, ] provides-extras = ["dev", "modal", "mace"] @@ -3004,3 +3037,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] + +[[package]] +name = "zensical" +version = "0.0.27" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "deepmerge" }, + { name = "markdown" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/83/969152d927b522a0fed1f20b1730575d86b920ce51530b669d9fad4537de/zensical-0.0.27.tar.gz", hash = "sha256:6d8d74aba4a9f9505e6ba1c43d4c828ba4ff7bb1ff9b005e5174c5b92cf23419", size = 3841776, upload-time = "2026-03-13T17:56:14.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/fe/0335f1a521eb6c0ab96028bf67148390eb1d5c742c23e6a4b0f8381508bd/zensical-0.0.27-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d51ebf4b038f3eea99fd337119b99d92ad92bbe674372d5262e6dbbabbe4e9b5", size = 12262017, upload-time = "2026-03-13T17:55:36.403Z" }, + { url = "https://files.pythonhosted.org/packages/02/cb/ac24334fc7959b49496c97cb9d2bed82a8db8b84eafaf68189048e7fe69a/zensical-0.0.27-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a627cd4599cf2c5a5a5205f0510667227d1fe4579b6f7445adba2d84bab9fbc8", size = 12147361, upload-time = "2026-03-13T17:55:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/31c981f61006fdaf0460d15bde1248a045178d67307bad61a4588414855d/zensical-0.0.27-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99cbc493022f8749504ef10c71772d360b705b4e2fd1511421393157d07bdccf", size = 12505771, upload-time = "2026-03-13T17:55:42.993Z" }, + { url = "https://files.pythonhosted.org/packages/30/1e/f6842c94ec89e5e9184f407dbbab2a497b444b28d4fb5b8df631894be896/zensical-0.0.27-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ecc20a85e8a23ad9ab809b2f268111321be7b2e214021b3b00f138936a87a434", size = 12455689, upload-time = "2026-03-13T17:55:46.055Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ad/866c3336381cca7528e792469958fbe2e65b9206a2657bef3dd8ed4ac88b/zensical-0.0.27-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da11e0f0861dbd7d3b5e6fe1e3a53b361b2181c53f3abe9fb4cdf2ed0cea47bf", size = 12791263, upload-time = "2026-03-13T17:55:49.193Z" }, + { url = "https://files.pythonhosted.org/packages/e5/df/fca5ed6bebdb61aa656dfa65cce4b4d03324a79c75857728230872fbdf7c/zensical-0.0.27-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e11d220181477040a4b22bf2b8678d5b0c878e7aae194fad4133561cb976d69", size = 12549796, upload-time = "2026-03-13T17:55:52.55Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e2/43398b5ec64ed78204a5a5929a3990769fc0f6a3094a30395882bda1399a/zensical-0.0.27-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06b9e308aec8c5db1cd623e2e98e1b25c3f5cab6b25fcc9bac1e16c0c2b93837", size = 12683568, upload-time = "2026-03-13T17:55:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/b3/3c/5c98f9964c7e30735aacd22a389dacec12bcc5bc8162c58e76b76d20db6e/zensical-0.0.27-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:682085155126965b091cb9f915cd2e4297383ac500122fd4b632cf4511733eb2", size = 12725214, upload-time = "2026-03-13T17:55:59.286Z" }, + { url = "https://files.pythonhosted.org/packages/50/0f/ebaa159cac6d64b53bf7134420c2b43399acc7096cb79795be4fb10768fc/zensical-0.0.27-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:b367c285157c8e1099ae9e2b36564e07d3124bf891e96194a093bc836f3058d2", size = 12860416, upload-time = "2026-03-13T17:56:02.456Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/d82bfccbf5a1f43256dbc4d1984e398035a65f84f7c1e48b69ba15ea7281/zensical-0.0.27-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:847c881209e65e1db1291c59a9db77966ac50f7c66bf9a733c3c7832144dbfca", size = 12819533, upload-time = "2026-03-13T17:56:05.487Z" }, + { url = "https://files.pythonhosted.org/packages/4d/1f/d25e421d91f063a9404c59dd032f65a67c7c700e9f5f40436ab98e533482/zensical-0.0.27-cp310-abi3-win32.whl", hash = "sha256:f31ec13c700794be3f9c0b7d90f09a7d23575a3a27c464994b9bb441a22d880b", size = 11862822, upload-time = "2026-03-13T17:56:08.933Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b5/5b86d126fcc42b96c5dbecde5074d6ea766a1a884e3b25b3524843c5e6a5/zensical-0.0.27-cp310-abi3-win_amd64.whl", hash = "sha256:9d3b1fca7ea99a7b2a8db272dd7f7839587c4ebf4f56b84ff01c97b3893ec9f8", size = 12059658, upload-time = "2026-03-13T17:56:11.859Z" }, +] diff --git a/zensical.toml b/zensical.toml new file mode 100644 index 0000000..e8b63fe --- /dev/null +++ b/zensical.toml @@ -0,0 +1,319 @@ +# ============================================================================ +# +# The configuration produced by default is meant to highlight the features +# that Zensical provides and to serve as a starting point for your own +# projects. +# +# ============================================================================ + +[project] + +# The site_name is shown in the page header and the browser window title +# +# Read more: https://zensical.org/docs/setup/basics/#site_name +site_name = "Rootstock Documentation" + +# The site_description is included in the HTML head and should contain a +# meaningful description of the site content for use by search engines. +# +# Read more: https://zensical.org/docs/setup/basics/#site_description +site_description = "Documentation for the Rootstock package." + +# The site_author attribute. This is used in the HTML head element. +# +# Read more: https://zensical.org/docs/setup/basics/#site_author +site_author = "Hayden Holbrook" + +# The site_url is the canonical URL for your site. When building online +# documentation you should set this. +# Read more: https://zensical.org/docs/setup/basics/#site_url +#site_url = "https://www.example.com/" + +# The copyright notice appears in the page footer and can contain an HTML +# fragment. +# +# Read more: https://zensical.org/docs/setup/basics/#copyright +copyright = """ +Copyright © 2026 The authors +""" + +# Zensical supports both implicit navigation and explicitly defined navigation. +# If you decide not to define a navigation here then Zensical will simply +# derive the navigation structure from the directory structure of your +# "docs_dir". The definition below demonstrates how a navigation structure +# can be defined using TOML syntax. +# +# Read more: https://zensical.org/docs/setup/navigation/ +nav = [ + { "Home" = "index.md" }, + { "Installation" = "installation.md" }, + { "Example Configs" = "clusters.md" }, + { "LAMMPS Integration" = "lammps.md" }, + { "Cluster Setup" = "cluster-setup.md" }, + { "Writing Environments" = "environments.md" }, + { "API Reference" = "api.md" }, + { "Architecture" = "architecture.md" }, + { "Development" = "development.md" }, +] + +# With the "extra_css" option you can add your own CSS styling to customize +# your Zensical project according to your needs. You can add any number of +# CSS files. +# +# The path provided should be relative to the "docs_dir". +# +# Read more: https://zensical.org/docs/customization/#additional-css +# +#extra_css = ["stylesheets/extra.css"] + +# With the `extra_javascript` option you can add your own JavaScript to your +# project to customize the behavior according to your needs. +# +# The path provided should be relative to the "docs_dir". +# +# Read more: https://zensical.org/docs/customization/#additional-javascript +#extra_javascript = ["javascripts/extra.js"] + +# ---------------------------------------------------------------------------- +# Section for configuring theme options +# ---------------------------------------------------------------------------- +[project.theme] + +# change this to "classic" to use the traditional Material for MkDocs look. +#variant = "classic" + +# Zensical allows you to override specific blocks, partials, or whole +# templates as well as to define your own templates. To do this, uncomment +# the custom_dir setting below and set it to a directory in which you +# keep your template overrides. +# +# Read more: +# - https://zensical.org/docs/customization/#extending-the-theme +# +#custom_dir = "overrides" + +# With the "favicon" option you can set your own image to use as the icon +# browsers will use in the browser title bar or tab bar. The path provided +# must be relative to the "docs_dir". +# +# Read more: +# - https://zensical.org/docs/setup/logo-and-icons/#favicon +# - https://developer.mozilla.org/en-US/docs/Glossary/Favicon +# +#favicon = "images/favicon.png" + +# Zensical supports more than 60 different languages. This means that the +# labels and tooltips that Zensical's templates produce are translated. +# The "language" option allows you to set the language used. This language +# is also indicated in the HTML head element to help with accessibility +# and guide search engines and translation tools. +# +# The default language is "en" (English). It is possible to create +# sites with multiple languages and configure a language selector. See +# the documentation for details. +# +# Read more: +# - https://zensical.org/docs/setup/language/ +# +language = "en" + +# Zensical provides a number of feature toggles that change the behavior +# of the documentation site. +features = [ + # Zensical includes an announcement bar. This feature allows users to + # dismiss it when they have read the announcement. + # https://zensical.org/docs/setup/header/#announcement-bar + "announce.dismiss", + + # If you have a repository configured and turn on this feature, Zensical + # will generate an edit button for the page. This works for common + # repository hosting services. + # https://zensical.org/docs/setup/repository/#content-actions + "content.action.edit", + + # If you have a repository configured and turn on this feature, Zensical + # will generate a button that allows the user to view the Markdown + # code for the current page. + # https://zensical.org/docs/setup/repository/#content-actions + #"content.action.view", + + # Code annotations allow you to add an icon with a tooltip to your + # code blocks to provide explanations at crucial points. + # https://zensical.org/docs/authoring/code-blocks/#code-annotations + "content.code.annotate", + + # This feature turns on a button in code blocks that allow users to + # copy the content to their clipboard without first selecting it. + # https://zensical.org/docs/authoring/code-blocks/#code-copy-button + "content.code.copy", + + # Code blocks can include a button to allow for the selection of line + # ranges by the user. + # https://zensical.org/docs/authoring/code-blocks/#code-selection-button + "content.code.select", + + # Zensical can render footnotes as inline tooltips, so the user can read + # the footnote without leaving the context of the document. + # https://zensical.org/docs/authoring/footnotes/#footnote-tooltips + "content.footnote.tooltips", + + # If you have many content tabs that have the same titles (e.g., "Python", + # "JavaScript", "Cobol"), this feature causes all of them to switch to + # at the same time when the user chooses their language in one. + # https://zensical.org/docs/authoring/content-tabs/#linked-content-tabs + "content.tabs.link", + + # With this feature enabled users can add tooltips to links that will be + # displayed when the mouse pointer hovers the link. + # https://zensical.org/docs/authoring/tooltips/#improved-tooltips + "content.tooltips", + + # With this feature enabled, Zensical will automatically hide parts + # of the header when the user scrolls past a certain point. + # https://zensical.org/docs/setup/header/#automatic-hiding + # "header.autohide", + + # Turn on this feature to expand all collapsible sections in the + # navigation sidebar by default. + # https://zensical.org/docs/setup/navigation/#navigation-expansion + # "navigation.expand", + + # This feature turns on navigation elements in the footer that allow the + # user to navigate to a next or previous page. + # https://zensical.org/docs/setup/footer/#navigation + "navigation.footer", + + # When section index pages are enabled, documents can be directly attached + # to sections, which is particularly useful for providing overview pages. + # https://zensical.org/docs/setup/navigation/#section-index-pages + "navigation.indexes", + + # When instant navigation is enabled, clicks on all internal links will be + # intercepted and dispatched via XHR without fully reloading the page. + # https://zensical.org/docs/setup/navigation/#instant-navigation + "navigation.instant", + + # With instant prefetching, your site will start to fetch a page once the + # user hovers over a link. This will reduce the perceived loading time + # for the user. + # https://zensical.org/docs/setup/navigation/#instant-prefetching + "navigation.instant.prefetch", + + # In order to provide a better user experience on slow connections when + # using instant navigation, a progress indicator can be enabled. + # https://zensical.org/docs/setup/navigation/#progress-indicator + #"navigation.instant.progress", + + # When navigation paths are activated, a breadcrumb navigation is rendered + # above the title of each page + # https://zensical.org/docs/setup/navigation/#navigation-path + "navigation.path", + + # When pruning is enabled, only the visible navigation items are included + # in the rendered HTML, reducing the size of the built site by 33% or more. + # https://zensical.org/docs/setup/navigation/#navigation-pruning + #"navigation.prune", + + # When sections are enabled, top-level sections are rendered as groups in + # the sidebar for viewports above 1220px, but remain as-is on mobile. + # https://zensical.org/docs/setup/navigation/#navigation-sections + "navigation.sections", + + # When tabs are enabled, top-level sections are rendered in a menu layer + # below the header for viewports above 1220px, but remain as-is on mobile. + # https://zensical.org/docs/setup/navigation/#navigation-tabs + #"navigation.tabs", + + # When sticky tabs are enabled, navigation tabs will lock below the header + # and always remain visible when scrolling down. + # https://zensical.org/docs/setup/navigation/#sticky-navigation-tabs + #"navigation.tabs.sticky", + + # A back-to-top button can be shown when the user, after scrolling down, + # starts to scroll up again. + # https://zensical.org/docs/setup/navigation/#back-to-top-button + "navigation.top", + + # When anchor tracking is enabled, the URL in the address bar is + # automatically updated with the active anchor as highlighted in the table + # of contents. + # https://zensical.org/docs/setup/navigation/#anchor-tracking + "navigation.tracking", + + # When search highlighting is enabled and a user clicks on a search result, + # Zensical will highlight all occurrences after following the link. + # https://zensical.org/docs/setup/search/#search-highlighting + "search.highlight", + + # When anchor following for the table of contents is enabled, the sidebar + # is automatically scrolled so that the active anchor is always visible. + # https://zensical.org/docs/setup/navigation/#anchor-following + # "toc.follow", + + # When navigation integration for the table of contents is enabled, it is + # always rendered as part of the navigation sidebar on the left. + # https://zensical.org/docs/setup/navigation/#navigation-integration + #"toc.integrate", +] + +# ---------------------------------------------------------------------------- +# You can configure your own logo to be shown in the header using the "logo" +# option in the "theme" subsection. The logo must be a relative path to a file +# in your "docs_dir", e.g., to use `docs/assets/logo.png` you would set: +# ---------------------------------------------------------------------------- +#logo = "assets/logo.png" + +# ---------------------------------------------------------------------------- +# If you don't have a dedicated project logo, you can use a built-in icon from +# the icon sets shipped in Zensical. Please note that the setting lives in a +# different subsection, and that the above take precedence over the icon. +# +# Read more: +# - https://zensical.org/docs/setup/logo-and-icons +# - https://github.com/zensical/ui/tree/master/dist/.icons +# ---------------------------------------------------------------------------- +#[project.theme.icon] +#logo = "lucide/smile" + +# ---------------------------------------------------------------------------- +# In the "font" subsection you can configure the fonts used. By default, fonts +# are loaded from Google Fonts, giving you a wide range of choices from a set +# of suitably licensed fonts. There are options for a normal text font and for +# a monospaced font used in code blocks. +# ---------------------------------------------------------------------------- +#[project.theme.font] +#text = "Inter" +#code = "Jetbrains Mono" + +# ---------------------------------------------------------------------------- +# In the "palette" subsection you can configure options for the color scheme. +# You can configure different color schemes, e.g., to turn on dark mode, +# that the user can switch between. Each color scheme can be further +# customized. +# +# Read more: +# - https://zensical.org/docs/setup/colors/ +# ---------------------------------------------------------------------------- +[[project.theme.palette]] +scheme = "default" +toggle.icon = "lucide/sun" +toggle.name = "Switch to dark mode" + +[[project.theme.palette]] +scheme = "slate" +toggle.icon = "lucide/moon" +toggle.name = "Switch to light mode" + +# ---------------------------------------------------------------------------- +# Repository configuration for edit links +# ---------------------------------------------------------------------------- +[project.repo] +url = "https://github.com/Garden-AI/rootstock" +name = "GitHub" + +# ---------------------------------------------------------------------------- +# The "extra" section contains miscellaneous settings. +# ---------------------------------------------------------------------------- +[[project.extra.social]] +icon = "fontawesome/brands/github" +link = "https://github.com/Garden-AI/rootstock"