跳转至

Python API

Everything the omgkit module exposes. Signatures and descriptions are generated from the source, so they cannot drift away from the code.

Import once

import omgkit

There are no submodules to reach into — the five reading functions and the seven classes below are the whole surface.

At a glance

Callable What it gives you
parse_smiles a Mol from a SMILES string
parse_smarts a Query from a SMARTS pattern
parse_reaction a Reaction from a reaction SMARTS
parse_molblock a Molblock from the contents of a .mol file
read_sdf a list of SdfRecord from the contents of an .sdf file
Mol a molecule — sanitize it, write it back out
Query a substructure query — match it against a molecule
Reaction a reaction template — run it on reactants
Outcome one result of running a reaction
Conformer a 3D structure — coordinates plus the molecule they belong to
Molblock one record read from a .mol/.sdf file — molecule plus its coordinates
SdfRecord one entry of an .sdf — that molblock, its data fields, or why it could not be read

3D coordinates

Mol.conformer() turns a molecule into one 3D structure. It is deterministic — no random seed, no retries you have to configure; the same molecule always comes back with the same coordinates.

>>> import omgkit
>>> conf = omgkit.parse_smiles("C[C@H](N)C(=O)O").conformer()
>>> conf
<omgkit.Conformer atoms=13 energy=0.000e0 converged=True chiral=1/1>
>>> conf.mol.num_atoms          # 13, not 6 — generation adds explicit hydrogens
13
>>> conf.coords[0]
(-1.1906..., -0.8985..., -0.0731...)

The coordinates belong to conf.mol, not to the molecule you called it on

Generation needs explicit hydrogens, so it works on a copy and adds them there. Your molecule is left untouched; conf.coords lines up with conf.mol, which has more atoms.

ValueError is raised when the molecule cannot be sanitized, or when its distance bounds contradict each other (about 1 molecule in 8831 on a drug-like corpus). The message says which.

Saving it

Conformer.to_molblock() gives you the contents of a .mol file. An .sdf is those records separated by $$$$:

with open("out.sdf", "w") as f:
    for smi in ["CCO", "C[C@H](N)C(=O)O"]:
        conf = omgkit.parse_smiles(smi).conformer()
        f.write(conf.to_molblock(title=smi))
        f.write("$$$$\n")

Aromatic bonds are kekulized on the way out — a molblock has no aromatic bond type, and writing one as a single bond would turn thiophene into tetrahydrothiophene without saying so. The second line of the block is the program name with no timestamp, so writing the same molecule twice gives byte-identical output.

Reading .mol files

parse_molblock reads one V2000 molblock — the contents of a .mol file, or one record of an .sdf up to its $$$$:

>>> m = omgkit.parse_smiles("C[C@H](N)O"); m.sanitize()
>>> block = m.to_molblock_2d(title="C[C@H](N)O")   # or: open("aminoethanol.mol").read()
>>> rec = omgkit.parse_molblock(block)
>>> rec
<omgkit.Molblock 4 atoms 2D "C[C@H](N)O">
>>> rec.mol.to_canonical_smiles()   # the wedge bond in the file is what makes this @
'C[C@H](N)O'
>>> rec.is_3d, len(rec.coords), rec.coords[0]
(False, 4, (-0.866, -0.5, 0.0))

It sanitizes for you — and it has to

The other parse functions hand back an un-sanitized molecule and leave the timing to you. This one cannot. A molblock keeps half its stereochemistry in the coordinates and wedge bonds, which live outside Mol; reading them onto the atoms needs implicit-hydrogen counts and symmetry classes, and both of those come out of sanitization. Leaving that step to the caller would mean a forgotten call silently drops the stereochemistry of the whole file — no error, same atom count, just no @ and no /.

3D files take a different route, and it is also taken for you

