ChemicalEnvironments were designed for the purpose of making moves/changes to the chemical perception of a SMIRNOFF forcefield. In this notebook we show how to use the environments to make these types of changes.
Authors
In [1]:
#!/usr/bin/env python
# generic scientific/ipython header
from __future__ import print_function
from __future__ import division
import os, sys
import copy
import numpy as np
In [2]:
import openeye.oechem as oechem
In [3]:
from openforcefield.typing.chemistry import environment
In [4]:
chemGroups = [ ('ewg1', '[#7,#8!-1,#16!-1,F,Cl,Br,I]'),
('ewg1di', '[#7!X1,#8!X1,#16!X1]') ]
In [5]:
# specific torsions from SMIRKS
torsionEnv1 = environment.TorsionChemicalEnvironment( smirks = "[#6X4:1]~[#6X4:2]-[#8X2;H1;+0:3]~[*:4]" )
torsionEnv2 = environment.TorsionChemicalEnvironment( smirks = "[#8X2;H0;+0:1]~[#6X4:2]-[#8X2;H1;+0:3]~[*:4]" )
torsionEnv3 = environment.TorsionChemicalEnvironment( smirks = "[#8X2;H0;+0:1]~[#6X4:2]-[#8:3]~[*:4]" )
print( torsionEnv3.asSMIRKS() )
In [6]:
# specific angles from sublists
angEnv1 = environment.AngleChemicalEnvironment( smirks = "[#8X2;H0;+0:1]~[#6X4:2]-[#8X2;H1;+0:3]")
angEnv2 = environment.AngleChemicalEnvironment( smirks = "[#6X4:1]~[#6X4:2]~[#6X4:3]")
angEnv3 = environment.AngleChemicalEnvironment( smirks = "[#6X4:1]~[#6X4:2]~;!@[#6X4:3]")
print( angEnv3.asSMIRKS() )
In [7]:
# specific bonds from sublists
bondEnv1 = environment.BondChemicalEnvironment( smirks = "[#8X2;H0;+0:1]~[#6X4:2]" )
bondEnv2 = environment.BondChemicalEnvironment( smirks = "[#8X2;H0;+0:1]~;!@[#6X4:2]" )
bondEnv3 = environment.BondChemicalEnvironment( smirks = "[H:1]-[#6X4:2]" )
bondEnv4 = environment.ChemicalEnvironment( smirks = '[*:2]~[#8:1]-[#6X4]~[#8X2;H0;+0]' )
print( bondEnv4.asSMIRKS() )
In [8]:
# specific vdW from sublists
atomEnv1 = environment.AtomChemicalEnvironment( smirks = "[#8X2;H0;+0:1]")
atomEnv2 = environment.AtomChemicalEnvironment( smirks = "[#6X4:1]")
atomEnv3 = environment.AtomChemicalEnvironment( smirks = "[H:1]")
atomH3 = environment.ChemicalEnvironment(
smirks = '[#1:1]-[#6](-[$ewg1])(-[$ewg1])-[$ewg1]', replacements = chemGroups)
print( atomH3.asSMIRKS() )
In [9]:
# Testing API for retrieval of atoms and bonds
# This will be used to make moves in chemical space (change the environment)
torsion = torsionEnv3
print( torsion.asSMIRKS() )
print( torsion.atom1.getORtypes() )
print( torsion.atom1.getANDtypes() )
atom4 = torsion.selectAtom( 4)
print('Selected atom : ',atom4.index, atom4.getORtypes(), atom4.getANDtypes() )
atmlist = torsion.getAtoms()
for atom in atmlist:
print(atom.index, atom.getORtypes(), atom.getANDtypes() )
bond2 = torsion.getBond(torsion.atom2,torsion.atom3)
print( bond2.getORtypes() )
print( bond2.getANDtypes() )
In [10]:
param = bondEnv4
print( param.asSMIRKS() )
# bond1 is the bond between indexed atoms 1 and 2.
# You can get this bond directly with selectBond:
print("Finding bond 1 directly...")
bond1 = param.selectBond(1)
print(bond1.getORtypes())
print("\nFinding bond 1 from atom1 and atom2...")
# OR you can get this bond from the atoms on either side:
firstAtom = param.selectAtom(1)
secondAtom = param.selectAtom(2)
print( 'firstAtom', firstAtom.index, firstAtom.getORtypes(), firstAtom.getANDtypes())
print( 'secondAtom', secondAtom.index, secondAtom.getORtypes(), secondAtom.getANDtypes())
bond1 = param.getBond(firstAtom, secondAtom)
print(bond1.getORtypes())
In [11]:
# Generic move lists without odds (initial guess), reformat below
ChemComponents = {}
ChemComponents['AtmBase'] = ['','#1','#5','#6','#7','#8','#9','#15','#16','#17','#35','#53']
ChemComponents['AtmORdecs'] = ['','X4','X3','X2','X1']
ChemComponents['AtmAndDecs'] = ['H0','+0',]
ChemComponents['BondBase'] = ['-',':','=','#','~']
ChemComponents['BondANDDecs'] = ['@','!@','!#']
In [12]:
# To reformat ChemComponents dict as tuples representing odds for each item,
# uncomment and run this block.
#for key in ChemComponents.keys():
# print('ChemComponentsWithOdds[\'%s\'] = [' % key)
# for item in ChemComponents[key]:
# print(' (\'%s\', 1),' % item )
# print(' ]')
Decorators need weights, and they need to be weighted differently for different atomic elements. Guessed examples based on experience with organic chemistry:
In [13]:
# General
atomComponentsWithOdds = {}
atomComponentsWithOdds['Basetypes'] = [
('#1', 10),
('#5', 10),
('#6', 10),
('#7', 10),
('#8', 10),
('#9', 1),
('#15', 2),
('#16', 4),
('#17', 1),
('#35', 1),
('#53', 1),
]
atomComponentsWithOdds['ORdecs'] = [ ('', 4),
('X4', 4), ('X3', 4), ('X2', 4), ('X1', 4),
('H3', 1), ('H2', 1), ('H1', 1), ('H0', 1)
]
# these AtmAndDecs are dummies just to get going; real chemistry puts H-count on each atom
atomComponentsWithOdds['ANDdecs'] = [ ('H0', 1), ('+0', 1), ]
bondComponentsWithOdds = {}
bondComponentsWithOdds['Basetypes'] = [ ('-', 1), (':', 1), ('=', 1), ('#', 1), ('~', 1), ]
bondComponentsWithOdds['ORdecs'] = []
bondComponentsWithOdds['ANDdecs'] = [ ('@', 1), ('!@', 1), ('!#', 1), ]
In [14]:
# for AlkEthOH
atomComponentsWithOdds = {}
atomComponentsWithOdds['Basetypes'] = [
('#1', 1),
('#5', 0),
('#6', 1),
('#7', 0),
('#8', 1),
('#9', 0),
('#15', 0),
('#16', 0),
('#17', 0),
('#35', 0),
('#53', 0),
]
atomComponentsWithOdds['ORdecs'] = [ ('', 4),
('X4', 4), ('X3', 4), ('X2', 4), ('X1', 4),
('H3', 1), ('H2', 1), ('H1', 1), ('H0', 1)
]
# these AtmAndDecs are dummies just to get going; real chemistry puts H-count on each atom
atomComponentsWithOdds['ANDdecs'] = [ ('H0', 1), ('+0', 1), ]
bondComponentsWithOdds = {}
bondComponentsWithOdds['Basetypes'] = [ ('-', 1), (':', 0), ('=', 0), ('#', 0), ('~', 1), ]
# TODO: determine how to handle ~ case, do we want to have it in the list or have a None option of some kind?
bondComponentsWithOdds['ORdecs'] = []
bondComponentsWithOdds['ANDdecs'] = [ ('@', 1), ('!@', 1), ('!#', 0), ]
In [15]:
# function copied from EnvMovesTypesAndWeights
def movesWithWeightsFromOdds( MovesWithOdds):
'''Processes a dictionary of movesWithOdds (lists of string/integer tuples)
into a dictionary of movesWithWeights usable to perform weighted
random choices with numpy's random.choice() function.
Argument: a MovesWithOdds dictionary of lists of string/integer tuples
Returns: a MovesWithWeights dictionary of pairs of a moveType-list with a
probabilites-list, the latter used by numpy's random.choice() function.'''
movesWithWeights = {}
for key in MovesWithOdds.keys():
moves = [ item[0] for item in MovesWithOdds[key] ]
odds = [ item[1] for item in MovesWithOdds[key] ]
weights = odds/np.sum(odds)
#print( key, moves, odds, weights)
movesWithWeights[key] = ( moves, weights)
return movesWithWeights
In [16]:
#print( ChemComponentsWithWeights)
atomComponentsWithWeights = movesWithWeightsFromOdds(atomComponentsWithOdds)
#print( 'atomComponentsWithWeights', atomComponentsWithWeights)
bondComponentsWithWeights = movesWithWeightsFromOdds(bondComponentsWithOdds)
#print( 'bondComponentsWithWeights', bondComponentsWithWeights)
masterComponentsWithWeights = { 'atom':atomComponentsWithWeights,
'bond':bondComponentsWithWeights}
#print( 'masterComponentsWithWeights', masterComponentsWithWeights)
In [17]:
def EnvMoveIsWellFormed( moveDict, msg=''):
'''Checks moveDict (dict of proposed chem env micro-moves) to see if
it is well-formed (i.e. before even looking at the chemical graph),
returning True unless there is an obvious problem.
Arguments:
moveDict: a dict of strings constituting proposed micro-moves of a chem env move.
msg: an informative message about the nature of the problem.
Returns True unless it finds a problem'''
if moveDict['action']=='joinAtom':
if moveDict['atomOrBond']=='bond':
msg = 'cannot join another atom to an existing bond'
return False
elif moveDict['ANDorOR']=='ANDtype':
msg = 'can only join another atom as an ORtype'
return False
return True
In [18]:
def getMoveDictDB(moveDictFilename):
"""
Processes moveTrees.uniq.*.txt files
"""
moveDictFile = open(moveDictFilename)
moveDictdb = []
for line in moveDictFile:
fields = line.split()
prob = fields[0]
moveDict = { 'action' : fields[1],
'atomOrBond' : fields[2],
'whichAtmBnd' : fields[3],
'ANDorOR' : fields[4],
}
if EnvMoveIsWellFormed(moveDict):
moveDictdb.append( (moveDict, prob) )
moveDictFile.close()
return moveDictdb
In [19]:
moveDictdb_byParam = dict()
for param_name in ['VdW', 'Bond', 'Angle', 'Torsion', 'Improper']:
moveDictdb_byParam[param_name] = getMoveDictDB("moveTrees.uniq.%s.txt" % param_name)
print(param_name, len(moveDictdb_byParam[param_name]))
In [20]:
# function copied from EnvMovesTypesAndWeights
def PickMoveItemWithProb( moveType, moveWithWeights):
'''
Picks a moveItem based on a moveType and a dictionary of moveTypes with associated probabilities
Parameters
----------
moveType: string corresponding to a key in the moveWithWeights dictionary, e.g. atomTor
moveWithWeights: a dictionary based on moveType keys which each point to a list of probabilites
associated with the position in the list
Returns
-------
the randomly-chosen position in the list, based on the probability, together with the probability'''
listOfIndexes = range(0, len( moveWithWeights[moveType][1]) )
listIndex = np.random.choice(listOfIndexes, p= moveWithWeights[moveType][1])
return moveWithWeights[moveType][0][listIndex], moveWithWeights[moveType][1][listIndex]
In [40]:
def GenerateORtype( ComponentsWithWeights):
'''Makes a weighted random choice of a new ORtype for an Atom or Bond from lists of candidate components.
These ORtypes are composites of a basetype (atomic number for atom, bond order for bond) and
an ORdec (decorator) which can associate a property (such as 'X3' meaning 3 connections on
the atom). It also calculates and associates the cumulative probability for that composite
ORtype based on the probabilities of each component used in making the choice.
Parameters
----------
ComponentsWithWeights: a MovesWithWeights dictionary of pairs of a moveType-list
with a probabilites-list
Returns
-------
newORtype, prob: a tuple pair with first element being a new ORtype and the second
being the cumulative probability of that ORtype.
None: if the attempt fails
'''
basetypeList = ComponentsWithWeights['Basetypes'][0]
#print( 'Basetypes:', ComponentsWithWeights['Basetypes'][0])
#print( 'Basetypes weights:', ComponentsWithWeights['Basetypes'][1])
if len(basetypeList)<1:
return None
newBasetype, prob = PickMoveItemWithProb( 'Basetypes', ComponentsWithWeights)
cumProb = prob
ORtypeList = ComponentsWithWeights['ORdecs'][0]
#print( 'ORdecs:', ORtypeList)
#print( 'ORdecs weights:', ComponentsWithWeights['ORdecs'][1])
if len(ORtypeList)<1:
return (newBasetype, []), prob
newORdecorator, prob = PickMoveItemWithProb( 'ORdecs', ComponentsWithWeights)
newORtype = (newBasetype, [newORdecorator])
cumProb *= prob
return newORtype, prob
In [41]:
def GenerateANDtype( ComponentsWithWeights):
'''Makes a weighted random choice of a new ANDtype for an Atom or Bond from a list of candidate
components.It also calculates and associates the cumulative probability for that
composite ANDtype based on the probabilities of each component used in making the choice.
Parameters
----------
ComponentsWithWeights: a MovesWithWeights dictionary of pairs of a moveType-list
with a probabilites-list
Returns
-------
newANDtype, prob: a tuple pair with first element being a new ANDtype and the second
being the cumulative probability of that ANDtype.
None: if the attempt fails
'''
ANDtypeList = ComponentsWithWeights['ANDdecs'][0]
#print( 'ANDdecs:', ANDtypeList)
#print( 'ANDdecs weights:', ComponentsWithWeights['ANDdecs'][1])
if len(ANDtypeList)<1:
return None
return PickMoveItemWithProb( 'ANDdecs', ComponentsWithWeights)
In [42]:
# test GenerateNwxfragORtype and GenerateNwxfragANDtype
nSamples = 3
for i in range(0,nSamples):
print( 'atom ORtypes:', GenerateORtype( masterComponentsWithWeights['atom']) )
for i in range(0,nSamples):
print( 'atom ANDtypes:', GenerateANDtype( masterComponentsWithWeights['atom']) )
for i in range(0,nSamples):
print( 'bond ORtypes:', GenerateORtype( masterComponentsWithWeights['bond']) )
for i in range(0,nSamples):
print( 'bond ANDtypes:', GenerateANDtype( masterComponentsWithWeights['bond']) )
In [43]:
def GetEnvBondChangeling( moveDict, Env):
'''
Returns a Bond object from the provided ChemicalEnvironment containing ORtypes and
ANDtypes. Whether a specific bond object or a randomly chosed one gets returned is based
on information in the moveDict (specifically moveDict['whichAtmBnd']).
If no bond object matching the moveDict requirements is found, it returns None.
Parameters
-----------
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves
Env: An openforcefield chemicalEnvironment
Returns
-------
an OpenForcefield ChemicalEnvironment bond object or None
Note - when this method was originally written selecting bonds was more difficult!
'''
if moveDict['atomOrBond']!='bond':
print( 'GetEnvBondChangeling: mistaken call to this function')
return None
# MoveDicts have two options at whichAtmBnd 'bond' or 'atom' followed by a number
# or the word unindexed
try:
index = int(moveDict['whichAtmBnd'][-1])
return Env.selectBond(index) # returns None if there is no bond at that index
except: # moveDict['whichAtmBnd'] should be the word unindexed
return Env.selectBond(moveDict['whichAtmBnd'])
In [44]:
# get MoveDictdb for this parameter:
moveDictdb = moveDictdb_byParam['Bond']
# test GetEnvBondChangeling
startParam = environment.ChemicalEnvironment( '[*:2]!@[#8:1]-[#6X3]=[#8X1;H0;+0]' )
param = copy.deepcopy( startParam)
moveDict = moveDictdb[9][0]
print( param.asSMIRKS())
print( moveDict)
changeling = GetEnvBondChangeling( moveDict, param)
if changeling is not None:
print( 'changeling.getORtypes():', changeling.getORtypes() )
else:
print( "No bond found with those requirements")
In [45]:
def GetEnvAtomChangeling( moveDict, Env):
'''Returns an Atom object from the provided ChemicalEnvironment.
Whether a specific atom object or a randomly chosed one gets returned is based
on information in the moveDict (specifically moveDict['whichAtmBnd']).
If no atom object is found, it returns None.
Parameters
-----------
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves
Env: Openforcefield ChemicalEnvironment
Returns
-------
Atom: an atom from the Env matching moveDict requirements
(or None if no atom with specifications was found)
'''
if moveDict['atomOrBond']!='atom':
print( 'GetEnvAtomChangeling: mistaken call to this function')
return None
try:
index = int(moveDict['whichAtmBnd'][-1])
return Env.selectAtom( index )
except:
return Env.selectAtom( moveDict['whichAtmBnd'] )
In [46]:
def GetEnvChangeling( moveDict, Env):
'''
Parent function to broker the getting of an Atom or Bond from the specified ChemicalEnvironment
Arguments
----------
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves
Env: Openforcefield ChemicalEnvironment
Returns
--------
changeline: an atom or bond meeting requirements in the moveDict
'''
atomOrBond = moveDict['atomOrBond']
if atomOrBond=='atom':
return GetEnvAtomChangeling( moveDict, Env)
elif atomOrBond=='bond':
return GetEnvBondChangeling( moveDict, Env)
else:
return None
In [47]:
# test GetEnvChangeling
startParam = atomH3
param = copy.deepcopy( startParam)
moveDict = moveDictdb[5][0]
print( 'Attempting to apply this proposed move:\n', moveDict )
print( 'To a ChemicalEnvironment that generates this SMIRKS string:\n', param.asSMIRKS() )
#for key in moveDict.keys():
# print( key, ':', moveDict[key] )
changeling = GetEnvChangeling( moveDict, param)
if changeling!=None:
print( 'changeling properties:', changeling.getORtypes(), changeling.getANDtypes() )
else:
print( 'GetEnvChangeling returned none. Was the proposed move viable?')
In [48]:
def ProposeNewType( moveDict, ComponentsWithWeights):
'''Proposes a new ORtype or ANDtype for a bond or atom object
based on a weighted random choice from a list of
candidate components. It also calculates and associates the cumulative probability
for that choice based on the probabilities of the component used in making the choice.
If the attempt to propose does not work, it returns None.
Arguments:
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves
ComponentsWithWeights: a MovesWithWeights dictionary of pairs of an ORtype or
ANDtype components with a probabilites-list.
Returns:
newType, prob: a tuple pair with first element being the proposed new type and
the second being the cumulative probability of that ORtype.
None: if the attempt fails
'''
#print( 'ProposeNewType moveDict:', moveDict )
atomOrBond = moveDict['atomOrBond']
if moveDict['ANDorOR']=='ORtype':
newPair = GenerateORtype( ComponentsWithWeights[atomOrBond])
elif moveDict['ANDorOR']=='ANDtype':
newPair = GenerateANDtype( ComponentsWithWeights[atomOrBond])
return newPair
In [49]:
# test ProposeNewType
for moveDict in moveDictdb:
print( moveDict[0]['atomOrBond'], moveDict[0]['ANDorOR'], ':',
ProposeNewType( moveDict[0], masterComponentsWithWeights))
In [50]:
def IsActionViable( moveDict, existing, proposal, msg):
'''Checks the viability a proposed chemical move against the existing content of
the bond or atom in the ChemicalEnvironment the move is to
operate upon. Depending on the nature of the move and the changeling (atom or bond) object, the
proposed move may be a priori impossible (e.g. deleting an ANDtype when the
ANDtype list is already empty). If such a circumstance is found, this function
returns False with a message. Otherwise it returns true with an empty string
for the message.
Parameters
----------
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves.
existing: The Atom or Bond's existing ORtype or ANDtype list of strings targeted by the move in moveDict.
proposal: a string; the proposed change to the existing ORtype or ANDtype list.
msg: a string; a message stating why the move is deemed not viable.'''
action = moveDict['action']
#print('IsActionViable: action:',action,'; existing types:',existing,'; proposed newType:',proposal)
if action=='add':
if proposal=='None':
print('proposed newType is null; willnot add')
return False
if proposal in existing:
print('proposed newType is in existing list; will not add')
return False
elif action=='delete':
# ToDo: check to make sure not completely deleting whole atom which connects at least two others
# Environments already do this - unnecessary
if len(existing)<1:
#msg = 'existing list is already empty; will not delete'
#print('IsActionViable: msg is', msg)
print('existing list is already empty; will not delete')
return False
if len(existing)==1 and moveDict['whichAtmBnd'][4]!='d':
#
print('will not delete indexed atom or bond with only one type')
return False
if len(existing)==1 and moveDict['atomOrBond']=='bond':
print('will not delete last bond joining two atoms')
return False
elif action=='swap':
if len(existing)<1 or proposal=='None':
print('missing one of existing type or newType; will not swap')
return False
if len(existing)==1 and existing[0]==proposal:
print('Swapping identical types is pointless; will not swap')
return False
elif action=='joinAtom':
# ToDo: insert valence check (don't join to atom with full valence)
# I think this should also be inside the environments, but it would probably treat everything
# with the same max valence, such as 4? 6? might need special cses?
# ToDo: insert subst position check (don't join to beta substituent)
# This is also in my environment TODO list, I think it makes more sense there
if len(existing)<1 or proposal=='None':
print('missing one of existing atom or new atom; will not join atom')
return False
return True
In [51]:
def SetEnvNewList( moveDict, changeling, newList):
'''Makes the move on the selected ChemicalEnvironment atom or bond object
by setting the selected ORtype or ANDtype list.
Parameters
-----------
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves.
changeling: the Atom or Bond object targeted by the move.
newList: the new list of strings to replace the existing one in the changeling.
Returns
-------
True if the move is successful, False otherwise.
'''
if moveDict['ANDorOR']=='ORtype':
changeling.setORtypes( newList )
return True
elif moveDict['ANDorOR']=='ANDtype':
changeling.setANDtypes( newList)
return True
else:
return False
In [52]:
def EffectMoveOnEnvList( moveDict, Env, changeling, existing, proposal):
'''Effects the action requested in the chemical move on the selected Atom or Bond
in the provided ChemicalEnvironment.
Arguments:
moveDict: a dict of strings constituting a sequence of proposed chem env micro-moves.
Env: an OpenForcefield ChemicalEnvironment (representing a force field parameter)
changeling: the Atom or Bond object in the Env targeted by the move.
existing: the OpenForcefield Networkx molecular changeling object's
existing ORtype or ANDtype list of strings targeted by the move in moveDict.
proposal: a string; the proposed change to the existing ORtype or ANDtype list.
Returns: True if the move is successful, False otherwise.
'''
action = moveDict['action']
newList = copy.deepcopy(existing)
#
# begin section for action delete and swap (swap is delete followed by add)
# begin with special case for actually removing an atom completely, ie delete the last atom ORtype
if action=='delete' and len(newList)==1 and moveDict['atomOrBond']=='atom'and moveDict['ANDorOR']=='ORtype':
# for time being just remove the whole thing; worry about detailed balance later
onlyEmpty = False
return Env.removeAtom( changeling, onlyEmpty)
# now the more general case for removing the last type from a list: make an empty list
if action=='delete' or action=='swap':
if len(newList)==1:
newList = []
# now to remove a random item from a list of more than one
else:
listOfIndexes = range(0, len( newList) )
idxToDelete = np.random.choice(listOfIndexes)
del newList[idxToDelete]
#
# begin section for action add and swap (swap is delete followed by add)
if action=='add' or action=='swap':
newList.append( proposal)
if not SetEnvNewList( moveDict, changeling, newList):
print('EffectMoveOnEnvList newList failed:', newList)
return False
#
# begin section for joinAtom
if action=='joinAtom':
print('EffectMoveOnEnvList: attaching [', proposal,'] to existing atom:', existing)
Env.addAtom( changeling, None, None, [proposal], None, None)
return True
In [53]:
def MoveEnv( Env, moveDict, weightdParts, msg=''):
'''Modifies a ChemicalEnvironment by applying a move described by
a moveDict (dict of proposed chem env micro-moves), based on weighted random choices of
chemical components in weightdParts. If the proposed move is not viable, it returns False.
Arguments:
Env: an OpenForcefield ChemicalEnvironment (representing a force field parameter)
moveDict: a list of strings constituting a sequence of proposed chem env micro-moves
weightdParts: dictionary of chemical components with probabilities of being chosen
msg: an informative message about the nature of the problem
Returns True unless it finds a problem'''
print( Env.asSMIRKS(), moveDict )
# stage 1: get the atom or bond we are going to work on
atomOrBond = moveDict['atomOrBond']
changeling = GetEnvChangeling( moveDict, Env)
if changeling==None:
msg = 'MoveEnv: changeling is None, returning false'
return False
print( 'MoveEnv changeling:', changeling.getORtypes(), changeling.getANDtypes() )
# stage 2: get the current list we need to modify
ANDorOR = moveDict['ANDorOR']
if ANDorOR=='ORtype':
currlist = changeling.getORtypes()
print('MoveEnv got ORtypes:', currlist)
elif ANDorOR=='ANDtype':
currlist = changeling.getANDtypes()
print('MoveEnv got ANDtypes:', currlist)
else:
msg = 'MoveEnv: could not retrieve requested ORtypes or ANDtypes'
return False
# stage 3: get the component list and associated weights
proposed = ProposeNewType( moveDict, masterComponentsWithWeights)
print("Proposed list: ", proposed)
if proposed==None:
msg = 'MoveEnv: proposed (newType, probability) is None, returning false'
return False
#stage 4: Test viability of desired action
mesg = ''
IsViable = IsActionViable( moveDict, currlist, proposed[0], mesg)
if not IsViable:
msg = 'MoveEnv: proposal is not viable, returning false. Details:\n'+mesg
return False
#stage 5: perform action
if not EffectMoveOnEnvList( moveDict, Env, changeling, currlist, proposed[0]):
msg = 'MoveEnv: problem effecting the move'
return False
return True
In [54]:
# test MoveEnv
startParam = angEnv2
param = copy.deepcopy( startParam)
moveDict = moveDictdb[6][0]
#print( param.asSMIRKS(), moveDict )
msg = ''
if not MoveEnv( param, moveDict, masterComponentsWithWeights, msg):
print('move failed, message is:', msg)
else:
print('move worked, new SMIRKS is:', param.asSMIRKS())
In [57]:
# test MoveEnv
startParam = angEnv2
moveDictdb = moveDictdb_byParam['Angle']
param = copy.deepcopy( startParam)
print( 'Starting with', param.asSMIRKS())
#print( len(moveDictdb) )
for i, moveDict in enumerate( moveDictdb):
param = copy.deepcopy( startParam)
moves = moveDict[0]
print( '\nworking on %2d %8s %4s %10s %7s :' %
(i, moves['action'], moves['atomOrBond'], moves['whichAtmBnd'], moves['ANDorOR']))
if not MoveEnv( param, moves, masterComponentsWithWeights, msg):
print(' Failed')
else:
print(' Succeeded : %s' % ( param.asSMIRKS()))
In [ ]: