Skip to content

Commit c246045

Browse files
authored
fix: predictLocalization, setParam and copyToComps (#596)
* fix: predictLocalization with CELLO output * fix: setParam bug * WIP: mergeModels copyToComps * fix: copyToComps keeps ...From fields * chore: fitTasks correct flags to fillGaps * fix: mergeModels and copyToComps ...From fields * fix: addRxns make subSystems nested cell arrays * refactor: exportModel speedup subSystem parsing * feat: runRAVENtests for quick local tests
1 parent 820cc5c commit c246045

35 files changed

Lines changed: 2197 additions & 1726 deletions

core/addRxns.m

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,15 +350,19 @@
350350
end
351351

352352
if isfield(rxnsToAdd,'subSystems')
353+
if ischar(rxnsToAdd.subSystems)
354+
rxnsToAdd.subSystems = {{rxnsToAdd.subSystems}};
355+
else
356+
for i=1:numel(rxnsToAdd.subSystems)
357+
if ischar(rxnsToAdd.subSystems{i})
358+
rxnsToAdd.subSystems{i}=rxnsToAdd.subSystems(i);
359+
end
360+
end
361+
end
353362
if numel(rxnsToAdd.subSystems)~=nRxns
354363
EM='rxnsToAdd.subSystems must have the same number of elements as rxnsToAdd.rxns';
355364
dispEM(EM);
356365
end
357-
for i=1:numel(rxnsToAdd.subSystems)
358-
if ischar(rxnsToAdd.subSystems{i})
359-
rxnsToAdd.subSystems{i}=rxnsToAdd.subSystems(i);
360-
end
361-
end
362366
%Fill with standard if it doesn't exist
363367
if ~isfield(newModel,'subSystems')
364368
newModel.subSystems=celllargefiller;

core/copyToComps.m

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,26 @@
2525
%
2626
% Usage: model=copyToComps(model,toComps,rxns,deleteOriginal,compNames,compOutside)
2727

28-
if nargin<3
29-
rxns=model.rxns;
30-
elseif ~islogical(rxns) && ~isnumeric(rxns)
31-
rxns=convertCharArray(rxns);
28+
arguments
29+
model (1,1) struct
30+
toComps {emptyOrTextOrCellOfText}
31+
rxns = model.rxns
32+
deleteOriginal {emptyOrLogicalScalar} = false
33+
compNames {emptyOrTextOrCellOfText} = toComps
34+
compOutside {emptyOrTextOrCellOfText} = '';
3235
end
33-
if nargin<4
34-
deleteOriginal=false;
36+
37+
if nargin >= 3 && ~islogical(rxns) && ~isnumeric(rxns)
38+
rxns = convertCharArray(rxns);
3539
end
36-
if nargin<5
37-
compNames=toComps;
38-
else
40+
if nargin >= 5
3941
compNames=convertCharArray(compNames);
4042
end
41-
if nargin<6
42-
compOutside=cell(numel(toComps),1);
43-
compOutside(:)={''};
44-
else
43+
if nargin >= 6
4544
compOutside=convertCharArray(compOutside);
45+
if length(compOutside) ~= length(compNames)
46+
error('compOutside and compNames should be of equal size.');
47+
end
4648
end
4749

4850
originalID=model.id;
@@ -79,15 +81,20 @@
7981
modelToAdd.compMiriams=modelToAdd.compMiriams(J);
8082
end
8183
modelToAdd.metComps=ones(numel(modelToAdd.mets),1);
82-
84+
if isfield(modelToAdd,'metFrom')
85+
modelToAdd = rmfield(modelToAdd,'metFrom');
86+
end
87+
if isfield(modelToAdd,'rxnFrom')
88+
modelToAdd = rmfield(modelToAdd,'rxnFrom');
89+
end
90+
if isfield(modelToAdd,'geneFrom')
91+
modelToAdd = rmfield(modelToAdd,'geneFrom');
92+
end
93+
8394
%Merge the models
84-
model=mergeModels({model;modelToAdd},'metNames');
95+
model=mergeModels({model;modelToAdd},'metNames',[],true);
8596
end
8697

87-
model=rmfield(model,'rxnFrom');
88-
model=rmfield(model,'metFrom');
89-
model=rmfield(model,'geneFrom');
90-
9198
if deleteOriginal==true
9299
model=removeReactions(model,rxns,true,true,true); %Also delete unused compartments
93100
end

core/fitTasks.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@
266266
%Only do gap-filling if it cannot be solved
267267
failed=false;
268268
try
269-
[~, ~, newRxns, newModel, exitFlag]=fillGaps(tModel,refModel,false,true,supressWarnings,rxnScores,params);
269+
[~, ~, newRxns, newModel, exitFlag]=fillGaps(tModel,refModel,false,true,supressWarnings,rxnScores);
270270
if exitFlag==-2
271271
EM=['"[' taskStructure(i).id '] ' taskStructure(i).description '" was aborted before reaching optimality. Consider increasing params.maxTime\n'];
272272
dispEM(EM,false);

core/mergeModels.m

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,83 @@
1-
function model=mergeModels(models,metParam,supressWarnings)
1+
function model=mergeModels(models,metParam,supressWarnings,copyToComps)
22
% mergeModels
33
% Merges models into one model structure. Reactions are added without any
44
% checks, so duplicate reactions might appear. Metabolites are matched by
55
% their name and compartment (metaboliteName[comp]), while genes are
66
% matched by their name.
77
%
8+
% Input:
89
% models a cell array with model structures
9-
% metParam string specifying whether to refer to metabolite name
10-
% (metNames) or ID (mets) for matching (default, metNames)
11-
% supressWarnings true if warnings should be supressed (optional, default
12-
% false)
10+
% metParam string metabolite name ('metNames') or ID ('mets') are
11+
% used for matching (optional, default 'metNames')
12+
% supressWarnings logical whether warnings should be supressed (optional,
13+
% default false)
14+
% copyToComps logical whether mergeModels is run via copyToComps
15+
% (optional, default false)
1316
%
14-
% model a model structure with the merged model. Follows the structure
15-
% of normal models but also has 'rxnFrom/metFrom/geneFrom' fields
16-
% to indicate from which model each reaction/metabolite/gene was
17-
% taken
17+
% Output:
18+
% model a model structure with the merged model. Follows the
19+
% structure of normal models but also has 'rxnFrom/
20+
% metFrom/geneFrom' fields to indicate from which model
21+
% each reaction/metabolite/gene was taken. If the model
22+
% already has 'rxnFrom/metFrom/geneFrom' fields, then
23+
% these fields are not modified.
1824
%
1925
% Usage: model=mergeModels(models)
2026

27+
arguments
28+
models;
29+
metParam {emptyOrTextScalar} = "metNames"
30+
supressWarnings {emptyOrLogicalScalar} = false
31+
copyToComps {emptyOrLogicalScalar} = false
32+
end
33+
34+
metParam = char(metParam);
35+
2136
%Just return the model
2237
if numel(models)<=1
2338
model=models{1};
2439
return;
2540
end
2641

27-
if nargin<2
28-
metParam='metNames';
29-
else
30-
metParam=char(metParam);
31-
end
42+
hasMetFrom = cellfun(@(s) isfield(s,'metFrom'), models);
43+
hasGeneFrom = cellfun(@(s) isfield(s,'geneFrom'), models);
44+
hasRxnFrom = cellfun(@(s) isfield(s,'rxnFrom'), models);
3245

33-
if nargin<3
34-
supressWarnings=false;
46+
for i = 1:numel(models)
47+
if copyToComps
48+
if hasMetFrom(1)
49+
models{2}.metFrom = repmat({''},numel(models{i}.mets),1);
50+
end
51+
elseif ~any(hasMetFrom)
52+
models{i}.metFrom = repmat({models{i}.id},numel(models{i}.mets),1);
53+
elseif ~hasMetFrom(i)
54+
models{i}.metFrom = repmat({''},numel(models{i}.mets),1);
55+
end
56+
if copyToComps
57+
if hasRxnFrom(1)
58+
models{2}.rxnFrom = repmat({''},numel(models{i}.rxns),1);
59+
end
60+
elseif ~any(hasRxnFrom)
61+
models{i}.rxnFrom = repmat({models{i}.id},numel(models{i}.rxns),1);
62+
elseif ~hasRxnFrom(i)
63+
models{i}.rxnFrom = repmat({''},numel(models{i}.rxns),1);
64+
end
65+
if copyToComps
66+
if hasGeneFrom(1)
67+
models{2}.geneFrom = repmat({''},numel(models{i}.genes),1);
68+
end
69+
elseif ~any(hasGeneFrom) && any(cellfun(@(s) isfield(s,'genes'), models))
70+
models{i}.geneFrom = repmat({models{i}.id},numel(models{i}.genes),1);
71+
elseif ~hasGeneFrom(i)
72+
models{i}.geneFrom = repmat({''},numel(models{i}.genes),1);
73+
end
3574
end
3675

3776
%Add new functionality in the order specified in models
3877
model=models{1};
3978
model.id='MERGED';
4079
model.name='';
4180

42-
model.rxnFrom=cell(numel(models{1}.rxns),1);
43-
model.rxnFrom(:)={models{1}.id};
44-
model.metFrom=cell(numel(models{1}.mets),1);
45-
model.metFrom(:)={models{1}.id};
46-
if isfield(models{1},'genes')
47-
model.geneFrom=cell(numel(models{1}.genes),1);
48-
model.geneFrom(:)={models{1}.id};
49-
end
50-
5181
if isfield(model,'equations')
5282
model=rmfield(model,'equations');
5383
end
@@ -78,15 +108,15 @@
78108
end
79109

80110
%Add all static stuff
81-
rxnFrom=cell(numel(models{i}.rxns),1);
82-
rxnFrom(:)={models{i}.id};
83-
model.rxnFrom=[model.rxnFrom;rxnFrom];
84-
model.rxns=[model.rxns;models{i}.rxns];
85-
model.rxnNames=[model.rxnNames;models{i}.rxnNames];
86-
model.lb=[model.lb;models{i}.lb];
87-
model.ub=[model.ub;models{i}.ub];
88-
model.c=[model.c;models{i}.c];
89-
model.rev=[model.rev;models{i}.rev];
111+
if any(hasRxnFrom) || (~copyToComps && ~any(hasRxnFrom))
112+
model.rxnFrom = [model.rxnFrom; models{i}.rxnFrom];
113+
end
114+
model.rxns = [model.rxns; models{i}.rxns];
115+
model.rxnNames = [model.rxnNames; models{i}.rxnNames];
116+
model.lb = [model.lb; models{i}.lb];
117+
model.ub = [model.ub; models{i}.ub];
118+
model.c = [model.c; models{i}.c];
119+
model.rev = [model.rev; models{i}.rev];
90120

91121
if isfield(models{i},'subSystems')
92122
if isfield(model,'subSystems')
@@ -287,12 +317,12 @@
287317
end
288318

289319
%Add static info on the metabolites
290-
metFrom=cell(numel(metsToAdd),1);
291-
metFrom(:)={models{i}.id};
292-
model.metFrom=[model.metFrom;metFrom];
293-
model.mets=[model.mets;models{i}.mets(metsToAdd)];
294-
model.metNames=[model.metNames;models{i}.metNames(metsToAdd)];
295-
model.b=[model.b;zeros(numel(metsToAdd),size(model.b,2))];
320+
if any(hasMetFrom)
321+
model.metFrom = [model.metFrom; models{i}.metFrom(metsToAdd)];
322+
end
323+
model.mets = [model.mets; models{i}.mets(metsToAdd)];
324+
model.metNames = [model.metNames; models{i}.metNames(metsToAdd)];
325+
model.b = [model.b; zeros(numel(metsToAdd),size(model.b,2))];
296326

297327
if isfield(model,'unconstrained')
298328
if isfield(models{i},'unconstrained')
@@ -481,13 +511,13 @@
481511
if isfield(models{i},'genes')
482512
if ~isfield(model,'genes')
483513
%If there was no gene info before
484-
model.genes=models{i}.genes;
485-
model.rxnGeneMat=[sparse(numel(model.rxns),numel(models{i}.genes));models{i}.rxnGeneMat];
486-
emptyGene=cell(numel(model.rxns),1);
487-
emptyGene(:)={''};
488-
model.grRules=[emptyGene;models{i}.grRules];
489-
model.geneFrom=cell(numel(models{i}.genes),1);
490-
model.geneFrom(:)={models{i}.id};
514+
model.genes = models{i}.genes;
515+
model.rxnGeneMat = [sparse(numel(model.rxns),numel(models{i}.genes));models{i}.rxnGeneMat];
516+
emptyGene = repmat({''},numel(model.rxns),1);
517+
model.grRules = [emptyGene;models{i}.grRules];
518+
if any(hasGeneFrom)
519+
model.geneFrom = models{i}.geneFrom;
520+
end
491521

492522
if isfield(models{i},'geneShortNames')
493523
model.geneShortNames=models{i}.geneShortNames;
@@ -513,11 +543,9 @@
513543
%Only add extra gene info on new genes. This might not be
514544
%correct and should be changed later...
515545
if ~isempty(genesToAdd)
516-
model.genes=[model.genes;models{i}.genes(genesToAdd)];
517-
emptyGene=cell(numel(genesToAdd),1);
518-
emptyGene(:)={models{i}.id};
519-
model.geneFrom=[model.geneFrom;emptyGene];
520-
model.rxnGeneMat=[model.rxnGeneMat sparse(size(model.rxnGeneMat,1),numel(genesToAdd))];
546+
model.genes = [model.genes; models{i}.genes(genesToAdd)];
547+
model.geneFrom = [model.geneFrom; models{i}.geneFrom(genesToAdd)];
548+
model.rxnGeneMat = [model.rxnGeneMat sparse(size(model.rxnGeneMat,1),numel(genesToAdd))];
521549

522550
if isfield(models{i},'geneShortNames')
523551
if isfield(model,'geneShortNames')
@@ -587,7 +615,7 @@
587615
%Remap the genes from the new model. The same thing as with
588616
%mets; this is a wasteful way to do it but I don't care right
589617
%now
590-
[a, b]=ismember(models{i}.genes,model.genes);
618+
a = ismember(models{i}.genes,model.genes);
591619

592620
%Just a check
593621
if ~all(a)

core/parseTaskList.m

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
% parseTaskList
33
% Parses a task list file.
44
%
5-
% inputFile a task list in Excel format. The file must contain a
6-
% sheet named TASKS, which in turn may contain the
7-
% following column headers (note, all rows starting with
8-
% a non-empty cell are removed. The first row after that
5+
% inputFile a task list in either Excel (*.xlsx, with a sheet named
6+
% TASKS with all relevant content) or tab-delimited
7+
% (*.txt) format. The file may contain the following
8+
% column headers (note, all rows starting with a
9+
% non-empty cell are removed. The first row after that
910
% is considered the headers):
1011
% ID
1112
% the only required header. Each task must have a

core/predictLocalization.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@
621621
outModel.compNames(2)=GSS.compartments(1);
622622
end
623623
end
624-
outModel.compNames=[outModel.compNames;GSS.compartments(2:end)'];
624+
outModel.compNames=[outModel.compNames;GSS.compartments(2:end)];
625625

626626
%Ugly little loop
627627
for i=1:numel(GSS.compartments)-1

core/setParam.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
params(~Lia)=[];
6969
indexes(~Lia)=[];
7070
paramType(~Lia)=[];
71-
dispEM('Reactions not present in model, will be ignored:',false,rxnLise(~Lia));
71+
dispEM('Reactions not present in model, will be ignored:',false,rxnList(~Lia));
7272
end
7373

7474
%Change the parameters

0 commit comments

Comments
 (0)