Stereochemistry in a 3D file lives in the coordinates themselves, so it is read from the signed volume of a centre's four ligands and from the sign of the torsion across a double bond — not from wedges and not from a planar projection. Which route runs is decided by the coordinates (any non-zero z makes it 3D), so the caller does not have to choose.

On the 8795 3D records of the reference corpus, 8779 come back identical to what the external implementation reads from the same bytes, 0 come back as a different molecule, and 16 differ only in stereochemistry — 12 of those are non-tetrahedral centres (@TB/@OH/@SP), which this route does not read, and 4 are trivalent phosphorus, where the two implementations draw the line in different places. The gate that measures this is harness/check_molblock3d_read.py.

Bonds the file marks as explicitly unknown — a crossed double bond, a wavy single bond — are left unassigned rather than being read off the drawing.

Reading .sdf files

read_sdf reads every record of an SDF at once:

for i, rec in enumerate(omgkit.read_sdf(open("library.sdf").read())):
    if rec.error:
        print(f"record {i}: {rec.error}")
        continue
    print(rec.block.mol.to_canonical_smiles(), dict(rec.data))

A record that cannot be read does not raise, and does not vanish

Raising would stop at the bad record and throw away everything after it. Skipping would make the count quietly smaller than the file's — the caller counts records and gets a different number, with nothing anywhere reporting it. So every record keeps its place in the list: a bad one has error set to a sentence and block set to None, and the ones after it read fine.

Real files have these. A ferrocene-type complex has more bonds on the metal than V2000 can express, so writers emit V3000 for it — and V3000 is refused here rather than misread.

data is a list of pairs, not a dict: repeated field names do occur (a vendor writing one line per measurement), and a dict would silently keep only the last. Values spanning several lines are joined with \n, and a line that starts with > inside a value is part of the value, not a new field.

The whole file is parsed at once — budget peak memory accordingly for very large libraries.

Writing .mol files

Two methods, for the two kinds of file:

Method Where the stereochemistry lives
2D Mol.to_molblock_2d wedge bonds (column 4 of the bond block)
3D Conformer.to_molblock the coordinates themselves

To draw the conformer instead of writing it out, Conformer.to_svg gives a space-filling, ball-and-stick, stick or wireframe figure, and Conformer.depiction_3d_report tells you where each atom landed on that canvas — see 3D molecule figures.

open("alanine.mol", "w").write(
    omgkit.parse_smiles("C[C@H](N)C(=O)O").to_molblock_2d(title="alanine")
)

The 2D file may have more atoms than your molecule

To draw a centre's configuration the layout sometimes has to add an explicit C–H — the wedge goes on exactly that bond. A centre whose configuration the layout cannot draw is left without a wedge rather than given an arbitrary one: the file then says "no stereo here", which is true, instead of saying something false.

Both are deterministic: same molecule in, byte-identical file out, no timestamp on the second line.

Descriptors for machine learning

Two methods hand you everything a graph neural network reads, one call each: Mol.atom_descriptors (twelve values per atom) and Mol.bond_descriptors (seven per bond).

>>> m = omgkit.parse_smiles("CC(=O)O"); m.sanitize()
>>> m.atom_descriptors()[0]["hybridization"]
'sp3'
>>> m.atom_descriptors()[0]["gasteiger_charge"]
0.0337...

Categorical values come back as names ("sp3", "ccw", "aromatic"), never as one-hot vectors or integer codes — the vocabulary is your featurizer's decision, not the library's. electronegativity is None for elements with no accepted Pauling value, and gasteiger_valid is False where the charge could not be computed; neither is filled in with a default, because a default merges "unknown" with "happens to be that number".

See the descriptors guide for the full table, a worked one-hot encoder, and why double-bond geometry is reported as cis/trans rather than Z/E.

Errors

Every parse function raises ValueError on malformed input. Mol.sanitize raises ValueError when the molecule cannot be sanitized — for instance an impossible valence.

parse_smiles puts a caret view in the message pointing at the offending character:

>>> omgkit.parse_smiles("CC(C")
Traceback (most recent call last):
  ...
