Skip to content

Commit 9137a69

Browse files
committed
Improved unit support for reactions without and mixed compartments (fixes #19).
1 parent 1561348 commit 9137a69

File tree

5 files changed

+83
-52
lines changed

5 files changed

+83
-52
lines changed

AutomaticUnits/AutomaticUnits.m

+8-3
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
(*FundamentalUnits is a core function for reducing a unit expression to its lowest units*)
9191
Unprotect[FundamentalUnits];
9292
FundamentalUnits[expr_]:=
93-
expr/.Unit[n_,dims_]:>Unit[n,PowerExpand[dims/.$ToFundamental]];
93+
expr/.Unit[n_,dims_]:>Unit[n,PowerExpand[dims/.$ToFundamental]];
9494
Protect[FundamentalUnits];
9595

9696
(*Declare new unit in terms of old unit. User function but also used to create the intitial built in units*)
@@ -342,8 +342,8 @@
342342
(*faster test when all the same unit*)
343343

344344
(*General test*)
345-
DimensionCompatibleUnitQ[a__Unit]:=Block[{differentunits=Union[{a}/.Unit[_,un_]->Unit[1,un]]},
346-
Which[
345+
DimensionCompatibleUnitQ[a__Unit]:=Block[{differentunits=Union[{a}/.Unit[_,un_]->Unit[1,un]]},
346+
Which[
347347
Apply[And,NumericQ/@#],True,
348348
Apply[SameQ,Map[Last,#]],True,
349349
True,Message[Unit::"incomp1",differentunits];False]&[
@@ -490,6 +490,11 @@
490490
Unit[n_?NumericQ,a__ Unit[m_?NumericQ,dims_]]:=Unit[n m,a dims];
491491
Unit[n_List,un_]:=Unit[#,un]&/@n;
492492

493+
(* The following lines have been added by Niko on 07-10-2013*)
494+
SetAttributes[integerChop,Listable];
495+
integerChop[number_Real]:=If[Round@number==number,Round@number,number]
496+
integerChop[other_?NumberQ]:=other
497+
Unit[n_,stuff_]:=Block[{$preventRecursion=True},Unit[n,stuff/.Power[a_,b_Real]:>Power[a,integerChop[b]]]]/;!TrueQ[$preventRecursion]
493498

494499

495500
(*Dimensionless quantities discard their Unit head*)

Tests/MASSTestsuite.mt

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ TestSuite[
2727
"GPRRelatedTests.mt"
2828
(*,
2929
"SBMLrelatedTests.mt"*)
30-
(* ,
31-
"UnitCheckingRelatedTests"
32-
*) ,
30+
,
31+
"UnitCheckingRelatedTests.mt"
32+
,
3333
"ThermodynamicsTests.mt"
3434
}
3535
]

Tests/ReactionRelatedTests.mt

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Test[
157157
Test[
158158
getStoichiometry[testRxn]
159159
,
160-
{1, 2., 1/2}
160+
{1, 2, 1/2}
161161
,
162162
TestID->"ReactionRelatedTests-20110511-Y7P2R4"
163163
]
@@ -173,7 +173,7 @@ Test[
173173
Test[
174174
getProdStoich[testRxn]
175175
,
176-
{2., 1/2}
176+
{2, 1/2}
177177
,
178178
TestID->"ReactionRelatedTests-20110511-H6P0K3"
179179
]

Tests/UnitCheckingRelatedTests.mt

+47-28
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,94 @@
11
(* Mathematica Test File *)
22

3-
testRxns = str2mass/@{"rxn1: a + b <-> c + d", "rxn2: a + b <-> c", "rxn3: a <-> b + c", "rxn4: a <-> 0", "rxn5: 0 <-> a"};
3+
testRxns=str2mass/@{"rxn1: a + b <-> c + d","rxn2: a + b <-> c","rxn3: a <-> b + c","rxn4: a <-> 0","rxn5: 0 <-> a",
4+
"cmprt_rxn1: a_c + b_c <-> c_c + d_c","cmprt_rxn2: a_c + b_c <-> c_c","cmprt_rxn3: a_c <-> b_c + c_c","cmprt_rxn4: a_c <-> 0","cmprt_rxn5: 0 <-> a_c",
5+
"trans1: a_c <=> a_e","trans2: a_c + b_d <=> a_e + 3.3 e_c","trans3: 0 <=> a_e + 3.3 e_c","cmprt_rxn_weird: a_c + b_c <-> c_c + 3.3 d_c"};
46

5-
cmpds=Union[Flatten[getCompounds/@testRxns]];
7+
model=constructModel[testRxns,UnitChecking->True];
68

7-
conc=MapIndexed[#->{1,Milli,Micro,Pico,Femto}[[First@#2]]Mole Liter^-1&,cmpds];
9+
cmpds=Union[Flatten[getSpecies/@testRxns]];
810

9-
param = Join[Keq[getID[#]]->1&/@testRxns,rateconst[getID[#]]->1&/@testRxns, rateconst[getID[#],False]->1&/@testRxns]
11+
metricPrefixes=Join[Sequence@@Table[{1,Milli,Micro,Pico,Femto,Giga,Tera,Yotta},{3}]]
12+
conc=MapIndexed[#->metricPrefixes[[First@#2]]Mole Liter^-1&,cmpds];
1013

11-
rates = makeRates[testRxns];
14+
param=Join[Keq[getID[#]]->1&/@testRxns,rateconst[getID[#]]->1&/@testRxns,rateconst[getID[#],False]->1&/@testRxns]
15+
16+
rates=stripTime@model["Rates"];
1217

13-
Print[adjustUnits[conc]];
1418

1519
Test[
1620
adjustUnits[conc]
1721
,
18-
{metabolite["a", _] -> (1000*Milli*Mole)/Liter, metabolite["b", _] -> (Milli*Mole)/Liter, metabolite["c", _] -> (Milli*Mole)/(1000*Liter), metabolite["d", _] -> (Milli*Mole)/(1000000000*Liter)}
22+
{metabolite["a", "c"] -> Unit[1000, "Millimole"/"Liter"], metabolite["a", "e"] -> Unit[1, "Millimole"/"Liter"], metabolite["a", None] -> Unit[1/1000, "Millimole"/"Liter"], metabolite["b", "c"] -> Unit[1/1000000000, "Millimole"/"Liter"], metabolite["b", "d"] -> Unit[1/1000000000000, "Millimole"/"Liter"], metabolite["b", None] -> Unit[1000000000000, "Millimole"/"Liter"], metabolite["c", "c"] -> Unit[1000000000000000, "Millimole"/"Liter"], metabolite["c", None] -> Unit[1000000000000000000000000000, "Millimole"/"Liter"], metabolite["d", "c"] -> Unit[1000, "Millimole"/"Liter"], metabolite["d", None] -> Unit[1, "Millimole"/"Liter"], metabolite["e", "c"] -> Unit[1/1000, "Millimole"/"Liter"]}
1923
,
2024
TestID->"UnitCheckingRelatedTests-20120822-X4U4H1"
2125
]
2226

2327
Test[
2428
adjustUnits[{m["atp","c"] -> Pico Mole (Centi Meter)^-1}]
2529
,
26-
{metabolite["atp", "c"] -> (Milli*Mole)/(10000000*Meter)}
30+
{metabolite["atp", "c"] -> Unit[1/10000000, "Millimole"/"Meter"]}
2731
,
2832
TestID->"UnitCheckingRelatedTests-20120927-G1X3U0"
2933
]
3034

3135
Test[
3236
adjustUnits[{m["atp","c"] -> Pico Mole (Centi Meter)^-2}]
3337
,
34-
{metabolite["atp", "c"] -> (Milli*Mole)/(100000*Meter^2)}
38+
{metabolite["atp", "c"] -> Unit[1/100000, "Millimole"/"Meter"^2]}
3539
,
3640
TestID->"UnitCheckingRelatedTests-20120927-E8P5U8"
3741
]
3842

3943
Test[
4044
adjustUnits[{m["atp","c"] -> Pico Mole Feet^-3}]
4145
,
42-
{metabolite["atp", "c"] -> (Milli*Mole)/(28316846592*Liter)}
46+
{metabolite["atp", "c"] -> Unit[1/28316846592, "Millimole"/"Liter"]}
4347
,
4448
TestID->"UnitCheckingRelatedTests-20120927-E8P5U8"
4549
]
4650

47-
(*Test[
48-
adjustUnits[param, testRxns]
49-
,
50-
{Keq["rxn1"] -> 1, Keq["rxn2"] -> (1000*Liter)/Mole, Keq["rxn3"] -> Mole/(1000*Liter), Keq["rxn4"] -> 1, Keq["rxn5"] -> 1, rateconst["rxn1"] -> 1, rateconst["rxn2"] -> 1, rateconst["rxn3"] -> 1, rateconst["rxn4"] -> 1, rateconst["rxn5"] -> 1, rateconst["rxn1", False] -> (1000*Liter)/(Hour*Mole), rateconst["rxn2", False] -> Hour^(-1), rateconst["rxn3", False] -> (1000*Liter)/(Hour*Mole), rateconst["rxn4", False] -> Hour^(-1), rateconst["rxn5", False] -> Hour^(-1)}
51+
Test[
52+
Quiet[adjustUnits[param, testRxns],{adjustUnits::noUnitsProvidedKeq,adjustUnits::noUnitsProvidedRateConst}]
5153
,
52-
{adjustUnits::noUnits}
54+
{Keq["rxn1"] -> 1, Keq["rxn2"] -> Unit[1, "Liter"/"Millimole"], Keq["rxn3"] -> Unit[1, "Millimole"/"Liter"], Keq["rxn4"] -> 1, Keq["rxn5"] -> 1, Keq["cmprt_rxn1"] -> 1, Keq["cmprt_rxn2"] -> Unit[1, "Liter"/"Millimole"], Keq["cmprt_rxn3"] -> Unit[1, "Millimole"/"Liter"], Keq["cmprt_rxn4"] -> 1, Keq["cmprt_rxn5"] -> 1, Keq["trans1"] -> 1, Keq["trans2"] -> Unit[1., "Millimole"^2.3/"Liter"^2.3], Keq["trans3"] -> 1, Keq["cmprt_rxn_weird"] -> Unit[1., "Millimole"^2.3/"Liter"^2.3], rateconst["rxn1", True] -> Unit[1, "Liter"^2/("Hour"*"Millimole")], rateconst["rxn2", True] -> Unit[1, "Liter"^2/("Hour"*"Millimole")], rateconst["rxn3", True] -> Unit[1, "Liter"/"Hour"], rateconst["rxn4", True] -> Unit[1, "Liter"/"Hour"], rateconst["rxn5", True] -> Unit[1, "Liter"/"Hour"], rateconst["cmprt_rxn1", True] -> Unit[1, "Liter"/("Hour"*"Millimole")], rateconst["cmprt_rxn2", True] -> Unit[1, "Liter"/("Hour"*"Millimole")], rateconst["cmprt_rxn3", True] -> Unit[1, "Hour"^(-1)], rateconst["cmprt_rxn4", True] -> Unit[1, "Hour"^(-1)], rateconst["cmprt_rxn5", True] -> Unit[1, "Hour"^(-1)], rateconst["trans1", True] -> Unit[1, "Liter"/"Hour"], rateconst["trans2", True] -> Unit[1, "Liter"^2/("Hour"*"Millimole")], rateconst["trans3", True] -> Unit[1., "Liter"^4.3/("Hour"*"Millimole"^3.3)], rateconst["cmprt_rxn_weird", True] -> Unit[1, "Liter"/("Hour"*"Millimole")], rateconst["rxn1", False] -> Unit[1, "Liter"^2/("Hour"*"Millimole")], rateconst["rxn2", False] -> Unit[1, "Liter"/"Hour"], rateconst["rxn3", False] -> Unit[1, "Liter"^2/("Hour"*"Millimole")], rateconst["rxn4", False] -> Unit[1, "Liter"/"Hour"], rateconst["rxn5", False] -> Unit[1, "Liter"/"Hour"], rateconst["cmprt_rxn1", False] -> Unit[1, "Liter"/("Hour"*"Millimole")], rateconst["cmprt_rxn2", False] -> Unit[1, "Hour"^(-1)], rateconst["cmprt_rxn3", False] -> Unit[1, "Liter"/("Hour"*"Millimole")], rateconst["cmprt_rxn4", False] -> Unit[1, "Hour"^(-1)], rateconst["cmprt_rxn5", False] -> Unit[1, "Hour"^(-1)], rateconst["trans1", False] -> Unit[1, "Liter"/"Hour"], rateconst["trans2", False] -> Unit[1., "Liter"^4.3/("Hour"*"Millimole"^3.3)], rateconst["trans3", False] -> Unit[1., "Liter"^4.3/("Hour"*"Millimole"^3.3)], rateconst["cmprt_rxn_weird", False] -> Unit[1., "Liter"^3.3/("Hour"*"Millimole"^3.3)]}
5355
,
5456
TestID->"UnitCheckingRelatedTests-20120822-I8U2C3"
55-
]*)
56-
57-
(*Test[
58-
{1 + 1}
59-
,
60-
{1 + 1}
61-
,
62-
TestID->"UnitCheckingRelatedTests-20120823-E7J5T9"
6357
]
6458

6559

66-
SetOptions[adjustParameters, "DefaultAmountUnit" -> Mole]
67-
6860
Test[
69-
adjustUnits[param, testRxns]
61+
SetOptions[adjustUnits, "DefaultAmountUnit" -> Mole, "DefaultVolumeUnit" -> Meter^3, "DefaultTimeUnit"->Second];
62+
Quiet[adjustUnits[param, testRxns],{adjustUnits::noUnitsProvidedKeq,adjustUnits::noUnitsProvidedRateConst}]
7063
,
71-
{Keq["rxn1"] -> 1, Keq["rxn2"] -> (1000*Liter)/Mole, Keq["rxn3"] -> Mole/(1000*Liter), Keq["rxn4"] -> 1, Keq["rxn5"] -> 1, rateconst["rxn1"] -> 1, rateconst["rxn2"] -> 1, rateconst["rxn3"] -> 1, rateconst["rxn4"] -> 1, rateconst["rxn5"] -> 1, rateconst["rxn1", False] -> (1000*Liter)/(Hour*Mole), rateconst["rxn2", False] -> Hour^(-1), rateconst["rxn3", False] -> (1000*Liter)/(Hour*Mole), rateconst["rxn4", False] -> Hour^(-1), rateconst["rxn5", False] -> Hour^(-1)}
64+
{Keq["rxn1"] -> 1, Keq["rxn2"] -> Unit[1, "Meter"^3/"Mole"], Keq["rxn3"] -> Unit[1, "Mole"/"Meter"^3], Keq["rxn4"] -> 1, Keq["rxn5"] -> 1, Keq["cmprt_rxn1"] -> 1, Keq["cmprt_rxn2"] -> Unit[1, "Meter"^3/"Mole"], Keq["cmprt_rxn3"] -> Unit[1, "Mole"/"Meter"^3], Keq["cmprt_rxn4"] -> 1, Keq["cmprt_rxn5"] -> 1, Keq["trans1"] -> 1, Keq["trans2"] -> Unit[1., "Mole"^2.3/"Meter"^6.8999999999999995], Keq["trans3"] -> 1, Keq["cmprt_rxn_weird"] -> Unit[1., "Mole"^2.3/"Meter"^6.8999999999999995], rateconst["rxn1", True] -> Unit[1, "Meter"^6/("Mole"*"Second")], rateconst["rxn2", True] -> Unit[1, "Meter"^6/("Mole"*"Second")], rateconst["rxn3", True] -> Unit[1, "Meter"^3/"Second"], rateconst["rxn4", True] -> Unit[1, "Meter"^3/"Second"], rateconst["rxn5", True] -> Unit[1, "Meter"^3/"Second"], rateconst["cmprt_rxn1", True] -> Unit[1, "Meter"^3/("Mole"*"Second")], rateconst["cmprt_rxn2", True] -> Unit[1, "Meter"^3/("Mole"*"Second")], rateconst["cmprt_rxn3", True] -> Unit[1, "Second"^(-1)], rateconst["cmprt_rxn4", True] -> Unit[1, "Second"^(-1)], rateconst["cmprt_rxn5", True] -> Unit[1, "Second"^(-1)], rateconst["trans1", True] -> Unit[1, "Meter"^3/"Second"], rateconst["trans2", True] -> Unit[1, "Meter"^6/("Mole"*"Second")], rateconst["trans3", True] -> Unit[1., "Meter"^12.899999999999999/("Mole"^3.3*"Second")], rateconst["cmprt_rxn_weird", True] -> Unit[1, "Meter"^3/("Mole"*"Second")], rateconst["rxn1", False] -> Unit[1, "Meter"^6/("Mole"*"Second")], rateconst["rxn2", False] -> Unit[1, "Meter"^3/"Second"], rateconst["rxn3", False] -> Unit[1, "Meter"^6/("Mole"*"Second")], rateconst["rxn4", False] -> Unit[1, "Meter"^3/"Second"], rateconst["rxn5", False] -> Unit[1, "Meter"^3/"Second"], rateconst["cmprt_rxn1", False] -> Unit[1, "Meter"^3/("Mole"*"Second")], rateconst["cmprt_rxn2", False] -> Unit[1, "Second"^(-1)], rateconst["cmprt_rxn3", False] -> Unit[1, "Meter"^3/("Mole"*"Second")], rateconst["cmprt_rxn4", False] -> Unit[1, "Second"^(-1)], rateconst["cmprt_rxn5", False] -> Unit[1, "Second"^(-1)], rateconst["trans1", False] -> Unit[1, "Meter"^3/"Second"], rateconst["trans2", False] -> Unit[1., "Meter"^12.899999999999999/("Mole"^3.3*"Second")], rateconst["trans3", False] -> Unit[1., "Meter"^12.899999999999999/("Mole"^3.3*"Second")], rateconst["cmprt_rxn_weird", False] -> Unit[1., "Meter"^9.899999999999999/("Mole"^3.3*"Second")]}
7265
,
7366
TestID->"UnitCheckingRelatedTests-20120822-U7R3N3"
74-
]*)
67+
]
68+
7569

70+
(*The following combinatorial code tests that rate and equilibrium constants always have the correct units regardless of the spatial dimension or compartimentalization of the model...*)
71+
combinatorialRxns =
72+
str2mass /@ {"1: a <=> p", "2: a + b <=> p", "3: a <=> p + q",
73+
"4: a[c] <=> p[c]", "5: a[c] + b[c] <=> p[c]",
74+
"6: a[c] <=> p[c] + q[c]", "7: a[c] <=> p[e]",
75+
"8: a[e] + b[c] <=> p[d]", "9: a[c] <=> p[e] + q[e]", "9: a[c] <=> p[e] + q[e] + z[g]", "10: a <=> p[e] + q + z[g]"};
76+
Do[
77+
SetOptions[adjustUnits, "DefaultAmountUnit" -> Millimole, "DefaultVolumeUnit" -> volumeUnit, "DefaultTimeUnit" -> Hour];
78+
adjustedParam = Quiet[adjustUnits[{k[getID[rxn]] -> 1, Keq[getID[rxn]] -> 2, k[getID[rxn], False] -> .1}, {rxn}], {adjustUnits::noUnitsProvidedKeq,adjustUnits::noUnitsProvidedRateConst}];
79+
units = Quiet[adjustUnits[adjustedParam, {rxn}],{adjustUnits::noUnitsProvidedKeq,adjustUnits::noUnitsProvidedRateConst}];
80+
rate = stripTime[Toolbox`Private`reaction2rate[rxn]];
81+
kkRate = keq2k[rate];
82+
numRate = rate /. m_metabolite :> RandomReal[] Millimole volumeUnit^-1 /. units /. parameter["Volume", "c"] -> 1.5 volumeUnit;
83+
numRate2 = kkRate /. m_metabolite :> RandomReal[] Millimole volumeUnit^-1 /. units /. parameter["Volume", "c"] -> 1.5 volumeUnit;
84+
With[{testID=StringJoin["UnitCheckingRelatedTests-",volumeUnit /. {Meter->"m",Meter^2->"m^2",Meter^3->"m^3",Liter->"l"} ,"-",ToString[rxn]]},
85+
Test[
86+
MatchQ[numRate, Unit[_?NumberQ, "Millimole"*("Hour")^-1]]&&MatchQ[numRate2, Unit[_?NumberQ, "Millimole"*("Hour")^-1]]
87+
,
88+
True
89+
,
90+
TestID->testID
91+
]
92+
];
93+
,{rxn, combinatorialRxns}, {volumeUnit, {Meter, Meter^2, Meter^3,Liter}}
94+
]

0 commit comments

Comments
 (0)