matteoferla/Fragmenstein

View on GitHub
documentation/notes/resonance_charge.md

Summary

Maintainability
Test Coverage
## Resonance

It should be said that Rosetta does not have a Drude particle,
nor would a charged virtual atom work (as they don't interact).

* If the molecule is polarisable the effects would be missed as the charges are
assigned to the topology of a molecule before placement.
* If the charge changes with isomerisation,
* either in tautomers (constitutional isomers) or stereoisomers, this will be covered by the `get_isomers` method of Victor.
* In the case of heterocycles, one may have the _possibility_ of charges running around the pi ring.

These resonance forms are not enumerated by `get_isomers`, not only because they are not
technically isomers, but because picking what a meaningful subset is tricky and
the algorithm is reported to hang in certain cases.

Indole provides a nice example of this:

```python
original = Chem.MolFromSmiles('c12c(cc[nH]2)cccc1')
#original = Chem.MolFromSmiles('c1ccccc1')

flags = Chem.rdchem.ResonanceFlags.ALLOW_CHARGE_SEPARATION + \
        Chem.rdchem.ResonanceFlags.UNCONSTRAINED_ANIONS + \
        Chem.rdchem.ResonanceFlags.UNCONSTRAINED_CATIONS

for mol in list(AllChem.ResonanceMolSupplier(original, flags=flags)):
    AllChem.ComputeGasteigerCharges(mol)
    from rdkit.Chem.Draw import SimilarityMaps
    AllChem.ComputeGasteigerCharges(mol)
    contribs = [mol.GetAtomWithIdx(i).GetDoubleProp('_GasteigerCharge') for i in range(mol.GetNumAtoms())]
    SimilarityMaps.GetSimilarityMapFromWeights(mol, contribs, colorMap='jet', contourLines=10)
```

These are quite extreme, but they could be stabilised by bonding for example.
In fact, tryptophan has two fluorescent lifetimes, which are affected by the polarity of the solvent.

These alteranate forms are not considered by Fragmenstein.

## Pipe dream
One option to do so **would** be to score the hits
and try every resonance form until the best is found.
Then once the follow-up conformer is ready find the resonance form
with atomic charges most similar to those they map to.

This problem has two parts.
This requires the ability to change atom charge in PyRosetta
(other than remaking a pose each time, which is insane).

To do this it seems one has to generate a new residuetype,
create a residue and swap it:

So given a pose with indole (as an example):

```python
params = Params.from_smiles('c12c(cc[nH]2)cccc1')
pose = params.to_pose()
```
Get the residue type:
```python
residue = pose.residue(1)
print(residue.atomic_charge(1), residue.atom_name(1))
rt:pyrosetta.rosetta.core.chemical.ResidueType = residue.type()
```
modify it (randomly in this case):
```python
mrt = pyrosetta.rosetta.core.chemical.MutableResidueType(rt)
sac = pyrosetta.rosetta.core.chemical.SetAtomicCharge(residue.atom_name(1), 0.8)
sac.apply(mrt)
```
Get a regular residue type:
```python
nrt = pyrosetta.rosetta.core.chemical.ResidueType.make(mrt)
```
Create the replacement:
```python
new = pyrosetta.rosetta.core.conformation.ResidueFactory.create_residue(nrt)
v = pyrosetta.rosetta.utility.vector1_std_pair_std_string_std_string_t(residue.natoms())
for i in range(1, residue.natoms()+1):
    v[i] = (residue.atom_name(i),residue.atom_name(i))
    new.set_xyz(i,residue.xyz(i))
```
Replace the old with the new:
```python
pose.replace_residue(1, new, False)
residue = pose.residue(1)
print(residue.atomic_charge(1), residue.atom_name(1))
```
This works, but would require correction for external bonding,
which may be a nuisance.

A partial charge is a value that goes between -1 and 1,
so finding the resonance form that best matches
would probably be a simple least squares.

The gozdillion dollar question is
how to test this is a good idea.