Reading and writing native SDF#

Exchanging explicit small-molecule chemistry without installing RDKit.

You can read a single V2000 or V3000 SDF record into native domains and write one selected structure back to SDF. This experimental adapter retains source hydrogens and explicit chemistry. It does not prepare a ligand for docking.

Reading a molecule#

import molsysmt as msm

source = msm.systems['caffeine']['caffeine.sdf']
molsys = msm.convert(source, to_form='molsysmt.MolSys')
assert molsys.topology.n_atoms == 24
assert molsys.topology.n_bonds == 25
assert msm.get(source, n_structures=True) == 1
assert msm.get(source, structure_indices=[], n_structures=True) == 0

The ten source hydrogens remain atoms. Formal charges and bond orders belong to chemical_states; coordinates belong to structures. SDF declares coordinates in angstroms. Native structures store nanometers, and writing converts explicitly back to angstroms regardless of your unit policy.

Inspecting chemistry before preparation#

assessment = msm.physchem.get_chemical_readiness(molsys)
assert assessment['fields']['formal_charge']['status'] == 'present'
assert assessment['connectivity']['declared_completeness'] == 'complete'
assert assessment['fields']['n_implicit_hydrogens']['status'] == 'missing'

The native reader retains explicit source chemistry. Its absent implicit-H counts remain unknown, even when all ten source H atoms have coordinates. The assessment neither adds atoms nor validates valence or chooses protonation. For complete supported graphs, optional detached aromatic perception is available through molsysmt.physchem.get_aromaticity(); see Getting aromaticity. It returns source-indexed atom/bond flags without filling the stored chemical state. Known contradictory flags fail. Use the chemical readiness guide to review coverage, source indices, declared evidence and unassessed checks before choosing a separate preparation operation. Select one structure explicitly when inspecting a trajectory.

Writing and selecting#

msm.convert(molsys, to_form='caffeine.sdf', ctfile_version='V3000')
msm.convert(molsys, selection=[0, 2, 4], to_form='selected.sdf')

With multiple structures, select exactly one using structure_indices=[index]. Without that selection, output fails. MolSys extraction with topology orders selected atoms by ascending source index; connectivity and coordinates use the same map. Output atom/bond serials are newly assigned sequential integers; they are read back as string IDs. V2000 writes four decimal places in coordinate fields and supports at most 999 atoms or bonds. Use V3000 for larger tables.

Preserving coordination#

V3000 coordination bonds (type 9) become bond_type='dative'. The adapter uses the first source endpoint as bond_donor_atom_index and the second as bond_acceptor_atom_index, matching the RDKit endpoint convention. This preserves declared orientation; it does not determine whether the atoms are suitable electronic donors or acceptors. Dative bonds do not merge native covalent components. Reading does not invent a covalent multiplicity for type 9.

Writing these relationships requires ctfile_version='V3000' and explicit donor/acceptor indices. It follows those roles even if the native table sorts the endpoints in the opposite order. Native numeric dative orders and custom component-joining assignments cannot be encoded; conversion reports identify their loss and strict mode rejects it. DISP=COORD and DISP=DATIVE source drawing styles are accepted but lost on native conversion, which reports the loss. Byte-preserving source copies retain them.

Inspecting information loss#

report = msm.get_conversion_report(molsys, to_form='caffeine_report.sdf')
assert report.to_form == 'file:sdf'
# The report query does not create caffeine_report.sdf.
print(report.outcome, report.is_exhaustive)

output, report = msm.convert(molsys, to_form='caffeine_report.sdf',
                             return_report=True)
print(report.outcome, report.is_exhaustive)

SDF cannot retain arbitrary native IDs/names, residue/chain hierarchy, partial charges, force-field parameters, interaction analyses or extra structural fields. Reports check chemical capabilities and selected native losses, but cross-form audits are not exhaustive. strict=True rejects detected losses before writing.

If your source has SD property blocks, ordinary native conversion raises an error because MolSys has no general storage for them yet. You can explicitly authorize their loss with discard_properties=True; return_report=True lists that loss, and strict mode still rejects it. To preserve source bytes and properties, use msm.copy(source, output_filename='source_copy.sdf').

An unselected SDF-to-SDF conversion (selection='all' and structure_indices='all') or file copy validates only the single-record envelope: header/count fields, M  END and $$$$. It preserves the original bytes even when their chemistry or legacy version marker is outside the native parser profile. For this identity operation, strict=True and an exhaustive exact report certify byte preservation, not chemical validity or docking readiness. Native conversion, attribute queries and selected copies still require supported chemical interpretation. Multiple records and malformed envelopes remain errors before the destination is opened.

Enabling stereochemistry explicitly#

Ordinary parsing remains native and rejects active stereo flags. To interpret tetrahedral and double-bond configurations, explicitly request the optional RDKit provider. All source hydrogen atoms remain on the same atom axis:

molsys = msm.convert('stereo.sdf', to_form='molsysmt.MolSys',
                     stereo_engine='rdkit')
msm.convert(molsys, to_form='stereo_out.sdf', ctfile_version='V3000',
            stereo_engine='rdkit')

Assignment is performed on the full graph before atom extraction. Absolute atom labels are stored in chemical_states; double bonds retain the relative cis/trans relation and explicit reference atoms. Use Getting CIP stereochemistry to query absolute E/Z independently. Writing verifies the encoded configuration by reading the payload back before opening the destination. Incompatible geometry or unencodable labels raise instead of overwriting the file. Byte-preserving identity copies need no stereo provider. Converter options are not validated by the public preflight report. Native subset extraction can invalidate a center’s chemical configuration; requesting serialization does not repair or guess the missing chemistry.

Checking chemical atom types#

You can check extracted atom types independently of their source form. Atom types are chemical element symbols, distinct from atom names and force-field types. Normalization preserves unspecified isotopes and recognizes the source hydrogen aliases D and T:

atom_types = msm.get(molsys, element='atom', atom_type=True)
assert msm.element.atom.is_atom_type(atom_types).all()
assert msm.element.atom.normalize_atom_types(['D', 'T', 'C']) == (
    ['H', 'H', 'C'], [2, 3, None]
)

The SDF reader uses the same explicit-symbol normalization internally. It does not infer elements from atom names or AutoDock labels. The public tools inspect values; they do not modify molsys.

Warning

The default native subset rejects active stereo flags/labels. The explicitly enabled provider interprets supported tetrahedral and double-bond encodings, including wedges and supported 3D source configurations. Both routes reject query atoms/bonds, valence overrides, reaction maps, Sgroups and unsupported radical/bond types. Explicit V3000 flags with their documented inactive value of zero are accepted; an unknown field with value zero is still an error. Coordination type 9 is supported only in V3000, not as a nonstandard V2000 extension. Writing rejects atom aromaticity that the supplied bond types cannot retain. It does not sanitize valence, infer hydrogen counts, perceive aromaticity from Kekule bonds or derive CIP labels from 3D coordinates by default. The optional provider performs the requested CIP analysis; enhanced stereo and parity-only encodings without a supported wedge/3D interpretation remain unsupported. Multiple SDF records are rejected. Keep the original file when it carries unsupported information.

Assigning named parameter types#

Once the graph, chemical state and indexed H satisfy the explicit prerequisites, use molsysmt.physchem.get_autodock_atom_types() for detached auditable labels or molsysmt.build.assign_autodock_atom_types() for a new native MolSys with named mechanical provenance. These tools classify chemical context; they do not prepare missing chemistry, calculate partial charges or merge H. See Getting AutoDock types for the exact bounded rules and Assigning AutoDock types for projections and PDBQT summaries.