community.jl 14.9 KB
Newer Older
St. Elmo's avatar
St. Elmo committed
1
"""
St. Elmo's avatar
St. Elmo committed
2
3
4
5
6
7
    add_objective!(
        community::CoreModel,
        objective_mets::Vector{String};
        objective_weights = Float64[],
        objective_column_index = 0,
    )
St. Elmo's avatar
St. Elmo committed
8

St. Elmo's avatar
St. Elmo committed
9
10
11
12
13
14
15
Add an objective to the `community` model. Supply the string names of the
objective metabolites in `objective_mets`. Optionally specify the weight to
assign each metabolite in the objective function, if unassigned then equal
weight is assumed. Also, optionally specify whether the objective already exists
in the model by assigning `objective_column_index`. If unassigned then an
objective column will be added, otherwise the column at `objective_column_index`
will be updated.
St. Elmo's avatar
St. Elmo committed
16

St. Elmo's avatar
St. Elmo committed
17
18
19
20
21
22
23
24
25
Note, the weights are negated inside the function so that the objective metabolites 
are seen as reagents/substrates, not products in the reaction equation. 

# Example
```
add_objective!(model, ["met1", "met2"]) # adds a new column with weights = [1,1]
add_objective!(model, ["met1", "met2"]; objective_weights=[0.1, 0.9]) # adds a new column
add_objective!(model, ["met1", "met2"]; objective_weights=[0.1, 0.9], objective_column_index=10) # updates column 10
```
St. Elmo's avatar
St. Elmo committed
26
"""
cylon-x's avatar
cylon-x committed
27
28
29
30
31
32
function add_objective!(
    community::CoreModel,
    objective_mets::Vector{String};
    objective_weights = Float64[],
    objective_column_index = 0,
)
St. Elmo's avatar
St. Elmo committed
33
34
    obj_inds = indexin(objective_mets, metabolites(community))
    if isempty(objective_weights)
cylon-x's avatar
cylon-x committed
35
        objective_weights = repeat([1.0], inner = length(objective_mets))
St. Elmo's avatar
St. Elmo committed
36
    end
St. Elmo's avatar
St. Elmo committed
37

St. Elmo's avatar
St. Elmo committed
38
39
40
41
    if objective_column_index == 0 # needs to be created
        nr, _ = size(community.S)
        objcol = spzeros(nr)
        objcol[obj_inds] .= -objective_weights
cylon-x's avatar
cylon-x committed
42

St. Elmo's avatar
St. Elmo committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
        # extend model by one reaction 
        community.S = hcat(community.S, objcol)
        community.xl = [community.xl; 0.0]
        community.xu = [community.xu; 1000.0]
        community.rxns = [community.rxns; "community_biomass"]
        community.c = spzeros(size(community.S, 2))
        community.c[end] = 1.0
    else # only update
        community.S[:, objective_column_index] .= 0.0 # reset
        community.S[obj_inds, objective_column_index] .= -objective_weights
        dropzeros!(community.S)
        community.c = spzeros(size(community.S, 2))
        community.c[objective_column_index] = 1.0
        community.xl[objective_column_index] = 0.0
        community.xu[objective_column_index] = 1000.0
    end
St. Elmo's avatar
St. Elmo committed
59
60
61
end

