MatSimPy (Materials Simulation in Python) is a toolkit for building, transforming, analyzing, and storing crystal and molecular structures, combining pymatgen's object model and serialization with ASE's lightweight, calculator-driven workflow. It provides builders and transformation pipelines, symmetry and electronic-structure analysis, bonding/topology analysis, multi-format I/O (VASP, CIF, XYZ, PDB, XSF, MOL, JSON, ASE), in-memory and Maggma storage backends, classical/ML/DFT-workflow calculators (Lennard-Jones, MatterSim, VASP, Gaussian, LAMMPS), a plugin registry system, and an optional AI command-line interface.
Version: v0.8.0
- Features
- Installation
- Quick Start
- Core Conventions
- AI Command Line
- Project Structure
- Examples
- Testing
- Documentation
- Core:
Crystal(periodic + lattice),Molecule(non-periodic),Lattice(14 Bravais types),Composition,Site,Element(118 elements). Immutable-return —add_atom,remove_atom,substitute,sort_atomsreturn new objects - Builders (7 domains, 27 functions):
from_prototype(9 prototypes: fcc/bcc/hcp/diamond/rocksalt/perovskite/zincblende/sc/CsCl),generate_slab,generate_random_alloy,build_nanotube,create_vacancy,add_adsorbate,build_heusler, etc. Registered viaBuilderRegistry - Transformations (5 categories, 26 functions):
translate,rotate,apply_strain,make_supercell,substitute,move_atoms,scale_lattice, etc. Registered viaTransformationRegistrywithTransformationSpecmetadata.TransformationPlanfor reproducible pipelines - Analysis (6 modules):
BondAnalyzer(bonds/angles/dihedrals/orders),StructureAnalyzer(COM/inertia/density),TopologyAnalyzer(connectivity/rings/paths), graph/neighbor functions,AtomSelection(9 selectors) - IO (9 formats): VASP/CIF/XYZ/PDB/XSF/MOL/JSON/ASE — auto-detected, table-driven
FormatRegistry.read()/write(), third-party conversion, and LaTeX table helpers - Storage:
DataStoragewithMemoryBackend(testing) +MaggmaBackend(persistent). Content-addressed sha256 IDs.DocumentEnvelopeschema - Extensibility: Plugin entry points for registries, including IO formats
- AI REPL: Optional LLM function calling via DeepSeek models. Built-in MatSimPy skills expose core, builders, transformations, analysis, IO, storage, calculators, and symmetry tools. Use
matsimpy-ai -c "build fcc Cu"for single-shot mode - Calculators:
LennardJones(classical). ML (MatterSim), DFT file/workflow interfaces for VASP, Gaussian, and LAMMPS — optional
pip install MatSimPyThis installs the core package with required dependencies:
- numpy
- scipy
- monty
- tabulate
- pyyaml
Clone the repository and install in editable mode:
git clone https://gitee.com/haidi-hfut/MatSimPy.git
cd MatSimPy
pip install -e .For development:
pip install -e .[dev]This includes:
- pytest
- pytest-cov
- pytest-xdist
MatSimPy supports optional features through extra dependencies:
# Machine learning calculators (torch, torch-geometric)
pip install -e .[ml]
# I/O converters (pymatgen, ase)
pip install -e .[io]
# Structure builders (pyxtal, rdkit)
pip install -e .[builders]
# Symmetry analysis (spglib)
pip install -e .[analysis]
# Data storage (maggma)
pip install -e .[storage]
# AI REPL
pip install -e .[ai]
# Everything
pip install -e .[all]Structures are immutable by default — all mutation methods return new objects without modifying the original.
from matsimpy import Crystal, Molecule, Lattice, Composition
# Crystal (fractional input by default)
crystal = Crystal(['Na', 'Cl'], [[0, 0, 0], [0.5, 0.5, 0.5]], Lattice.cubic(5.64))
print(crystal.formula) # NaCl
print(crystal.volume) # ~179.4 ų
# Mutation returns a new object — must capture the result
doped = crystal.add_atom(['H', 'O'], [[0.1, 0, 0], [0.9, 0, 0]])
print(len(crystal)) # 2 (original unchanged)
print(len(doped)) # 4 (new object)
# Chained mutations (returns new objects each step)
result = crystal.substitute(0, 'K').add_atom('H', [0.1, 0, 0])
print(result.formula) # KClH
# Molecule (Cartesian)
molecule = Molecule(['O', 'H', 'H'], [[0, 0, 0], [0.96, 0, 0], [-0.24, 0.93, 0]])
print(molecule.formula) # OH2
print(molecule.get_center_of_mass())
# Transformations return new objects
translated = molecule.translate([1.0, 0, 0])
rotated = translated.rotate(90, [0, 0, 1])
# Work with composition
comp = Composition('Fe2O3')
print(comp['Fe']) # 2
print(comp['O']) # 3
print(comp.mass) # Fast! (compute-once cached)
# Mass and mole fractions
mass_frac = comp.mass_fractions() # {'Fe': 0.699, 'O': 0.301}
mole_frac = comp.mole_fractions() # {'Fe': 0.4, 'O': 0.6}from matsimpy.analysis import MoleculeGraph, create_structure_graph
from matsimpy import Molecule
molecule = Molecule(['O', 'H', 'H'], [[0, 0, 0], [0.96, 0, 0], [-0.24, 0.93, 0]])
# OOP API (recommended)
graph = MoleculeGraph(molecule, cutoff=2.0)
print(graph.num_nodes) # Number of atoms
print(graph.num_edges) # Number of bonds
print(graph.is_connected) # Connectivity check
print(graph.diameter) # Graph diameter
print(graph.statistics) # All stats at once
# Find shortest path
path = graph.get_shortest_path(0, 2)
# Convert to NetworkX
nx_graph = graph.to_networkx()
# Or use functional API
from matsimpy.analysis import get_adjacency_matrix, get_coordination_numbers
adj = get_adjacency_matrix(molecule, cutoff=3.0)
coord = get_coordination_numbers(crystal, cutoff=5.0)from matsimpy.io import crystals_to_latex_table, save_latex_table
from matsimpy import Molecule, Crystal, Lattice
# Export crystals to LaTeX table
crystal1 = Crystal(['Na', 'Cl'], [[0, 0, 0], [0.5, 0.5, 0.5]], Lattice.cubic(5.63))
crystal2 = Crystal(['Na', 'Cl'], [[0, 0, 0], [0.5, 0.5, 0.5]], Lattice.cubic(5.64))
crystals = [crystal1, crystal2]
latex = crystals_to_latex_table(
crystals,
caption='NaCl polymorphs',
label='tab:nacl',
)
print(latex)
# Save to file
save_latex_table(crystals, 'structures.tex')from matsimpy.builders import (
from_prototype, generate_slab,
build_tetrahedral, build_carbon_nanotube,
create_vacancy, create_interstitial, add_adsorbate,
generate_random_alloy, create_simple_interface,
)
# Build bulk structures from prototypes
fcc_cu = from_prototype('fcc', 'Cu', 3.61)
bcc_fe = from_prototype('bcc', 'Fe', 2.87)
diamond_c = from_prototype('diamond', 'C', 3.57)
# Create surface slabs with adsorbates
slab = generate_slab(fcc_cu, (1, 1, 1), min_slab_size=10.0, min_vacuum_size=15.0)
with_ads = add_adsorbate(slab, 'O', (0.5, 0.5), 2.0)
# Generate supercell
from matsimpy.transformation import make_supercell
supercell = make_supercell(fcc_cu, [4, 4, 4])
# Build molecules
ch4 = build_tetrahedral('C', ['H', 'H', 'H', 'H'], 1.09)
# Create defects
with_vacancy = create_vacancy(fcc_cu, 0)
with_interstitial = create_interstitial(fcc_cu, 'H', position=[0.5, 0.5, 0.5])
# Generate alloys
alloy = generate_random_alloy(supercell, ['Ni'], 'Cu', [0.25])
# Build nanotubes
cnt = build_carbon_nanotube(10, 0)
# Create interfaces
interface = create_simple_interface(fcc_cu, bcc_fe)Transformation helpers are functional: they return new structures and preserve
the original object. Crystal-returning transformations preserve periodic
boundary conditions (pbc) unless a function explicitly documents otherwise.
Site metadata (site_properties) is preserved, reindexed, duplicated, or
dropped by an explicit policy for transformations that reorder or synthesize
sites.
from matsimpy.transformation import (
translate, rotate, apply_strain, scale_lattice,
substitute, make_supercell, move_atoms,
)
from matsimpy.transformation.registry import registry
# Geometric transformations
translated = translate(crystal, [1, 1, 1])
rotated = rotate(translated, 90.0, [0, 0, 1])
# Lattice transformations
strained = apply_strain(
crystal,
[[0.05, 0, 0], [0, 0, 0], [0, 0, 0]], # 5% uniaxial strain
)
scaled = scale_lattice(crystal, 1.1) # Scale by 10%
# Chemical transformations
substituted = substitute(crystal, [0], 'Ge')
# Atomic transformations
moved = move_atoms(crystal, [0, 1], [0.1, 0, 0])
# Structural transformations
supercell = make_supercell(crystal, [2, 2, 2]) # 2x2x2 supercell
# Registry — apply transformations by name with type validation
result = registry.apply('translate', crystal, vector=[1, 0, 0])
spec = registry.get('translate') # TransformationSpec metadataMetadata and capability notes:
swap_atoms()andsort_atoms()reindexsite_propertieswith the atoms.merge_atoms()keeps the merged site's source metadata;split_atom()copies the source site's metadata to each split site.make_supercell()preservespbcand repeats site metadata for generated images, including non-diagonal integer scaling matrices.standardize_cell()uses spglib when available and intentionally dropssite_propertieswhen the standardized cell may reorder or change sites.- APIs that are not implemented yet, such as
get_niggli_reduced(),fragment_molecule(), andgenerate_conformers(), raiseNotImplementedErrorinstead of returning placeholder structures.
from matsimpy.transformation.composite import (
TransformationPipeline, ParameterSweep, BatchProcessor
)
from matsimpy.transformation import make_supercell, apply_strain
from matsimpy.transformation.registry import registry
from matsimpy.builders.bulk import from_prototype
# 1. TransformationPlan — serializable sequence of named, registered transforms
from matsimpy.transformation import TransformationStep, TransformationPlan
plan = TransformationPlan(
steps=[
TransformationStep("make_supercell", {"scaling_matrix": [2, 2, 2]}),
TransformationStep("translate", {"vector": [1.0, 0, 0]}),
],
name="2x2x2 supercell + shift",
)
crystal = from_prototype('diamond', 'Si', 5.43)
result = registry.apply_plan(plan, crystal)
# 2. Parameter sweep
sweep = ParameterSweep(
base_structure=crystal,
transformations={
'strain': {
'func': apply_strain,
'params': {
'strain_matrix': [
[[0.00, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0.01, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0.02, 0, 0], [0, 0, 0], [0, 0, 0]],
]
}
}
},
mode='cartesian'
)
for struct, params in sweep:
print(f"Strain: {params['strain']}, Formula: {struct.formula}")
# 3. Batch processing
def make_2x2x2_supercell(s):
return make_supercell(s, [2, 2, 2])
processor = BatchProcessor(
transformations=[make_2x2x2_supercell],
n_workers=1,
progress=True,
error_handling='skip'
)
structures = [from_prototype('fcc', 'Cu', 3.61) for _ in range(10)]
results = processor.process(structures)
for result in results:
if result.success:
print(f"Processed: {result.structure.formula}")High-level interface (recommended):
from matsimpy.io import read, write
# Auto-detect format from file extension
write(crystal, 'structure.vasp')
write(molecule, 'molecule.xyz')
# Read structures (format auto-detected)
crystal = read('structure.vasp')
molecule = read('molecule.xyz')
# Explicit format specification
crystal = read('file.txt', format='vasp')
write(crystal, 'output.txt', format='cif', title='My Structure')Format-specific functions (advanced use):
from matsimpy.io.vasp import write_POSCAR, read_POSCAR
from matsimpy.io.xyz import write_XYZ, read_XYZ
# Direct format-specific access
write_POSCAR(crystal, 'structure.vasp')
molecule = read_XYZ('molecule.xyz')from matsimpy import Crystal, Lattice
from matsimpy.calculator import LennardJones, Mattersim
# Classical potential calculator
crystal = Crystal(['Ar'], [[0,0,0]], Lattice.cubic(5.0))
calc = LennardJones(sigma=3.4, epsilon=0.0104)
crystal.calc = calc
# Get energy and forces
energy = crystal.get_potential_energy()
forces = crystal.get_forces()
# ML calculator (uses config defaults)
ml_calc = Mattersim(model_path='model.pth', model_type='mace')
crystal.calc = ml_calc
energy = crystal.get_potential_energy()from matsimpy.config import get_config, ConfigManager
# Get configuration values
device = get_config('calculator.ml.default_device') # 'cpu'
model_dir = get_config('paths.models') # '~/.matsimpy/models'
# Modify configuration
config = ConfigManager()
config.set('calculator.ml.default_device', 'cuda', save=True)from matsimpy.builders.bulk import from_prototype
from matsimpy.symmetry import get_conventional_cell
# Create crystal structure
crystal = from_prototype('diamond', 'Si', 5.43)
# Get symmetry information
sym_info = crystal.get_symmetry_info()
print(f"Space group: {sym_info['space_group_symbol']}") # Fd-3m
print(f"Point group: {sym_info['point_group']}") # m-3m
print(f"Crystal system: {sym_info['crystal_system']}") # Cubic
# Get conventional cell
conventional = crystal.get_conventional_cell()
print(f"Primitive: {len(crystal)} atoms") # 2 atoms
print(f"Conventional: {len(conventional)} atoms") # 8 atomsfrom matsimpy.storage import DataStorage, MemoryBackend
from matsimpy.builders.bulk import from_prototype
# In-memory storage (for testing)
storage = DataStorage(backend=MemoryBackend())
# For persistent storage, use MaggmaBackend:
# from matsimpy.storage import MaggmaBackend
# storage = DataStorage(backend=MaggmaBackend(store_path='mydata.json'))
# Store crystal structure — returns stable sha256 ID
crystal = from_prototype('diamond', 'Si', 5.43)
doc_id = storage.store_data(crystal, metadata={'description': 'Si primitive cell'})
# Store calculation results
results = {'energy': -10.5, 'forces': [[0,0,0]]}
storage.store_data(results, metadata={'calculator': 'LJ'})
# Retrieve — auto-decodes to Crystal or raw dict
retrieved = storage.retrieve_data(doc_id)
print(retrieved.formula) # Si2
# Query by metadata
lj_results = storage.query({'metadata.calculator': 'LJ'})
# ID is content-addressed — same data → same ID
id1 = storage.store_data(crystal)
id2 = storage.store_data(crystal)
assert id1 == id2
storage.close()Interactive LLM-powered assistant. Chat in natural language — the AI calls MatSimPy functions.
# Install with AI extras
pip install -e .[ai]
# Set API key
export DEEPSEEK_API_KEY="sk-..."
# Interactive REPL
matsimpy-ai
matsimpy-ai --workspace ~/my-project --model deepseek-v4-pro
# Single-shot command
matsimpy-ai -c "create fcc Cu crystal and save to cu.vasp"
matsimpy-ai -v -c "analyze bonds in nacl.cif"from matsimpy.ai import AIEngine, SkillManager, Skill
from matsimpy.ai.skills import core, builders, analysis
engine = AIEngine(model="deepseek-v4-pro")
for mod in (core, builders, analysis):
engine.skill_manager.register(Skill(mod.SKILL_NAME, mod.SKILL_DESCRIPTION, mod.get_functions()))
engine.skill_manager.load("core")
# Natural language → function calls
response = engine.chat("Create an FCC Cu crystal and analyze its structure")
print(response)Skills load progressively — builders load when user mentions "slab" or "vacancy", analysis loads for "bond" or "symmetry". Agent memory persists across sessions (~/.matsimpy/ai/memory/).
The AI REPL and matsimpy-ai -c share the same agent runtime. The runtime records searchable task traces in ~/.matsimpy/ai/state.db, keeps compact memory files in ~/.matsimpy/ai/memory/, and proposes reusable workflow skills as drafts. Draft skills are disabled until approved with /approve-skill <name>.
Runtime commands:
/trace
/drafts
/approve-skill <name>
- All mutation APIs return new objects (
add_atom,remove_atom,substitute,sort_atoms, transformations, etc.) - Internal numpy arrays are read-only to prevent accidental in-place edits.
Structure.positionsis always Cartesian (Å).Crystalstores fractional internally and provides:Crystal.frac_positions(fractional)Crystal.cart_positions(Cartesian)Crystal.positions(Cartesian, consistent withStructure)
matsimpy/
├── core/ # Core data structures
│ ├── crystal.py # Crystal class
│ ├── molecule.py # Molecule class
│ ├── structure.py # Structure ABC
│ ├── lattice.py # Lattice class
│ ├── composition.py # Composition class
│ ├── site.py # Site classes
│ ├── periodic_table.py # Element and periodic table
│ ├── _validation.py # Core validation helpers
│ └── protocols.py # StructureLike, CrystalLike, MoleculeLike
│
├── builders/ # Structure builders
│ ├── bulk/ # Bulk crystal structures (prototypes, symmetry)
│ ├── surface/ # Surface slabs and adsorbates
│ ├── alloy/ # Alloy generation (random, ordered, intermetallic, Heusler)
│ ├── molecule/ # Molecular structure builders
│ ├── defects/ # Point defect creation (vacancy, interstitial, substitution, etc.)
│ ├── interface/ # Interface/hybrid structure builders
│ ├── nanostructure/ # Nanotubes and twisted structures
│ ├── registry.py # BuilderRegistry + BuilderSpec
│ └── _register.py # Auto-registration of built-in builders
│
├── transformation/ # Structure transformations
│ ├── geometric/ # Translation, rotation
│ ├── lattice/ # Lattice strain, scaling, transformations
│ ├── atomic/ # Atom manipulation and organization
│ ├── chemical/ # Chemical substitutions
│ ├── structural/ # Supercell, molecular operations
│ ├── composite/ # Pipeline, ParameterSweep, BatchProcessor, TransformationPlan
│ ├── spec.py # TransformationSpec dataclass
│ ├── registry.py # TransformationRegistry singleton
│ └── _register.py # Auto-registration of built-in transformations
│
├── io/ # File IO, converters, and export helpers
│ ├── core.py # High-level read/write interface
│ ├── registry.py # FormatHandler + FormatRegistry (table-driven dispatch)
│ ├── vasp.py # VASP POSCAR/CONTCAR
│ ├── cif.py # CIF format
│ ├── xyz.py # XYZ format
│ ├── pdb.py # PDB format
│ ├── mol.py # MOL format
│ ├── xsf.py # XSF format
│ ├── ase.py # ASE format and converters
│ ├── pymatgen.py # pymatgen converters
│ ├── latex.py # LaTeX table export
│ └── json.py # JSON serialization
│
├── analysis/ # Analysis tools
│ ├── graph.py # StructureGraph, MoleculeGraph, CrystalGraph
│ ├── neighbors.py # Neighbor finding (find_points_in_spheres)
│ ├── bonding.py # BondAnalyzer (bonds, angles, dihedrals)
│ ├── structure.py # StructureAnalyzer (COM, inertia, density)
│ ├── topology.py # TopologyAnalyzer (connectivity, rings, paths)
│ └── selection.py # AtomSelection + select_by_species, etc.
│
├── storage/ # Data storage
│ ├── facade.py # DataStorage (user-facing API)
│ ├── schema.py # DocumentEnvelope + stable ID generation
│ ├── codec.py # DocumentCodec (encode/decode)
│ ├── backend.py # StoreBackend protocol
│ ├── memory_store.py # MemoryBackend (in-memory, for testing)
│ └── maggma_store.py # MaggmaBackend (persistent, via maggma)
│
├── utils/ # General-purpose utilities
│ ├── validation.py # validate_vector3, validate_positive_scalar, etc.
│ ├── dict_utils.py # get_nested_value, set_nested_value, copy_properties
│ └── path_utils.py # expand_path
│
├── calculator/ # Energy/force calculators
├── config/ # Global configuration (ConfigManager)
├── symmetry/ # Symmetry analysis (spglib)
├── ai/ # Optional AI REPL and skill runtime
├── exceptions.py # MatSimPyError, FormatError, StructureTypeError, etc.
├── plugins.py # Plugin discovery via entry points
└── constants.py # POSITION_TOL, LATTICE_TOL
MatSimPy includes an optional AI command-line interface in the [ai] extra:
# Install AI dependencies
pip install -e .[ai]
# Launch interactive REPL
matsimpy-ai
# Run a single natural-language task
matsimpy-ai -c "create fcc Cu crystal and save to cu.vasp"The AI CLI provides natural-language access to structure generation, transformations, analysis, format conversion, storage, and calculator helpers through MatSimPy tool calls.
Comprehensive examples are available in the examples/ directory:
- Core Module:
core_basic.py,core_advanced.py - Builders:
builders_bulk.py,builders_surface.py,builders_alloy.py,builders_molecule.py,builders_defects.py,builders_nanostructure.py - Transformations:
transformation_geometric.py,transformation_lattice.py,transformation_chemical.py,transformation_structural.py,transformation_composite.py - IO:
io_basic.py - Workflows:
workflow_basic.py - Calculators:
calculator_basic.py - Storage:
storage_basic.py - Symmetry:
symmetry_basic.py - Config:
config_basic.py
Run an example:
python examples/core_basic.py
python examples/builders_bulk.pyThe project includes comprehensive tests:
# Run all tests
pytest
# Run with coverage
pytest --cov=matsimpy --cov-report=html
# Run specific modules
pytest tests/test_core*.py -v # Core modules
pytest tests/test_graph*.py -v # Graph analysis
pytest tests/test_io_latex.py -v # LaTeX export
pytest tests/test_composite*.py -v # High-throughput tools
# Performance and quality
pytest --tb=no -q # Quick run
pytest tests/ -v --durations=10 # Show slowest testsCurrent status in CI/local runs varies by optional extras; core suite is green.
- Sphinx Documentation: See
docs/directory for comprehensive Sphinx documentation- Build with:
cd docs && make html - View at:
docs/_build/html/index.html
- Build with:
- AI Documentation: See
AIdocs/directory for AI-related documentation and session summaries- November 2025 Quality Enhancement - Complete details of recent improvements
- Examples: See
examples/directory for comprehensive usage examples - API Reference: See module docstrings and
examples/for detailed usage - Project Structure: See above for module organization
Contributions are welcome! Please feel free to submit issues or pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.
haidi wang
Email: haidi@hfut.edu.cn
Repository: https://gitee.com/haidi-hfut/MatSimPy
MatSimPy is inspired by pymatgen and ASE and aims to provide a modern, efficient alternative for materials simulation with comprehensive structure building capabilities.
Note: v0.8.0 — optional AI REPL with LLM function calling, shared agent runtime, task traces, agent memory, and approval-gated draft skills.