Skip to main content

Atoms

The Atomiverse SDK provides an Atoms class that extends ASE's Atoms with convenience methods for building 3D structures from SMILES strings and for converting existing ASE structures into the Atomiverse subclass.

Because Atoms is a subclass of ase.Atoms, you can pass it to any function that expects an ASE Atoms — including Atomiverse jobs, ASE calculators, and ASE I/O utilities.

Convert Existing ASE Objects

Use from_ase_atoms() when you already have a plain ase.Atoms instance and want Atomiverse-specific helpers such as visualize():

from ase import Atoms as ASEAtoms
from atomiverse import Atoms

ase_atoms = ASEAtoms(
"H2O",
positions=[
[0.0, 0.0, 0.0],
[0.0, 0.757, 0.587],
[0.0, -0.757, 0.587],
],
)

water = Atoms.from_ase_atoms(ase_atoms)
print(type(water).__name__)
# Atoms

from_ase_atoms() preserves the source object's geometry, cell, periodic boundary conditions, metadata, custom arrays, constraints, and attached calculator.

Single Molecules

from atomiverse import Atoms

water = Atoms.from_smiles("O")

print(water.get_chemical_formula())
# H2O

print(len(water))
# 3 (one oxygen, two hydrogens)

from_smiles() automatically detects and stores the total charge and spin multiplicity derived from the SMILES notation in atoms.info["charge"] and atoms.info["multiplicity"]. For example, Atoms.from_smiles("[OH-]") produces an object where atoms.info["charge"] == -1.

Pass charge and multiplicity explicitly to jobs

Jobs such as SinglePointEnergy and Optimization do not read charge and multiplicity from atoms.info automatically — they default to charge=0 and multiplicity=1. Always forward the stored values when submitting a calculation to avoid silent errors. Using .get() with defaults is good practice when atoms may come from sources other than from_smiles():

hydroxide = Atoms.from_smiles("[OH-]")

job = SinglePointEnergy(
atoms=hydroxide,
level_of_theory=GFN2_XTB,
charge=hydroxide.info.get("charge", 0),
multiplicity=hydroxide.info.get("multiplicity", 1),
)

Resolve and Load a Molecule

Use from_molecule_resolve() when you want to go straight from a common name or external identifier to an Atoms object.

from atomiverse import Atoms

aspirin = Atoms.from_molecule_resolve("aspirin")
chembl = Atoms.from_molecule_resolve("CHEMBL25", source="chembl")

This method resolves the query, takes the best available match, and returns it as an Atomiverse Atoms object with the imported molecule provenance attached in atoms.info["atomiverse_resolution"].

Supported source values are:

  • auto
  • pubchem
  • chembl

If the query does not produce a single clear match, the method raises the same resolution exceptions as resolve_molecule().

Multi-Molecule Systems

Use a dot (.) in the SMILES string to include several molecules in the same structure. Each dot-separated component becomes a separate molecule within a single Atoms object:

from atomiverse import Atoms

# Two water molecules in one system
two_waters = Atoms.from_smiles("O.O")

# Sodium chloride ion pair
nacl = Atoms.from_smiles("[Na+].[Cl-]")

# Reactant complex with explicit arrangement
complex = Atoms.from_smiles("CC=O.N")

This is useful when you need to model intermolecular interactions, solvation shells, or reaction complexes where the molecules are not covalently bonded.

Reaction SMILES

Reaction SMILES use >> to separate reactants from products. from_reaction_smiles returns two Atoms objects — one for each side:

from atomiverse import Atoms

reactant, product = Atoms.from_reaction_smiles("C=C>>CC")

print(reactant.get_chemical_formula())
# C2H4
print(product.get_chemical_formula())
# C2H6

This pairs well with the reaction workflow tools in Run Mode for transition state searches.

Visualize in the Atomiverse Viewer

You can open any atomiverse.Atoms object directly in the web viewer:

from atomiverse import Atoms

water = Atoms.from_smiles("O")

viewer_url = water.visualize()
print(viewer_url)

visualize() creates a share-state URL for the structure. In a live Jupyter notebook, visualize() displays the interactive embedded viewer inline by default and still returns the share URL as a string. Outside notebooks, it keeps the browser-tab behavior. If display is not available (for example on a headless machine), the SDK prints the link and still returns the URL.

The viewer's molecule information panel also displays structure metadata such as formula, SMILES, charge, multiplicity, point group, and symmetry number when available.

To control the behavior explicitly:

viewer_url = water.visualize(mode="browser")   # force new-tab behavior
viewer_url = water.visualize(mode="notebook") # force inline notebook embed
viewer_url = water.visualize(mode="link") # only return/print the link

If you want to skip browser launching entirely:

viewer_url = water.visualize(mode="link")

To save the same PNG render used by the Atomiverse frontend viewer:

water.visualize("water.png")
water.visualize("water@2x.png", dpi=192)

The output path must end in .png. Use dpi= for higher-resolution exports.

open_browser= is still accepted as a temporary compatibility alias, but mode= is the preferred public API.

Share-link security

Viewer links created by visualize() are share links. Anyone with the URL can open that structure in the viewer. Treat these links as sensitive when working with private structures.

You can also go the other direction: take a share link created in the web viewer (or returned by Atoms.visualize()) and load it into the Python SDK:

from atomiverse import Atoms

atoms = Atoms.from_link(
"https://atomiverse.com/viewer?share=<your-share-id>"
)

from_link() accepts either the full viewer URL or the raw 64-character share ID. If the share link points to a trajectory, the SDK loads the last frame.

Putting It Together

Once you have an Atoms object, pass it directly to a job:

import atomiverse
from atomiverse import Atoms, JobFailedError, SinglePointEnergy
from atomiverse.levels import GFN2_XTB

atomiverse.configure(api_key="your-api-key")

water = Atoms.from_smiles("O")

job = SinglePointEnergy(
atoms=water,
level_of_theory=GFN2_XTB,
)

job.submit()

try:
print(job.require_result().energy)
except JobFailedError as exc:
print(exc.failure.error)

See Single Point Energy for the full walkthrough.