"""
St. Elmo's avatar
St. Elmo committed
62
    add_model_with_exchanges(
St. Elmo's avatar
St. Elmo committed
63
64
65
66
67
68
69
        community::CoreModel,
        model::MetabolicModel,
        exchange_rxn_ids::Vector{String},
        exchange_met_ids::Vector{String};
        model_name = "unknown_species",
        biomass_id = nothing,
    )::CoreModel
St. Elmo's avatar
St. Elmo committed
70
71

Add `model` to `community`, which is a pre-existing community model with
St. Elmo's avatar
St. Elmo committed
72
`exchange_rxn_ids` and `exchange_met_ids`. The `model_name` is appended to
St. Elmo's avatar
St. Elmo committed
73
74
75
76
77
each reaction and metabolite, see [`join`](@ref). If `biomass_id` is specified
then a biomass metabolite for `model` is also added to the resulting model. The
column corresponding to the `biomass_id` reaction then produces this new biomass
metabolite with unit coefficient. Note, `exchange_rxn_ids` and
`exchange_met_ids` must already exist in the `community` model.
St. Elmo's avatar
St. Elmo committed
78

St. Elmo's avatar
St. Elmo committed
79
80
# Example
```
St. Elmo's avatar
St. Elmo committed
81
community = add_model_with_exchanges(community, model, exchange_rxn_ids, exchange_met_ids; model_name="species_2", biomass_id="BIOMASS_Ecoli_core_w_GAM")
St. Elmo's avatar
St. Elmo committed
82
```
St. Elmo's avatar
St. Elmo committed
83
"""
St. Elmo's avatar
St. Elmo committed
84
function add_model_with_exchanges(
St. Elmo's avatar
St. Elmo committed
85
    community::CoreModel,
St. Elmo's avatar
St. Elmo committed
86
    model::MetabolicModel,
cylon-x's avatar
cylon-x committed
87
    exchange_rxn_ids::Vector{String},
St. Elmo's avatar
St. Elmo committed
88
    exchange_met_ids::Vector{String};
St. Elmo's avatar
St. Elmo committed
89
    model_name = "unknown_species",
cylon-x's avatar
cylon-x committed
90
    biomass_id = nothing,
St. Elmo's avatar
St. Elmo committed
91
)::CoreModel
cylon-x's avatar
cylon-x committed
92

St. Elmo's avatar
St. Elmo committed
93
94
    exchange_met_community_inds = indexin(exchange_met_ids, metabolites(community))
    exchange_rxn_community_inds = indexin(exchange_rxn_ids, reactions(community))
cylon-x's avatar
cylon-x committed
95
96
    if any(isnothing.(exchange_met_community_inds)) ||
       any(isnothing.(exchange_rxn_community_inds))
cylon-x's avatar
cylon-x committed
97
98
        throw(
            DomainError(
St. Elmo's avatar
St. Elmo committed
99
100
                "exchange metabolite/reaction not found.",
                "Exchange metabolites/reactions must already be contained in the community model.",
cylon-x's avatar
cylon-x committed
101
102
            ),
        )
St. Elmo's avatar
St. Elmo committed
103
    end
St. Elmo's avatar
St. Elmo committed
104

St. Elmo's avatar
St. Elmo committed
105
106
    n_cmodel_rows, n_cmodel_cols = size(stoichiometry(community))
    n_model_rows, n_model_cols = size(stoichiometry(model))
St. Elmo's avatar
St. Elmo committed
107
108
109
    # A note on the variable names here.Suppose M is some sparse matrix, then I
    # = row indices, J = column indices and V = values at the associated
    # indices. So I[a] = i, J[a]=j and then M[i,j] = V[a]
St. Elmo's avatar
St. Elmo committed
110
111
    Iadd, Jadd, Vadd = findnz(stoichiometry(model))

St. Elmo's avatar
St. Elmo committed
112
113
114
    # shift to fit into community         
    Iadd .+= n_cmodel_rows
    Jadd .+= n_cmodel_cols
cylon-x's avatar
cylon-x committed
115

St. Elmo's avatar
St. Elmo committed
116
    # when adding a single model not that many reactions, push! okay?
St. Elmo's avatar
St. Elmo committed
117
    exchange_rxn_model_idxs = indexin(exchange_rxn_ids, reactions(model))
St. Elmo's avatar
St. Elmo committed
118
    for i = 1:length(exchange_rxn_ids)
St. Elmo's avatar
St. Elmo committed
119
        isnothing(exchange_rxn_model_idxs[i]) && continue
St. Elmo's avatar
St. Elmo committed
120
        push!(Iadd, exchange_met_community_inds[i]) # already present ex met in community model
St. Elmo's avatar
St. Elmo committed
121
        push!(Jadd, n_cmodel_cols + exchange_rxn_model_idxs[i]) # new column hence the offset
St. Elmo's avatar
St. Elmo committed
122
123
124
        push!(Vadd, 1.0)
    end

St. Elmo's avatar
St. Elmo committed
125
126
    biomass_met = 0.0
    if biomass_id != "" # add biomass metabolite
cylon-x's avatar
cylon-x committed
127
        biomass_rxn = first(indexin([biomass_id], reactions(model)))
St. Elmo's avatar
St. Elmo committed
128
129
130
        push!(Iadd, n_model_rows + n_cmodel_rows + 1)
        push!(Jadd, biomass_rxn + n_cmodel_cols)
        push!(Vadd, 1.0)
cylon-x's avatar
cylon-x committed
131
        biomass_met = 1
