14.

Computer Lab – Chemical Kinetics 2

Goal: The purpose of this lab is to compare two- and three-state models of protein folding kinetics using the steady-state approximation

Introduction

This exercise is based on the following paper:

Teilum, K., Maki, K., Kragelund, B. B., Poulsen, F. M., & Roder, H. (2002). Early kinetic intermediate in the folding of acyl-CoA binding protein detected by fluorescence labeling and ultrarapid mixing. Proceedings of the National Academy of Sciences of the United States of America, 99(15), 9807–9812. doi:10.1073/pnas.152321499

In [1]:
import IPython
url = 'https://www.rcsb.org/3d-view/2CB8/1'
iframe = '<iframe src=' + url + ' width=800 height=800></iframe>'
IPython.display.HTML(iframe)
/Users/rr/miniconda3/lib/python3.8/site-packages/IPython/core/display.py:419: UserWarning: Consider using IPython.display.IFrame instead
  warnings.warn("Consider using IPython.display.IFrame instead")
Out[1]:

In this work, the folding kinetics of an 86-residue four helix-bundle protein called ACBP (acyl-CoA binding protein) was measured using time-resolved fluorescence spectroscopy (monitoring UV tryptophan fluorescence at 280 nm), after fast dilution from chemical denaturant (guanidinium hydrochloride, GuHCl) in a capillary mixer. The authors find that their data can be fit to the following kinetic model,

$$ U \underset{k_{IU}}{\stackrel{k_{UI}}{\rightleftharpoons}} I \underset{k_{NI}}{\stackrel{k_{IN}}{\rightleftharpoons}} N \tag{1}$$

where U is a highly fluorescent unfolded state, I is a partially folded intermediate, and N is the folded state.


Procedure

Modeling the three-state mechanism

Use SimBiology to construct a model of the three-state folding kinetics, using the following kinetic rate parameters determined by the authors at 299 K:

$$ U \underset{k_{IU}= 7000 s^{-1}}{\stackrel{k_{UI} = 11000 s^{-1}}{\rightleftharpoons}} I \underset{k_{NI} = 0.07 s^{-1}}{\stackrel{k_{IN} = 650 s^{-1}}{\rightleftharpoons}} N $$

Here is some experimental data that might be helpful in validating your model

Time (ms)

Fluorescence intensity for the U state

High concentration = more intensity

0.2 1.1
0.4 1.0
0.6 0.98
0.8 0.96
1 0.91
2 0.6
20 0.33
40 0.32
60 0.31
80 0.29

Using initial concentrations of [U] = 10.0, [I] = 0.0, and [N] = 10.0, visualize the concentrations of each species over time. Draw or print/plot the results below.

Using the results of your SimBiology model, estimate the populations of U and N at equilibrium.

Estimate from this the equilibrium constant K = [Nequilibrium]/[Uequilibrium] and the free energy of folding.


Modeling the two-state mechanism

Let’s now apply the steady-state approximation to this three-state model. This approximation is a simplification saying that the concentration of the intermediate [I] is constant throughout the reaction. Since the formation of the intermediate is fast compared to the slower, rate-limiting step of folding to the native state N, the steady-state approximation is still fairly accurate.

To find the steady-state approximation, we go through the following steps:

1. Write down an expression for d[I]/dt :

\begin{equation} \frac{d[I]}{dt} = -k_{IU}[I] - k_{IN}[I] + k_{UI}[U] + k_{NI}[I]\tag{1.1} \end{equation}

2. Solve $\frac{d[I]}{dt} = 0$ (i.e. steady-state conditions) to get $[I]$ as a function of $[U]$ and $[N]$ :

\begin{align} 0 &= -k_{IU}[I] - k_{IN}[I] + k_{UI}[U] + k_{NI}[N]\\ [I](k_{IU} + k_{IN}) &= k_{UI}[U] + k_{NI}[N]\\ [I] &= (k_{UI}[U] + k_{NI}[N])/(k_{IU} + k_{IN}) \end{align}

3. Next, we plug this expression for $[I]$ into the rate laws for $[U]$ and $[N]$:

\begin{align} \frac{d[U]}{dt} &= -k_{UI}[U] + k_{IU}[I]\\ &= -k_{UI}[U] + k_{IU} (k_{UI}[U] + k_{NI}[N])/(k_{IU} + k_{IN}) \tag{1.2}\\ \frac{d[N]}{dt} &= -k_{NI}[N] + k_{IN}[I]\\ &= -k_{NI}[N] + k_{IN} (k_{UI}[U] + k_{NI}[N])/(k_{IU} + k_{IN}) \tag{1.3} \end{align}
In [13]:
import numpy as np
import pandas as pd
In [14]:
def ODE_eqs(C, t, k):
    r"""See eqs 1.1, 1.2 and 1.3
    :math:`U \underset{k_{IU}}{\stackrel{k_{UI}}{\rightleftharpoons}} \
    I \underset{k_{NI}}{\stackrel{k_{IN}}{\rightleftharpoons}} N`

    Args:
        C(list): collection of concentrations for each species [L, R, LR, ...]
        t(float): iterate over some time-course
        k(list): collection of rate constants [kf1, kb1, kf2, ...]
    """
     
    U, I, N = C[0], C[1], C[2]
    kUI, kIU, kIN, kNI = k[0], k[1], k[2], k[3]
    dIdt = - kIU*I - kIN*I + kUI*U + kNI*I
    dUdt = - kUI*U + kIU*I
    dNdt = - kNI*N + kIN*I
    return [dUdt, dIdt, dNdt]



def ODE_eqs_SS(C, t, k):
    r"""See eqs 1.1, 1.2 and 1.3
    :math:`U \underset{k_{IU}}{\stackrel{k_{UI}}{\rightleftharpoons}} \
    I \underset{k_{NI}}{\stackrel{k_{IN}}{\rightleftharpoons}} N`

    Args:
        C(list): collection of concentrations for each species [L, R, LR, ...]
        t(float): iterate over some time-course
        k(list): collection of rate constants [kf1, kb1, kf2, ...]
    """
     
    U, I, N = C[0], C[1], C[2]
    kUI, kIU, kIN, kNI = k[0], k[1], k[2], k[3]
    dIdt = - kIU*I - kIN*I + kUI*U + kNI*I
    dUdt = - kUI*U + kIU*(kUI*U+kNI*N)/(kIU+kIN)
    dNdt = - kNI*N + kIN*(kUI*U+kNI*N)/kIU+kIN
    return [dUdt, dIdt, dNdt]



def simulate(steps, simulation_time, parameters, rate_eqs):
    """Method for integrating the ODEs with SciPy odeint
    https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html

    Args:
        steps(int): number of iterations
        simulation_time(int): duration of simulation (units: s)
        parameters(dict): {"species": ["L", "R", "LR"],\
                          "k": [kf1, kb1, kf2, kb2, ...],\  # rate constants
                          "InitConc": [L, R, P, ...]}       # initial concentrations (units: M)
        rate_eqs(function): method containing the ordinary differential equations
    Returns:
        Pandas DataFrame, plot
    """
    
    from scipy.integrate import odeint
    print(f"Number of species: {len(parameters['InitConc'])}")
    time = np.linspace(0, simulation_time, steps) 
    C = odeint(rate_eqs, parameters["InitConc"], time, (parameters["k"],)).transpose()
    data = {"time": time, **{"%s"%s: C[i] for i,s in enumerate(parameters["species"])}}
    df = pd.DataFrame(data)
    plot = df.plot(x="time", y=parameters["species"], figsize=(10, 4),
                   xlim=(data["time"][0], data["time"][-1]))
    return df,plot


def dG(parameters, df):
    """Print ∆G = -RTln(K) for model and 'equilibrium' concentrations
    
    Args:
        parameters(dict): {"species": ["L", "R", "LR"],\
                          "k": [kf1, kb1, kf2, kb2, ...],\  # rate constants
                          "InitConc": [L, R, P, ...]}       # initial concentrations (units: M)
        df(DataFrame): pandas DataFrame of simulation 
    """
    
    R, T = 8.314, 299 #units: Jmmol^-1K^-1 , units: K
    print("\nFrom equilibrium concentrations:")
    U = df["U"][df.index[-1]]
    N = df["N"][df.index[-1]]
    K = N/U
    dG = -R*T*np.log(K)
    print(f"K = {N: 0.3g}/{U: 0.3g} = {K: 0.3g}")
    print(f"∆G = -RTln(K) = {dG/1000: 0.3g} kJ mol^-1")
    print("\nFrom model parameters:")
    kUI = parameters["k"][0]
    kIU = parameters["k"][1]
    kIN = parameters["k"][2]
    kNI = parameters["k"][3]
    kf = kIN*(kUI/(kIU+kIN))
    kb = kNI*(kIU/(kIU+kIN))
    K = kf/kb
    dG = -R*T*np.log(K)
    print(f"k_forward = {kf: 0.3g} s^-1")
    print(f"k_backward = {kb: 0.3g} s^-1")
    print(f"K = {K: 0.3g}")
    print(f"∆G = -RTln(K) = {dG/1000: 0.3g} kJ mol^-1")