ValueError: CC(C
    ^ 括号不匹配

sanitize is in place and may leave a partial result

If sanitization fails, the molecule may already have been modified. Callers that need all-or-nothing should copy() first. The binding deliberately does not add that copy for you: a hidden deep copy would double the cost of every batch, and the caller would have no way to know.


Parsing

omgkit.parse_smiles builtin

parse_smiles(smiles)

解析 SMILES,返回一个 Mol

解析失败时抛 ValueError,消息里带插字号视图指出出错在第几个字符。

只解析,不净化。 芳香标志、环信息、隐式氢数、杂化都还是空的 —— 要用它们(或者要写规范 SMILES、要画图、要描述符)先调 Mol.sanitize()

omgkit.parse_smarts builtin

parse_smarts(smarts)

解析 SMARTS。

omgkit.parse_reaction builtin

parse_reaction(smarts)

解析反应 SMARTS(反应物>试剂>产物 的三段式)。

omgkit.parse_molblock builtin

parse_molblock(text)

读一条 V2000 molblock(.mol 文件的内容,或 .sdf$$$$ 之前的一段)。

读不出来时抛 ValueError,消息里说明是哪一行的什么字段 —— V3000 会被明确 拒收,而不是当成 V2000 硬读出一个错分子。

它替你多做了两步,而且必须多做

别的解析函数(如 parse_smiles)交回来的是没净化的分子,由调用方自己 决定什么时候净化。这里不一样:

  • SMILES 的立体写在串里,净化推迟不丢任何东西;
  • molblock 的立体一半在坐标与楔形里,而那两样在 Mol 之外。给它们打上 标记要先知道每个原子有几个隐式氢、要用对称等价类 —— 两样都是净化算出来的。

所以顺序只能是"读 → 净化 → 回来打立体标记",而中间那一步一旦交给调用方, 漏了不会报错,只会静默地把整个文件的立体丢掉。与 Mol.sanitize 把顺反感知并进来是同一个理由:绑定层是给人直接用的,把必须成对的两步拆开 就是个陷阱。

二维三维都读立体

二维靠楔形定手性、靠平面投影定顺反;三维靠有符号体积定手性、靠二面角定顺反。 走哪条由坐标自己说了算(有任何一个 z 不为零就是三维),调用方不必分。 读出来之后的那两步:净化,然后回来打立体标记。只有这一处

单条(parse_molblock)与整份 SDF(read_sdf)都走它。两处各写一遍的话, 迟早一边打了立体、另一边没打,而那种差别不报错。

omgkit.read_sdf builtin

read_sdf(text)

逐条读一个 SDF(.sdf 文件的内容),返回 list[SdfRecord]

读不了的那条不抛异常,也不消失

抛异常会停在坏记录上,后面几千条一起丢掉;静默跳过会让分母悄悄变小 —— 调用方数出来的条数与文件里的不符,而没有任何地方报错。两种都不行。

所以每条都在返回的列表里占一个位置:读不了的那条 error 是一句话、 blockNone,后面的照读不误。怎么处理由调用方决定:

for i, rec in enumerate(omgkit.read_sdf(text)):
    if rec.error:
        print(f"第 {i} 条读不了:{rec.error}")
        continue
    print(rec.block.mol.to_canonical_smiles(), rec.data)

真实语料里这一档是有的:金属茂类配合物的键数超出 V2000 的表达能力,写出方 自己就换成了 V3000,而 V3000 这里明确拒收。

整份读进内存

文本本来就整份在内存里(参数就是个 str),这里再把每条都解析出来。 超大文件(几十万条)的峰值内存要按这个估。

立体与 parse_molblock 同一条路

每条都是"读 → 净化 → 回来打立体标记",与单条那个函数共用同一段代码。 三维文件的立体同样读得出来(有符号体积定手性、二面角定顺反), 走哪条由坐标自己说了算 —— 理由见 parse_molblock


Mol

omgkit.Mol

一个分子。

对应 Rust 侧的 MolBuilder —— 可变的、逐分子的表示,适合建图与改写。 列式的 MolBatch 是另一件事,等批处理接口再暴露。

__doc__ class-attribute

__doc__ = '一个分子。\n\n对应 Rust 侧的 `MolBuilder` —— 可变的、逐分子的表示,适合建图与改写。\n列式的 `MolBatch` 是另一件事,等批处理接口再暴露。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

atomic_nums property

atomic_nums

逐原子的原子序数,按存储顺序。

返回 list[int]不能直接返回 Vec<u8> —— PyO3 把它特判成 bytes,于是 mol.atomic_nums 会得到 b'\x06\x08' 这种东西: 索引出来仍是 int,长度也对,但类型错了,而且错得很安静。

bonds property

bonds

逐键的 (起点, 终点, 键级),按存储顺序。返回 list[tuple[int, int, float]]

键级是数值:单键 1.0、芳香 1.5、双键 2.0、三键 3.0、四重 4.0、 未指定 0.0(配位键按 1.0 记)。这是 BondOrder::as_double() 的值 —— 不在绑定层另发明一套编号,那种编号只有 Python 这边有,Rust 侧的判据 一概盖不到。

formal_charges property

formal_charges

逐原子的形式电荷,按存储顺序。返回 list[int]

num_atoms property

num_atoms

原子数。

num_bonds property

num_bonds

键数。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).

