mxlmodels 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,683 @@
1
+ """Matuszynska 2016 NPQ model: non-photochemical quenching via PsbS and xanthophyll cycle.
2
+
3
+ Reference: Matuszyńska, Anna, et al.
4
+ "A mathematical model of non-photochemical quenching to study short-term light memory in plants."
5
+ Biochimica et Biophysica Acta (BBA)-Bioenergetics 1857.12 (2016): 1860-1869
6
+ """
7
+
8
+ import numpy as np
9
+ from mxlpy import Derived, Model
10
+ from mxlpy.surrogates import qss
11
+
12
+
13
+ def _ph(
14
+ h: float,
15
+ ) -> float:
16
+ """Convert lumenal proton concentration to pH (conversion factor 2.5e-4)."""
17
+ return -np.log10(h * 2.5e-4)
18
+
19
+
20
+ def _ph_inv(
21
+ ph: float,
22
+ ) -> float:
23
+ """Convert pH to lumenal proton concentration (inverse of _ph)."""
24
+ return 3.2e4 * 10**-ph
25
+
26
+
27
+ def _keq_qapq(
28
+ f: float,
29
+ e0_qa: float,
30
+ e0_pq: float,
31
+ p_h_st: float,
32
+ r: float,
33
+ t: float,
34
+ ) -> float:
35
+ """Equilibrium constant for QA -> PQ electron transfer, including stroma pH correction."""
36
+ RT = r * t
37
+ DG1 = -f * e0_qa
38
+ DG2 = -2 * f * e0_pq + 2 * p_h_st * np.log(10) * RT
39
+ DG0 = -2 * DG1 + DG2
40
+ return np.exp(-DG0 / RT)
41
+
42
+
43
+ def _keq_cytb6f(
44
+ p_h_lu: float,
45
+ f: float,
46
+ e0_pq: float,
47
+ r: float,
48
+ t: float,
49
+ e0_pc: float,
50
+ p_h_st: float,
51
+ ) -> float:
52
+ """Equilibriu constant of Cytochrome b6f."""
53
+ RT = r * t
54
+ DG1 = -2 * f * e0_pq + 2 * RT * np.log(10) * p_h_lu
55
+ DG2 = -f * e0_pc
56
+ DG3 = RT * np.log(10) * (p_h_st - p_h_lu)
57
+ DG = -DG1 + 2 * DG2 + 2 * DG3
58
+ return np.exp(-DG / RT)
59
+
60
+
61
+ def _keq_atp_synth(
62
+ p_h_lu: float,
63
+ dg_atp: float,
64
+ p_h_st: float,
65
+ r: float,
66
+ t: float,
67
+ pi: float,
68
+ ) -> float:
69
+ """Return equilibrium constant of ATP synthase.
70
+
71
+ See Matuszynska et al 2016 or Ebenhöh et al. 2011,2014.
72
+ """
73
+ RT = r * t
74
+ DG = dg_atp - np.log(10) * (p_h_st - p_h_lu) * (14 / 3) * RT
75
+ return pi * np.exp(-DG / RT)
76
+
77
+
78
+ def _moiety_1s(
79
+ x: float,
80
+ x_total: float,
81
+ ) -> float:
82
+ """Calculate conservation relationship for one substrate.
83
+
84
+ Used for creating derived variables that represent moiety conservation,
85
+ such as calculating the free form of a species when you know the total.
86
+
87
+ Parameters
88
+ ----------
89
+ x
90
+ Concentration of one form of the species
91
+ x_total
92
+ Total concentration of all forms
93
+
94
+ Returns
95
+ -------
96
+ Float
97
+ Concentration of the other form (x_total - x)
98
+
99
+ Examples
100
+ --------
101
+ >>> moiety_1s(0.3, 1.0) # If total is 1.0 and one form is 0.3, other is 0.7
102
+ 0.7
103
+ >>> # Example: If ATP + ADP = total_adenosine
104
+ >>> moiety_1s(0.8, 1.5) # [ADP] = total_adenosine - [ATP]
105
+ 0.7
106
+
107
+ """
108
+ return x_total - x
109
+
110
+
111
+ def _quencher(
112
+ psb_s: float,
113
+ zx: float,
114
+ psb_sp: float,
115
+ k_z_sat: float,
116
+ gamma_0: float,
117
+ gamma_1: float,
118
+ gamma_2: float,
119
+ gamma_3: float,
120
+ ) -> float:
121
+ """Quencher mechanism.
122
+
123
+ accepts:
124
+ psbS: fraction of non-protonated PsbS protein
125
+ Vx: fraction of Violaxanthin
126
+ """
127
+ Zs = zx / (zx + k_z_sat)
128
+
129
+ return (
130
+ gamma_0 * (1 - Zs) * psb_s
131
+ + gamma_1 * (1 - Zs) * psb_sp
132
+ + gamma_2 * Zs * psb_sp
133
+ + gamma_3 * Zs * psb_s
134
+ )
135
+
136
+
137
+ def _b2(
138
+ pq_ox: float,
139
+ pq_red: float,
140
+ quencher: float,
141
+ pfd: float,
142
+ k_pqh2: float,
143
+ keq_qapq: float,
144
+ kh: float,
145
+ kf: float,
146
+ kp: float,
147
+ psii_tot: float,
148
+ ) -> float:
149
+ """Analytical PSII B2 state (dark-closed, Matuszynska 2016): fraction with PQ reduced."""
150
+ return (
151
+ psii_tot
152
+ * (
153
+ k_pqh2 * kf**2 * pq_red
154
+ + 2 * k_pqh2 * kf * kh * pq_red * quencher
155
+ + k_pqh2 * kf * kp * pq_red
156
+ + k_pqh2 * kh**2 * pq_red * quencher**2
157
+ + k_pqh2 * kh * kp * pq_red * quencher
158
+ + keq_qapq * kf * kp * pfd
159
+ + keq_qapq * kh * kp * pfd * quencher
160
+ )
161
+ / (
162
+ k_pqh2 * keq_qapq * kf**2 * pq_ox
163
+ + 2 * k_pqh2 * keq_qapq * kf * kh * pq_ox * quencher
164
+ + k_pqh2 * keq_qapq * kf * kp * pq_ox
165
+ + k_pqh2 * keq_qapq * kf * pfd * pq_ox
166
+ + k_pqh2 * keq_qapq * kh**2 * pq_ox * quencher**2
167
+ + k_pqh2 * keq_qapq * kh * kp * pq_ox * quencher
168
+ + k_pqh2 * keq_qapq * kh * pfd * pq_ox * quencher
169
+ + k_pqh2 * kf**2 * pq_red
170
+ + 2 * k_pqh2 * kf * kh * pq_red * quencher
171
+ + k_pqh2 * kf * kp * pq_red
172
+ + k_pqh2 * kf * pfd * pq_red
173
+ + k_pqh2 * kh**2 * pq_red * quencher**2
174
+ + k_pqh2 * kh * kp * pq_red * quencher
175
+ + k_pqh2 * kh * pfd * pq_red * quencher
176
+ + k_pqh2 * kp * pfd * pq_red
177
+ + keq_qapq * kf * kp * pfd
178
+ + keq_qapq * kh * kp * pfd * quencher
179
+ + keq_qapq * kp * pfd**2
180
+ )
181
+ )
182
+
183
+
184
+ def _b0(
185
+ pq_ox: float,
186
+ pq_red: float,
187
+ quencher: float,
188
+ pfd: float,
189
+ k_pqh2: float,
190
+ keq_qapq: float,
191
+ kh: float,
192
+ kf: float,
193
+ kp: float,
194
+ psii_tot: float,
195
+ ) -> float:
196
+ """Analytical PSII B0 state (dark-closed, Matuszynska 2016): fraction with PQ oxidised."""
197
+ return (
198
+ k_pqh2
199
+ * keq_qapq
200
+ * pq_ox
201
+ * psii_tot
202
+ * (
203
+ kf**2
204
+ + 2 * kf * kh * quencher
205
+ + kf * kp
206
+ + kh**2 * quencher**2
207
+ + kh * kp * quencher
208
+ )
209
+ / (
210
+ k_pqh2 * keq_qapq * kf**2 * pq_ox
211
+ + 2 * k_pqh2 * keq_qapq * kf * kh * pq_ox * quencher
212
+ + k_pqh2 * keq_qapq * kf * kp * pq_ox
213
+ + k_pqh2 * keq_qapq * kf * pfd * pq_ox
214
+ + k_pqh2 * keq_qapq * kh**2 * pq_ox * quencher**2
215
+ + k_pqh2 * keq_qapq * kh * kp * pq_ox * quencher
216
+ + k_pqh2 * keq_qapq * kh * pfd * pq_ox * quencher
217
+ + k_pqh2 * kf**2 * pq_red
218
+ + 2 * k_pqh2 * kf * kh * pq_red * quencher
219
+ + k_pqh2 * kf * kp * pq_red
220
+ + k_pqh2 * kf * pfd * pq_red
221
+ + k_pqh2 * kh**2 * pq_red * quencher**2
222
+ + k_pqh2 * kh * kp * pq_red * quencher
223
+ + k_pqh2 * kh * pfd * pq_red * quencher
224
+ + k_pqh2 * kp * pfd * pq_red
225
+ + keq_qapq * kf * kp * pfd
226
+ + keq_qapq * kh * kp * pfd * quencher
227
+ + keq_qapq * kp * pfd**2
228
+ )
229
+ )
230
+
231
+
232
+ def _fluorescence(
233
+ q: float,
234
+ _b0: float,
235
+ _b2: float,
236
+ k_h: float,
237
+ k_f: float,
238
+ k_p: float,
239
+ ) -> float:
240
+ """Fluorescence function."""
241
+ return k_f / (k_h * q + k_f + k_p) * _b0 + k_f / (k_h * q + k_f) * _b2
242
+
243
+
244
+ def _b1(
245
+ pq_ox: float,
246
+ pq_red: float,
247
+ quencher: float,
248
+ pfd: float,
249
+ k_pqh2: float,
250
+ keq_qapq: float,
251
+ kh: float,
252
+ kf: float,
253
+ kp: float,
254
+ psii_tot: float,
255
+ ) -> float:
256
+ """Analytical PSII B1 state (light-open, Matuszynska 2016): fraction absorbing light with PQ oxidised."""
257
+ return (
258
+ k_pqh2
259
+ * keq_qapq
260
+ * pfd
261
+ * pq_ox
262
+ * psii_tot
263
+ * (kf + kh * quencher)
264
+ / (
265
+ k_pqh2 * keq_qapq * kf**2 * pq_ox
266
+ + 2 * k_pqh2 * keq_qapq * kf * kh * pq_ox * quencher
267
+ + k_pqh2 * keq_qapq * kf * kp * pq_ox
268
+ + k_pqh2 * keq_qapq * kf * pfd * pq_ox
269
+ + k_pqh2 * keq_qapq * kh**2 * pq_ox * quencher**2
270
+ + k_pqh2 * keq_qapq * kh * kp * pq_ox * quencher
271
+ + k_pqh2 * keq_qapq * kh * pfd * pq_ox * quencher
272
+ + k_pqh2 * kf**2 * pq_red
273
+ + 2 * k_pqh2 * kf * kh * pq_red * quencher
274
+ + k_pqh2 * kf * kp * pq_red
275
+ + k_pqh2 * kf * pfd * pq_red
276
+ + k_pqh2 * kh**2 * pq_red * quencher**2
277
+ + k_pqh2 * kh * kp * pq_red * quencher
278
+ + k_pqh2 * kh * pfd * pq_red * quencher
279
+ + k_pqh2 * kp * pfd * pq_red
280
+ + keq_qapq * kf * kp * pfd
281
+ + keq_qapq * kh * kp * pfd * quencher
282
+ + keq_qapq * kp * pfd**2
283
+ )
284
+ )
285
+
286
+
287
+ def _psii(
288
+ _b1: float,
289
+ k_p: float,
290
+ ) -> float:
291
+ """Reduction of PQ due to ps2."""
292
+ return k_p * 0.5 * _b1
293
+
294
+
295
+ def _two_divided_value(
296
+ x: float,
297
+ ) -> float:
298
+ """Return 2/x; used for scaling 2-proton stoichiometry by buffering capacity."""
299
+ return 2 / x
300
+
301
+
302
+ def _ptox(
303
+ pqh_2: float,
304
+ pfd: float,
305
+ k_cytb6f: float,
306
+ k_ptox: float,
307
+ o2_ex: float,
308
+ pq_tot: float,
309
+ keq_cytb6f: float,
310
+ ) -> float:
311
+ """Oxidation of the PQ pool through cytochrome and PTOX."""
312
+ kPFD = k_cytb6f * pfd
313
+ k_ptox = k_ptox * o2_ex
314
+ a1 = kPFD * keq_cytb6f / (keq_cytb6f + 1)
315
+ a2 = kPFD / (keq_cytb6f + 1)
316
+ return (a1 + k_ptox) * pqh_2 - a2 * (pq_tot - pqh_2)
317
+
318
+
319
+ def _four_divided_value(
320
+ x: float,
321
+ ) -> float:
322
+ """Return 4/x; used for scaling 4-proton stoichiometry by buffering capacity."""
323
+ return 4 / x
324
+
325
+
326
+ def _atp_synthase(
327
+ atp_st: float,
328
+ at_pase_ac: float,
329
+ kf_at_psynth: float,
330
+ keq_at_psynth: float,
331
+ ap_tot: float,
332
+ ) -> float:
333
+ """Production of ATP by ATPsynthase."""
334
+ return at_pase_ac * kf_at_psynth * (ap_tot - atp_st - atp_st / keq_at_psynth)
335
+
336
+
337
+ def _neg_fourteenthirds_divided_value(
338
+ x: float,
339
+ ) -> float:
340
+ """Return -(14/3)/x; used for HPR proton stoichiometry of ATP synthase scaled by buffering capacity."""
341
+ return -(14 / 3) / x
342
+
343
+
344
+ def _atp_synthase_activase(
345
+ at_pase_ac: float,
346
+ pfd: float,
347
+ k_act_at_pase: float,
348
+ k_deact_at_pase: float,
349
+ ) -> float:
350
+ """Activation of ATPsynthase by light."""
351
+ switch = pfd > 0.0
352
+ if switch:
353
+ return k_act_at_pase * (1 - at_pase_ac)
354
+ return -k_deact_at_pase * at_pase_ac
355
+
356
+
357
+ def _proton_leak(
358
+ h_lu: float,
359
+ k_leak: float,
360
+ h_st: float,
361
+ ) -> float:
362
+ """Transmembrane proton leak."""
363
+ return k_leak * (h_lu - h_st)
364
+
365
+
366
+ def _neg_divided_value(
367
+ x: float,
368
+ ) -> float:
369
+ """Return -1/x; used for negative proton leak stoichiometry scaled by buffering capacity."""
370
+ return -1 / x
371
+
372
+
373
+ def _atp_consumption(
374
+ atp_st: float,
375
+ k_at_pconsum: float,
376
+ ) -> float:
377
+ """ATP consuming reaction."""
378
+ return k_at_pconsum * atp_st
379
+
380
+
381
+ def _xantophyll_cycle(
382
+ vx: float,
383
+ h_lu: float,
384
+ nhx: float,
385
+ k_p_h_sat_inv: float,
386
+ k_dv: float,
387
+ k_ez: float,
388
+ x_tot: float,
389
+ ) -> float:
390
+ """Xanthophyll cycle."""
391
+ a = h_lu**nhx / (h_lu**nhx + k_p_h_sat_inv**nhx)
392
+ return k_dv * a * vx - k_ez * (x_tot - vx)
393
+
394
+
395
+ def _psbs_protonation(
396
+ psb_s: float,
397
+ h_lu: float,
398
+ nhl: float,
399
+ k_p_h_sat_lhc_inv: float,
400
+ k_prot: float,
401
+ k_deprot: float,
402
+ psb_s_tot: float,
403
+ ) -> float:
404
+ """Protonation of PsbS protein."""
405
+ a = h_lu**nhl / (h_lu**nhl + k_p_h_sat_lhc_inv**nhl)
406
+ return k_prot * a * psb_s - k_deprot * (psb_s_tot - psb_s)
407
+
408
+
409
+ def _ps2states_2016a_analytical(
410
+ pq_ox: float,
411
+ pq_red: float,
412
+ quencher: float,
413
+ pfd: float,
414
+ k_pqh2: float,
415
+ keq_qapq: float,
416
+ kh: float,
417
+ kf: float,
418
+ kp: float,
419
+ psii_tot: float,
420
+ ) -> tuple[float, float, float, float]:
421
+ """PSII state populations (Matuszynska 2016 NPQ, analytical closed-form variant)."""
422
+ x0 = kf**2
423
+ x1 = kf * kp
424
+ x2 = kh * quencher
425
+ x3 = kp * x2
426
+ x4 = 2 * x2
427
+ x5 = kf * x4
428
+ x6 = kh**2 * quencher**2
429
+ x7 = keq_qapq * kp
430
+ x8 = k_pqh2 * pq_ox
431
+ x9 = keq_qapq * x8
432
+ x10 = k_pqh2 * pq_red
433
+ x11 = kf * x10
434
+ x12 = kp * x10
435
+ x13 = pfd * x9
436
+ x14 = x10 * x2
437
+ x15 = pfd * x7
438
+ x16 = (
439
+ keq_qapq * pfd * x1
440
+ + x0 * x10
441
+ + x1 * x10
442
+ + x10 * x3
443
+ + x10 * x6
444
+ + x11 * x4
445
+ + x15 * x2
446
+ )
447
+ x17 = psii_tot / (
448
+ kf * x13
449
+ + pfd**2 * x7
450
+ + pfd * x11
451
+ + pfd * x12
452
+ + pfd * x14
453
+ + x0 * x9
454
+ + x1 * x9
455
+ + x13 * x2
456
+ + x16
457
+ + x2 * x7 * x8
458
+ + x5 * x9
459
+ + x6 * x9
460
+ )
461
+ x18 = pfd * x17
462
+ _B0 = x17 * x9 * (x0 + x1 + x3 + x5 + x6)
463
+ _B1 = x18 * x9 * (kf + x2)
464
+ _B2 = x16 * x17
465
+ _B3 = x18 * (x11 + x12 + x14 + x15)
466
+ return _B0, _B1, _B2, _B3
467
+
468
+
469
+ def create_model() -> Model:
470
+ """Matuszynska 2016 NPQ model: non-photochemical quenching via PsbS and xanthophyll cycle.
471
+
472
+ Reference: Matuszyńska, Anna, et al.
473
+ "A mathematical model of non-photochemical quenching to study short-term light memory in plants."
474
+ Biochimica et Biophysica Acta (BBA)-Bioenergetics 1857.12 (2016): 1860-1869
475
+ """
476
+ m: Model = Model()
477
+ m = m.add_variable("pq_red", initial_value=0)
478
+ m = m.add_variable("protons", initial_value=6.32975752e-05)
479
+ m = m.add_variable("vmax_atp_synthase", initial_value=0)
480
+ m = m.add_variable("atp", initial_value=25.0)
481
+ m = m.add_variable("psbs_de", initial_value=1)
482
+ m = m.add_variable("vx", initial_value=1)
483
+ m = m.add_parameter("PSII_tot", value=2.5)
484
+ m = m.add_parameter("PQ_tot", value=20)
485
+ m = m.add_parameter("AP_tot", value=50)
486
+ m = m.add_parameter("PsbS_tot", value=1)
487
+ m = m.add_parameter("X_tot", value=1)
488
+ m = m.add_parameter("O2_ex", value=8)
489
+ m = m.add_parameter("Pi", value=0.01)
490
+ m = m.add_parameter("k_Cytb6f", value=0.104)
491
+ m = m.add_parameter("k_ActATPase", value=0.01)
492
+ m = m.add_parameter("k_DeactATPase", value=0.002)
493
+ m = m.add_parameter("k_ATPsynth", value=20.0)
494
+ m = m.add_parameter("k_ATPconsum", value=10.0)
495
+ m = m.add_parameter("k_PQH2", value=250.0)
496
+ m = m.add_parameter("k_H", value=5000000000.0)
497
+ m = m.add_parameter("k_F", value=625000000.0)
498
+ m = m.add_parameter("k_P", value=5000000000.0)
499
+ m = m.add_parameter("k_PTOX", value=0.01)
500
+ m = m.add_parameter("pH_st", value=7.8)
501
+ m = m.add_parameter("k_leak", value=1000)
502
+ m = m.add_parameter("b_H", value=100)
503
+ m = m.add_parameter("hpr", value=4.666666666666667)
504
+ m = m.add_parameter("k_DV", value=0.0024)
505
+ m = m.add_parameter("k_EZ", value=0.00024)
506
+ m = m.add_parameter("K_pHSat", value=5.8)
507
+ m = m.add_parameter("nhx", value=5.0)
508
+ m = m.add_parameter("K_ZSat", value=0.12)
509
+ m = m.add_parameter("nhl", value=3)
510
+ m = m.add_parameter("k_deprot", value=0.0096)
511
+ m = m.add_parameter("k_prot", value=0.0096)
512
+ m = m.add_parameter("K_pHSatLHC", value=5.8)
513
+ m = m.add_parameter("gamma_0", value=0.1)
514
+ m = m.add_parameter("gamma_1", value=0.25)
515
+ m = m.add_parameter("gamma_2", value=0.6)
516
+ m = m.add_parameter("gamma_3", value=0.15)
517
+ m = m.add_parameter("F", value=96.485)
518
+ m = m.add_parameter("R", value=0.0083)
519
+ m = m.add_parameter("T", value=298)
520
+ m = m.add_parameter("E0_QA", value=-0.14)
521
+ m = m.add_parameter("E0_PQ", value=0.354)
522
+ m = m.add_parameter("E0_PC", value=0.38)
523
+ m = m.add_parameter("DG_ATP", value=30.6)
524
+ m = m.add_parameter("PPFD", value=100)
525
+ m = m.add_derived(
526
+ "pH_lu",
527
+ fn=_ph,
528
+ args=["protons"],
529
+ )
530
+ m = m.add_derived(
531
+ "H_st",
532
+ fn=_ph_inv,
533
+ args=["pH_st"],
534
+ )
535
+ m = m.add_derived(
536
+ "K_pHSat_inv",
537
+ fn=_ph_inv,
538
+ args=["K_pHSat"],
539
+ )
540
+ m = m.add_derived(
541
+ "K_pHSatLHC_inv",
542
+ fn=_ph_inv,
543
+ args=["K_pHSatLHC"],
544
+ )
545
+ m = m.add_derived(
546
+ "K_QAPQ",
547
+ fn=_keq_qapq,
548
+ args=["F", "E0_QA", "E0_PQ", "pH_st", "R", "T"],
549
+ )
550
+ m = m.add_derived(
551
+ "K_cytb6f",
552
+ fn=_keq_cytb6f,
553
+ args=["pH_lu", "F", "E0_PQ", "R", "T", "E0_PC", "pH_st"],
554
+ )
555
+ m = m.add_derived(
556
+ "K_ATPsynth",
557
+ fn=_keq_atp_synth,
558
+ args=["pH_lu", "DG_ATP", "pH_st", "R", "T", "Pi"],
559
+ )
560
+ m = m.add_derived(
561
+ "pq_ox",
562
+ fn=_moiety_1s,
563
+ args=["pq_red", "PQ_tot"],
564
+ )
565
+ m = m.add_derived(
566
+ "adp",
567
+ fn=_moiety_1s,
568
+ args=["atp", "AP_tot"],
569
+ )
570
+ m = m.add_derived(
571
+ "psbs_pr",
572
+ fn=_moiety_1s,
573
+ args=["psbs_de", "PsbS_tot"],
574
+ )
575
+ m = m.add_derived(
576
+ "zx",
577
+ fn=_moiety_1s,
578
+ args=["vx", "X_tot"],
579
+ )
580
+ m = m.add_derived(
581
+ "Q",
582
+ fn=_quencher,
583
+ args=[
584
+ "psbs_de",
585
+ "zx",
586
+ "psbs_pr",
587
+ "K_ZSat",
588
+ "gamma_0",
589
+ "gamma_1",
590
+ "gamma_2",
591
+ "gamma_3",
592
+ ],
593
+ )
594
+ m = m.add_derived(
595
+ "Fluo",
596
+ fn=_fluorescence,
597
+ args=["Q", "B0", "B2", "k_H", "k_F", "k_P"],
598
+ )
599
+ m = m.add_reaction(
600
+ "v_PSII",
601
+ fn=_psii,
602
+ args=["B1", "k_P"],
603
+ stoichiometry={
604
+ "pq_red": 1,
605
+ "protons": Derived(fn=_two_divided_value, args=["b_H"]),
606
+ },
607
+ )
608
+ m = m.add_reaction(
609
+ "v_PQ",
610
+ fn=_ptox,
611
+ args=["pq_red", "PPFD", "k_Cytb6f", "k_PTOX", "O2_ex", "PQ_tot", "K_cytb6f"],
612
+ stoichiometry={
613
+ "pq_red": -1,
614
+ "protons": Derived(fn=_four_divided_value, args=["b_H"]),
615
+ },
616
+ )
617
+ m = m.add_reaction(
618
+ "atp_synthase",
619
+ fn=_atp_synthase,
620
+ args=["atp", "vmax_atp_synthase", "k_ATPsynth", "K_ATPsynth", "AP_tot"],
621
+ stoichiometry={
622
+ "atp": 1,
623
+ "protons": Derived(fn=_neg_fourteenthirds_divided_value, args=["b_H"]),
624
+ },
625
+ )
626
+ m = m.add_reaction(
627
+ "atp_activase",
628
+ fn=_atp_synthase_activase,
629
+ args=["vmax_atp_synthase", "PPFD", "k_ActATPase", "k_DeactATPase"],
630
+ stoichiometry={"vmax_atp_synthase": 1},
631
+ )
632
+ m = m.add_reaction(
633
+ "proton_leak",
634
+ fn=_proton_leak,
635
+ args=["protons", "k_leak", "H_st"],
636
+ stoichiometry={"protons": Derived(fn=_neg_divided_value, args=["b_H"])},
637
+ )
638
+ m = m.add_reaction(
639
+ "v_ATPcons",
640
+ fn=_atp_consumption,
641
+ args=["atp", "k_ATPconsum"],
642
+ stoichiometry={"atp": -1},
643
+ )
644
+ m = m.add_reaction(
645
+ "v_Xcyc",
646
+ fn=_xantophyll_cycle,
647
+ args=["vx", "protons", "nhx", "K_pHSat_inv", "k_DV", "k_EZ", "X_tot"],
648
+ stoichiometry={"vx": -1},
649
+ )
650
+ m = m.add_reaction(
651
+ "v_PsbSP",
652
+ fn=_psbs_protonation,
653
+ args=[
654
+ "psbs_de",
655
+ "protons",
656
+ "nhl",
657
+ "K_pHSatLHC_inv",
658
+ "k_prot",
659
+ "k_deprot",
660
+ "PsbS_tot",
661
+ ],
662
+ stoichiometry={"psbs_de": -1},
663
+ )
664
+ m = m.add_surrogate(
665
+ "ps2states",
666
+ qss.Surrogate(
667
+ model=_ps2states_2016a_analytical,
668
+ args=[
669
+ "pq_ox",
670
+ "pq_red",
671
+ "Q",
672
+ "PPFD",
673
+ "k_PQH2",
674
+ "K_QAPQ",
675
+ "k_H",
676
+ "k_F",
677
+ "k_P",
678
+ "PSII_tot",
679
+ ],
680
+ outputs=["B0", "B1", "B2", "B3"],
681
+ ),
682
+ )
683
+ return m # noqa: RET504