St. Elmo's avatar
St. Elmo committed
132
    end
St. Elmo's avatar
St. Elmo committed
133

St. Elmo's avatar
St. Elmo committed
134
135
    n_metabolites_total = n_model_rows + n_cmodel_rows + biomass_met
    n_reactions_total = n_cmodel_cols + n_model_cols
cylon-x's avatar
cylon-x committed
136

St. Elmo's avatar
St. Elmo committed
137
    I, J, V = findnz(stoichiometry(community))
St. Elmo's avatar
St. Elmo committed
138
139
140
141
142
    I = [I; Iadd]
    J = [J; Jadd]
    V = [V; Vadd]
    S = sparse(I, J, V, n_metabolites_total, n_reactions_total)

St. Elmo's avatar
St. Elmo committed
143
144
145
146
    # A note on the variables here. The bounds are vectors of upper and lower
    # bounds for each reaction. So lbs = [lb_1, lb_2, lb_i, ...], ubs = [ub_1,
    # ub_2, ub_i, ...] for reaction i. See the bounds function for more
    # information 
St. Elmo's avatar
St. Elmo committed
147
    lbsadd, ubsadd = bounds(model)
St. Elmo's avatar
St. Elmo committed
148
    lbs, ubs = bounds(community)
St. Elmo's avatar
St. Elmo committed
149
    lbs = [lbs; lbsadd]
St. Elmo's avatar
St. Elmo committed
150
    ubs = [ubs; ubsadd]
St. Elmo's avatar
St. Elmo committed
151

St. Elmo's avatar
St. Elmo committed
152
    rxnsadd = "$(model_name)_" .* reactions(model)
St. Elmo's avatar
St. Elmo committed
153
    if !isnothing(biomass_id)
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
154
        metsadd = ["$(model_name)_" .* metabolites(model); "$(model_name)_" * biomass_id]
St. Elmo's avatar
St. Elmo committed
155
    else
St. Elmo's avatar
St. Elmo committed
156
        metsadd = "$(model_name)_" .* metabolites(model)
St. Elmo's avatar
St. Elmo committed
157
    end
St. Elmo's avatar
St. Elmo committed
158
159
160
161
162
163
164
165
    rxns = [reactions(community); rxnsadd]
    mets = [metabolites(community); metsadd]

    # adds to the original community data, could possibly reset?
    I, V = findnz(balance(community))
    b = sparsevec(I, V, n_metabolites_total)
    I, V = findnz(objective(community))
    c = sparsevec(I, V, n_reactions_total)
cylon-x's avatar
cylon-x committed
166

St. Elmo's avatar
St. Elmo committed
167
    return CoreModel(S, b, c, lbs, ubs, rxns, mets)
St. Elmo's avatar
St. Elmo committed
168
end
St. Elmo's avatar
St. Elmo committed
169

