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.

How can I silence warnings or remove exports?

For exports, each export_... keyword argument to Options can be individually set to false or "" to silence this particular export. See also the paragraph on export options.

How can I check that the detected topology corresponds to my input?

This is the focus of the Visualization tutorial.

How can I choose a particular clustering algorithm?

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

The 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

Note that Auto is equivalent to both AllNodes and SingleNodes when the StructureType is set to MOF.

See below for the reason why the Standard topology is xbi and not ToposPro's "3,4,8T15".

CrystalNets.jl incorrectly detects bonds or reports a periodic structure as "non-periodic"

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 cutoff_coeff, 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 Options.

The topology has a name given by ToposPro but CrystalNets.jl yields "UNKNOWN ..."

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.

Why is the topology computed with Standard different from ToposPro's standard?

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 split_O_vertex=false in the Options.
  • a paddle-wheel pattern is grouped into a single cluster by CrystalNets.jl but not by ToposPro. To solve this, use detect_paddlewheels=false in the Options.

How can I do a database topology analysis with CrystalNets.jl?

The built-in way to do this consists in using the determine_topology_dataset function, or guess_topology_dataset in some cases. These functions expect the path of a directory containing CIF files within (possibly in subdirectories).

How can I directly access the genome of my structure instead of its name?

The result of determine_topology is either a TopologicalGenome or a Vector{Tuple{Vector{Int},TopologyResult}}, depending on whether the input contains multiple interpenetrating subnets or not. In the second case, extract the relevant TopologyResult.

A TopologyResult can store the result for different clustering options, so the topological genome should be chosen by extracting the relevant result. For example:

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)

julia> genome_allnodes = result[Clustering.AllNodes]

julia> typeof(genome_allnodes)

In case where all clusterings lead to the same genome, it can simply be accessed by calling first(result).

Having obtained a TopologicalGenome, the topological genome itself can accessed by converting it to a PeriodicGraph:

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))])

The string representation of the genome is simply string(genome):

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"