@@ -1789,7 +1789,7 @@ def __eq__(self, other):
17891789 No effort is made to ensure that the atoms are in the same order or that any annotated properties are preserved.
17901790
17911791 """
1792- return FrozenMolecule .are_isomorphic (self , other , return_atom_map = False )
1792+ return FrozenMolecule .are_isomorphic (self , other , return_atom_map = False )[ 0 ]
17931793
17941794 def to_smiles (self , toolkit_registry = GLOBAL_TOOLKIT_REGISTRY ):
17951795 """
@@ -1900,7 +1900,6 @@ def are_isomorphic(
19001900 bond_stereochemistry_matching = True ,
19011901 ):
19021902 """
1903- #TODO write tests for imorphic checks and mapping.
19041903 Determines whether the two molecules are isomorphic by comparing their graphs.
19051904
19061905 Parameters
@@ -1932,8 +1931,6 @@ def are_isomorphic(
19321931 # Do a quick hill formula check first
19331932 if FrozenMolecule .to_hill_formula (mol1 ) != FrozenMolecule .to_hill_formula (mol2 ):
19341933 return False , None
1935- else :
1936- print (FrozenMolecule .to_hill_formula (mol1 ), FrozenMolecule .to_hill_formula (mol2 ))
19371934
19381935 # Build the user defined matching functions
19391936 def node_match_func (x , y ):
@@ -1976,10 +1973,15 @@ def find_data_type(data):
19761973 """Find the data type and return the networkx graph"""
19771974
19781975 try :
1976+ # Molecule class instance
19791977 return data .to_networkx ()
19801978 except AttributeError :
1981- if isinstance (data , nx .Graph ):
1982- return data
1979+ try :
1980+ # TopologyMolecule class instance
1981+ return data .reference_molecule .to_networkx ()
1982+ except AttributeError :
1983+ if isinstance (data , nx .Graph ):
1984+ return data
19831985
19841986 mol1_netx = find_data_type (mol1 )
19851987 mol2_netx = find_data_type (mol2 )
@@ -2033,7 +2035,7 @@ def is_isomorphic_with(self, other):
20332035
20342036 # what level of matching do we want here?
20352037 # should we expose some options as well?
2036- return FrozenMolecule .are_isomorphic (self , other , return_atom_map = False )
2038+ return FrozenMolecule .are_isomorphic (self , other , return_atom_map = False )[ 0 ]
20372039
20382040 def generate_conformers (self ,
20392041 toolkit_registry = GLOBAL_TOOLKIT_REGISTRY ,
@@ -3374,44 +3376,37 @@ def from_mapped_smiles(cls, mapped_smiles):
33743376 :return: openforcefield.topology.molecule.Molecule
33753377 """
33763378
3377- # extract the atomic mapping
3378- mapping = []
3379- for atom in mapped_smiles .split (':' ):
3380- try :
3381- mapping .append (int (atom [:2 ]))
3382- except ValueError :
3383- try :
3384- mapping .append (int (atom [:1 ]))
3385- except ValueError :
3386- pass
3379+ # create the molecule from the smiles and check we have the right number of indexes
3380+ # in the mapped SMILES
3381+ offmol = cls .from_smiles (mapped_smiles , hydrogens_are_explicit = True )
33873382
33883383 # check we found some mapping
3389- if len (mapping ) == 0 :
3384+ try :
3385+ mapping = offmol ._properties ['atom_map' ]
3386+ except KeyError :
33903387 raise SmilesParsingError ('The given SMILES has no indexing, please generate a valid explicit hydrogen '
33913388 'mapped SMILES using cmiles.' )
3392- # create the molecule from the smiles and check we have the right number of indexes
3393- # in the mapped SMILES
3394- offmol = cls .from_smiles (mapped_smiles , hydrogens_are_explicit = True )
33953389
33963390 if len (mapping ) != offmol .n_atoms :
33973391 raise SmilesParsingError ('The mapped smiles does not contain enough indexes to remap the molecule.' )
33983392
3399- # Make a valid atom map dict Dict[new_index: old_index] and remap
3400- # use -1 here as the cmiles maps from 1 not 0
3401- atom_mapping = dict ((i , index - 1 ) for i , index in enumerate (mapping ))
3393+ # remap the molecule using the atom map found in the smiles
3394+ # the order is mapping = Dict[current_index: new_index]
34023395
3403- return offmol .remap (atom_mapping , new_to_old = False )
3396+ return offmol .remap (mapping , new_to_current = False )
34043397
34053398 @classmethod
3406- def from_qcarchive (cls , qca_json , client_instance = None , allow_undefined_stereo = False ):
3399+ def from_qcschema (cls , qca_dict , client = None , allow_undefined_stereo = False ):
34073400 """
34083401 Create a Molecule from a QCArchive entry based on the cmiles information.
34093402
3410- If we also have a client instance we can go and attach the starting geometry.
3403+ If we also have a client instance/address we can go and attach the starting geometry.
34113404
34123405 Parameters
34133406 ----------
3414- qca_mol : a QCArchive json representation
3407+ qca_dict : a QCArchive dict with json encoding
3408+ #TODO should this also accept the entry record instance that we can type check?
3409+ client : a qcportal.FractalClient instance or addess of an archive that we can search for the geometry.
34153410
34163411 Returns
34173412 -------
@@ -3420,10 +3415,13 @@ def from_qcarchive(cls, qca_json, client_instance=None, allow_undefined_stereo=F
34203415
34213416 Examples
34223417 --------
3418+ >>> import qcportal as ptl
3419+ >>> client = ptl.FractalClient()
3420+ >>>
34233421
34243422 """
34253423
3426- if 'canonical_isomeric_explicit_hydrogen_mapped_smiles' in qca_json ['attributes' ].keys ():
3424+ if 'canonical_isomeric_explicit_hydrogen_mapped_smiles' in qca_dict ['attributes' ].keys ():
34273425 # make a new molecule that has been reordered to match the cmiles mapping
34283426 offmol = cls .from_mapped_smiles (qca_json ['attributes' ]['canonical_isomeric_explicit_hydrogen_mapped_smiles' ])
34293427 if client_instance is not None :
@@ -3492,44 +3490,54 @@ def from_pdb(cls, file_path, smiles, allow_undefined_stereo=False):
34923490 else :
34933491 raise InvalidConformerError ('The PDB and SMILES structures do not match.' )
34943492
3495- def remap (self , mapping_dict , new_to_old = True ):
3493+ def remap (self , mapping_dict , current_to_new = True ):
34963494 """
34973495 Remap all of the indexes in the molecule to match the given mapping dict
3498- :param mapping_dict: A dictionary of the mapping between in the indexes
3499- :param new_to_old : The dict is {new_index: old_index } if True else {old_index: new_index }
3500- :return:
3496+ :param mapping_dict: A dictionary of the mapping between in the indexes, this should start from 0.
3497+ :param current_to_new : The dict is {current_index: new_index } if True else {new_index: current_index }
3498+ :return: a new openforcefield.topology.molecule.Molecule instance with all attributes transferred
35013499 """
35023500
35033501 if self .n_virtual_sites != 0 :
35043502 raise NotImplementedError ('We can not remap virtual sites yet!' )
35053503
3504+ # make sure the size of the mapping matches the current molecule
3505+ if len (mapping_dict ) != self .n_atoms :
3506+ raise ValueError (f'There are too many mapping indices({ len (mapping_dict )} ) for the amount of atoms in this '
3507+ f'molecule({ self .n_atoms } )' )
3508+
35063509 # make two mapping dicts we need new to old for atoms
35073510 # and old to new for bonds
3508- if new_to_old :
3509- n_to_o = mapping_dict
3510- o_to_n = dict ((v , u ) for u , v in mapping_dict .items ())
3511+ if current_to_new :
3512+ cur_to_new = mapping_dict
3513+ new_to_cur = dict ((v , u ) for u , v in mapping_dict .items ())
35113514 else :
3512- o_to_n = mapping_dict
3513- n_to_o = dict ((v , u ) for u , v in mapping_dict .items ())
3515+ new_to_cur = mapping_dict
3516+ cur_to_new = dict ((v , u ) for u , v in mapping_dict .items ())
35143517
35153518 new_molecule = Molecule ()
35163519 new_molecule .name = self .name
35173520
3518- # add the atoms list
3519- for i in range (self .n_atoms ):
3520- # get the old atom info
3521- old_atom = self ._atoms [n_to_o [i ]]
3522- new_molecule .add_atom (atomic_number = old_atom .atomic_number ,
3523- formal_charge = old_atom .formal_charge ,
3524- is_aromatic = old_atom .is_aromatic ,
3525- stereochemistry = old_atom .stereochemistry ,
3526- name = old_atom .name )
3527- # add the bonds
3521+ try :
3522+ # add the atoms list
3523+ for i in range (self .n_atoms ):
3524+ # get the old atom info
3525+ old_atom = self ._atoms [new_to_cur [i ]]
3526+ new_molecule .add_atom (atomic_number = old_atom .atomic_number ,
3527+ formal_charge = old_atom .formal_charge ,
3528+ is_aromatic = old_atom .is_aromatic ,
3529+ stereochemistry = old_atom .stereochemistry ,
3530+ name = old_atom .name )
3531+ # this is the first time we access the mapping catch an index error here corresponding to mapping that starts
3532+ # from 0 or higher
3533+ except IndexError :
3534+ raise IndexError (f'The mapping supplied is missing a relation corresponding to atom({ i } )' )
3535+
3536+ # add the bonds but with atom indexes in a sorted ascending order
35283537 for bond in self ._bonds :
3529- a1 = o_to_n [bond .atom1_index ]
3530- a2 = o_to_n [bond .atom2_index ]
3531- new_molecule .add_bond (atom1 = a1 ,
3532- atom2 = a2 ,
3538+ atoms = sorted ([cur_to_new [bond .atom1_index ], cur_to_new [bond .atom2_index ]])
3539+ new_molecule .add_bond (atom1 = atoms [0 ],
3540+ atom2 = atoms [1 ],
35333541 bond_order = bond .bond_order ,
35343542 is_aromatic = bond .is_aromatic ,
35353543 stereochemistry = bond .stereochemistry ,
@@ -3538,15 +3546,15 @@ def remap(self, mapping_dict, new_to_old=True):
35383546 # remap the charges
35393547 new_charges = np .zeros (self .n_atoms )
35403548 for i in range (self .n_atoms ):
3541- new_charges [i ] = self .partial_charges [n_to_o [i ]].value_in_unit (unit .elementary_charge )
3549+ new_charges [i ] = self .partial_charges [new_to_cur [i ]].value_in_unit (unit .elementary_charge )
35423550 new_molecule .partial_charges = new_charges * unit .elementary_charge
35433551
35443552 # remap the conformers there can be more than one
35453553 if self .conformers is not None :
35463554 for conformer in self .conformers :
35473555 new_conformer = np .zeros ((self .n_atoms , 3 ))
35483556 for i in range (self .n_atoms ):
3549- new_conformer [i ] = conformer [n_to_o [i ]].value_in_unit (unit .angstrom )
3557+ new_conformer [i ] = conformer [new_to_cur [i ]].value_in_unit (unit .angstrom )
35503558 new_molecule .add_conformer (new_conformer * unit .angstrom )
35513559
35523560 # move any properties across
0 commit comments