def get_subset(df, index):
    return pd.DataFrame(df.loc[index, df.columns])
In [15]:
# U = I = N
#[U] = 10.0, [I] = 0.0, and [N] = 10.0,
"""
k𝑈𝐼=11000𝑠−1, k𝐼𝑁=650𝑠−1
k𝐼𝑈=7000𝑠−1, k𝑁𝐼=0.07𝑠−1
"""
parameters = {"species": ["U", "I", "N", ],
             "InitConc": [10.0, 0.0, 10.0, ],
             #            [kf1, kb1, kf2, ...]
             "k":        [11000.0, 7000.0, 650.0, 0.07]}
df,plot = simulate(steps=10000, simulation_time=0.009,#simulation_time=0.005,
                   parameters=parameters, rate_eqs=ODE_eqs) 
plot = plot.set_title(r"$L \rightleftharpoons I \rightleftharpoons N$")
fig = plot.get_figure()
#fig.savefig("figure.png")
df
Number of species: 3
Out[15]:
time U I N
0 0.000000e+00 10.000000 0.000000 10.000000
1 9.000900e-07 9.901788 0.098183 10.000028
2 1.800180e-06 9.805154 0.194732 10.000113
3 2.700270e-06 9.710072 0.289672 10.000254
4 3.600360e-06 9.616517 0.383030 10.000451
... ... ... ... ...
9995 8.996400e-03 0.119933 0.181757 19.688535
9996 8.997300e-03 0.119890 0.181693 19.688640
9997 8.998200e-03 0.119848 0.181629 19.688745
9998 8.999100e-03 0.119806 0.181565 19.688850
9999 9.000000e-03 0.119764 0.181501 19.688955

10000 rows × 4 columns

In [23]:
# Find where the first derivative is zero
#print(f'd[I]/dt = 0 @ t = {df["time"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} s')
#print(f'[I] = {df["I"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
#print(f'[U] = {df["U"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
#print(f'[N] = {df["N"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
#sub = get_subset(df, index=range(0,800))
#sub.plot(x="time",figsize=(10, 4))
#sub
In [16]:
# Stead State Approximation
parameters = {"species": ["U", "I", "N", ],
             "InitConc": [10.0, 0.0, 10.0, ],
             #            [kf1, kb1, kf2, ...]
             "k":        [11000.0, 7000.0, 650.0, 0.07]}
df,plot = simulate(steps=10000, simulation_time=0.005,
                   parameters=parameters, rate_eqs=ODE_eqs_SS) 
plot = plot.set_title(r"""Steady State 
$L \rightleftharpoons I \rightleftharpoons N$""")
fig = plot.get_figure()
#fig.savefig("figure.png")
df
Number of species: 3
Out[16]:
time U I N
0 0.000000e+00 10.000000 0.000000 10.000000
1 5.000500e-07 9.995328 0.054888 10.005431
2 1.000100e-06 9.990658 0.109540 10.010860
3 1.500150e-06 9.985990 0.163958 10.016286
4 2.000200e-06 9.981324 0.218143 10.021710
... ... ... ... ...
9995 4.998000e-03 0.095161 0.155550 24.073931
9996 4.998500e-03 0.095118 0.155479 24.074304
9997 4.999000e-03 0.095074 0.155407 24.074677
9998 4.999500e-03 0.095030 0.155336 24.075049
9999 5.000000e-03 0.094987 0.155264 24.075422

10000 rows × 4 columns

