Moves

A move in mcpy proposes a new atomic configuration. The ensemble then evaluates the corresponding acceptance rule – Metropolis for number-conserving moves, de-Broglie for insertions and deletions – and either commits the new configuration or rolls back.

Every move exposes do_trial_move(atoms), which proposes a trial structure and, for number-changing moves, signals the change via delta_particles. A run typically mixes several move types using a MoveSelector.

MoveSelector

MoveSelector samples moves from a weighted list. One ensemble step runs n_moves trial moves, and each trial draws its own move. By default n_moves is round(sum(weights)) (at least 1), so the weights [1, 1] give two trials per step. Pass n_moves to set the count directly. The selector tracks acceptance statistics on two levels:

  • per-interval counters – reset after each log write; reported in outfile as the recent acceptance ratio of each move,

  • cumulative counters – never reset; reported in the final summary at the end of the run.

Trial moves that cannot be proposed (for example, a deletion when no atom of the requested species lies inside the cell) are tracked separately and excluded from the acceptance-ratio denominator, so the reported ratios reflect viable attempts only.

from mcpy.moves import MoveSelector, InsertionMove, DeletionMove

selector = MoveSelector(
    probabilities=[1, 1],
    move_list=[
        InsertionMove(cell, ["O"], seed=1),
        DeletionMove(cell, ["O"], seed=2),
    ],
)

Weights need not sum to one – they are interpreted as relative frequencies.

The acceptance rule assumes that a species is inserted and deleted with equal probability per trial in the same cell. GrandCanonicalEnsemble checks this when you create it. For each species and cell, it adds up the insertion weights and the deletion weights (a move with several species splits its weight equally between them). If the two sums differ, it raises ValueError, because the difference shifts \(\mu\) by \(kT \ln(p_{\mathrm{ins}}/p_{\mathrm{del}})\). Give the insertion move and the deletion move of a species the same weight, the same species list and the same cell object.

Built-in moves

The following move classes are available from mcpy.moves.

The displacement, shake, permutation and MD moves act on atoms anywhere in the box. They can carry an exchanged atom out of the region of the insertion and deletion moves, or into it. GrandCanonicalEnsemble rejects such trials for a SphericalCell, a DomeCell and the top of a CustomCell (see Cells).

InsertionMove

Attempts to insert an atom of one of the selected species at a random point drawn from the attached cell. Sets delta_particles = +1 on a successful proposal. Optional max_atoms skips the trial (no mutation, no energy evaluation) when the structure already contains at least that many free atoms of the selected species. Free atoms are the atoms outside molecules (molecule_id < 0), in the whole system, so the substrate counts too. Optional min_insert is a seed clearance for relaxed trials (ADR 0004). Use it only with a relaxing calculator: with single-point energies it shifts \(\mu\) by \(kT \ln(V_{\mathrm{free}}/V_{\mathrm{seed}})\), and the ensemble warns.

DeletionMove

Attempts to delete a randomly chosen atom of one of the selected species lying inside the attached cell. Sets delta_particles = -1 on a successful proposal; returns falsy when no candidate atom exists or when optional min_atoms would be violated, which the MoveSelector records as a failure rather than as a rejection. min_atoms counts free atoms (molecule_id < 0) of the species in the whole system, like max_atoms. Atoms held by FixAtoms are never deleted, because no insertion can put them back.

DisplacementMove

Displaces one randomly selected atom by a random vector with magnitude up to max_displacement. Particle count is preserved (delta_particles = 0).

The optional n_steps parameter (default 1) bundles n_steps distinct atom displacements into a single trial – the energy is evaluated once, after all displacements, and the whole compound perturbation is accepted or rejected together. This is the building block of basin-hopping with displacement moves: a one-atom step usually relaxes back into the same basin, so a meaningful escape requires several atoms to move at once. If n_steps is larger than the number of movable atoms, the move displaces all movable atoms.

PermutationMove

Swaps the species labels of two atoms drawn from different species groups. Useful for sampling compositional ordering (homotop space) in multi-component systems at fixed stoichiometry.

The optional n_swaps parameter (default 1) applies n_swaps independent pair swaps in a single trial – one energy evaluation, one accept/reject. For basin-hopping on alloy ordering, a single swap is typically too gentle (relaxation returns to a near-identical basin); a perturbation of order \(\sim 1\text{--}5\%\) of the atom count per trial is a sensible default. In a replica-exchange run, scaling n_swaps with replica temperature – small n_swaps for cold refiners, large n_swaps for hot explorers – keeps acceptance ratios balanced across the ladder.

ShakeMove

Displaces all atoms by independent random vectors bounded by r_max. Useful as a global perturbation, typically combined with a local relaxation in the calculator wrapper. Atoms held by FixAtoms do not move. Without FixAtoms, the move removes the mass-weighted mean displacement, so a free particle does not drift through the box.

BrownianMove

Runs a short Velocity-Verlet trajectory at a target temperature as the trial move. Useful as a relaxation-like perturbation that explores the local basin more thoroughly than a single random displacement.

AlchemiBrownianMove

Runs a short Langevin MD trajectory on the GPU as the trial move, reusing the model of an Alchemi calculator, so no second model is loaded. The MD needs forces: pass an AlchemiFCalculator, or an AlchemiCalculator built with energy_only=False (an AlchemiCalculator that loads its own model is energy-only by default).

Both MD moves take a debug option, trajectory_file. When you set it, the move appends every MD step of every call to that file as an extended-XYZ frame. The move writes the frames before the acceptance test, so the file also contains the rejected runs. Each frame holds the positions, the velocities (as momenta) and the forces. The info line holds md_run (the call count of the move), md_step, time_fs, T_target_K, the instantaneous T_K, and epot, ekin and etot in eV. Use it on short runs only, because it writes one frame per MD step.

Without FixAtoms, both MD moves remove the center-of-mass motion. BrownianMove zeros the net momentum of the initial velocities, and AlchemiBrownianMove zeros the center-of-mass velocity after every Langevin step. Otherwise each accepted move would shift the whole particle a little, and the shifts would add up to a random walk. With FixAtoms, the fixed atoms hold the system in place, and the COM motion is kept. If every atom is fixed, the move is a failed proposal.

Warning

BrownianMove and AlchemiBrownianMove do not satisfy exact detailed balance. The acceptance uses the potential energy only. It has no kinetic-energy term, and an MD or Langevin trajectory is not a symmetric proposal. Use these moves for basin-hopping-style exploration with a relaxing calculator. Do not use them with an energy-only calculator when you need equilibrium averages.

Molecular moves

Three moves exchange and move whole rigid molecules. They identify a molecule by the per-atom molecule_id array and take the ASE template and the species name used as the key in mu. Simulating molecular adsorbates explains the bookkeeping and the chemical potential.

MoleculeInsertionMove

Inserts the template at a random center of mass drawn from the cell’s free volume, with a uniform random orientation. Optional max_molecules caps the population.

MoleculeDeletionMove

Deletes one whole molecule, drawn uniformly among those whose center of mass lies in the cell’s exchangeable region. Optional min_molecules keeps a floor.

MoleculeDisplacementMove

Translates one molecule by up to max_displacement and rotates it about its center of mass, rigidly. Optional max_angle caps the rotation for strongly anchored adsorbates. A move that would carry the center of mass out of the cell counts as a rejection. The test uses cell.is_point_inside, so for a CustomCell the top is a hard wall.