Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2e23dd1
feat: API with SLURM support
ltalirz Feb 8, 2026
c4cacce
fix tests
ltalirz Feb 8, 2026
7793026
chore: clean up api tests
ltalirz Feb 8, 2026
a912df7
get API to actually work
ltalirz Feb 8, 2026
abd8266
fix workflow setup
ltalirz Feb 8, 2026
081ba24
simplify
ltalirz Feb 8, 2026
7eee40f
fix api tests
ltalirz Feb 8, 2026
d5d092f
try fixing api integration test
ltalirz Feb 8, 2026
9e85497
add response model
ltalirz Feb 8, 2026
38e3043
cleanup
ltalirz Feb 8, 2026
b1b0fb9
fix: Use executorlib with context (#122)
jan-janssen Feb 9, 2026
c265e98
shut down sqlite connection
ltalirz Feb 9, 2026
94cda98
fix test logfile warning
ltalirz Feb 9, 2026
6ad56bf
fix test
ltalirz Feb 9, 2026
14178f3
do not block event loop
ltalirz Feb 9, 2026
63c89ae
fix api unit tests
ltalirz Feb 9, 2026
6af3546
Test with flux (#124)
jan-janssen Feb 9, 2026
ae84d20
fix: back to multi-connection task store
ltalirz Feb 9, 2026
2367fe3
put back gitignore
ltalirz Feb 9, 2026
220fba0
bring back api
ltalirz Feb 9, 2026
4bac361
move stuff around
ltalirz Feb 9, 2026
34df6a9
fix: bring back /check
ltalirz Feb 9, 2026
15f5805
fix: fail integration test
ltalirz Feb 9, 2026
359bbc5
fix yaml string format
ltalirz Feb 9, 2026
46f51d4
try
ltalirz Feb 9, 2026
735ac99
fix: move away from context manager
ltalirz Feb 13, 2026
53d6265
fix: drop wait=False from executor startup
ltalirz Feb 13, 2026
aa0ac52
fix: use testclusterexecutor on CI
ltalirz Feb 13, 2026
c3b74a0
fix import
ltalirz Feb 13, 2026
41a7bed
fix bogus submission logic
ltalirz Feb 13, 2026
0a6893c
chore: simplify logic
ltalirz Feb 13, 2026
4db69e7
fix broken api unit tests
ltalirz Feb 13, 2026
14cfb90
fix
ltalirz Feb 13, 2026
a0745df
fix future resolution
ltalirz Feb 13, 2026
51be467
bump executorlib
ltalirz Feb 14, 2026
68a5918
feat: use get_future_from_cache
ltalirz Feb 15, 2026
1ae065f
delete wrong test
ltalirz Feb 15, 2026
5746ec3
fix
ltalirz Feb 15, 2026
8289817
Revert "fix"
ltalirz Feb 16, 2026
0dc6ac0
move to flux for integration
ltalirz Feb 16, 2026
d480fe5
try2
ltalirz Feb 16, 2026
4cd6554
add pysqa
ltalirz Feb 16, 2026
3dcd4fb
fix: do not touch future after executor shutdown
ltalirz Feb 18, 2026
49a8be2
fix warning in integration test
ltalirz Feb 18, 2026
138460c
switch to lifetime management
ltalirz Feb 18, 2026
0f988df
reduce code duplication
ltalirz Feb 18, 2026
09fe51f
drop executor from /check
ltalirz Feb 18, 2026
ab14f8e
always set error
ltalirz Feb 18, 2026
44e3d92
Merge branch 'main' into feat/api-slurm
ltalirz Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/amorphouspy_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,21 @@ jobs:
shell: bash -l {0}
working-directory: amorphouspy_api
run: |
amorphouspy_INTEGRATION=1 uvicorn amorphouspy_api.app:app --port 8002 &
pytest -m integration -s --durations=0 --cov=src/amorphouspy_api --cov-report=xml --cov-report=term --cov-append
cat > test.sh << 'EOF'
#!/bin/bash
uvicorn amorphouspy_api.app:app --port 8002 &
pytest -m integration -s \
--durations=0 \
--cov=src/amorphouspy_api \
--cov-report=xml \
--cov-report=term \
--cov-append
EOF
chmod +x test.sh
flux start ./test.sh
env:
AMORPHOUSPY_INTEGRATION: "1"
EXECUTOR_TYPE: "flux"

- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def compute_angles(
>>> bins, hist = compute_angles(structure, center_type=1, neighbor_type=2, cutoff=3.0)

"""
ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
_ids, types, coords, box_size = get_properties_for_structure_analysis(structure)

neighbors = get_neighbors(
coords,
Expand Down
2 changes: 1 addition & 1 deletion amorphouspy/src/amorphouspy/analysis/cavities.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def compute_cavities(

"""
# Extract properties using the provided helper
ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
_ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
type_dict = type_to_dict(types)

# Use a context manager to ensure the temporary file is cleaned up
Expand Down
2 changes: 1 addition & 1 deletion amorphouspy/src/amorphouspy/analysis/cte.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def cte_from_volume_temperature_data(
volume = np.array(volume)[sorted_indices]

# fit and calculate CTE
slope, intercept = np.polyfit(temperature, volume, 1)
slope, _intercept = np.polyfit(temperature, volume, 1)
CTE = slope / volume[0]

return float(CTE)
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def compute_rdf(
>>> r, rdfs, cn = compute_rdf(structure, r_max=10.0, n_bins=500)

"""
ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
_ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
# Input validation and type conversion
coords = np.asarray(coords, dtype=np.float64)
types = np.asarray(types, dtype=np.int64)
Expand Down
2 changes: 1 addition & 1 deletion amorphouspy/src/amorphouspy/analysis/rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def compute_guttmann_rings(
... )

"""
ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
_ids, types, coords, box_size = get_properties_for_structure_analysis(structure)
type_dict = type_to_dict(types)
with tempfile.NamedTemporaryFile("w+", suffix=".xyz", delete=True) as tmp:
write_xyz(filename=tmp.name, coords=coords, types=types, box_size=box_size, type_dict=type_dict)
Expand Down
2 changes: 1 addition & 1 deletion amorphouspy/src/amorphouspy/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def _integer_fu_from_total(Nfu_target: int, mol_frac: dict[str, float]) -> dict[
n = {ox: int(np.floor(w[ox])) for ox in x}
rem = Nfu_target - sum(n.values())
if rem > 0:
order = sorted(x.keys(), key=lambda k: (w[k] - n[k]), reverse=True)
order = sorted(x.keys(), key=lambda k: w[k] - n[k], reverse=True)
for i in range(rem):
n[order[i % len(order)]] += 1
return n
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def analyze_structure(atoms: Atoms) -> StructureData: # noqa: C901, PLR0912, PL
total_mass_g = atoms.get_masses().sum() / avogadro_number # Convert amu to g
density = total_mass_g / volume_cm3

type_map, network_formers, modifiers, oxygen_present = _classify_elements(unique_z)
type_map, network_formers, modifiers, _oxygen_present = _classify_elements(unique_z)
former_types = [z for z, sym in type_map.items() if sym in network_formers]
modifier_types = [z for z, sym in type_map.items() if sym in modifiers]
O_type = [z for z, sym in type_map.items() if sym == "O"]
Expand Down
2 changes: 1 addition & 1 deletion amorphouspy/src/amorphouspy/workflows/viscosity.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def viscosity_simulation(
)

# Stage 2: Production simulation for viscosity at T
structure_final, parsed_output = _run_lammps_md(
_structure_final, parsed_output = _run_lammps_md(
structure=structure1,
potential=potential,
tmp_working_directory=tmp_working_directory,
Expand Down
4 changes: 2 additions & 2 deletions amorphouspy/src/tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_structure_atom_counts_molar() -> None:
assert atom_counts[elem] == expected, f"{elem} atoms should be {expected} for {n_molecules} mode."

# Test with target_atoms
atoms, atom_counts = ps.create_random_atoms(
_atoms, atom_counts = ps.create_random_atoms(
composition=composition,
n_molecules=None,
target_atoms=target_atoms,
Expand Down Expand Up @@ -110,7 +110,7 @@ def test_structure_atom_counts_weight() -> None:
assert atom_counts[elem] == expected, f"{elem} atoms should be {expected} for {n_molecules} mode."

# Test with target_atoms
atoms, atom_counts = ps.create_random_atoms(
_atoms, atom_counts = ps.create_random_atoms(
composition=weight_composition,
n_molecules=None,
target_atoms=target_atoms,
Expand Down
29 changes: 20 additions & 9 deletions amorphouspy_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ This FastAPI-based service provides a Model Context Protocol (MCP) interface for

```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ FastAPI App │ ── │ SQLite Cache │ ── │ Worker Process
│ FastAPI App │ ── │ SQLite Cache │ ── │ executorlib
│ │ │ │ │ │
│ • Request hash │ │ • Task metadata │ │ • amorphouspy
│ • Cache lookup │ │ • Results │ │ • LAMMPS sims
│ • Task creation │ │ • Hash index │ │ • File cleanup
│ • Request hash │ │ • Task metadata │ │ • Local exec
│ • Cache lookup │ │ • Results │ │ • SLURM cluster
│ • Task creation │ │ • Hash index │ │ • Job caching
└─────────────────┘ └─────────────────┘ └─────────────────┘
```

Expand All @@ -32,17 +32,28 @@ This FastAPI-based service provides a Model Context Protocol (MCP) interface for
- Tracks task states: `processing` → `complete`/`error`
- Survives server restarts and process crashes

#### 3. **Async Processing with Process Isolation**
- Uses `ProcessPoolExecutor` to run simulations in separate processes
- Avoids blocking the FastAPI event loop
- Proper signal handling for subprocess management
- Automatic temporary file cleanup using `tempfile.TemporaryDirectory()`
#### 3. **Job Execution with executorlib**
- Supports local execution (`SingleNodeExecutor`) or SLURM cluster (`SlurmClusterExecutor`)
- Executor type configured via environment variables
- Built-in job caching at the executor level
- Re-submitting same job returns cached result or running future

#### 4. **Model Context Protocol (MCP) Integration**
- Exposes simulation capabilities as MCP tools
- Compatible with Claude, VS Code, and other MCP clients
- Server-Sent Events (SSE) endpoint at `/mcp`

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `EXECUTOR_TYPE` | Executor backend: `local` or `slurm` | `local` |
| `EXECUTOR_CORES` | Number of CPU cores per worker | `4` |
| `SLURM_PARTITION` | SLURM partition name (slurm only) | - |
| `SLURM_TIME` | SLURM job time limit (slurm only) | - |
| `AMORPHOUSPY_PROJECTS` | Directory for project/cache files | `./projects` |
| `API_BASE_URL` | Base URL for visualization links | - |


## Installation

Expand Down
22 changes: 22 additions & 0 deletions amorphouspy_api/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Shared test fixtures for amorphouspy_api tests."""

from pathlib import Path

import pytest

from amorphouspy_api.database import close_task_store, init_task_store


@pytest.fixture(autouse=True)
def _fresh_task_store(tmp_path: Path) -> None:
"""Provide a fresh temporary task store for every test.

This ensures tests are isolated from each other and from any
persistent database left over from previous runs.
"""
# Re-initialise the singleton so every call to get_task_store()
# (in routers, visualization, tests, …) returns the fresh instance.
db_path = tmp_path / "test_tasks.db"
init_task_store(db_path)
yield
close_task_store()
2 changes: 1 addition & 1 deletion amorphouspy_api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ markers = [
addopts = ["-m", "not integration"]
filterwarnings = [
"ignore::DeprecationWarning:defusedxml.*",
"ignore:.*__get_pydantic_core_schema__.*",
"ignore::pydantic.PydanticDeprecatedSince211",
"ignore:.*multi-threaded.*fork.*",
]

Expand Down
Loading
Loading