2828
2929import tempfile as _tempfile
3030
31- from .. import _is_notebook
31+ from .. import _isVerbose
3232from .. import IO as _IO
3333from .. import _Utils
3434from ..Units .Charge import electron_charge as _electron_charge
3535from .._SireWrappers import Molecule as _Molecule
3636
3737
38- def formalCharge (molecule ):
38+ def formalCharge (molecule , property_map = {} ):
3939 """
4040 Compute the formal charge on a molecule. This function requires that
4141 the molecule has explicit hydrogen atoms.
@@ -46,6 +46,11 @@ def formalCharge(molecule):
4646 molecule : :class:`Molecule <BioSimSpace._SireWrappers.Molecule>`
4747 A molecule object.
4848
49+ property_map : dict
50+ A dictionary that maps system "properties" to their user defined
51+ values. This allows the user to refer to properties with their
52+ own naming scheme, e.g. { "charge" : "my-charge" }
53+
4954 Returns
5055 -------
5156
@@ -58,6 +63,9 @@ def formalCharge(molecule):
5863 "'molecule' must be of type 'BioSimSpace._SireWrappers.Molecule'"
5964 )
6065
66+ if not isinstance (property_map , dict ):
67+ raise TypeError ("'property_map' must be of type 'dict'" )
68+
6169 from rdkit import Chem as _Chem
6270 from rdkit import RDLogger as _RDLogger
6371
@@ -71,15 +79,56 @@ def formalCharge(molecule):
7179 # Zero the total formal charge.
7280 formal_charge = 0
7381
74- # Run in the working directory.
75- with _Utils .cd (work_dir ):
76- # Save the molecule to a PDB file.
77- _IO .saveMolecules ("tmp" , molecule , "PDB" )
78-
79- # Read the ligand PDB into an RDKit molecule.
80- mol = _Chem .MolFromPDBFile ("tmp.pdb" )
81-
82- # Compute the formal charge.
83- formal_charge = _Chem .rdmolops .GetFormalCharge (mol )
84-
85- return formal_charge * _electron_charge
82+ # Get the fileformat property name.
83+ property = property_map .get ("fileformat" , "fileformat" )
84+
85+ # Preferentially use the file format that the molecule was loaded from.
86+ try :
87+ # Get the raw list of formats.
88+ raw_formats = molecule ._sire_object .property (property ).value ().split ("," )
89+
90+ # Remove all formats other than PDB and SDF.
91+ formats = [f for f in raw_formats if f in ["PDB" , "SDF" ]]
92+
93+ if len (formats ) == 0 :
94+ formats = ["PDB" , "SDF" ]
95+ elif len (formats ) == 1 :
96+ if formats [0 ] == "PDB" :
97+ formats .append ("SDF" )
98+ else :
99+ formats .append ("PDB" )
100+ except :
101+ formats = ["PDB" , "SDF" ]
102+
103+ # List of exceptions.
104+ exceptions = []
105+
106+ # Try going via each format in turn.
107+ for format in formats :
108+ try :
109+ with _Utils .cd (work_dir ):
110+ # Save the molecule in the given format.
111+ _IO .saveMolecules ("tmp" , molecule , format )
112+
113+ # Load with RDKit.
114+ if format == "SDF" :
115+ rdmol = _Chem .MolFromMolFile ("tmp.sdf" )
116+ else :
117+ rdmol = _Chem .MolFromPDBFile ("tmp.pdb" )
118+
119+ # Compute the formal charge.
120+ formal_charge = _Chem .rdmolops .GetFormalCharge (rdmol )
121+
122+ return formal_charge * _electron_charge
123+
124+ except Exception as e :
125+ exceptions .append (e )
126+
127+ # If we got this far, then we failed to compute the formal charge.
128+ msg = "Failed to compute the formal charge on the molecule."
129+ if _isVerbose ():
130+ for e in exceptions :
131+ msg += "\n \n " + str (e )
132+ raise RuntimeError (msg )
133+ else :
134+ raise RuntimeError (msg ) from None
0 commit comments