pkienzle / periodictable

Extensible periodic table for python
http://periodictable.readthedocs.org
Other
135 stars 36 forks source link

Syntax for deuteration, exchange percentage and D2O percentage in solvent #35

Open pkienzle opened 3 years ago

pkienzle commented 3 years ago

For labile H we need exchange percentage (default 90%) for the protein and D₂O percentage for the solvent (default 0%). For non-labile H we need deuteration percentage (default 0%). All three percentages can be present for the same material.

Perhaps x %D y %D2O z %exch? A little confusing since %wt and %vol are used in the context of (x %vol A // y % B // C).

pkienzle commented 7 months ago

Do we also need a swelling fraction for the protein which increases volume and decreases contrast as solvent is incorporated?

Deuteration can be handled using H-D ratios in the underlying formula but this is somewhat awkward and won't work for fasta sequences. Maybe {percentage}%D {mixture}? Or more generally, {percentage}%{isotope} {mixture} for example to specify the 58-Ni percentage in a multilayer, with the remainder at natural abundance?

Specifying mixtures by concentration (mg/mL) is supported (e.g., 5 mg NaCl@2.2 // 30 mL H2O@1), but this assumes volumes are conserved when mixing. May need additional syntax to say how density changes while mixing.

Together, 5 mg aa:LEED // 30 mL 20%D H2O@1n. Labile hydrogen can use the D:H ratio of the final component of mixture. The Formula class will need to keep additional information if we want to report volume fraction of material. Right now it only tracks the relative number of atoms and throws away the mixture information.

pkienzle commented 7 months ago

We may want to preserve the parse tree for mixtures in the Formula class. This would let us query the D:H ratio in the final mixture component when computing the sld after labile hydrogen substitution. We could store this number directly during the parse, but it may be convenient for third party packages to have access to the parse tree.

pkienzle commented 7 months ago

You can fake 70%deuterated in the formula by replacing all H with (H.3D.7). Similarly, for 90%exchanged can replace H[1] with (H.1H[1].9). For both %exchange and %deuterated the exchange applies first, with the new H.1 then substituted by deuteration, giving (H.3D.7) for H and ((H.3D.7).1H[1].9) for H[1] in the original formula.

For fasta calculations you will need to do this in two steps, first finding compact formula for the sequence then replacing the H and H[1] for the exchanges.

Awkward but doable.

In code it might look something like this (untested):

def deuterate(compound, deuteration_percent, exchange_percent, solvent_percent):
    parts = []
    dp, ep, sp = deuteration_percent/100, exchange_percent/100, solvent_percent/100
    for count, el in compound.hill.structure:
        if el is H[1]:
            # unexchanged percent (1-ep) is partially deuterated (dp) giving H:(1-ep)*(1-dp), D:(1-ep)*dp
            # exchanged percent ep follows solvent percent giving H:ep*(1-sp), D:ep*sp
            # adding gives:
            part_H = (1-ep)*(1-dp)+ep*(1-sp)
            part_D = (1-ep)*dp + ep*sp
            el = ((part_H, H), (part_D, D))
        elif el is H:
            el = ((1-dp, H), (dp, D))
        parts.append((count, el))
    return formula(parts, natural_density=compound.natural_density)

compound = deuterate(formula("C8H10H[1]2NOS@1.2"), 70, 90, 30)