require(“dseams”)#
require("dseams") loads lua/dseams.lua on dseams_core.
require("yoda") loads the same table.
Cage flags are hexagonal cage (HC) and double-diamond cage (DDC). The chill bond-order classifier (CHILL) and CHILL+ return state names. Pair correlation here means a radial distribution function (RDF).
Fennel call shape lives in Fennel.
Compiled names on dseams.core live in
Compiled registrations.
Public names#
local dseams = require("dseams")
local cloud = dseams.read("water.lammpstrj", {type = 2})
local types = dseams.chill_plus(cloud, {cutoff = 3.5, type = 2})
local nl = dseams.core.neighListO(3.5, cloud, 2)
readsuffix-dispatching loader.
neighborscutoff neighbour list by atom ID.
neighbors_pairI-J cutoff neighbour list.
cnsite-site coordination number.
rdfpartial three-dimensional RDF.
running_cnrunning integral of that pair correlation.
knnk-nearest graph by atom ID.
chill_plusCHILL+ state names, no output file.
chillCHILL labels, no dump on disk.
cagesseeded HC/DDC per-atom flags.
hbondshydrogen-bond adjacency table.
densityCartesian number-density profile.
site_tableparse a type-to-site mapping.
pairsmutual nearest cation-anion pairs.
domainlargest mapped-site domain statistics.
corerequire("dseams_core"); compiled registrations.
These names form the public surface. Locals in lua/dseams.lua
(suffix, opts) stay private. Callers may drop the option
table. Defaults match lua/dseams.lua.
The engine CLI injects compiled names as globals. Library scripts
call those names on dseams.core.
Calling conventions#
PointCloud userdata#
Readers return a PointCloud userdata. Loading dseams_core
registers the usertype, so PointCloud.new() is a global after
require. Workflow scripts pass that scratch object into the
legacy readers.
PointCloud userdata fields:
nopparticle count.
currentFrameframe index just read.
box()box lengths, 3-number table.
boxLow()box origin, 3-number table.
iceTypes()per-particle state name strings.
iceTypes() yields one of cubic, hexagonal, water,
interfacial, clathrate, interClathrate, reCubic, reHex,
unclassified.
Tables vs userdata#
Compiled names that take a neighbour list or a ring list take that
container by value. sol2 builds a C++ vector<vector<int>> from
either a nested Lua table or a container userdata already on the
stack.
sol::as_nested and sol::as_table wrappers hand back a Lua
table. A raw bind of a C++ vector hands back container userdata.
Both shapes accept # and ipairs.
A C++ reference parameter accepts userdata and treats a fresh Lua table as garbage. The nested-table getters below take the list by value, so both shapes bind.
bondNetworkByIndexneighbour list in; Lua table out.
getPrimitiveRingsneighbour list in; container userdata out.
prismAnalysisrings plus nList in; integer out.
bulkRingNumberAnalysisring list plus nList in; writes a histogram.
A Lua table from dseams.neighbors or getHbondNetwork feeds
those names directly. neighborList userdata binds the same way.
New-style names (neighListO, ringNetwork,
seededCageAffiliation, getHbondNetwork*, and the rest on
dseams.core) take a table or userdata and hand back a Lua table.
readFrame*, chillPlus_*, and chill_* stay userdata in and
userdata out.
neighborList is the spelling of neighListO that returns
container userdata. That spelling takes a cutoff, a PointCloud,
and a type. Those compiled names skip C++ default arguments on a
raw bind. Wrapped names spell optionals in Lua (sol::optional).
Names without a Lua wrapper need every argument the bound C++
signature requires.
~read~(path[, opts])#
Suffix-dispatching loader. Returns a PointCloud.
suffix |
backend |
notes |
|---|---|---|
|
|
the file; |
|
|
missing unless this tree compiled readcon |
|
|
absent without chemfiles; |
other (LAMMPS dump) |
|
|
opts.frame is the 1-based frame index (default 1). opts.all = true
calls core.readLammpsTrj and keeps every atom. Otherwise
opts.type is the LAMMPS type ID to keep. The helper has no
region filter.
A dump slice that shrinks nop is
dseams.core.readLammpsTrjreduced(path, frame, type, true, lo, hi).
core.readLammpsTrjO takes the same five arguments after the path
and sets inSlice. lo == hi leaves that direction open.
{0,0,0} / {50,0,0} is x in [0, 50], with y and z open.
The type argument is any LAMMPS type (the O is historical).
~neighbors~(cloud[, opts])#
Cutoff neighbour list by atom ID. Calls
core.neighListO(opts.cutoff or 3.5, cloud, opts.type or 1).
Hands back a Lua table of rows (self ID first).
Pair neighbours#
neighbors_pair(cloud[, opts]) returns an I-J cutoff neighbour list. Calls
core.neighListPair(opts.cutoff or 3.5, cloud, opts.type_i or 1, opts.type_j or 2). Like-type pairs reuse neighListO.
~cn~(cloud[, opts])#
Site-site coordination number. Calls core.calcCN with
opts.type_i (default 1), opts.type_j (default 2),
opts.cutoff (default 4.5), and opts.bins (default
floor(cutoff / 0.1)). rhoJ is nJ / volume from the
partial RDF.
~rdf~(cloud[, opts])#
Partial three-dimensional radial distribution function. Calls
core.calcRDF3D with opts.type_i (default 1), opts.type_j
(default 2), opts.cutoff (default 12.0), and opts.bins (default
floor(cutoff / 0.05)). Hands back {r = r, g = g}.
Running coordination number#
running_cn(cloud[, opts]) integrates the partial g_IJ.
Shared options and defaults match rdf. The call is
core.calcRunningCN. Hands back {r = r, cn = cn}, with
rhoJ = nJ / volume.
~knn~(cloud[, opts])#
k-nearest graph by atom ID. Calls core.kNearestNeighbourList.
opts.kdefaults to 4opts.cutoff(candidate cutoff) defaults to 5.0opts.typedefaults to 1opts.mutualdefaults totrue; the booleanfalseselects the union graph
CHILL+#
chill_plus(cloud[, opts]) builds neighbors(cloud, opts), runs
core.getCorrelPlus(cloud, nl, false), and returns
core.getIceTypePlusNoPrint(cloud, nl, false) (1-based array of
state names). Writes no file. Mutates cloud.
~chill~(cloud[, opts])#
Same pipeline with core.getCorrel and core.getIceTypeNoPrint.
Mutates cloud. Writes no file.
~cages~(cloud[, opts])#
Seeded HC/DDC membership. Builds mutual and union k-nearest graphs
(opts.k 4, opts.cutoff 5.0, opts.type 1), converts each to an
index list, keeps six-membered rings, and calls
core.seededCageAffiliation. Hands back a table {hc = hc, ddc = ddc}
of per-atom flags.
opts.complete is the fifth argument of
seededCageAffiliation. The default is false. true runs the
engine ring-adjacent walk: the last vertex of a six-ring whose
other vertices already carry an HC or DDC label.
local aff = dseams.cages(cloud, {type = 2})
local filled = dseams.cages(cloud, {type = 2, complete = true})
~cages_by_signature~(cloud, spec[, opts])#
Closed polyhedra whose faces match a ring-size census. spec is a
comma list (4:6,6:8) or a named table entry (sodalite,
alpha, 512, 51262, hc, ddc). Builds the cutoff
neighbour graph (opts.cutoff, opts.type), primitive rings up
to opts.max_ring (default 8), and calls core.findBySignature.
Hands back an array of tables {signature=, faces=, vertices=,
certificate=}. Named hc and ddc use the TUM finders.
local sod = dseams.cages_by_signature(cloud, "sodalite", {type = 1})
print(#sod, sod[1] and #sod[1].vertices)
~guest_occupancy~(cloud, cages, guests[, opts])#
Guests (methane, THF, ions; zero-based cloud indices) placed in cages
given as vertex index lists, each guest to the nearest periodic cage
centroid within opts.radius (default 4.0). Hands back
{guestsPerCage=, cageOfGuest=, centreDistance=, occupied=,
multiply=, free=}; cageOfGuest is zero-based and -1 for a
free guest. core.periodicCentroid(cloud, atoms) is the centroid
with every atom unwrapped to its minimum image about the first.
~fingerprint~(rows[, opts])#
Label-independent topology keys of the bonded graph given as index
rows (core.neighbourListByIndex). opts.hops (default 2),
opts.max_ring (default 7) and opts.colours (one integer class
per row, so species never match across types). Hands back {key=,
method=, atomKeys=, classes=, ringCensus=, hops=}; method is
nauty when the engine links nauty, else wl.
~topology_library~(rows, label[, opts])#
The frame’s distinct keys as a key library text under label;
opts.library extends an existing text. Same opts as
fingerprint.
~classify_topology~(rows, library[, opts])#
Names every atom by a key library text, or by a Lua sequence of
library texts built at different hop counts: then the deepest library
that holds an atom’s key names it and opts.hops is ignored. Hands
back {labels=, counts=, depth=, matched=}; unmatched atoms carry
"" under counts.unmatched and depth 0.
local lib2 = dseams.topology_library(rows, "Ic", {hops = 2})
local lib3 = dseams.topology_library(rows, "Ic", {hops = 3})
local named = dseams.classify_topology(rows, { lib3, lib2 })
print(named.matched, named.depth[1])
~ion_environment~(cloud, ice, ions[, opts])#
Ions read against a per-atom ice flag list (1-based booleans) by their
first water shell: ice when every shell molecule is labelled,
liquid when none is, front otherwise. ions are zero-based
cloud indices, opts.type the water type (default 1),
opts.cutoff the shell radius (default 3.5). Hands back {ion=,
shell=, iceFraction=, state=, members=, nIce=, nFront=, nLiquid=};
members[i] lists ion i’s shell.
~shell_ring_census~(rings, shell[, opts])#
Rings (zero-based index lists, as core.ringNetwork returns them)
with a vertex in shell, counted by size up to opts.max_ring
(default 7): census[s + 1] is the number of rings of size s.
With env.members[i] as the shell this is the hydration-shell ring
census of ion i.
~seededCageAffiliation~(strictRings, strictNList, permRings, permNList[, ringAdjacentCompletion])#
Compiled name on dseams.core. Mutual-graph seeds, permissive-graph
fill. Hands back {hc = hc, ddc = ddc}, each a 1-based array of
per-atom flags of length cloud.nop.
Those per-atom flags are the return table.
seededCageAffiliation takes the four ring and neighbour
arguments by value. A nested Lua table and a container
userdata both bind.
The fifth argument is ringAdjacentCompletion, a boolean that
defaults to false when the call drops it. Pass true and the
accepted labels go through ring::ringAdjacentCompletion on the
permissive six-rings. HC flags and DDC flags run as two walks. The
walk is the all-but-one rule: a six-ring whose vertices all carry
that cage label but one fills the last vertex, and the walk
repeats until a fixed point.
local aff = core.seededCageAffiliation(six_s, idx_s, six_u, idx_u)
local filled = core.seededCageAffiliation(six_s, idx_s, six_u, idx_u, true)
dseams.cages passes opts.complete or false as that fifth
argument. example_lua/library/topology.lua calls the compiled
name with true.
Worked example: the legacy ring chain#
legacy_chain.lua under example_lua/library/ is the
worked example for the by-value getters. The script reads
input/traj/exampleTraj.lammpstrj (oxygen type 2). A neighbour
table comes from dseams.neighbors, then feeds
getHbondNetwork, bondNetworkByIndex, getPrimitiveRings, and
prismAnalysis. neighborList userdata binds those names too.
Both paths agree on the ring count. prismAnalysis writes
topoINT/nPrisms.dat.
local nList = dseams.neighbors(cloud, {cutoff = 3.5, type = 2})
local hbn = core.getHbondNetwork(path, cloud, nList, 1, 1)
local byIndex = core.bondNetworkByIndex(cloud, hbn)
local rings = core.getPrimitiveRings(byIndex, 6)
core.prismAnalysis(out .. "/", rings, byIndex, cloud, 6, 1, 1, 1, false)
The meson test name is dseams_legacy_chain. From a configured
build directory:
meson test -C bbdir dseams_legacy_chain
~hbonds~(cloud[, opts])#
Hydrogen-bond adjacency for the selected sites. The neighbour graph
uses opts.cutoff (default 3.5) and opts.type (default 1).
With
opts.path, the helper reads hydrogens from that trajectory;opts.framedefaults to 1 andopts.h_typedefaults to 1.With
opts.h_cloud, the helper uses the supplied hydrogenPointCloudinstead.opts.distandopts.angleuse the engine defaults 2.42 and 30.0 when dropped.
Hands back a nested Lua table. Supplying neither path nor
h_cloud is an error.
~density~(cloud[, opts])#
Cartesian number density along opts.axis: "x", "y", "z",
or the matching zero-based index. The default direction is "z".
opts.bins defaults to that span divided into about 0.1
length-unit bins.
Type mode uses
opts.type(default 0) and returns{centres = centres, rho = rho, axis = axis, atom_type = atom_type}.Site mode requires both
opts.tableandopts.kindand returns{centres = centres, rho = rho, axis = axis, site_kind = site_kind}.
Site mapping table#
site_table(spec) parses a comma-separated mapping such as
"1=cationHead,2=anion,3=tail". That call yields a SiteTable
userdata accepted by density, pairs, and domain. Site kinds
live on dseams.core.Kind (alias SiteKind), including polar
and apolar.
~pairs~(cloud, opts)#
Requires opts.table. Maps the cloud to ionic sites and returns
mutual nearest unlike pairs as
{pairs = pairs, count = count, n_cation = n_cation, n_anion = n_anion}.
Each pair contains the original atom IDs.
~domain~(cloud, opts)#
Requires opts.table and opts.kind. The graph joins mapped sites
within opts.cutoff (default 3.5). Hands back
{site_kind = site_kind, n = n, largest = largest, percolation = percolation}.
n is the selected site count and percolation = largest / n.
core#
require("dseams_core") returns this table. Every name from
luaApi::registerAll (src/lua_api.cpp) lives here.
Usertypes (PointCloud, RingUpdater, AffiliationUpdater) live
on the Lua state, off this table. RingUpdater.new(6) works after
require.
Signatures for every compiled name live in Compiled registrations.