St. Elmo's avatar
St. Elmo committed
170
"""
St. Elmo's avatar
St. Elmo committed
171
    join_with_exchanges(models::Vector{M}, 
St. Elmo's avatar
St. Elmo committed
172
173
174
175
        exchange_rxn_ids::Vector{String}, 
        exchange_met_ids::Vector{String}; 
        add_biomass_objective=false, 
        biomass_ids::Vector{String}, 
St. Elmo's avatar
St. Elmo committed
176
        model_names=String[]
St. Elmo's avatar
St. Elmo committed
177
178
    ) 

St. Elmo's avatar
St. Elmo committed
179
180
Return a `CoreModel` representing the community model of `models` joined through
their `exchange_rxn_ids` and `exchange_met_ids`. These exchange reactions and
St. Elmo's avatar
St. Elmo committed
181
metabolites link to environmental metabolites and reactions.
St. Elmo's avatar
St. Elmo committed
182
Optionally specify `model_names` to append a specific name to each reaction
St. Elmo's avatar
St. Elmo committed
183
184
185
186
187
and metabolite of an organism for easier reference (default is `species_i` for
each model index i in `models`). Note, the bounds of the environmental variables
are all set to zero. Thus, to run a simulation you need to constrain them
appropriately. All the other bounds are inherited from the models used to
construct the community model.
St. Elmo's avatar
St. Elmo committed
188
189
190
191

If `add_biomass_objective` is true then `biomass_ids` needs to be supplied as
well. This creates a model with an extra reaction added to the end of the
stoichiometric matrix (last column) that can be assigned as the objective
St. Elmo's avatar
St. Elmo committed
192
193
194
195
196
reaction. It also creates biomass "metabolites" that can be used in this
objective reaction. Note, this reaction is unspecified, further action needs to
be taken to specify it, e.g. assign weights to the last column of the
stoichiometric matrix in the rows corresponding to the biomass metabolites.

St. Elmo's avatar
St. Elmo committed
197
To further clarify how this `join` works. Suppose you have 2 organisms with
St. Elmo's avatar
St. Elmo committed
198
199
200
201
stoichiometric matrices S₁ and S₂ and you want to link them with
`exchange_met_ids = [em₁, em₂, em₃, ...]` and `exchange_rxn_ids = [er₁, er₂,
er₃, ...]`. Then a new community stoichiometric matrix is constructed that looks
like this:
St. Elmo's avatar
St. Elmo committed
202
203
204
205
206
207
208
209
210
211
212
213
```
            _      er₁  er₂  er₃  ...  b_
           |S₁                           |
           |   S₂                        |
        em₁|                             |
S   =   em₂|                             |
        em₃|                             |
        ...|                             |
        bm₁|                             |  
        bm₂|_                           _|

```
St. Elmo's avatar
St. Elmo committed
214
215
216
217
218
219
220
The exchange reactions in each model get linked to environmental metabolites,
`emᵢ`, and these get linked to environmental exchanges, `erᵢ`. These `erᵢ`
behave like normal single organism exchange reactions. When
`add_biomass_objective` is true each model's biomass becomes a pseudo-metabolite
(`bmᵢ`). These can be weighted in column `b`, called the `community_biomass`
reaction in the community model, if desired. Refer to the tutorial if this is
unclear.
St. Elmo's avatar
St. Elmo committed
221

St. Elmo's avatar
St. Elmo committed
222
# Example
St. Elmo's avatar
St. Elmo committed
223
```
St. Elmo's avatar
St. Elmo committed
224
225
226
m1 = load_model(core_model_path)
m2 = load_model(CoreModel, core_model_path)

St. Elmo's avatar
St. Elmo committed
227
228
229
# need to list ALL the exchanges that will form part of the entire model
exchange_rxn_ids = filter(looks_like_exchange_reaction, reactions(m1))
exchange_met_ids = [first(keys(reaction_stoichiometry(m1, ex_rxn))) for ex_rxn in exchange_rxn_ids]
St. Elmo's avatar
St. Elmo committed
230
231
232

biomass_ids = ["BIOMASS_Ecoli_core_w_GAM", "BIOMASS_Ecoli_core_w_GAM"]

St. Elmo's avatar
St. Elmo committed
233
community = join_with_exchanges(
St. Elmo's avatar
St. Elmo committed
234
235
236
237
238
239
    [m1, m2],
    exchange_rxn_ids,
    exchange_met_ids;
    add_biomass_objective = true,
    biomass_ids = biomass_ids,
)
St. Elmo's avatar
St. Elmo committed
240
```
St. Elmo's avatar
St. Elmo committed
241
"""
St. Elmo's avatar
St. Elmo committed
242
function join_with_exchanges(
St. Elmo's avatar
St. Elmo committed
243
    models::Vector{M},
cylon-x's avatar
cylon-x committed
244
245
246
247
    exchange_rxn_ids::Vector{String},
    exchange_met_ids::Vector{String};
    add_biomass_objective = true,
    biomass_ids = String[],
St. Elmo's avatar
St. Elmo committed
248
    model_names = String[],
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
249
)::CoreModel where {M<:MetabolicModel}
cylon-x's avatar
cylon-x committed
250

St. Elmo's avatar
St. Elmo committed
251
    if add_biomass_objective && isempty(biomass_ids)
cylon-x's avatar
cylon-x committed
252
253
254
255
256
257
        throw(
            DomainError(
                "biomass_ids",
                "Please add supply the string ids of the biomass functions when `add_biomass_objective` is true.",
            ),
        )
St. Elmo's avatar
St. Elmo committed
258
259
    end

St. Elmo's avatar
St. Elmo committed
260
261
262
263
264
265
266
267
268
    if length(exchange_met_ids) != length(exchange_rxn_ids)
        throw(
            DomainError(
                "Exchange identifiers are misspecified",
                "The lenghts of the exchange metabolite and reactions are different.",
            ),
        )
    end

St. Elmo's avatar
St. Elmo committed
269
270
271
272
273
    # get offsets to construct community S
    reaction_lengths = [n_reactions(model) for model in models]
    metabolite_lengths = [n_metabolites(model) for model in models]
    reaction_offset = [0; cumsum(reaction_lengths[1:end-1])]
    metabolite_offset = [0; cumsum(metabolite_lengths[1:end-1])]
cylon-x's avatar
cylon-x committed
274

St. Elmo's avatar
St. Elmo committed
275
276
277
278
279
280
281
282
    # get each model's S matrix (needed for the size calculations)
    stoichs = [stoichiometry(model) for model in models]
    nnzs = [findnz(stoich) for stoich in stoichs] # nonzero indices per model

    # size calculations
    column_add = add_biomass_objective ? 1 : 0 # objective rxn
    row_add = add_biomass_objective ? length(models) : 0 # biomass as metabolites
    nnz_add = add_biomass_objective ? (1 + length(models)) : 0
cylon-x's avatar
cylon-x committed
283
284
285
    nnz_total =
        sum(length(first(nnz)) for nnz in nnzs) +
        length(models) * length(exchange_rxn_ids) +
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
286
287
        length(exchange_met_ids) +
        nnz_add
St. Elmo's avatar
St. Elmo committed
288
289
290
291
    n_reactions_metabolic = sum(reaction_lengths)
    n_reactions_total = n_reactions_metabolic + length(exchange_rxn_ids) + column_add
    n_metabolites_metabolic = sum(metabolite_lengths)
    n_metabolites_total = n_metabolites_metabolic + length(exchange_met_ids) + row_add
cylon-x's avatar
cylon-x committed
292

St. Elmo's avatar
St. Elmo committed
293
294
295
    # Create empty storage vectors
    lbs = spzeros(n_reactions_total)
    ubs = spzeros(n_reactions_total)
cylon-x's avatar
cylon-x committed
296
297
298
299
300
    rxns = Array{String,1}(undef, n_reactions_total)
    mets = Array{String,1}(undef, n_metabolites_total)
    I = Array{Int,1}(undef, nnz_total)
    J = Array{Int,1}(undef, nnz_total)
    V = Array{Float64,1}(undef, nnz_total)
St. Elmo's avatar
St. Elmo committed
301

St. Elmo's avatar
St. Elmo committed
302
    # build metabolic components, block diagonals
St. Elmo's avatar
St. Elmo committed
303
    kstart = 1
cylon-x's avatar
cylon-x committed
304
    for i = 1:length(models)
St. Elmo's avatar
St. Elmo committed
305
        kend = kstart + length(nnzs[i][3]) - 1
cylon-x's avatar
cylon-x committed
306
        rng = kstart:kend
St. Elmo's avatar
St. Elmo committed
307
308
309
310
311
        I[rng] .= nnzs[i][1] .+ metabolite_offset[i]
        J[rng] .= nnzs[i][2] .+ reaction_offset[i]
        V[rng] .= nnzs[i][3]
        kstart += length(nnzs[i][3])
    end
St. Elmo's avatar
St. Elmo committed
312

St. Elmo's avatar
St. Elmo committed
313
    # build environmental - exchange links
cylon-x's avatar
cylon-x committed
314
    for i = 1:length(models)
St. Elmo's avatar
St. Elmo committed
315
        exchange_rxn_inds = indexin(exchange_rxn_ids, reactions(models[i]))
St. Elmo's avatar
St. Elmo committed
316
317
        exchange_met_inds = indexin(exchange_met_ids, metabolites(models[i]))
        for (n, (ex_rxn, ex_met)) in enumerate(zip(exchange_rxn_inds, exchange_met_inds)) # each exchange rxn has one exchange met
St. Elmo's avatar
St. Elmo committed
318
            isnothing(ex_rxn) && continue
St. Elmo's avatar
St. Elmo committed
319
            isnothing(ex_met) && continue
St. Elmo's avatar
St. Elmo committed
320
321
322
            # connect environmental metabolite with exchange metabolite
            I[kstart] = n_metabolites_metabolic + n
            J[kstart] = ex_rxn + reaction_offset[i]
St. Elmo's avatar
St. Elmo committed
323
            V[kstart] = -stoichs[i][ex_met, ex_rxn] # ex is normally negative, make positive
cylon-x's avatar
cylon-x committed
324
            kstart += 1
St. Elmo's avatar
St. Elmo committed
325
        end
St. Elmo's avatar
St. Elmo committed
326
    end
St. Elmo's avatar
St. Elmo committed
327
328

    # # build diagonal environmental exchanges
cylon-x's avatar
cylon-x committed
329
    for i = 1:length(exchange_rxn_ids)
St. Elmo's avatar
St. Elmo committed
330
331
332
333
334
        I[kstart] = n_metabolites_metabolic + i
        J[kstart] = n_reactions_metabolic + i
        V[kstart] = -1.0
        kstart += 1
    end
St. Elmo's avatar
St. Elmo committed
335

St. Elmo's avatar
St. Elmo committed
336
337
    if add_biomass_objective
        n_before_biomass_row = n_metabolites_metabolic + length(exchange_met_ids)
cylon-x's avatar
cylon-x committed
338
        for i = 1:length(models)
St. Elmo's avatar
St. Elmo committed
339
            biomass_ind = first(indexin([biomass_ids[i]], reactions(models[i])))
cylon-x's avatar
cylon-x committed
340
            I[kstart] = i + n_before_biomass_row
St. Elmo's avatar
St. Elmo committed
341
            J[kstart] = biomass_ind + reaction_offset[i]
cylon-x's avatar
cylon-x committed
342
343
            V[kstart] = 1.0
            kstart += 1
St. Elmo's avatar
St. Elmo committed
344
345
        end
    end
St. Elmo's avatar
St. Elmo committed
346

cylon-x's avatar
cylon-x committed
347
348
349
350
351
352
    S = sparse(
        I[1:kstart-1],
        J[1:kstart-1],
        V[1:kstart-1],
        n_metabolites_total,
        n_reactions_total,
St. Elmo's avatar
St. Elmo committed
353
    ) # could be that some microbes don't have all the exchanges, hence kstart-1
St. Elmo's avatar
St. Elmo committed
354

St. Elmo's avatar
St. Elmo committed
355
356
    _reaction_offsets = cumsum(reaction_lengths)
    _metabolite_offsets = cumsum(metabolite_lengths)
cylon-x's avatar
cylon-x committed
357
    for i = 1:length(models)
St. Elmo's avatar
St. Elmo committed
358
        species = isempty(model_names) ? "species_$(i)" : model_names[i]
St. Elmo's avatar
St. Elmo committed
359
        tlbs, tubs = bounds(models[i])
St. Elmo's avatar
St. Elmo committed
360
361
362
        lbs[reaction_offset[i]+1:_reaction_offsets[i]] .= tlbs
        ubs[reaction_offset[i]+1:_reaction_offsets[i]] .= tubs
        rxns[reaction_offset[i]+1:_reaction_offsets[i]] =
cylon-x's avatar
cylon-x committed
363
            "$(species)_" .* reactions(models[i])
St. Elmo's avatar
St. Elmo committed
364
        mets[metabolite_offset[i]+1:_metabolite_offsets[i]] =
cylon-x's avatar
cylon-x committed
365
            "$(species)_" .* metabolites(models[i])
St. Elmo's avatar
St. Elmo committed
366
    end
St. Elmo's avatar
St. Elmo committed
367
    mets[_metabolite_offsets[end]+1:_metabolite_offsets[end]+length(exchange_met_ids)] .=
cylon-x's avatar
cylon-x committed
368
        exchange_met_ids
St. Elmo's avatar
St. Elmo committed
369
    rxns[_reaction_offsets[end]+1:_reaction_offsets[end]+length(exchange_rxn_ids)] .=
cylon-x's avatar
cylon-x committed
370
371
        exchange_rxn_ids

St. Elmo's avatar
St. Elmo committed
372
373
    if add_biomass_objective
        rxns[end] = "community_biomass"
cylon-x's avatar
cylon-x committed
374
        for i = 1:length(models)
St. Elmo's avatar
St. Elmo committed
375
            species = isempty(model_names) ? "species_$(i)" : model_names[i]
cylon-x's avatar
cylon-x committed
376
            mets[end-length(biomass_ids)+i] = "$(species)_" .* biomass_ids[i]
377
        end
St. Elmo's avatar
St. Elmo committed
378
    end
cylon-x's avatar
cylon-x committed
379
380

    return CoreModel(S, spzeros(size(S, 1)), spzeros(size(S, 2)), lbs, ubs, rxns, mets)
St. Elmo's avatar
St. Elmo committed
381
end