CIFlat

Documentation

Getting started

There are two ways to use CIFlat: the hosted web app, which needs no installation, or the Python program, from the command line or from your own code. Both run the same conversion and the same checks.

The web app

  1. Open the hosted app on Hugging Face.
  2. Drop one or more structure files onto Structure files. A CIF is the main input; .mmcif, .mol, .sdf and .smi files are accepted too.
  3. Leave the options at their defaults, or change them (see below), and start the conversion.
  4. Look at the previews and their status, then download the files: CDXML, MOL (V3000) and a PNG preview for each fragment.

A structure takes about a minute on the free CPU tier. Polynuclear or heavily disordered structures take longer.

Options on the web form and what they do
OptionChoices (default first)Effect
Document styleacs, acs-wide, classic, defaultBond length and label size of the drawing. acs follows the ACS Document 1996 proportions (14.4 pt bonds, 10 pt labels). acs-wide gives crowded metal clusters more room.
Formal chargesall, no-metals, noneWhich charges are drawn. no-metals leaves the metal centres bare for you to annotate.
Delocalised ambidentate groupsbridging, none, allDraws carboxylate, carbonate, nitro, sulfonate, thiocyanate and azide groups with equal bonds and no charge: only those spanning more than one metal or chelating (bridging), every one, or none.
Metal–ligand bondsplain lines, coordination arrowsPlain lines are the ACS convention. Arrows point from donor to metal.
Largest fragment onlyoff, onDrops solvent and counter-ions. By default every fragment is drawn.
Polymers and wheelsautomatic, whole assembly, 1–3 repeat unitsHow much of a polymer or a large wheel to draw. Automatic draws one bracketed repeat unit of a chain.
Drawings per structureseveral to compare, oneSeveral searches run side by side; the best is kept and the others are shown as alternatives.
Layout time budget45 s (15–180 s)Time for the layout search per fragment. Above 60 s, several searches run in parallel and the best drawing is kept.
Element coloursnoneColours the labels of chosen elements. Bonds stay black and the layout is unchanged.

After a run with several drawings, the app may ask Which drawing do you prefer? Answering is optional; see Privacy for what an answer records.

Installing the program

CIFlat is written in Python and needs no GPU and no database. The source code is not yet public ([source code location to be announced]). Once you have it, create a virtual environment and install the pinned dependencies:

uv venv .venv
uv pip install --python .venv/bin/python -r requirements.txt

requirements.txt pins the tested stack: RDKit, NumPy, SciPy, Pillow, NetworkX and Indigo, at exact versions. Drawings are reproducible within the same pinned environment, options and input. An optional compiled layout core written in Rust makes the layout search about 1.98 times faster. It is switched on with CIFLAT_CORE=native and falls back to Python, without error, when it is not installed.

The command line

The program runs as a Python module. Convert one file:

python -m ciflat convert NAME.cif -o outputs

The output files are named after the input and written to outputs/. The command prints the status of each fragment, the result of every whole-structure check that did not pass, and any warnings. Add -v to list every check and every file written.

The exit code is the status
Exit codeStatus
0ready
1needs_review
2invalid, or the conversion failed

To look at the structural analysis without writing anything (formula, charge, coordination numbers, bridges, and every metal–donor contact with its distance and classification):

python -m ciflat analyse NAME.cif

Useful options

--mode fast|normal|slow
Effort of the layout search. normal (the default) gives each fragment a 45 s budget. slow runs several searches with different seeds in parallel and keeps the best drawing. --time-budget S sets the budget directly, and --seed N chooses the seed.
--style acs|acs-wide|classic|default
Drawing proportions, as on the web form.
--charges all|no-metals|none and --delocalise none|bridging|all
Display conventions for charges and ambidentate groups. They do not change the chemistry written to MOL and SMILES.
--dative-bonds
Draws metal–ligand bonds as coordination arrows instead of plain lines.
--semi-coordinate auto|all|essential
Which long (semi-coordinate) contacts appear in the picture. auto leaves out only the dashed contacts that would make an otherwise planar drawing non-planar. They are listed in the report and kept in a separate _all_contacts.cdxml drawing.
--condense auto|always|never
Abbreviations such as NO2, OMe or Ph, chosen before layout. In a fragment with four or more metals, auto also writes terminal alkoxides and longer alkyl groups as labels. A group is never abbreviated when an atom inside it is bonded to a metal.
--excerpt N
Draws N consecutive repeat units of a polymer or wheel. 0 never cuts.
--largest-only, --keep-hydrogens
Keep only the largest fragment, or draw hydrogen atoms explicitly.
--alternatives
Also writes expanded, all-contact and dative drawings where they apply.
--colour Cu=blue --colour N=red
Colours element labels. The layout is unchanged.
--charge N
Sets the total charge of the species. The default, auto, keeps ordinary metal oxidation states without forcing a total.

Bond-perception tolerances are not cosmetic

--covalent-tolerance, --metal-tolerance and --semi-tolerance change which contacts count as bonds, and so change the chemistry. The defaults are the values described in How it works.

Run python -m ciflat convert --help for the full list.

Batch mode

Convert every file in one or more directories, several at a time:

python -m ciflat batch structures/ -o outputs -j 2

-j sets the number of worker processes (at most 8; the default is 2). --subdirs puts each structure's files in a folder of its own. Batch mode writes outputs/manifest.json, which records each input's status, the files written, the warnings and the verification. Its exit code is the worst status among the inputs.

The Python API

Load a file, then convert a molecule:

from ciflat.io.sources import load
from ciflat.pipeline import convert

source = load("NAME.cif")
result = convert(source.molecules[0], "outputs", "NAME")
print(result.status)      # "ready", "needs_review" or "invalid"
print(result.summary())   # formula, charge, metal centres, drawing quality
print(result.files)       # paths of the files written

load returns a source whose molecules are the fragments of the structure: the complex, counter-ions and solvent. To convert a whole input with the same whole-structure checks as the command line and the web app, use convert_source:

from ciflat.pipeline import ConversionOptions, convert_source

results, overall = convert_source(source, "outputs", "NAME", ConversionOptions())
for r in results:
    print(r.status, r.summary())
print(overall.as_dict()["summary"])

Outputs

For each fragment drawn (NAME is the stem; a structure with several fragments gets NAME_1, NAME_2, …):

FileContents
NAME.cdxmlThe editable drawing, the primary output. Open it directly in ChemDraw to keep CIFlat's layout. A cleanup there, or an import from SMILES, computes a different layout.
NAME_V3000.mol, NAME_V2000.molThe full chemistry, with abbreviations expanded and every contact kept. V3000 writes a π-bonded ring as one multicentre bond. The V2000 file is written when the structure is small enough for that format.
NAME.smiSMILES of the same chemistry.
NAME.svg, NAME_preview.pngPreviews of the drawing.
NAME_coordination.csvEvery metal–donor contact, with its distance, its classification, and whether it is drawn.
NAME_report.jsonThe report (below).
NAME_all_contacts.cdxmlWritten when contacts were left out of the main drawing: the same drawing with them put back.

With --alternatives, the program also writes NAME_expanded.cdxml (no abbreviations), NAME_dative.cdxml and NAME_dative_V3000.mol where they apply.

The report

Every conversion writes a report so that a result can be checked rather than trusted. NAME_report.json holds:

A result marked needs_review therefore says what to look at. See Checks and statuses.