Quickstart#

[1]:
import ase
import matplotlib.pyplot as plt
import numpy as np
from ase.io import read

import topo_metrics as tm
import topo_metrics.analysis as tm_plt

%config InlineBackend.figure_format = 'svg'

loading structures#

The simplest way to get started is via ASE. Load any structure file that ASE can read, then pass the resulting Atoms object directly to topo-metrics.

Here we use two SiO\(_2\) crystal structures: sodalite (a zeolite) and \(\alpha\)-quartz. To construct the graph, we apply a radial cut-off of \(r=1.7~\)Å between silicon and oxygen atoms. With remove_types={"O"}, the graph is reduced to silicon nodes connected via oxygen bridges: oxygen atoms define the connectivity, but the oxygen vertices themselves are removed.

[2]:
sod = read("data/SOD.cif")
qtz = read("data/alpha-quartz.cif")

assert isinstance(sod, ase.Atoms)
assert isinstance(qtz, ase.Atoms)

graph_sod = tm.Topology.from_ase(ase_atoms=sod, cutoff=1.7, remove_types={"O"})
graph_qtz = tm.Topology.from_ase(ase_atoms=qtz, cutoff=1.7, remove_types={"O"})

print(
    "`graph_sod`: ",
    graph_sod,
    "\n`graph_qtz`: ",
    graph_qtz,
)
`graph_sod`:  Topology(nodes=12, edges=24, has_lattice=True)
`graph_qtz`:  Topology(nodes=3, edges=6, has_lattice=True)

topological genome#

We can confirm that graph construction was successful by checking the topological genome (as determined by CrystalNets.jl).

[3]:
graph_sod.get_topological_genome(), graph_qtz.get_topological_genome()
[3]:
('sod, SOD', 'qtz')

rings analysis#

The per-node rings analysis is accessed by Topology.get_clusters(). Both nets are uninodal, indicated by the single VertexSymbol and CARVS entries.

[4]:
results_sod = graph_sod.get_clusters()
results_sod
[4]:
RingsResults(
    depth=12,
    strong_rings=False,
    ring_size_count=RingSizeCounts(n_rings=46, min=4, max=12),
    VertexSymbol=[4.4.6.6.6.6],
    CARVS={4(2).6(4).12(32)}
)
[5]:
results_qtz = graph_qtz.get_clusters()
results_qtz
[5]:
RingsResults(
    depth=12,
    strong_rings=False,
    ring_size_count=RingSizeCounts(n_rings=18, min=6, max=8),
    VertexSymbol=[6.6.6(2).6(2).8(7).8(7)],
    CARVS={6(6).8(40)}
)

ring size distribution#

Global ring-size counts are stored in RingsResults.ring_size_count, which has two attributes: sizes and counts. For quick inspection, a built-in distribution plotter is provided.

[6]:
rcounts = results_sod.ring_size_count
rcounts
[6]:
RingSizeCounts(n_rings=46, min=4, max=12)
[7]:
np.vstack([rcounts.sizes, rcounts.counts]).T[:16]
[7]:
array([[ 1,  0],
       [ 2,  0],
       [ 3,  0],
       [ 4,  6],
       [ 5,  0],
       [ 6,  8],
       [ 7,  0],
       [ 8,  0],
       [ 9,  0],
       [10,  0],
       [11,  0],
       [12, 32],
       [13,  0],
       [14,  0],
       [15,  0],
       [16,  0]])
[8]:
fig, ax = tm_plt.plot_ring_size_distributions(
    results=[results_sod, results_qtz], labels=[r"$\bf SOD$", r"$\bf qtz$"]
)
ax.set_ylim(top=35)
plt.show()
../_images/examples_quickstart_12_0.svg
[ ]: