This page covers a few issues that may occur when using CrystalNets.jl. If your problem is not mentioned here, you can open an issue.
Sure! If you only want to identify the topology of a structure, the easiest way is to use the website interface at https://progs.coudert.name/topology
If you want to use the package in a programmatic way without learning Julia, it is possible to use it through Python. To do so, please check the dedicated Python interface tutorial.
This is the focus of the Visualization tutorial.
- From the module: see
- From the executable: use the
Pass the appropriate option from
Clustering to the
clusterings keyword argument of
Options, or use the
-c flag from the executable. For example, to compute the topology of UiO-66 with the Points-of-Extension (PE) clustering algorithm, do
julia> path_to_uio66 = joinpath(dirname(dirname(pathof(CrystalNets))), "test", "cif", "UiO-66.cif"); julia> determine_topology(path_to_uio66; structure=StructureType.MOF, clusterings=[Clustering.PE]) PE: ubt
clusterings keyword argument accepts a list of
Clusterings you can check multiple clusterings at once while factoring useless computations. For example:
julia> determine_topology(path_to_uio66; structure=StructureType.MOF, clusterings=[Clustering.Standard, Clustering.Auto]) Standard: xbi AllNodes, SingleNodes: fcu
Auto is equivalent to both
SingleNodes when the
StructureType is set to
See below for the reason why the
Standard topology is xbi and not ToposPro's "3,4,8T15".
You can use the Visualization tutorial to identify which bonds are incorrectly detected or missing.
Check that the input file is clean: if an atom is represented in multiple possible positions for instance (as is common in CIF files), CrystalNets.jl could mistake them as multiple atoms, which may break some heuristics of the bond-detection algorithm. Solvent residues may also be incorrectly bonded in some circumstances and should be removed. The
ignore_atoms keyword argument in the [
Options] may be useful in this regard.
You may want to provide a different
structure keyword argument in the
Options taken among the possible instances of
StructureType. These can modify the default heuristics for bond-guessing: for example, using
structure=StructureType.MOF gives metals a larger radius for the purpose of guessing bonds.
There are several customizable heuristics for bond-guessing available among the
Options. Of particular interest are
ignore_homoatomic_bonds and several of the options in the "Miscellaneous" section.
If nothing works, the last solution consists in providing an input file with explicit bonds set, and use the
bonding=Input keyword argument to
In CrystalNets.jl, a net is identified if the graph of the crystal is isomorphic to the net. This correspondence is exact and the algorithm can only fail in case of unstable nets, which are reported as such. To do so, CrystalNets.jl actually solves the more complex graph canonicalization problem which consists in finding a "genome" (a sequence of numbers) provably unique for each net and such that two isomorphic nets have the same genome. Each genome is then associated with a name. See this article for more information on the implementation in the program Systre, from which CrystalNets.jl is derived.
ToposPro does not exactly solve the periodic graph isomorphism problem: instead, it identifies the graph of a crystal as a known net if both share a number of properties (coordination sequences up to a certain point, point symbols and vertex symbols, see this article). This is usually an excellent approximation, but it is mathematically unsound as there may exist two non-isormorphic nets sharing these three properties. In particular, this means that there cannot be a unique topological genome defined for these "nets" recognized by ToposPro. As a consequence, they cannot be used by CrystalNets.jl.
While both algorithms usually align, the result may not be the same in all cases because they are not defined in the same way.
Most often, the difference will come from either:
- an oxygen atom having three (or more) bonds becomes a vertex for ToposPro but is removed by CrystalNets.jl. To solve this, use
- a paddle-wheel pattern is grouped into a single cluster by CrystalNets.jl but not by ToposPro. To solve this, use
The built-in way to do this consists in using the
determine_topology_dataset function. This function expects the path of a directory containing CIF files within (possibly in subdirectories).
determine_topology is an
length gives the number of interpenetrated substructures. Each of its values, for instance
x, is a tuple
(topo, n) meaning that the substructure is an
n-fold catenated net of topology
topo itself is a
TopologyResult, which stores the result of a topology computation for possibly several clusterings. The
TopologicalGenome associated to a given clustering can be extracted by indexing the
TopologyResult, for instance
t = topo[Clustering.SingleNodes] (or simply
t = topo[:SingleNodes]).
julia> path_to_im19 = joinpath(dirname(dirname(pathof(CrystalNets))), "test", "cif", "IM-19.cif"); julia> result = determine_topology(path_to_im19; structure=StructureType.MOF) AllNodes: rna SingleNodes: bpq julia> typeof(result) InterpenetratedTopologyResult julia> length(result) 1 julia> topo, n = only(result); julia> n # catenation multiplicity 1 julia> topo AllNodes: rna SingleNodes: bpq julia> typeof(topo) TopologyResult julia> genome_allnodes = topo[Clustering.AllNodes] rna julia> typeof(genome_allnodes) TopologicalGenome
In case where all clusterings lead to the same genome, it can simply be accessed by calling
Having obtained a
TopologicalGenome, the topological genome itself can accessed by converting it to a
julia> genome = PeriodicGraph(genome_allnodes) PeriodicGraph3D(6, PeriodicEdge3D[(1, 2, (0,0,0)), (1, 3, (0,0,0)), (1, 4, (0,0,0)), (1, 4, (0,0,1)), (1, 5, (0,0,0)), (1, 6, (0,0,0)), (2, 4, (0,0,1)), (2, 6, (-1,0,0)), (3, 4, (0,0,1)), (3, 5, (0,-1,0)), (4, 5, (0,0,0)), (4, 6, (0,0,0))])
In case of error during topology identification, the returned
genome is a
The string representation of the genome is simply
julia> string(genome) "3 1 2 0 0 0 1 3 0 0 0 1 4 0 0 0 1 4 0 0 1 1 5 0 0 0 1 6 0 0 0 2 4 0 0 1 2 6 -1 0 0 3 4 0 0 1 3 5 0 -1 0 4 5 0 0 0 4 6 0 0 0"