atom_descriptors method descriptor

atom_descriptors()

逐原子的描述符,按存储顺序。返回 list[dict]

每个字典 12 个键:

类型 说明
atomic_num int 元素种类。0 是通配原子 *
total_degree int 显式邻居数 + 总氢数
formal_charge int 形式电荷
chiral_tag str 手性标记的几何类别,不是 R/S
total_num_hs int 显式声明 + 隐式推断,不含独立的 [H] 原子
hybridization str 杂化
is_aromatic bool 是否芳香
is_in_ring bool 是否在环上
mass float 标了同位素用该核素的精确质量,否则用标准原子量
electronegativity float | None Pauling 电负性
gasteiger_charge float Gasteiger 部分电荷
gasteiger_valid bool 上一项算不算得出来
交的是描述符,不是编码

分类量给的是名字("sp3""ccw"),不是 one-hot,也不是整数编号。 词表该收哪些元素、留不留"其它"兜底桶,是特征化那一侧的决定 —— 在这里定死等于把某一个模型的词表焊进库里。

两处"算不出"

electronegativityNone 表示该元素没有公认的 Pauling 值 (稀有气体、Pm/Eu/Tb/Yb/Fr 等);gasteiger_validFalse 表示该原子 落在 Gasteiger 参数表之外(多数金属),此时 gasteiger_chargenaninf,并且会沿着图扩散——同一个分子里的碳也可能因此失效。 两者都如实交出,不拿 0 顶:那会让"不知道"和"恰好是 0"变成同一格。

前置

要先 sanitize()。没净化的分子不会报错,只会让芳香、环、杂化、共轭、 隐式氢数全是解析时的占位值。

bond_descriptors method descriptor

bond_descriptors()

逐键的描述符,按存储顺序。返回 list[dict]

每个字典 7 个键:beginend(两端原子下标)、order(键级的名字)、 is_conjugatedis_in_ringstereo(双键顺反)、stereo_atoms

顺反是 cis/trans,不是 Z/E

Z/E 按 CIP 优先级定义,而 CIP 排序本库没有实现。这里给的是"相对 stereo_atoms 那两个参照原子"的顺反。两项必须一起看:四取代双键上 参照挑得不同,同一个几何会得出相反的顺反值。带上参照之后,顺反与 Z/E 承载的几何信息相同,要 Z/E 的调用方自己排 CIP 换算。 没有顺反时 stereo_atomsNone

前置

要先 sanitize()。顺反尤其要注意:它由净化之后那一步方向键折算填写, 而 sanitize() 已经把两步并在一起了 —— 只跑 Rust 侧的净化不够,那样 每根双键的 stereo 都会是 "none",而且不报错。

conformer method descriptor

conformer()

生成一个三维构型。

不改动本分子:内部先深拷贝一份,在那一份上净化、感知顺反、补显式氢, 再生成。所以返回的 Conformer 里那个 mol 的原子数通常 比这里多(多出来的是氢),而 coords 对应的是的原子表,不是这个。

走的是 Rust 侧的 omgkit_conf::pipeline::conformer_for —— 那五步的顺序 与理由都在库里,绑定这一层一步化学都不做。

全程无随机数:同一个分子每次都给同一组坐标。

净化过不去、界矩阵自相矛盾时抛 ValueError

copy method descriptor

copy()

深拷贝。

depiction_report method descriptor

depiction_report()

画这个分子时没能画好的地方,返回 dict[str, list]

内容
degraded 布局不得不退化的地方(桥环等),每项一个说明串
unresolved 消冲突之后仍然挤在一起的原子对
crossings 仍然交叉的键对
unwedged 没能画出构型的立体中心。配位几何(@SP/@TB/@OH)这一版画不出来,一律在这里
misdrawn_stereo 画出来的几何与记录的顺反不符的双键。八元以上的环里的反式双键会落在这里 —— 环按凸多边形画,环内双键一律画成顺式

五个都是空的,这张图才把分子完整地表达出来了。

下标相对被画的那个分子 —— 为了承载楔形可能补了显式氢,那时原子数比 num_atoms 大;前 num_atoms 个与本分子逐项对应。

净化过不去时抛 ValueError

remove_hs method descriptor

remove_hs()

把可以合并的显式氢并进邻居的氢计数,返回删掉的氢原子数。

就地修改,而且原子下标会全部改变 —— 删原子必然重排下标。带同位素、 电荷、映射号、自由基的氢,以及桥氢与承载双键方向的氢都会留着:多留一个 只是图里多个节点,删错会丢信息。

sanitize method descriptor

sanitize()

跑净化管线,再把双键的方向键换算成双键自己的顺反属性。

就地修改,失败时抛 ValueError

失败后分子可能已被部分修改 —— 需要"要么全成功要么不动"的调用方 应当先 copy()。这一条与 Rust 侧的语义一致,不在绑定层偷偷加保护: 悄悄多做一次深拷贝会让批处理的开销凭空翻倍,而调用方无从知情。

为什么这里要多做一步顺反感知

净化那 12 步里没有它 —— 感知要用对称等价类,那在净化的上一层, 调不到(理由见 omgkit_io::stereo 的模块文档)。Rust 侧因此约定由调用方 在净化之后自己调一次。

可这条约定放到 Python 这边就成了陷阱:只有方向键的分子一旦被 [PyReaction::run] 编辑,承载方向的那根单键可能被删掉,双键明明没被碰过, 几何却跟着没了 —— 产物照样合法、原子数照样对,只是顺反悄悄丢了。

方向是写法,顺反是性质。感知一次之后信息记在双键上,只要两个参照 原子还在就活得下来。绑定层是给人直接用的,把这一步并进来比留一条要人 记住的约定稳妥。

to_canonical_smiles method descriptor

to_canonical_smiles()

写成规范 SMILES —— 同一个分子无论原子怎么编号,都得到同一个字符串。

to_molblock_2d method descriptor

to_molblock_2d(title='')

画一张二维结构图,写成 V2000 molblock(.mol 文件的内容)。

不改动本分子:内部先深拷贝一份,在那一份上净化、感知顺反、排布局。

立体靠楔形,不是坐标

二维图的手性写在键块第四列(1 实楔、6 虚楔)。为了把某个中心的构型画 出来,布局有时要补一根显式 C–H —— 楔形恰恰打在那根键上。所以写出去 的原子数可能比这个分子多,与 Conformer 那边同理。

画不出构型的中心不会被硬画:那种中心在文件里就是"没写立体",而不是 随便给一个。

Conformer.to_molblock 的分工

那个写三维:立体在坐标本身里,楔形是空的。这个写二维:所有 z 都是 0,立体全靠楔形。两种文件都合法,读的人按坐标是不是平的自己分。

芳香键会先凯库勒化,理由与三维那条一样。第二行是程序名,不写时间戳。

净化过不去、或者分子大到 V2000 装不下(原子或键超过 999)时抛 ValueError

to_smiles method descriptor

to_smiles()

写成 SMILES,按当前的原子存储顺序。


Query

omgkit.Query

一个 SMARTS 查询。

__doc__ class-attribute

__doc__ = '一个 SMARTS 查询。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

num_atoms property

num_atoms

查询里的原子数。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).

match method descriptor

match(mol, *, uniquify=True, max_matches=0, use_chirality=True)

找出这个查询在分子里的全部匹配。

返回 list[list[int]],每个内层列表按查询原子顺序给出对应的分子原子下标。

每次调用都重新算一遍分子的查询性质(环成员数、最小环大小等)。不缓存: sanitize() 之类的操作会就地改分子,缓存一旦失效就会静默给出错答案, 而那种错比多算一遍贵得多。要在同一个分子上匹配很多模式时,这一层 目前还没有复用入口。


Reaction

omgkit.Reaction

一条反应模板。

__doc__ class-attribute

__doc__ = '一条反应模板。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

num_product_templates property

num_product_templates

产物模板的个数。

num_reactant_templates property

num_reactant_templates

反应物模板的个数。调 run 时要给同样多的分子。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).

run method descriptor

run(reactants, *, max_products=0, atom_mapping=False, byproducts=False)

对一组反应物跑这条反应。

每个反应物模板配一个互不相同的输入分子;分子数与模板数不等时返回 空列表 —— 那一档是 run_on_substrate 的形状(多个片段落在同一个分子上)。

递入顺序不影响出不出产物

位置不是化学。 谁先谁后是你敲键盘的顺序,不该决定这条反应跑不跑得 起来。所以顺序对不上时不会交白卷:引擎先试"第 i 个配第 i 个",给不出 产物才去找别的一一对应。顺序本来就对得上时,开销与只试那一种完全相同。

于是返回空只剩一个意思:这批分子上没有反应位点

这一条是量出来的:USPTO-50k 正向语料按记录自带的分子顺序直接调用, 约 689 条交白卷;抽样 4000 条逐条核过,其中 59 条全部只是顺序对不上, 没有一条是真匹配不上 —— 而调用方拿到的是同一个空列表。

atom_mapping 为真时,每个结果的 reactants 填上带映射号的反应物副本, 产物侧对应原子打同一个号 —— 两侧合起来就是一条完整的原子映射反应。 byproducts 为真时,把模板丢弃的原子收口成分子填进 Outcome.byproducts。默认关闭:收口要在副本上 净化产物才算得出氢预算,不是零开销。

run_on_substrate method descriptor

run_on_substrate(substrate, *, max_products=0, atom_mapping=False, byproducts=False)

把整个反应物侧当作一张图上的查询来跑,而不是按位置配对。

run 要求"第 i 个分子配第 i 个模板片段",于是模板片段数 比分子数多时直接返回空 —— 而那正是分子内反应的形状:两个片段落在 同一个分子上。本方法把输入拼成一张图,让每个片段在整张图上自由找位置, 只要求各片段匹配到的原子两两不重叠。

  • 分子间:与 run 结果一致,且不必再枚举输入的排列
  • 分子内:run 表达不了的那一档
  • 盐:阳离子与阴离子是同一个分子的两个组分,模板可以同时碰到

代价是搜索空间变大,耗时不如 run 可预测;要稳定耗时就用 run


Outcome

omgkit.Outcome

一组产物,连同(可选的)带映射号的反应物副本。

__doc__ class-attribute

__doc__ = '一组产物,连同(可选的)带映射号的反应物副本。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

byproduct_budget property

byproduct_budget

收口的原子账,键为 open_valence / fragment_hydrogens / delta_h / need / remaining / delta_charge / fragment_charge / charge_shift。用来自己复核结论。

byproduct_verdict property

byproduct_verdict

收口的结论:"nothing" / "capped" / "bonded(n)" / "unresolved(原因)";没开 byproducts 时为 "off"

"unresolved(...)"byproducts 必然为空 —— 收不了口就不给分子, 编一个出来比不给更糟:它拓扑合法、能净化、看不出破绽,只是错的。

byproducts property

byproducts

收口出来的副产物。只在 run(..., byproducts=True) 且账闭合时非空。

discarded property

discarded

discarded[i] = 第 i 个输入分子里没有进入任何产物的原子下标。

这是事实,与收不收得了口无关 —— 收口失败时它照样有值,而那正是最 需要它的时候。

products property

products

产物,每个产物模板一个分子。

reactants property

reactants

带原子映射号的反应物副本。只在 run(..., atom_mapping=True) 时非空。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).


Conformer

omgkit.Conformer

一个三维构型。

Mol.conformer() 产出。里面既有坐标,也有坐标对应的 那个分子 —— 生成时补了显式氢,原子表与原分子不是同一份。

__doc__ class-attribute

__doc__ = '一个三维构型。\n\n由 `Mol.conformer()` 产出。里面既有坐标,也有**坐标对应的\n那个分子** —— 生成时补了显式氢,原子表与原分子不是同一份。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

chiral_ok property

chiral_ok

其中在交付坐标上号正确的个数。应当等于 chiral_total —— 不等就是把某个中心摆成了对映体。

chiral_total property

chiral_total

手性中心总数。

converged property

converged

精修有没有收敛(梯度降到阈值以下)。

coords property

coords

逐原子的 (x, y, z),单位 Å,顺序与 mol 的原子表一致。

energy property

energy

精修之后的误差函数值。0 表示所有距离都落进了界内。

energy_before property

energy_before

精修之前的误差函数值 —— 与 energy 一起看才知道精修干了多少活。

iterations property

iterations

精修迭代了多少次。

mol property

mol

坐标对应的那个分子(补过显式氢的那一份)。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).

depiction_3d_report method descriptor

depiction_3d_report(style='ball-and-stick')

三维图的诊断:视角定不定得下来,以及每个原子落在画布哪里。

返回的字典:

内容
style 用的哪套样式
widthheight 画布尺寸(磅)
degenerate 主轴不唯一。对称性强制两个主惯量相等时为真(甲烷、四氯化碳、氨、乙炔)。图不是错的,但它的取向没有承载任何信息 —— 别照着它比两个分子的姿态
atoms 每个原子一项:xy(画布坐标,磅)、radius(球半径,磅,不画球的样式是 0)、depth(深度,Å,越大越靠前)

想在图上加标注就用 atoms —— SVG 里的圆没有原子号,从图形反推是猜。

to_molblock method descriptor

to_molblock(title='')

写成 V2000 molblock(.mol 文件的内容),末尾带 M END

.sdf 时每条后面接数据字段和 $$$$:

with open("out.sdf", "w") as f:
    for smi in smiles_list:
        conf = omgkit.parse_smiles(smi).conformer()
        f.write(conf.to_molblock(title=smi))
        f.write("$$$$\n")

芳香键会先凯库勒化 —— molblock 里没有"芳香键"这回事,留着它写出去 要么歧义、要么被读成饱和环。凯库勒化失败(比如芳香体系里有通配原子)时 抛 ValueError,不写一个读回来是另一个分子的文件。

