Skip to content

M4L4 — Enumeration

Enumerates the substructures and elementary reactions involved in the formation of the M4L4 macrocycle.

This page covers assembly enumeration and reaction enumeration. For reaction classification, see M4L4 — Classification.

Setup (Optional)

Enabling logging lets you monitor the progress of enumeration.

import logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

Template

M and L each have two binding sites (0, 1). X is the leaving ligand that occupies vacant metal sites.

from nasap_net import Assembly, Bond, Component

M = Component(kind='M', sites=[0, 1])
L = Component(kind='L', sites=[0, 1])
X = Component(kind='X', sites=[0])

The target M4L4 is defined directly as the enumeration template. Substructures of this template are enumerated in the next step.

M4L4 = Assembly(
    components={
        'M0': M, 'M1': M, 'M2': M, 'M3': M,
        'L0': L, 'L1': L, 'L2': L, 'L3': L,
    },
    bonds=[
        Bond('M0', 1, 'L0', 0), Bond('L0', 1, 'M1', 0),
        Bond('M1', 1, 'L1', 0), Bond('L1', 1, 'M2', 0),
        Bond('M2', 1, 'L2', 0), Bond('L2', 1, 'M3', 0),
        Bond('M3', 1, 'L3', 0), Bond('L3', 1, 'M0', 0),
    ],
)

Subassembly enumeration

Enumerates substructures from the template. With metal_kinds='M' and leaving_ligand=X, all vacant metal sites in each substructure are filled with X.

For example, ML is a substructure of M4L4, but what is actually enumerated is MLX (ML with its vacant site filled). ML itself is not included in the results.

from nasap_net import enumerate_assemblies

assemblies = list(enumerate_assemblies(
    template=M4L4,
    leaving_ligand=X,
    metal_kinds='M',
))

14 assemblies are enumerated.

Manual addition

enumerate_assemblies only enumerates subgraphs of M4L4, so M3L3 (triangular ring) is not included. It can be added manually if it should be part of the network.

M3L3 = Assembly(
    components={
        'M0': M, 'M1': M, 'M2': M,
        'L0': L, 'L1': L, 'L2': L,
    },
    bonds=[
        Bond('M0', 1, 'L0', 0), Bond('L0', 1, 'M1', 0),
        Bond('M1', 1, 'L1', 0), Bond('L1', 1, 'M2', 0),
        Bond('M2', 1, 'L2', 0), Bond('L2', 1, 'M0', 0),
    ],
)

assemblies.append(M3L3)

The total number of assemblies is now 15.

ID assignment

assign_composition_formula_ids assigns a composition-formula-based ID to each assembly. Assemblies with the same formula are numbered sequentially (e.g., M2L2-1, M2L2-2, ...).

The assigned ID can be accessed via the id_ attribute of each Assembly object (e.g., assemblies[0].id_).

from nasap_net import assign_composition_formula_ids

assemblies = assign_composition_formula_ids(assemblies, order=['M', 'L', 'X'])

Reaction enumeration

Enumerates elementary reactions for four types of ligand exchange: X→L, L→X, L→L, and X→X. min_temp_ring_size=3 constrains the minimum ring size that can form in the transition state, excluding reactions that would pass through an unrealistically small ring such as M2L2. (Optional.)

from nasap_net import enumerate_reactions, MLEKind

reactions = list(enumerate_reactions(
    assemblies=assemblies,
    mle_kinds=[
        MLEKind(metal='M', leaving='X', entering='L'),
        MLEKind(metal='M', leaving='L', entering='X'),
        MLEKind(metal='M', leaving='L', entering='L'),
        MLEKind(metal='M', leaving='X', entering='X'),
    ],
    min_temp_ring_size=3,
))

216 reactions are enumerated.

Results

Let's inspect the enumerated assemblies and reactions.

First, display the list of assemblies.

print(f'Number of assemblies: {len(assemblies)}')
for assembly in sorted(assemblies, key=lambda a: a.id_):
    print(assembly.id_)
Number of assemblies: 15
L
M2L2X
M2L3
M2LX2
M3L2X2
M3L3
M3L3X
M3L4
M4L3X2
M4L4
M4L4X
ML2
MLX
MX2
X

Then display the reactions. Reaction supports sorting, so sorted() can be used.

print(f'Number of reactions: {len(reactions)}')
for reaction in sorted(reactions):
    print(reaction)
Number of reactions: 216
M2L2X + L -> M2L2X + L (x2)
M2L2X + L -> M2L3 + X (x2)
M2L2X + L -> ML2 + MLX (x2)
M2L2X + L -> MLX + ML2 (x2)
M2L2X + M2L2X -> M3L2X2 + ML2 (x2)
M2L2X + M2L2X -> M3L3X + MLX (x2)
M2L2X + M2L2X -> M4L3X2 + L (x2)
M2L2X + M2L2X -> M4L4X + X (x2)
...(216 reactions total)

Saving

import os
from nasap_net import save_assemblies, save_reactions

os.makedirs('output', exist_ok=True)
save_assemblies(assemblies, 'output/assemblies.yaml', overwrite=True)
save_reactions(reactions, 'output/reactions.csv', overwrite=True)

Next: M4L4 — Classification