4. Finally, to get rates for a two-state $U\rightleftharpoons N$ model of folding, consider that at equilibrium, $\frac{d[U]}{dt} = \frac{d[N]}{dt} = 0$. Set the two expressions for $\frac{d[U]}{dt}$ and $\frac{d[N]}{dt}$ above to be equal to each other, and rearrange the equation to get it in the following form:

$$k_{forward} [U] = k_{backward}[N]$$

What are your computed values of for $k_{forward}$ and $k_{backward}$? (Hint: They should be close to the values of $k_{IN}$ and $k_{NI}$, since $I \to N$ is the rate-limiting step.)

Estimate from $k_{forward}$ and $k_{backward}$ the equilibrium constant ($K = k_{f}/k_{b}$) and free energy of folding.


$$k_{forward} = k_{IN} \left(\frac{k_{UI}}{k_{IU} + k_{IN}} \right)$$$$k_{backward} = k_{NI} \left(\frac{k_{IU}}{k_{IU} + k_{IN}}\right)$$

NOTE: it takes some simplification to get this

2-state mechanism

$$K = \frac{k_{f}}{k_{b}} = \frac{[N]}{[U]} $$$$\Delta G = -RT ln(K)$$$$k_{forward} = k_{IN} \left(\frac{k_{UI}}{k_{IU} + k_{IN}} \right) = 650(11000/(7000+650)) = 935 s^{-1}$$$$k_{backward} = k_{NI} \left(\frac{k_{IU}}{k_{IU} + k_{IN}}\right) = 0.07(7000/(7000+650)) = 0.064 s^{-1}$$

Estimate: $K = 635/0.064 = 14609$

$$\Delta G = -RT ln(K) = -8.314 J.mol^{-1}.K^{-1} (299 K)(ln(2.63))\frac{1kJ}{1000 J} = -24 kJ.mol^{-1} $$
In [24]:
## Find where the first derivative is zero
#print(f'd[I]/dt = 0 @ t = {df["time"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} s')
#print(f'd[I]/dt = 0 @ [I] = {df["I"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
#print(f'd[I]/dt = 0 @ [U] = {df["U"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
#print(f'd[I]/dt = 0 @ [N] = {df["N"][df["I"][np.where(np.diff(df["I"]) <= 0.0)[0]].index[0]]: 0.3g} M')
In [19]:
dG(parameters, df)
From equilibrium concentrations:
K =  24.1/ 0.095 =  253
∆G = -RTln(K) = -13.8 kJ mol^-1

From model parameters:
k_forward =  935 s^-1
k_backward =  0.0641 s^-1
K =  1.46e+04
∆G = -RTln(K) = -23.8 kJ mol^-1
In [21]:
# Stead State Approximation
parameters = {"species": ["U", "I", "N", ],
             "InitConc": [10.0, 0.0, 10.0, ],
             #            [kf1, kb1, kf2, ...]
             "k":        [11000.0, 7000.0, 650.0, 0.07]}
df,plot = simulate(steps=10000, simulation_time=0.04,#0.029,
                   parameters=parameters, rate_eqs=ODE_eqs_SS) 
plot = plot.set_title(r"""Steady State 
$L \rightleftharpoons I \rightleftharpoons N$""")
fig = plot.get_figure()
#fig.savefig("figure.png")
df
Number of species: 3
Out[21]:
time U I N
0 0.000000 10.000000 0.000000 10.000000
1 0.000004 9.962683 0.432566 10.043383
2 0.000008 9.925505 0.850480 10.086613
3 0.000012 9.888466 1.254190 10.129692
4 0.000016 9.851566 1.644131 10.172620
... ... ... ... ...
9995 0.039984 0.003168 0.004547 46.923449
9996 0.039988 0.003168 0.004547 46.926050
9997 0.039992 0.003168 0.004548 46.928652
9998 0.039996 0.003169 0.004548 46.931253
9999 0.040000 0.003169 0.004548 46.933854

10000 rows × 4 columns

In [22]:
dG(parameters, df)
From equilibrium concentrations:
K =  46.9/ 0.00317 =  1.48e+04
∆G = -RTln(K) = -23.9 kJ mol^-1

From model parameters:
k_forward =  935 s^-1
k_backward =  0.0641 s^-1
K =  1.46e+04
∆G = -RTln(K) = -23.8 kJ mol^-1