第二行写的是程序名,不写时间戳 —— 同一个分子每次写出都逐字节相同。

to_svg method descriptor

to_svg(style='ball-and-stick')

画成三维分子图,返回一段 SVG。

style 取四套之一 —— 名字与半径都取自 Jmol 自己文档里的 standard rendering styles:

style 球半径 键(圆柱)半径 看什么
"space-filling" 100% 范德华半径 不画 分子占多大地方
"ball-and-stick"(默认) 23% vdW 0.15 Å 键长键角、构型
"stick" 与键同粗 0.30 Å 骨架走向
"wireframe" 不画 0.01 Å 大体系、快速预览

按元素上 CPK(Jmol)色,键的两半各随自己那一端的颜色

conf = omgkit.parse_smiles("CC(=O)Oc1ccccc1C(=O)O").conformer()
open("aspirin.svg", "w").write(conf.to_svg())

画的是 mol 那一份(补过显式氢的)—— 三维图里氢是看得见的 实体,不画的话读图的人看到的是另一个分子。

style 不认识时抛 ValueError,并把认识的四个列出来。


Molblock

omgkit.Molblock

一个从 .mol / .sdf 文件读出来的记录:分子,加上它在文件里的坐标。

parse_molblock 产出。分开成一个类而不是直接给 Mol,是因为坐标 不在 Mol 里:molblock 的立体化学一半靠坐标表达,把坐标丢掉等于把这一半 丢掉,而丢的时候一声不响。

__doc__ class-attribute

__doc__ = '一个从 `.mol` / `.sdf` 文件读出来的记录:分子,加上它在文件里的坐标。\n\n由 `parse_molblock` 产出。分开成一个类而不是直接给 `Mol`,是因为坐标\n**不在** `Mol` 里:molblock 的立体化学一半靠坐标表达,把坐标丢掉等于把这一半\n丢掉,而丢的时候一声不响。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

coords property

coords

逐原子的 (x, y, z),顺序与 mol 的原子表一致。

二维图的 z 一律是 0。

is_3d property

is_3d

坐标是不是三维的(有任何一个 z 不为 0)。

文件里没有哪个字段直说这件事,只能这么判 —— 与外部实现同法。

mol property

mol

分子。已经净化过,立体也打上了 —— 二维靠楔形与平面投影, 三维靠有符号体积与二面角,两条路都接上了。

title property

title

文件第一行的标题。

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).


SdfRecord

omgkit.SdfRecord

SDF 里的一条记录。

读不了的那条也在这里,error 不是 None,blockNone —— 见 read_sdf 的文档。

__doc__ class-attribute

__doc__ = 'SDF 里的一条记录。\n\n**读不了的那条也在这里**,`error` 不是 `None`,`block` 是 `None` ——\n见 `read_sdf` 的文档。'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

__module__ class-attribute

__module__ = 'omgkit'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

block property

block

分子那一段。这条读不了时是 None

data property

data

数据字段,按文件里出现的顺序,list[tuple[str, str]]

不是字典:同名字段在真实文件里出现过(供应商把多次测量各写一行), 换成字典会静默地只留最后一条。名字重不重是调用方的判断。

这条读不了时是空的。

error property

error

这条读不了的原因;读得了时是 None

__new__ builtin

__new__(*args, **kwargs)

Create and return a new object. See help(type) for accurate signature.

__repr__ method descriptor

__repr__()

Return repr(self).


Not yet exposed

The Rust side has more than the Python side. These are reachable from Rust today and are not yet wrapped:

Rust What it is
omgkit_core::MolBatch the columnar batch and its zero-copy per-molecule views
omgkit_io::smarts writing SMARTS output for molecules and reactions
omgkit_chem individual stages running one sanitization stage at a time
omgkit_depict rendering SVG/PNG depiction — only the 2D molblock output is wrapped
omgkit_io::molblock::write_sdf_record multi-record SDF output with data fields

See the Rust API if you need them.