Skip to content

Commit 377d90c

Browse files
authored
Merge pull request #5 from ModelSEED/dev
ups
2 parents e019633 + b6c3328 commit 377d90c

File tree

2 files changed

+92
-91
lines changed

2 files changed

+92
-91
lines changed

modelseedpy/core/fbahelper.py

Lines changed: 79 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,31 @@
33
import logging
44
from chemicals import periodic_table
55
import re
6+
from cobra.core import Gene, Metabolite, Model, Reaction # !!! Gene, Metabolite, and Model are never used
7+
from cobra.util import solver as sutil # !!! sutil is never used
68
import time
7-
from cobra.core import Gene, Metabolite, Model, Reaction
8-
from cobra.util import solver as sutil
99
from modelseedpy.biochem import from_local
10-
from scipy.odr.odrpack import Output
10+
from scipy.odr.odrpack import Output # !!! Output is never used
11+
from chemw import ChemMW
12+
from warnings import warn
1113
#from Carbon.Aliases import false
1214

1315
logger = logging.getLogger(__name__)
1416

15-
elementmass = {}
16-
for element in periodic_table:
17-
elementmass[element.symbol] = element.MW
18-
19-
2017
class FBAHelper:
2118

2219
@staticmethod
23-
def add_autodrain_reactions_to_community_model(model,auto_sink = ["cpd02701", "cpd11416", "cpd15302"]):
20+
def add_autodrain_reactions_to_community_model(model,auto_sink = ["cpd02701", "cpd15302"]):
2421
#Adding missing drains in the base model
2522
drain_reactions = []
2623
for metabolite in model.metabolites:
2724
msid = FBAHelper.modelseed_id_from_cobra_metabolite(metabolite)
2825
if msid in auto_sink:
29-
if msid != "cpd11416" or metabolite.compartment == "c0":
26+
if metabolite.compartment == "c0":
3027
met_id = metabolite.id
31-
if all(rxn not in model.reactions for rxn in [f"EX_{met_id}", f"DM_{met_id}", f"SK_{met_id}"]):
28+
if all([rxn not in model.reactions for rxn in [f"EX_{met_id}", f"DM_{met_id}", f"SK_{met_id}"]]):
3229
drain_reaction = FBAHelper.add_drain_from_metabolite_id(model,metabolite.id,0,100,"DM_")
33-
if drain_reaction != None:
30+
if not drain_reaction:
3431
logger.info("Adding "+met_id+" DM")
3532
drain_reactions.append(drain_reaction)
3633
model.add_reactions(drain_reactions)
@@ -48,25 +45,26 @@ def add_drain_from_metabolite_id(model, cpd_id, uptake, excretion, prefix='EX_',
4845
"""
4946
if cpd_id in model.metabolites:
5047
cobra_metabolite = model.metabolites.get_by_id(cpd_id)
51-
drain_reaction = Reaction(id=f'{prefix}{cpd_id}',
52-
name=prefix_name + cobra_metabolite.name,
53-
lower_bound=-1*uptake,
54-
upper_bound=excretion)
48+
drain_reaction = Reaction(
49+
id=f'{prefix}{cpd_id}',
50+
name=prefix_name + cobra_metabolite.name,
51+
lower_bound = -uptake,
52+
upper_bound = excretion)
5553
drain_reaction.add_metabolites({cobra_metabolite : -1})
5654
drain_reaction.annotation["sbo"] = 'SBO:0000627'
5755
#model.add_reactions([drain_reaction])
5856
return drain_reaction
5957
return None
60-
58+
6159
@staticmethod
62-
def set_reaction_bounds_from_direction(reaction, direction, add=0):
60+
def set_reaction_bounds_from_direction(reaction, direction, add=False):
6361
if direction == "<":
6462
reaction.lower_bound = -100
65-
if add == 0:
63+
if not add:
6664
reaction.upper_bound = 0
6765
if direction == ">":
6866
reaction.upper_bound = 100
69-
if add == 0:
67+
if not add:
7068
reaction.lower_bound = 0
7169
reaction.update_variable_bounds()
7270

@@ -76,9 +74,7 @@ def set_objective_from_target_reaction(model,target_reaction,minimize = False):
7674
sense = "max"
7775
if minimize:
7876
sense = "min"
79-
model.objective = model.problem.Objective(
80-
1 * target_reaction.flux_expression,
81-
direction=sense)
77+
model.objective = model.problem.Objective(target_reaction.flux_expression, direction=sense)
8278
return target_reaction
8379

8480
@staticmethod
@@ -87,8 +83,7 @@ def modelseed_id_from_cobra_metabolite(metabolite):
8783
m = re.search('^(cpd\d+)', metabolite.id)
8884
return m[1]
8985
#TODO: should check to see if ModelSEED ID is in the annotations for the compound
90-
else:
91-
return None
86+
return None
9287

9388
@staticmethod
9489
def modelseed_id_from_cobra_reaction(reaction):
@@ -101,18 +96,16 @@ def modelseed_id_from_cobra_reaction(reaction):
10196

10297
@staticmethod
10398
def metabolite_mw(metabolite):
104-
mw = 0
105-
elements = metabolite.elements
106-
for element in elements:
107-
if element not in elementmass:
108-
print("Missing mass for element "+element+" in compound "+metabolite.id+". Element will be ignored when computing MW")
109-
else:
110-
mw += elements[element]*elementmass[element]
111-
return mw
99+
try:
100+
chem_mw = ChemMW()
101+
chem_mw.mass(metabolite.formula)
102+
return chem_mw.raw_mw
103+
except:
104+
warn("The compound "+metabolite.id+" possesses an unconventional formula {metabolite.formula}; hence, the MW cannot be computed.")
112105

113106
@staticmethod
114107
def elemental_mass():
115-
return elementmass
108+
return {element.symbol:element.MW for element in periodic_table}
116109

117110
@staticmethod
118111
def get_modelseed_db_api(modelseed_path):
@@ -131,8 +124,8 @@ def is_biomass(reaction):
131124
return reaction.id[0:3] == "bio"
132125

133126
@staticmethod
134-
def exchange_hash(model):
135-
exchange_hash = {}
127+
def exchange_hash(model): #!!! This function is pointless?
128+
exchange_hash = {} # !!! this variable is never used
136129
for reaction in model.reactions:
137130
if len(reaction.metabolites) == 1:
138131
for metabolite in reaction.metabolites:
@@ -141,102 +134,88 @@ def exchange_hash(model):
141134

142135
@staticmethod
143136
def find_reaction(model,stoichiometry):
144-
output = FBAHelper.stoichiometry_to_string(stoichiometry)
145-
atpstring = output[0]
137+
reaction_strings = FBAHelper.stoichiometry_to_string(stoichiometry)
138+
atpstring = reaction_strings[0]
146139
rxn_hash = FBAHelper.rxn_hash(model)
147140
if atpstring in rxn_hash:
148141
return rxn_hash[atpstring]
149142
return None
150143

151144
@staticmethod
152-
def msid_hash(model):
145+
def msid_hash(model):
153146
output = {}
154-
for cpd in model.metabolites:
155-
msid = FBAHelper.modelseed_id_from_cobra_metabolite(cpd)
147+
for met in model.metabolites:
148+
msid = FBAHelper.modelseed_id_from_cobra_metabolite(met)
156149
if msid != None:
157150
if msid not in output:
158151
output[msid] = []
159-
output[msid].append(cpd)
152+
output[msid].append(met)
160153
return output
161154

162155
@staticmethod
163156
def rxn_hash(model):
164157
output = {}
165158
for rxn in model.reactions:
166-
strings = FBAHelper.stoichiometry_to_string(rxn.metabolites)
167-
output[strings[0]] = [rxn,1]
168-
output[strings[1]] = [rxn,-1]
159+
reaction_strings = FBAHelper.stoichiometry_to_string(rxn.metabolites)
160+
output[reaction_strings[0]] = [rxn,1]
161+
output[reaction_strings[1]] = [rxn,-1]
169162
return output
170163

171164
@staticmethod
172165
def rxn_compartment(reaction):
173166
compartments = list(reaction.compartments)
174167
if len(compartments) == 1:
175168
return compartments[0]
176-
cytosol = None
177-
othercomp = None
169+
cytosol = othercomp = None
178170
for comp in compartments:
179-
if comp[0:1] != "e":
180-
if comp[0:1] == "c":
181-
cytosol = comp
182-
else:
183-
othercomp = comp
184-
if othercomp is not None:
185-
return othercomp
186-
return cytosol
171+
if comp[0:1] == "c":
172+
cytosol = comp
173+
elif comp[0:1] != "e":
174+
othercomp = comp
175+
return othercomp or cytosol
187176

188177
@staticmethod
189178
def stoichiometry_to_string(stoichiometry):
190-
reactants = []
191-
products = []
179+
reactants, products = [], []
192180
for met in stoichiometry:
193-
coef = stoichiometry[met]
181+
stoich = stoichiometry[met]
194182
if not isinstance(met, str):
195-
if FBAHelper.modelseed_id_from_cobra_metabolite(met) == "cpd00067":
196-
met = None
197-
else:
198-
met = met.id
199-
if met != None:
200-
if coef < 0:
183+
met = None if FBAHelper.modelseed_id_from_cobra_metabolite(met) == "cpd00067" else met.id
184+
if met:
185+
if stoich < 0:
201186
reactants.append(met)
202187
else:
203188
products.append(met)
204-
reactants.sort()
205-
products.sort()
206-
return ["+".join(reactants)+"="+"+".join(products),"+".join(products)+"="+"+".join(reactants)]
189+
return ["+".join(sorted(reactants))+"="+"+".join(sorted(products)),"+".join(sorted(products))+"="+"+".join(sorted(reactants))]
207190

208191
@staticmethod
209192
def add_atp_hydrolysis(model,compartment):
210-
#Searching for ATP hydrolysis compounds
211-
coefs = {"cpd00002":[-1,compartment],"cpd00001":[-1,compartment],"cpd00008":[1,compartment],"cpd00009":[1,compartment],"cpd00067":[1,compartment]}
212-
msids = ["cpd00002","cpd00001","cpd00008","cpd00009","cpd00067"]
193+
# Searching for ATP hydrolysis compounds
194+
coefs = {"cpd00002": [-1,compartment], "cpd00001": [-1,compartment], "cpd00008": [1,compartment],
195+
"cpd00009": [1,compartment], "cpd00067": [1,compartment]}
213196
stoichiometry = {}
214197
id_hash = FBAHelper.msid_hash(model)
215-
for msid in msids:
198+
for msid, content in coefs.items():
216199
if msid not in id_hash:
217200
logger.warning("Compound "+msid+" not found in model!")
218201
return None
219202
else:
220203
for cpd in id_hash[msid]:
221-
if cpd.compartment == coefs[msid][1]:
222-
stoichiometry[cpd] = coefs[msid][0]
204+
if cpd.compartment == content[1]:
205+
stoichiometry[cpd] = content[0]
223206
output = FBAHelper.find_reaction(model,stoichiometry)
224-
if output != None and output[1] == ">":
207+
if output and output[1] == 1: # !!! the second element of the output is 1/0 and not a direction string
225208
return {"reaction":output[0],"direction":">","new":False}
226-
cobra_reaction = Reaction("rxn00062_"+compartment,
227-
name="ATP hydrolysis",
228-
lower_bound=0,
229-
upper_bound=1000)
230-
cobra_reaction.annotation["sbo"] = "SBO:0000176" #biochemical reaction
231-
cobra_reaction.annotation["seed.reaction"] = "rxn00062"
209+
cobra_reaction = Reaction("rxn00062_"+compartment, name="ATP hydrolysis", lower_bound=0, upper_bound=1000)
210+
cobra_reaction.annotation.update({"sbo":"SBO:0000176", "seed.reaction":"rxn00062"}) #biochemical reaction
232211
cobra_reaction.add_metabolites(stoichiometry)
233212
model.add_reactions([cobra_reaction])
234213
return {"reaction":cobra_reaction,"direction":">","new":True}
235214

236215
@staticmethod
237-
def parse_id(object):
238-
if re.search('(.+)_([a-z])(\d+)$', object.id) != None:
239-
m = re.search('(.+)_([a-z])(\d+)$', object.id)
216+
def parse_id(cobra_obj):
217+
if re.search('(.+)_([a-z])(\d+)$', cobra_obj.id):
218+
m = re.search('(.+)_([a-z])(\d+)$', cobra_obj.id)
240219
return (m[1],m[2],int(m[3]))
241220
return None
242221

@@ -247,11 +226,26 @@ def medianame(media):
247226
return media.id
248227

249228
@staticmethod
250-
def validate_dictionary(dictionary,required_keys,optional_keys):
229+
def validate_dictionary(dictionary,required_keys, optional_keys={}):
251230
for item in required_keys:
252231
if item not in dictionary:
253232
raise ValueError('Required key '+item+' is missing!')
254233
for key in optional_keys:
255234
if key not in dictionary:
256-
dictionary[key] = defaults[key]
235+
dictionary[key] = optional_keys[key]
257236
return dictionary
237+
238+
@staticmethod
239+
def get_reframed_model(kbase_model,):
240+
from reframed import from_cobrapy
241+
242+
reframed_model = from_cobrapy(kbase_model)
243+
if hasattr(kbase_model, 'id'):
244+
reframed_model.id = kbase_model.id
245+
reframed_model.compartments.e0.external = True
246+
return reframed_model
247+
248+
@staticmethod
249+
def parse_df(df):
250+
from numpy import array
251+
return array(dtype=object, object=[array(df.index), array(df.columns), df.to_numpy()])

modelseedpy/core/msatpcorrection.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from modelseedpy.core.msmodel import get_gpr_string, get_reaction_constraints_from_direction
1010
from cobra.core import Gene, Metabolite, Model, Reaction
1111
from modelseedpy.core.msmodelutl import MSModelUtil
12-
from modelseedpy.core import FBAHelper, MSGapfill
12+
from modelseedpy.core import FBAHelper, MSGapfill, MSMedia
1313
from modelseedpy.fbapkg.mspackagemanager import MSPackageManager
1414

1515
logger = logging.getLogger(__name__)
@@ -43,7 +43,12 @@ def __init__(self, model, core_template, atp_medias, compartment="c0",
4343
else:
4444
output = self.modelutl.add_atp_hydrolysis(compartment)
4545
self.atp_hydrolysis = output["reaction"]
46-
self.atp_medias = atp_medias
46+
self.atp_medias = []
47+
for media in atp_medias:
48+
if isinstance(media,MSMedia):
49+
self.atp_medias.append([media,0.01])
50+
else:
51+
self.atp_medias.append(media)
4752
self.max_gapfilling = max_gapfilling
4853
self.gapfilling_delta = gapfilling_delta
4954
self.coretemplate = core_template
@@ -162,19 +167,21 @@ def evaluate_growth_media(self):
162167
logger.debug(f'ATP bounds: ({self.atp_hydrolysis.lower_bound}, {self.atp_hydrolysis.upper_bound})')
163168
#self.model.objective.set_linear_coefficients({self.atp_hydrolysis.forward_variable:1})
164169
pkgmgr = MSPackageManager.get_pkg_mgr(self.model)
165-
for media in self.atp_medias:
170+
for media_tuple in self.atp_medias:
171+
media = media_tuple[0]
166172
logger.debug('evaluate media %s', media)
167173
pkgmgr.getpkg("KBaseMediaPkg").build_package(media)
168174
logger.debug('model.medium %s', self.model.medium)
169175
solution = self.model.optimize()
170176
logger.debug('evaluate media %s - %f (%s)', media.id, solution.objective_value, solution.status)
171177
self.media_gapfill_stats[media] = None
172178
output[media.id] = solution.objective_value
173-
if solution.objective_value == 0 or solution.status != 'optimal':
174-
self.media_gapfill_stats[media] = self.msgapfill.run_gapfilling(media, self.atp_hydrolysis.id)
179+
if solution.objective_value < media_tuple[1] or solution.status != 'optimal':
180+
self.media_gapfill_stats[media] = self.msgapfill.run_gapfilling(media, self.atp_hydrolysis.id,media_tuple[1])
175181
#IF gapfilling fails - need to activate and penalize the noncore and try again
176-
elif solution.objective_value > 0 or solution.status == 'optimal':
182+
elif solution.objective_value >= media_tuple[1]:
177183
self.media_gapfill_stats[media] = {'reversed': {}, 'new': {}}
184+
logger.debug('gapfilling stats:',json.dumps(self.media_gapfill_stats[media],indent=2))
178185

179186
if MSATPCorrection.DEBUG:
180187
with open('debug.json', 'w') as outfile:

0 commit comments

Comments
 (0)