Source code for scine_puffin.jobs.conformers
# -*- coding: utf-8 -*-
__copyright__ = """ This code is licensed under the 3-clause BSD license.
Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.
See LICENSE.txt for details.
"""
from scine_puffin.config import Configuration
from .templates.job import calculation_context, job_configuration_wrapper
from .templates.scine_connectivity_job import ConnectivityJob
from ..utilities import masm_helper
[docs]class Conformers(ConnectivityJob):
"""
A job generating all possible conformers (guesses) for a given structure
with molassembler.
The given model is used as a hint for the quality of the bond orders that
are to be used when generating the ``molassembler`` molecule.
**Order Name**
``conformers``
**Optional Settings**
Optional settings are read from the ``settings`` field, which is part of
any ``Calculation`` stored in a SCINE Database.
Possible settings for this job are:
add_based_on_distance_connectivity :: bool
If ``True``, the structure's connectivity is derived from interatomic
distances via the utils.BondDetector: The bond orders used for
interpretation are set to the maximum between those given in the
``bond_orders`` property and 1.0, whereever the utils.BondDetector
detects a bond. (default: True)
sub_based_on_distance_connectivity :: bool
If ``True``, the structure's connectivity is derived from interatomic
distances via the utils.BondDetector: The bond orders used for
interpretation are removed, whereever the utils.BondDetector does not
detect a bond. (default: True)
enforce_bond_order_model :: bool
If ``True``, only processes ``bond_orders`` that were generated with
the specified model. If ``False``, eventually falls back to any
``bond_orders`` available for the structure. (default: True)
dihedral_retries :: int
The number of attempts to generate the dihedral decision
during conformer generation. (default: 100)
**Required Packages**
- SCINE: Database (present by default)
- SCINE: molassembler (present by default)
- SCINE: Utils (present by default)
**Generated Data**
If successful the following data will be generated and added to the
database:
Structures
A set of conformers guesses derived from the graph representation of the
initial structure. All generated conformers will have a graph
(``masm_cbor_graph``) and decision list set (``masm_decision_list``).
"""
def __init__(self):
super().__init__()
self.name = "Scine conformer generation"
@job_configuration_wrapper
def run(self, manager, calculation, config: Configuration) -> bool:
import scine_database as db
import scine_molassembler as masm
structure = db.Structure(calculation.get_structures()[0], self._structures)
atoms = structure.get_atoms()
# Check if the structure has a compound linked
if structure.has_compound():
compound = db.Compound(structure.get_compound(), self._compounds)
else:
compound = False
# Generate database results
db_results = calculation.get_results()
db_results.clear()
with calculation_context(self):
# Get settings and check for incorrect settings
self.connectivity_settings_from_only_connectivity_settings()
# Query bond orders of the specified model
db_bond_orders = self.query_bond_orders(structure)
# construct bond orders from db property
bond_orders = self.bond_orders_from_db_bond_orders(structure, db_bond_orders)
# Load data into molassembler molecule
results = masm_helper.get_molecules_result(
atoms,
bond_orders,
self.connectivity_settings,
calculation.get_model().periodic_boundaries,
)
if len(results.molecules) > 1:
self.raise_named_exception("Too many molecules, expected only one.")
masm.Options.Thermalization.disable()
alignment = masm.BondStereopermutator.Alignment.BetweenEclipsedAndStaggered
generator = masm.DirectedConformerGenerator(results.molecules[0], alignment)
# objects for store function
result_model = db.Model("guess", "", "")
result_model.program = "molassembler"
charge = structure.get_charge()
multiplicity = structure.get_multiplicity()
structures = self._structures
def store(decision_list, conformation):
"""Enumeration callback storing the conformation in the DB"""
# Relabel decision_lists into binned lists
bin_list = generator.bin_midpoint_integers(decision_list)
# Update positions
atoms.positions = conformation
# New structure
new_structure = db.Structure()
new_structure.link(structures)
new_id = new_structure.create(atoms, charge, multiplicity, result_model, db.Label.MINIMUM_GUESS)
if structure.has_graph("masm_cbor_graph"):
new_structure.set_graph("masm_cbor_graph", structure.get_graph("masm_cbor_graph"))
new_structure.set_graph("masm_decision_list", "-".join([str(i) for i in bin_list]))
new_structure.set_comment("Conformer guess generated from " + str(structure.id()))
db_results.add_structure(new_id)
# If the structure has a compound add the new conformer
if compound:
compound.add_structure(new_id)
new_structure.set_compound(compound.id())
enumeration_settings = generator.EnumerationSettings()
# Stopgap until refinement with conflicting dihedral terms is more reliable
enumeration_settings.dihedral_retries = self.connectivity_settings["dihedral_retries"]
enumeration_settings.fitting = masm.BondStereopermutator.FittingMode.Nearest
generator.enumerate(callback=store, seed=42, settings=enumeration_settings)
calculation.set_results(db_results)
return self.postprocess_calculation_context()
@staticmethod
def required_programs():
return ["database", "molassembler", "utils"]