Skip to content

Ions against the cage assignment

Ions sit outside the hydrogen-bond network. The cage assignment runs on the water. Each ion is then classed by its first water shell: ice when every neighbour in the shell is ice, liquid when none of it is, and front otherwise.

CLI

seams ions needs --ion-types. --ion-cutoff is the first-shell radius (default --cutoff). --complete is the same ring completion as seams cages.

seams ions brine.lammpstrj --type 1 --ion-types 3,4 --ion-cutoff 3.5 --complete

Stdout is nop N ice I ions M in-ice A front B liquid C shell S shell-ice F.

This is not seams cn --ions, which is cage degree on ionCloud and needs --site.

Python

Build the frame with every species in the cloud. atom_type still names the water.

from pydseams import Frame

frame = Frame.from_file("brine.lammpstrj", atom_type=1, all_atoms=True)
env = frame.ion_environment((3, 4), ring_adjacent=True, cutoff=3.5)
print(env.nIce, env.nFront, env.nLiquid)

ASE mixed water-salt selections keep ions in the cloud and store atomic numbers as c_type:

from pydseams import Frame

frame = Frame.from_ase(atoms, select=("O", "Na", "Cl"), bonded="cutoff")
print(frame.ion_environment((11, 17)))

The environment lists each ion's shell in members, and hydration_shell_rings counts the primitive rings of the water network that pass through each shell, by size. An ion is not a vertex of the network, so the rings it would have closed are gone; the census says how far the network survives around it:

env, census = frame.hydration_shell_rings((3, 4))
for ion, row in zip(env.ion, census):
    print(ion, row[6], "six-rings through the shell")

Guests inside cages are a different question from ions at vertices. guest_occupancy places guests (methane, THF, an ion) in cages given as vertex index lists, by the nearest periodic cage centroid within a radius, and reports occupied, multiply occupied and free:

cages = [c["vertices"] for c in frame.cages_by_signature("512")]
occ = frame.guest_occupancy(cages, guest_types=(2,), radius=4.0)
print(occ.occupied, occ.multiply, occ.free)

On the command line the same count follows the signature search:

seams cages hydrate.lammpstrj --type 1 --graph cutoff --signature 512 --guest-types 2 --guest-radius 4.0

pydseams.features.ion_environment takes per-molecule states from IceFeaturizer instead of calling seeded_affiliation itself.

from pydseams import Trajectory
from pydseams.features import ION_ICE, IceFeaturizer, ion_environment

traj = Trajectory("brine.lammpstrj", frame=1, atom_type=1, all_atoms=True)
feat = IceFeaturizer(traj, ion_types=(3, 4))
x, states = feat.frame_features()
ions, shell, fraction, ion_states = ion_environment(traj, states, (3, 4))
trapped = ions[ion_states == ION_ICE]

Lua

local dseams = require("dseams")
local cloud = dseams.read("brine.lammpstrj", {all = true})
local aff = dseams.cages(cloud, {type = 1, complete = true})
local ice = {}
for i = 1, cloud.nop do
  ice[i] = (aff.hc[i] or aff.ddc[i]) and true or false
end
local env = dseams.ion_environment(cloud, ice, {0}, {type = 1, cutoff = 3.5})
print(env.nIce, env.nFront, env.nLiquid)

ion_environment takes the cloud, a per-atom ice flag list, and a list of ion indices (0-based). o.type is the water type (default 1). o.cutoff is the first-shell radius (default 3.5). env.members[i] lists ion i's shell, and dseams.shell_ring_census(rings, env.members[i], {max_ring = 6}) counts the rings through it by size. dseams.guest_occupancy(cloud, cages, guests, {radius = 4.0}) places guests in cages given as vertex index lists.

Book: features how-to.