Examples#
Use the Quickstart Guide to launch the NIM before running the following examples.
For the request format and required atomic data fields, refer to API Reference.
Install the Python libraries needed for the examples on this page:
pip install requests ase numpy aiohttp
Using ASE for Structure I/O#
Use the NIM for BGR to process a chemistry dataset by combining it with the I/O
capabilities from the Atomic Simulation Environment (ASE). With a file
structures.extxyz containing multiple structures in ASE
extended XYZ format,
and NIM for BGR running at http://localhost:8000/v1/infer, the
following script runs batched optimization. Refer to the example
structures.extxyz file.
Important
For periodic systems, ASE.Atoms objects must have cell and pbc attributes defined.
For isolated molecules, omit the cell field or set pbc to false.
The following Python script reads structures from structures.extxyz,
performs batched optimization, and produces a structures_opt.extxyz file with
optimized structures.
import requests
import ase.io
import numpy as np
EXTXYZ_FILE = "structures.extxyz"
INFER_URL = "http://localhost:8000/v1/infer"
# Read atoms from file
atoms_list = ase.io.read(EXTXYZ_FILE, index=":")
if not isinstance(atoms_list, list):
atoms_list = [atoms_list]
# Convert ASE Atoms to API format
def ase_to_api(atoms, structure_id=None):
data = {
'coord': atoms.positions.flatten().tolist(),
'numbers': atoms.numbers.tolist(),
'charge': atoms.info.get('charge', 0),
'mult': atoms.info.get('mult', 1),
}
if atoms.cell.volume > 0:
data['cell'] = atoms.cell.array.flatten().tolist()
data['pbc'] = atoms.pbc.tolist()
if structure_id:
data['structure_id'] = structure_id
return data
# Tag structures with initial index for tracking
atoms_data = [ase_to_api(a, f"struct_{i}") for i, a in enumerate(atoms_list)]
# Create input request
input_data = {'atoms': atoms_data}
# Submit request and wait for results
response = requests.post(
INFER_URL,
json=input_data,
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
result = response.json()
optimized = result['atoms']
# Convert results back to ASE and write
optimized_atoms = []
for opt in optimized:
a = ase.Atoms(
positions=np.array(opt['coord']).reshape(-1, 3),
numbers=opt['numbers']
)
if opt.get('cell'):
a.set_cell(np.array(opt['cell']).reshape(3, 3))
if opt.get('pbc'):
a.set_pbc(opt['pbc'])
a.info['energy'] = opt['energy']
a.info['converged'] = opt['converged']
a.info['optimizer_nsteps'] = opt['optimizer_nsteps']
a.arrays['forces'] = np.array(opt['forces']).reshape(-1, 3)
optimized_atoms.append(a)
# Check results
not_converged = sum(1 for a in optimized_atoms if not a.info['converged'])
if not_converged > 0:
print(f'Warning: {not_converged} structures were not converged!')
# Write results
ase.io.write('structures_opt.extxyz', optimized_atoms)
print(f"Optimized {len(optimized_atoms)} structures")
Concurrent Requests#
The inference API accepts multiple concurrent requests; the server processes them and returns responses in order. There is no dedicated async/polling API. Achieve concurrent behavior by issuing multiple requests and waiting on the responses.
Concurrent behavior example (Python)
import concurrent.futures
import requests
def run_infer(payload):
return requests.post("http://localhost:8000/v1/infer", json=payload)
# Example: run three inference requests concurrently
payloads = [
{"atoms": [{"coord": [0, 0, 0, 0, 0, 1], "numbers": [1, 1], "cell": [10, 0, 0, 0, 10, 0, 0, 0, 10]}]}
for _ in range(3)
]
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(run_infer, payloads))
for r in results:
r.raise_for_status()
print(r.json()["status"])
Asynchronous example (Python)
Use asyncio with an async HTTP client to
send multiple inference requests without blocking. Install aiohttp
(for example, pip install aiohttp) and run the following:
import asyncio
import aiohttp
async def infer(session, payload):
async with session.post("http://localhost:8000/v1/infer", json=payload) as resp:
resp.raise_for_status()
return await resp.json()
async def main():
payloads = [
{"atoms": [{"coord": [0, 0, 0, 0, 0, 1], "numbers": [1, 1], "cell": [10, 0, 0, 0, 10, 0, 0, 0, 10]}]}
for _ in range(3)
]
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(*[infer(session, p) for p in payloads])
for r in results:
print(r["status"])
asyncio.run(main())
Partial Optimization With Active Masks#
Freeze specific atoms during optimization by using the active_mask field:
import requests
# Optimize only the top two atoms, freeze the bottom one
package = {
'atoms': [{
'coord': [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0],
'numbers': [1, 1, 1],
'cell': [10, 0, 0, 0, 10, 0, 0, 0, 10],
'active_mask': [False, True, True] # Freeze first atom
}],
}
result = requests.post("http://localhost:8000/v1/infer", json=package)
print(result.json())