Analytical solution by Johann M. Rohwer.
Graphical representation of the model in Figure S1
Key:
T = total Trx
TSH = reduced Trx
TSS = oxidised Trx
NH = NADH
OX = oxidised target, e.g. PSS
import sympy as s
s.init_printing()
k1, k2, NH, OX, T = s.symbols("k1, k2, NH, OX, T")
TSS, TSH = s.symbols("TSS, TSH")
v1 = k1*NH*TSS # Trx reductase
v2 = k2*OX*TSH # reduction of target
v1.subs(TSS, T-TSH) # express moiety components to have only one free variable
# solve for steady state
TSHsol = s.solve(v1.subs(TSS, T-TSH)-v2, TSH)[0]
TSHsol
# substitute into v2 to calculate flux (v1 also works)
Jsol = v2.subs(TSH, TSHsol)
Jsol
# differentiate with respect to OX
s.diff(Jsol, OX)
A = s.simplify(_)
# Differentiate steady-state solution of TSH with respect to OX
B = s.diff(TSHsol, OX)
# this now gives the slope of the co-response plot when plotting
# flux against TrxSH upon an 'OX' perturbation.
A/B
To get the slope when plotting against redox charge, you just have to multiply this value by $T$, i.e.
$k_1 \times NH \times T$.
Also note that this value is independent of $OX$ or $k_2$, i.e. the slope does not depend on the value of the demand. The slope is therefore constant and there is a perfect correlation with redox charge.
# now do the same for an NADH perturbation, derivative of flux
s.diff(Jsol,NH)
C=s.simplify(_)
C
# derivative of TrxSH
s.diff(TSHsol, NH)
D = s.simplify(_)
D
# the slope of the J vs TrxSH co=response
C/D
To get the slope when plotting against redox charge, you just have to multiply this value by $T$, i.e.
$k_2 \times OX \times T$.
Also note that this value is independent of $NH$ or $k_1$, i.e. the slope does not depend on the value of the supply. The slope is therefore constant and there is a perfect correlation with redox charge.
Note also the symmetry between the supply and demand cases, of course since we model both with mass action.
The first step (reduction of TrxSS by NADPH) is now modelled with irreversible Michaelis-Menten kinetics, as this is an enzyme-catalyzed step (thioredoxin reductase). The second step is modelled with mass action as previously.
Key:
k1 = kcat1
TR = total TR
Kn = K1_NADPH
Kt = K1_TrxSS
TR, Kn, Kt = s.symbols("TR, Kn, Kt")
v1 = (k1*TR*(NH/Kn)*(TSS/Kt))/((1+NH/Kn)*(1+TSS/Kt))
v1
# v2 remains unchanged
v2
v1.subs(TSS, T-TSH) # express moiety components to have only one free variable
# solve for steady state
TSHsol = s.solve(v1.subs(TSS, T-TSH)-v2, TSH)
TSHsol
# substitute into v2 to calculate flux (just use the first solution for starters)
Jsol = v2.subs(TSH, TSHsol[0])
Jsol
# now do the same for an NADH perturbation, derivative of flux
s.diff(Jsol,NH)
C = s.simplify(_)
C
s.diff(TSHsol[0],NH)
D = s.simplify(_)
D
C/D
Same result as above. For supply perturbations, the slope is independent of $NH$ or $TR$, and therefore constant. I.e. we have a perfect correlation with redox charge.
# demand perturbation
s.diff(Jsol, OX)
A = s.simplify(_)
A
s.diff(TSHsol[0], OX)
B = s.simplify(_)
B
slope = A/B
slope
s.simplify(slope)
This expression does not readily simplify.
Key:
T = total Trx
TSH = reduced Trx
TSS = oxidised Trx
P = total Prx
PSH = reduced Prx
PSS = oxidised Prx
NH = NADPH
H2O2 = peroxide
k1, k2, k3, NH, H2O2, T, P = s.symbols("k1, k2, k3, NH, H2O2, T, P")
TSS, TSH, PSS, PSH = s.symbols("TSS, TSH, PSS, PSH")
# define all three reactions in terms of independent variables TSH and PSH
v1 = (k1*NH*TSS).subs(TSS, T-TSH) # Trx reductase
v2 = (k2*PSS*TSH).subs(PSS, P-PSH) # Prx reduction
v3 = k3*PSH*H2O2 # peroxide reduction
# solve for steady state
sol = s.solve((v1-v2, v2-v3), TSH, PSH, dict=True)
sol[1]
PSHsol = sol[1][PSH]
TSHsol = sol[1][TSH]
s.simplify(TSHsol)
s.simplify(PSHsol)
# work out flux
J1 = v1.subs(TSH, TSHsol)
J1s = s.simplify(J1)
J1s
# same for substituting into v3 (check that it gives same answer)
J3 = v3.subs(PSH, PSHsol)
s.simplify(J3)
# same vor substituting into v2
J2 = v2.subs(TSH, TSHsol).subs(PSH, PSHsol)
s.simplify(J2)
# verify that they are actually the same
s.simplify(J2-J3)
# derivative of flux w.r.t. peroxide
s.diff(J1s, H2O2)
A = s.simplify(_)
A
# derivative of TSH wrt peroxide
s.diff(TSHsol, H2O2)
B = s.simplify(_)
B
# this is the analytical solution of the slope of the coresponse of flux and TrxSH (scale to get RC)
# upon demand (peroxide) perturbation
slope_ox = A/B
slope_ox
In spite of all the complex algebra this simiplifies to a very simple expression, which is the same as for the single-cycle system and moreover is independent of H2O2. I.e. it predicts that the correlation of flux with redox charge should be perfect (constant slope) for demand perturbation.
# derivative of flux w.r.t. NADPH
s.diff(J1s, NH)
C = s.simplify(_)
C
# derivative of TSH w.r.t. NADPH
s.diff(TSHsol, NH)
D = s.simplify(_)
slope_red = C/D
# this is the analytical solution of the slope of the coresponse of flux and TrxSH
# (i.e. scale this to get redox charge)
# upon supply (NADPH) perturbation
slope_red
s.simplify(slope_red)
This expression does not simplify, so the correlation is not perfect as for the demand perturbation.
For symmetry reasons, the correlation of flux with PrxSH upon an NADPH perturbation should be perfect.
# this was the derivative of flux w.r.t. NADPH
C
# get the derivative of PrxSH
s.diff(PSHsol, NH)
E = s.simplify(_)
slope_red2 = C/E
slope_red2