Solid-Liquid Classification and Clustering#

pyscal provides two functions for identifying solid and liquid regions:

  1. find_solids — classifies each atom as solid or liquid based on Steinhardt parameters, optionally clustering solid atoms and returning the largest cluster size

  2. find_clusters — groups connected atoms into clusters based on a boolean condition

import pyscal
from pyscal.structures import make_crystal
from ase.io import read
import numpy as np

Finding Solid Atoms#

find_solids requires neighbors to be found first. Key parameters:

  • bonds: minimum number of solid-like connections or fraction (0-1)

  • threshold: dot product threshold for solid-like connections (default: 0.5)

  • cluster: if True (default), also run clustering and return largest cluster size

# Perfect FCC crystal - should be all solid
fcc = make_crystal("fcc", lattice_constant=3.6, repetitions=(4, 4, 4))
pyscal.find_neighbors(fcc, method="cutoff", cutoff=0)

# find_solids returns the largest cluster size when cluster=True (default)
largest = pyscal.find_solids(fcc, bonds=0.5, threshold=0.5)
print(f"Largest solid cluster: {largest} atoms out of {len(fcc)}")

# Per-atom solid/liquid classification is stored
solid = np.array(fcc.arrays.get('pyscal_solid', []))
if len(solid) > 0:
    n_solid = np.sum(solid > 0)
    print(f"Solid atoms: {n_solid} / {len(fcc)} ({100*n_solid/len(fcc):.0f}%)")
Largest solid cluster: 256 atoms out of 256
Solid atoms: 256 / 256 (100%)

Custom Clustering#

find_clusters groups atoms based on any boolean condition. It requires a condition array.

# Cluster based on the solid classification from find_solids
solid_mask = fcc.arrays.get('pyscal_solid', np.zeros(len(fcc))) > 0
largest_size = pyscal.find_clusters(fcc, condition=solid_mask)

print(f"Largest cluster size: {largest_size}")

# Cluster IDs are stored in atoms.arrays
cluster_ids = fcc.arrays.get('pyscal_cluster', np.array([]))
if len(cluster_ids) > 0:
    unique_clusters = np.unique(cluster_ids[cluster_ids >= 0])
    print(f"Number of clusters: {len(unique_clusters)}")
Largest cluster size: 256
Number of clusters: 1

Working with a Mixed System#

The real power of solid/liquid classification shows with mixed systems.

# Read a liquid-like configuration
lqd = read("conf.lqd.Al.dump", format="lammps-dump-text")
pyscal.find_neighbors(lqd, method="cutoff", cutoff=0)

largest = pyscal.find_solids(lqd, bonds=0.5, threshold=0.5)
print(f"Largest solid cluster: {largest} atoms out of {len(lqd)}")
Largest solid cluster: 0 atoms out of 500