#
# @BEGIN LICENSE
#
# Psi4: an open-source quantum chemistry software package
#
# Copyright (c) 2007-2018 The Psi4 Developers.
#
# The copyrights for code used from other parties are included in
# the corresponding files.
#
# This file is part of Psi4.
#
# Psi4 is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3.
#
# Psi4 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with Psi4; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# @END LICENSE
#
"""Module with utility functions that act on molecule objects."""
from __future__ import absolute_import
import numpy as np
from psi4 import core
from psi4.driver.p4util import constants, filter_comments
from psi4.driver.inputparser import process_pubchem_command, pubchemre
from psi4.driver import qcdb
from psi4.driver.p4util.exceptions import *
[docs]def molecule_set_attr(self, name, value):
"""Function to redefine __setattr__ method of molecule class."""
fxn = object.__getattribute__(self, "is_variable")
isvar = fxn(name)
if isvar:
fxn = object.__getattribute__(self, "set_variable")
fxn(name, value)
return
object.__setattr__(self, name, value)
[docs]def molecule_get_attr(self, name):
"""Function to redefine __getattr__ method of molecule class."""
fxn = object.__getattribute__(self, "is_variable")
isvar = fxn(name)
if isvar:
fxn = object.__getattribute__(self, "get_variable")
return fxn(name)
return object.__getattribute__(self, name)
@classmethod
def molecule_from_string(cls,
molstr,
dtype=None,
name=None,
fix_com=None,
fix_orientation=None,
fix_symmetry=None,
return_dict=False,
enable_qm=True,
enable_efp=True,
missing_enabled_return_qm='none',
missing_enabled_return_efp='none',
verbose=1):
molrec = qcdb.molparse.from_string(molstr=molstr,
dtype=dtype,
name=name,
fix_com=fix_com,
fix_orientation=fix_orientation,
fix_symmetry=fix_symmetry,
return_processed=False,
enable_qm=enable_qm,
enable_efp=enable_efp,
missing_enabled_return_qm=missing_enabled_return_qm,
missing_enabled_return_efp=missing_enabled_return_efp,
verbose=verbose)
if return_dict:
return core.Molecule.from_dict(molrec['qm']), molrec
else:
return core.Molecule.from_dict(molrec['qm'])
@classmethod
def molecule_from_arrays(cls,
geom=None,
elea=None,
elez=None,
elem=None,
mass=None,
real=None,
elbl=None,
name=None,
units='Angstrom',
input_units_to_au=None,
fix_com=None,
fix_orientation=None,
fix_symmetry=None,
fragment_separators=None,
fragment_charges=None,
fragment_multiplicities=None,
molecular_charge=None,
molecular_multiplicity=None,
missing_enabled_return='error',
tooclose=0.1,
zero_ghost_fragments=False,
nonphysical=False,
mtol=1.e-3,
verbose=1,
return_dict=False):
"""Construct Molecule from unvalidated arrays and variables.
Light wrapper around :py:func:`~qcdb.molparse.from_arrays`
that is a full-featured constructor to dictionary representa-
tion of Molecule. This follows one step further to return
Molecule instance.
Parameters
----------
See :py:func:`~qcdb.molparse.from_arrays`.
Returns
-------
:py:class:`psi4.core.Molecule`
"""
molrec = qcdb.molparse.from_arrays(geom=geom,
elea=elea,
elez=elez,
elem=elem,
mass=mass,
real=real,
elbl=elbl,
name=name,
units=units,
input_units_to_au=input_units_to_au,
fix_com=fix_com,
fix_orientation=fix_orientation,
fix_symmetry=fix_symmetry,
fragment_separators=fragment_separators,
fragment_charges=fragment_charges,
fragment_multiplicities=fragment_multiplicities,
molecular_charge=molecular_charge,
molecular_multiplicity=molecular_multiplicity,
domain='qm',
missing_enabled_return=missing_enabled_return,
tooclose=tooclose,
zero_ghost_fragments=zero_ghost_fragments,
nonphysical=nonphysical,
mtol=mtol,
verbose=verbose)
if return_dict:
return core.Molecule.from_dict(molrec), molrec
else:
return core.Molecule.from_dict(molrec)
@classmethod
def molecule_from_schema(cls, molschema, return_dict=False, verbose=1):
"""Construct Molecule from non-Psi4 schema.
Light wrapper around :py:func:`~psi4.core.Molecule.from_arrays`.
Parameters
----------
molschema : dict
Dictionary form of Molecule following known schema.
return_dict : bool, optional
Additionally return Molecule dictionary intermediate.
verbose : int, optional
Amount of printing.
Returns
-------
mol : :py:class:`psi4.core.Molecule`
molrec : dict, optional
Dictionary representation of instance.
Only provided if `return_dict` is True.
"""
if (molschema.get('schema_name', '').startswith('qc_schema') and
(molschema.get('schema_version', '') == 1)):
# Lost Fields
# -----------
# * 'comment'
# * 'provenance'
ms = molschema['molecule']
if 'fragments' in ms:
frag_pattern = ms['fragments']
else:
frag_pattern = [np.arange(len(ms['symbols']))]
dcontig = qcdb.Molecule.contiguize_from_fragment_pattern(frag_pattern,
geom=ms['geometry'],
elea=None,
elez=None,
elem=ms['symbols'],
mass=ms.get('masses', None),
real=ms.get('real', None),
elbl=None,
throw_reorder=True)
molrec = qcdb.molparse.from_arrays(geom=dcontig['geom'],
elea=None,
elez=None,
elem=dcontig['elem'],
mass=dcontig['mass'],
real=dcontig['real'],
elbl=None,
name=ms.get('name', None),
units='Bohr',
input_units_to_au=None,
fix_com=ms.get('fix_com', None),
fix_orientation=ms.get('fix_orientation', None),
fix_symmetry=None,
fragment_separators=dcontig['fragment_separators'],
fragment_charges=ms.get('fragment_charges', None),
fragment_multiplicities=ms.get('fragment_multiplicities', None),
molecular_charge=ms.get('molecular_charge', None),
molecular_multiplicity=ms.get('molecular_multiplicity', None),
domain='qm',
#missing_enabled_return=missing_enabled_return,
#tooclose=tooclose,
#zero_ghost_fragments=zero_ghost_fragments,
#nonphysical=nonphysical,
#mtol=mtol,
verbose=verbose)
else:
raise ValidationError("""Schema not recognized, schema_name/schema_version: {}/{} """.format(molschema.get('schema_name', 'NA'), molschema.get('schema_version', 'NA')))
if return_dict:
return core.Molecule.from_dict(molrec), molrec
else:
return core.Molecule.from_dict(molrec)
[docs]def dynamic_variable_bind(cls):
"""Function to dynamically add extra members to
the core.Molecule class.
"""
cls.__setattr__ = molecule_set_attr
cls.__getattr__ = molecule_get_attr
# for py3-only, all these `cls.fn = qcdb.Molecule._raw_fn` can be
# replaced by `cls.fn = qcdb.Molecule.fn` and in qcdb/molecule.py
# itself, the raw, staticmethod fns can use their official names again
# and not need the append line at bottom of file. what I do for you,
# py2 ...
cls.to_arrays = qcdb.Molecule._raw_to_arrays
cls.to_dict = qcdb.Molecule._raw_to_dict
cls.BFS = qcdb.Molecule._raw_BFS
cls.B787 = qcdb.Molecule._raw_B787
cls.scramble = qcdb.Molecule._raw_scramble
cls.from_arrays = molecule_from_arrays
cls.from_string = molecule_from_string
cls.to_string = qcdb.Molecule._raw_to_string
cls.from_schema = molecule_from_schema
cls.to_schema = qcdb.Molecule._raw_to_schema
dynamic_variable_bind(core.Molecule) # pass class type, not class instance
#
# Define geometry to be used by PSI4.
# The molecule created by this will be set in options.
#
# geometry("
# O 1.0 0.0 0.0
# H 0.0 1.0 0.0
# H 0.0 0.0 0.0
#
[docs]def geometry(geom, name="default"):
"""Function to create a molecule object of name *name* from the
geometry in string *geom*. Permitted for user use but deprecated
in driver in favor of explicit molecule-passing. Comments within
the string are filtered.
"""
core.efp_init()
geom = pubchemre.sub(process_pubchem_command, geom)
geom = filter_comments(geom)
molecule = core.Molecule.create_molecule_from_string(geom)
molecule.set_name(name)
# Attempt to go ahead and construct the molecule
try:
molecule.update_geometry()
except:
core.print_out("Molecule: geometry: Molecule is not complete, please use 'update_geometry'\n"
" once all variables are set.\n")
activate(molecule)
return molecule
[docs]def activate(mol):
"""Function to set molecule object *mol* as the current active molecule.
Permitted for user use but deprecated in driver in favor of explicit
molecule-passing.
"""
core.set_active_molecule(mol)