Solid-State Structures
Visualize crystals, surfaces, and periodic systems with unit cell display.
See the MolecularViewer API reference for the full parameter list.
Live Demo: Cu(111) Surface — Fixed Atoms (FixAtoms Constraint)
Cu(111) 4-layer slab with the bottom 2 layers frozen. Fixed atoms keep their element color and are marked with a semi-transparent yellow overlay.
from ase.build import fcc111
from ase.constraints import FixAtoms
from aseview import MolecularViewer
import numpy as np
slab = fcc111('Cu', size=(3, 3, 4), vacuum=8.0)
# Fix bottom 2 layers
z_coords = slab.get_positions()[:, 2]
z_sorted = np.sort(np.unique(np.round(z_coords, 2)))
fixed_z = set(z_sorted[:2])
fixed_indices = [i for i, pos in enumerate(slab.get_positions())
if round(pos[2], 2) in fixed_z]
slab.set_constraint(FixAtoms(indices=fixed_indices))
viewer = MolecularViewer(slab, style="metallic", showConstraint=True, showCell=True)
viewer.show()
Live Demo: Silicon Crystal
Silicon in diamond structure (2x2x2 supercell):
Live Demo: NaCl Crystal
Sodium chloride in rocksalt structure:
Live Demo: Partial Occupancy And Periodic Polyhedra
The center site below is 60% Fe and 30% Mn, with the unfilled 10% shown as a
fully opaque white vacancy sector rather than an open or transparent gap. Its oxygen
coordination crosses the periodic cell boundary. One-hop bonded atoms starts
off, so the viewer initially stays inside the selected cell. Turn it on to reveal
the boundary-completion atoms, bonds, and full octahedron. Cell-boundary atoms
separately mirrors sites lying exactly on a face, edge, or corner. Replicas added
with Add Periodicity (±x, ±y, or ±z) remain connected to one another.
import numpy as np
from ase import Atoms
from aseview import MolecularViewer
atoms = Atoms(
symbols=["Fe", "O", "O", "O"],
scaled_positions=[
[0.5, 0.5, 0.5],
[0.0, 0.5, 0.5],
[0.5, 0.0, 0.5],
[0.5, 0.5, 0.0],
],
cell=np.eye(3) * 4.0,
pbc=True,
)
atoms.info["occupancy"] = {
"0": {"Fe": 0.6, "Mn": 0.3},
"1": {"O": 1.0},
"2": {"O": 1.0},
"3": {"O": 1.0},
}
atoms.new_array("spacegroup_kinds", np.arange(len(atoms), dtype=int))
viewer = MolecularViewer(
atoms,
showCell=True,
showBond=True,
showPolyhedron=True,
polyhedronColorMode="geometry",
)
viewer.show()
Live Demo: Larger Partial-Occupancy Perovskite
This 2×2×2 perovskite contains 40 representative atoms. Every A site is Ba₀.₅₅Sr₀.₃₅□₀.₁₀, every B site is Ti₀.₇₅Zr₀.₂₅, and each oxygen site is 95% occupied. The default Cartoon view demonstrates outlined partial sectors; switch among all ten Style options to compare their matching mesh or billboard treatment. White sectors always denote unoccupied fractions. Bonds and Ti–O polyhedra start hidden to keep the larger structure legible, and can be enabled from Display Settings and Polyhedron Settings.
import numpy as np
from ase import Atoms
from aseview import MolecularViewer
unit_cell = Atoms(
symbols=["Ba", "Ti", "O", "O", "O"],
scaled_positions=[
[0.0, 0.0, 0.0],
[0.5, 0.5, 0.5],
[0.5, 0.5, 0.0],
[0.5, 0.0, 0.5],
[0.0, 0.5, 0.5],
],
cell=np.eye(3) * 4.02,
pbc=True,
)
atoms = unit_cell.repeat((2, 2, 2))
atoms.info["occupancy"] = {
"0": {"Ba": 0.55, "Sr": 0.35},
"1": {"Ti": 0.75, "Zr": 0.25},
"2": {"O": 0.95},
}
kind_by_symbol = {"Ba": 0, "Ti": 1, "O": 2}
atoms.new_array(
"spacegroup_kinds",
np.array([kind_by_symbol[s] for s in atoms.get_chemical_symbols()]),
)
viewer = MolecularViewer(
atoms,
style="cartoon",
showCell=True,
showBond=False,
showPolyhedron=False,
polyhedronCenterElements=["Ti"],
polyhedronNeighborElements=["O"],
polyhedronColorMode="geometry",
viewDirection=[-1.0, 0.65, -0.35],
viewUp=[0.0, 0.0, 1.0],
)
viewer.show()
Live Demo: Au(111) + H₂O Adsorption
H₂O molecule adsorbing on Au(111) surface - relaxation trajectory with energy plot:
Live Demo: Graphene Phonons
Graphene nanoribbon with phonon normal modes (breathing, ZA, ZO modes):
Live Demo: Carbon Nanotube Vibrations
(5,0) Carbon nanotube with vibrational modes (RBM, longitudinal, G-band):
Live Demo: FCC Copper
Copper FCC crystal (3x3x3 supercell):
Building Crystals
Bulk Structures
Common Crystal Structures
| Structure | ASE Function | Example |
|---|---|---|
| FCC | bulk('Cu', 'fcc', a=3.61) |
Cu, Ag, Au, Al, Ni |
| BCC | bulk('Fe', 'bcc', a=2.87) |
Fe, W, Cr, Mo |
| Diamond | bulk('Si', 'diamond', a=5.43) |
Si, Ge, C |
| Rocksalt | bulk('NaCl', 'rocksalt', a=5.64) |
NaCl, MgO, LiF |
| Zincblende | bulk('GaAs', 'zincblende', a=5.65) |
GaAs, ZnS |
| Wurtzite | bulk('ZnO', 'wurtzite', ...) |
ZnO, GaN |
| HCP | bulk('Mg', 'hcp', a=3.21, c=5.21) |
Mg, Ti, Zn |
Supercells
# Create supercell
atoms = bulk('Si', 'diamond', a=5.43)
supercell = atoms * (3, 3, 3) # 3x3x3 supercell
viewer = MolecularViewer(supercell, showCell=True)
viewer.show()
Surfaces and Slabs
FCC Surfaces
from ase.build import fcc111, fcc100, fcc110
# Au(111) surface - 4 layers, 3x3 cell, 5A vacuum
au111 = fcc111('Au', size=(3, 3, 4), vacuum=5.0)
# Pt(100) surface
pt100 = fcc100('Pt', size=(4, 4, 3), vacuum=6.0)
viewer = MolecularViewer(au111, style="metallic", showCell=True)
viewer.show()
Surface Adsorption Trajectory
from ase.io import read
from aseview import MolecularViewer
# Read relaxation trajectory (e.g., from VASP or ASE optimizer)
traj = read("adsorption_relax.traj", index=":")
# Visualize with energy plot
viewer = MolecularViewer(
traj,
style="metallic",
showCell=True,
showEnergyPlot=True # Shows energy convergence
)
viewer.show()
BCC Surfaces
from ase.build import bcc111, bcc100, bcc110
# Fe(110) surface
fe110 = bcc110('Fe', size=(3, 3, 4), vacuum=5.0)
viewer = MolecularViewer(fe110, showCell=True)
viewer.show()
General Surface
from ase.build import surface
# Create any Miller index surface
atoms = bulk('Cu', 'fcc', a=3.61)
cu_211 = surface(atoms, (2, 1, 1), layers=4, vacuum=5.0)
viewer = MolecularViewer(cu_211, showCell=True)
viewer.show()
Low-Dimensional Materials
Graphene
from ase.build import graphene_nanoribbon
# Zigzag nanoribbon
gnr = graphene_nanoribbon(4, 6, type='zigzag', saturated=True, vacuum=5.0)
# Armchair nanoribbon
gnr_arm = graphene_nanoribbon(4, 6, type='armchair', saturated=True, vacuum=5.0)
viewer = MolecularViewer(gnr, style="cartoon", showCell=True)
viewer.show()
Carbon Nanotubes
from ase.build import nanotube
# (n, m) nanotube indices
cnt_6_0 = nanotube(6, 0, length=4, vacuum=5.0) # Zigzag
cnt_6_6 = nanotube(6, 6, length=4, vacuum=5.0) # Armchair
cnt_8_4 = nanotube(8, 4, length=4, vacuum=5.0) # Chiral
viewer = MolecularViewer(cnt_6_0, style="neon", backgroundColor="#000000")
viewer.show()
Phonon / Vibrational Modes
Visualize phonon modes for periodic systems using NormalViewer:
CNT Radial Breathing Mode
from ase.build import nanotube
from aseview import NormalViewer
import numpy as np
cnt = nanotube(5, 0, length=2, vacuum=5.0)
positions = cnt.get_positions()
center = positions.mean(axis=0)
# Create radial breathing mode (RBM)
mode_rbm = []
for pos in positions:
r = pos[:2] - center[:2]
r_norm = np.linalg.norm(r)
if r_norm > 0.1:
disp = r / r_norm * 0.4 # Radial displacement
mode_rbm.append([disp[0], disp[1], 0.0])
else:
mode_rbm.append([0.0, 0.0, 0.0])
viewer = NormalViewer(
cnt,
mode_vectors=[mode_rbm],
frequencies=[280.0], # RBM frequency
showModeVector=True,
style="neon",
backgroundColor="#000000"
)
viewer.show()
Graphene Phonons
from ase.build import graphene_nanoribbon
from aseview import NormalViewer
import numpy as np
graphene = graphene_nanoribbon(3, 3, type='zigzag', saturated=False, vacuum=5.0)
positions = graphene.get_positions()
# Out-of-plane ZA mode
mode_za = []
for pos in positions:
phase = 0.5 * (pos[0] + pos[1])
mode_za.append([0.0, 0.0, 0.4 * np.sin(phase)])
viewer = NormalViewer(
graphene,
mode_vectors=[mode_za],
frequencies=[450.0],
showModeVector=True,
style="cartoon"
)
viewer.show()
Unit Cell Display
Toggle unit cell visibility:
viewer = MolecularViewer(
crystal,
showCell=True, # Show unit cell
cellLineWidth=2.0, # Cell line thickness
cellColor="#888888" # Cell color
)
viewer.show()
Reading Structure Files
VASP
from ase.io import read
from aseview import MolecularViewer
# Read POSCAR/CONTCAR
atoms = read("POSCAR")
viewer = MolecularViewer(atoms, showCell=True)
viewer.show()
CIF Files
atoms = read("structure.cif", fractional_occupancies=True)
viewer = MolecularViewer(atoms, showCell=True)
viewer.show()
ASE represents a disordered crystallographic site with one representative atom and stores its alternatives in atoms.info["occupancy"], indexed through atoms.arrays["spacegroup_kinds"]. aseview preserves that metadata automatically. Mixed sites are rendered as occupancy-proportional sectors in the selected atom style; an occupancy sum below one adds an opaque white vacancy sector so the atom remains closed. Bond and polyhedron topology uses ASE's representative element for the site. See the small periodic demo and larger perovskite demo for rendered results.
The optional frame field is also accepted directly:
frame = {
"symbols": ["Fe"],
"positions": [[0, 0, 0]],
"cell": [[5, 0, 0], [0, 5, 0], [0, 0, 5]],
"pbc": [True, True, True],
"species": [[
{"symbol": "Fe", "occupancy": 0.6},
{"symbol": "Mn", "occupancy": 0.4},
]],
}
MolecularViewer(frame).show()
Other Formats
| Format | Extension | Example |
|---|---|---|
| VASP | POSCAR, CONTCAR | aseview POSCAR |
| CIF | .cif | aseview structure.cif |
| XSF | .xsf | aseview charge.xsf |
| Quantum ESPRESSO | .in | aseview pw.in -f espresso-in |
| LAMMPS | .data | aseview system.data -f lammps-data |
Trajectory for Solid-State
MD Trajectory
from ase.io import read
from aseview import MolecularViewer
# Read VASP MD trajectory
traj = read("XDATCAR", index=":")
viewer = MolecularViewer(traj, showCell=True, showEnergyPlot=True)
viewer.show()
Relaxation Trajectory
# Read optimization trajectory
from ase.io import read
opt_traj = read("relax.traj", index=":")
viewer = MolecularViewer(
opt_traj,
showCell=True,
showEnergyPlot=True
)
viewer.show()
Style Recommendations
| Structure Type | Recommended Style |
|---|---|
| Metals | metallic |
| Semiconductors | glossy |
| Ionic crystals | default |
| Carbon materials | cartoon or neon |
| Surfaces | metallic |
Tips for Large Systems
For systems with many atoms: