Source code for schrodinger.application.matsci.elementalprops

"""
Classes and functions to deal with element data.

Copyright Schrodinger, LLC. All rights reserved."""

import json
import os

from schrodinger.application.matsci import msutils
from schrodinger.infra import mm

MAX_BONDS_LABEL = 'USE_MAX_BONDS'
INFRA_LABEL = 'USE_SMALL_MOLECULE_VALENCIES'
DEFAULT_INFRA_EXCEPTIONS = {'Pr': 10, 'O': 5, 'Na': 6, 'Cl': 6, 'I': 6}

MAX_VALENCIES_FILEPATH = os.path.join(msutils.get_matsci_user_data_dir(),
                                      'max_valencies.json')

# These should be treated as constants, if need update, make a copy (!!)
_max_valencies = None  # Updated in get_max_valencies()
_mmct_max_valencies = None  # Updated in get_mmct_max_valencies()
_max_chem_valencies = None  # Updated in get_max_chem_valencies()
_max_chem_valencies_with_exceptions = None  # Updated in get_max_chem_valencies_with_exceptions()

# Loads element data from json file
with open(os.path.join(os.path.dirname(__file__), 'elementalprops.json'),
          'rt') as f:
    _pt_data = json.load(f)


[docs]class ElementalProperties(object): """ Class that holds data of chemical elements. """
[docs] def __init__(self, symbol): """ Initialize object. :type symbol: str :param symbol: Chemical symbol """ self.symbol = symbol
@property def eneg(self): """ Get Pauling electronegativity. :rtype: float :return: Pauling electronegativity of self.symbol. """ return _pt_data[self.symbol]['X'] @property def cradius(self): """ Get covalent radius of the element. :rtype: float :return: Covalent radius of the element (in A) """ return _pt_data[self.symbol]['covalent_radius'] @property def iradius(self): """ Get ionic radius of the element. :rtype: float :return: Ionic radius of the element (in A) """ return _pt_data[self.symbol]['ionic_radius']
[docs]def get_max_valencies(): """ Get the dictionary containing max valencies for all elements. Uses a file to allow users to customize the valencies for elements. Based on the options in the file, uses mmct maximum number of bonds for all elements, OR max valence defined in infra mmelement, OR the valencies specified in the file. If the file doesn't exist, uses max valence in mmelement with the exceptions specified in the module, and creates the file with the resulting dictionary. The max valencies are determined the first time the function is called. All subsequent calls return the previously determined valencies. :rtype: dict :return: Dictionary containing maximum allowed bonds for each element """ global _max_valencies if _max_valencies is not None: return _max_valencies file_exists = os.path.exists(MAX_VALENCIES_FILEPATH) if not file_exists: # Default to infra with exceptions _max_valencies = dict(get_max_chem_valencies_with_exceptions()) # Write the json file json_out = { MAX_BONDS_LABEL: False, INFRA_LABEL: False, } json_out.update(_max_valencies) with open(MAX_VALENCIES_FILEPATH, 'w') as file_handle: json.dump(json_out, file_handle, indent=0) return _max_valencies with open(MAX_VALENCIES_FILEPATH) as file_handle: try: json_in = json.load(file_handle) # Avoid stopping workflows by catching all exceptions, since # this function can be called deep inside different workflows. except Exception: print("\nERROR in parsing json file with maximum valencies:\n" + str(MAX_VALENCIES_FILEPATH) + "\nUsing default max valencies instead.\n") _max_valencies = dict(get_max_chem_valencies_with_exceptions()) return _max_valencies if json_in.get(MAX_BONDS_LABEL) is True: # Takes precedence over INFRA. _max_valencies = dict(get_mmct_max_valencies()) return _max_valencies if json_in.get(INFRA_LABEL) is True: _max_valencies = dict(get_max_chem_valencies()) return _max_valencies _max_valencies = dict(get_max_chem_valencies()) # Update _max_valencies with only known keys for key in _max_valencies: if key in json_in: _max_valencies[key] = json_in[key] return _max_valencies
[docs]def get_max_chem_valencies_with_exceptions(): """ Get dictionary based on get_max_chem_valencies, updated with infra exceptions. :rtype: dict :return: Return global dictionary. If need to update please copy (!!) """ global _max_chem_valencies_with_exceptions if _max_chem_valencies_with_exceptions is not None: return _max_chem_valencies_with_exceptions _max_chem_valencies_with_exceptions = dict(get_max_chem_valencies()) _max_chem_valencies_with_exceptions.update(DEFAULT_INFRA_EXCEPTIONS) return _max_chem_valencies_with_exceptions
[docs]def get_max_chem_valencies(): """ Get dictionary based on get_mmct_max_valencies, updated with infra max valence. :rtype: dict :return: Return global dictionary. If need to update please copy (!!) """ global _max_chem_valencies if _max_chem_valencies is not None: return _max_chem_valencies _max_chem_valencies = dict(get_mmct_max_valencies()) for atomic_number in range(1, mm.MMELEMENTS_VALENCE_MAX + 1): atomic_symbol = mm.mmelement_get_symbol_by_atomic_number(atomic_number) atom_max_bonds = mm.mmelement_get_max_valence(atomic_number) _max_chem_valencies[atomic_symbol] = atom_max_bonds return _max_chem_valencies
[docs]def get_mmct_max_valencies(): """ Creates a dictionary that allows mm.MMCT_MAXBOND - 1 bonds for all elements :rtype: dict :return: Return global dictionary. If need to update please copy (!!) """ global _mmct_max_valencies if _mmct_max_valencies is not None: return _mmct_max_valencies _mmct_max_valencies = {} max_possible_bonds = mm.MMCT_MAXBOND - 1 for atomic_number in range(1, mm.MMELEMENTS_MAX + 1): atomic_symbol = mm.mmelement_get_symbol_by_atomic_number(atomic_number) _mmct_max_valencies[atomic_symbol] = max_possible_bonds # Dummy atoms _mmct_max_valencies["DU"] = max_possible_bonds _mmct_max_valencies["Lp"] = max_possible_bonds _mmct_max_valencies[""] = max_possible_bonds return _mmct_max_valencies