Skip to content

Commit 16130f8

Browse files
author
Christopher Henry
committed
Checking in latest code with new fba packages and fixes to phenotype simulation, biochemistry, and gapfilling
1 parent ca05193 commit 16130f8

File tree

10 files changed

+496
-298
lines changed

10 files changed

+496
-298
lines changed

modelseedpy/biochem/modelseed_biochem.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,9 @@ class ModelSEEDBiochem:
570570
default_biochemistry = None
571571

572572
@staticmethod
573-
def get(create_if_missing=True):
573+
def get(create_if_missing=True,path=config.get("biochem", "path")):
574574
if not ModelSEEDBiochem.default_biochemistry:
575-
ModelSEEDBiochem.default_biochemistry = from_local(
576-
config.get("biochem", "path")
577-
)
575+
ModelSEEDBiochem.default_biochemistry = from_local(path)
578576
return ModelSEEDBiochem.default_biochemistry
579577

580578
def __init__(

modelseedpy/community/commphitting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from scipy.optimize import newton
2828
from matplotlib import pyplot
2929
from math import inf, isclose
30-
from deepdiff import DeepDiff
30+
#from deepdiff import DeepDiff
3131
from pandas import DataFrame
3232
from itertools import chain
3333
from pprint import pprint

modelseedpy/core/msatpcorrection.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from modelseedpy.helpers import get_template
1010

1111
logger = logging.getLogger(__name__)
12+
logger.setLevel(
13+
logging.INFO
14+
) # When debugging - set this to INFO then change needed messages below from DEBUG to INFO
1215

1316
min_gap = {
1417
"Glc.O2": 5,
@@ -73,11 +76,6 @@ def __init__(
7376
# Setting atpcorrection attribute in model utl so link is bidirectional
7477
self.modelutl.atputl = self
7578

76-
if default_media_path:
77-
self.default_media_path = default_media_path
78-
else:
79-
self.default_media_path = _path + "/../data/atp_medias.tsv"
80-
8179
self.compartment = compartment
8280

8381
if atp_hydrolysis_id and atp_hydrolysis_id in self.model.reactions:

modelseedpy/core/msgrowthphenotypes.py

Lines changed: 218 additions & 129 deletions
Large diffs are not rendered by default.

modelseedpy/core/msmedia.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class MSMedia:
4040
def __init__(self, media_id, name=""):
4141
self.id = media_id
4242
self.name = name
43+
self.media_ref = None # Reference to the media object in the model
4344
self.mediacompounds = DictList()
4445

4546
@staticmethod
@@ -60,6 +61,41 @@ def from_dict(media_dict):
6061
media.mediacompounds += media_compounds
6162
return media
6263

64+
@staticmethod
65+
def from_kbase_object(media_object):
66+
"""
67+
Create MSMedia from KBase media object.
68+
:param media_object: KBase media object
69+
:return: MSMedia instance
70+
"""
71+
media_id = media_object.id
72+
media_ref = None
73+
media_name = media_object.name if media_object.name else media_id
74+
if media_object.info is not None:
75+
media_id = media_object.info.id
76+
media_name = media_object.info.id
77+
media_ref = media_object.info.reference
78+
output = MSMedia(media_id, name=media_name)
79+
output.media_ref = media_ref
80+
media_compounds = []
81+
for mediacpd in media_object.mediacompounds:
82+
newmediacpd = MediaCompound(mediacpd.id, -1*mediacpd.maxFlux, -1*mediacpd.minFlux, concentration=mediacpd.concentration)
83+
media_compounds.append(newmediacpd)
84+
output.mediacompounds += media_compounds
85+
return output
86+
87+
def to_dict(self):
88+
"""
89+
Parameters:
90+
cmp (str): compound suffix (model compartment)
91+
Returns:
92+
dict(str) -> (float,float): compound_ids mapped to lower/upper bound
93+
"""
94+
output = {}
95+
for compound in self.mediacompounds:
96+
output[compound.id] = compound.upper_bound
97+
return output
98+
6399
def get_media_constraints(self, cmp="e0"):
64100
"""
65101
Parameters:

modelseedpy/core/msmodelutl.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,20 +393,41 @@ def __init__(self, model):
393393

394394
########I/O functions
395395
@staticmethod
396-
def from_cobrapy_json(filename):
397-
model = cobra.io.load_json_model(filename)
396+
def from_cobrapy(filename):
397+
"""
398+
Loads a cobrapy model from a file.
399+
400+
Parameters
401+
----------
402+
filename: str
403+
The name of the file to load the model from.
404+
405+
Returns
406+
-------
407+
MSModelUtil
408+
An MSModelUtil object containing the loaded model.
409+
"""
410+
if filename[-5:].lower() == ".json":
411+
model = cobra.io.load_json_model(filename)
412+
elif filename[-4:].lower() == ".xml":
413+
#Resetting the logging level in cobrapy to avoid excess output
414+
logging.getLogger("cobra.io.sbml").setLevel(logging.ERROR)
415+
model = cobra.io.read_sbml_model(filename)
398416
return MSModelUtil(model)
399-
400-
def save_model(self, filename):
417+
418+
def save_model(self, filename, format="json"):
401419
"""
402420
Saves the associated cobrapy model to a json file
403421
404422
Parameters
405423
----------
406424
filename: name of the file the model should be saved to
407425
"""
408-
cobra.io.save_json_model(self.model, filename)
409-
426+
if format == "json":
427+
cobra.io.save_json_model(self.model, filename)
428+
elif format == "xml":
429+
cobra.io.write_sbml_model(self.model, filename)
430+
410431
def printlp(self,model=None,path="",filename="debug",print=False):
411432
if print:
412433
if len(path) > 0:
@@ -539,8 +560,8 @@ def build_metabolite_hash(self):
539560
self.add_name_to_metabolite_hash(met.id, met)
540561
self.add_name_to_metabolite_hash(met.name, met)
541562
for anno in met.annotation:
542-
if isinstance(met.annotation[anno], list):
543-
for item in met.annotation[anno]:
563+
if isinstance(met.annotation[anno], list) or isinstance(met.annotation[anno], set):
564+
for item in list(met.annotation[anno]):
544565
self.add_name_to_metabolite_hash(item, met)
545566
else:
546567
self.add_name_to_metabolite_hash(met.annotation[anno], met)
@@ -608,7 +629,7 @@ def msid_hash(self):
608629
def exchange_list(self):
609630
exchange_reactions = []
610631
for reaction in self.model.reactions:
611-
if reaction.id[:3] == "EX_":
632+
if reaction.id[:3] in ["EX_","EXF"]:
612633
exchange_reactions.append(reaction)
613634
return exchange_reactions
614635

modelseedpy/fbapkg/gapfillingpkg.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -870,12 +870,13 @@ def filter_database_based_on_tests(self,test_conditions,growth_conditions=[],bas
870870
filtered_list = self.modelutl.reaction_expansion_test(
871871
self.parameters["original_reactions"], test_conditions,positive_growth=growth_conditions
872872
)
873-
for item in filtered_list:
874-
logger.debug("Filtering:" + item[0].id + item[1])
875-
if item[1] == ">":
876-
self.model.reactions.get_by_id(item[0].id).upper_bound = 0
877-
else:
878-
self.model.reactions.get_by_id(item[0].id).lower_bound = 0
873+
if filtered_list:
874+
for item in filtered_list:
875+
logger.debug("Filtering:" + item[0].id + item[1])
876+
if item[1] == ">":
877+
self.model.reactions.get_by_id(item[0].id).upper_bound = 0
878+
else:
879+
self.model.reactions.get_by_id(item[0].id).lower_bound = 0
879880
# Restoring gapfilling objective function and minimal objective constraint
880881
self.reset_objective_minimum(self.parameters["minimum_obj"])
881882
self.model.objective = self.parameters["gfobj"]

modelseedpy/fbapkg/objconstpkg.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,22 @@ def __init__(self, model):
1111
BaseFBAPkg.__init__(self, model, "objective constraint", {}, {"objc": "none"})
1212

1313
def build_package(self, lower_bound, upper_bound):
14-
self.build_constraint(lower_bound, upper_bound)
14+
return self.build_constraint(lower_bound, upper_bound)
1515

1616
def build_constraint(self, lower_bound, upper_bound):
1717
coef = self.model.solver.objective.get_linear_coefficients(
1818
self.model.solver.objective.variables
1919
)
20-
#Check if the constraint already exists
21-
found = False
20+
#Check if the constraint already exists and if so, just updating bounds in place
2221
for name in self.constraints["objc"]:
2322
constraint = self.constraints["objc"][name]
2423
existing_coef = constraint.get_linear_coefficients(
2524
constraint.variables
2625
)
2726
if coef == existing_coef:
28-
print("Match found!")
2927
constraint.lb = lower_bound
3028
constraint.ub = upper_bound
3129
return constraint
32-
3330
return BaseFBAPkg.build_constraint(
3431
self, "objc", lower_bound, upper_bound, coef, None
3532
)

modelseedpy/fbapkg/objectivepkg.py

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,72 +11,122 @@
1111
logging.WARNING#INFO
1212
) # When debugging - set this to INFO then change needed messages below from DEBUG to INFO
1313

14-
# Base class for FBA packages
15-
class ObjectivePkg(BaseFBAPkg):
16-
def __init__(self, model):
17-
BaseFBAPkg.__init__(self, model, "objective builder", {}, {})
18-
self.objective_cache = {}
19-
self.objective_string_cache = {}
14+
class ObjectiveTerm:
15+
def __init__(self, variable, coefficient,direction=""):
16+
self.coefficient = coefficient
17+
self.variable = variable
18+
self.direction = direction
2019

21-
def build_package(self,objective_string,objective_name=None,cache_current_objective_name=None):
22-
#Saving unmodified objective string
23-
if objective_name == None:
24-
objective_name = objective_string
25-
#Caching objective and objective string
26-
self.objective_string_cache[objective_name] = objective_string
27-
#Caching the current objective if a name was specified
28-
if cache_current_objective_name != None:
29-
self.objective_cache[cache_current_objective_name] = self.model.objective
30-
#Creating empty objective - always max
31-
self.objective_cache[objective_name] = self.model.problem.Objective(Zero, direction="max")
32-
#Parsing objective string of form: MAX{(1)bio1|(1)rxn00001_c0}
33-
obj_coef = dict()
34-
#Checking if there is a directionality in the objective
20+
@staticmethod
21+
def from_string(term_string):
22+
coefficient = 1
23+
variable = None
24+
direction = ""
25+
#Checking for coefficient
26+
if term_string[0:1] == "(":
27+
array = term_string.split(")")
28+
coefficient = float(array[0][1])
29+
term_string = array[1]
30+
#Checking for a +/- on term
31+
if term_string[0:1] == "+" or term_string[0:1] == "-":
32+
variable = term_string[1:]
33+
direction = term_string[0:1]
34+
else:
35+
variable = term_string
36+
direction = ""
37+
return ObjectiveTerm(variable, coefficient, direction)
38+
39+
def to_string(self):
40+
return "("+str(self.coefficient)+")"+self.direction+self.variable
41+
42+
43+
#Class for defining an objective function in a modelseedpy model.
44+
class ObjectiveData:
45+
def __init__(self, terms, sign=1):
46+
self.sign = sign
47+
self.terms = terms
48+
49+
@staticmethod
50+
def from_string(objective_string):
3551
sign = 1
52+
terms = []
3653
if objective_string[0:3] == "MAX":
3754
objective_string = objective_string[4:-1]#Clearing out the directionality MAX{}
3855
elif objective_string[0:3] == "MIN":
3956
sign = -1
4057
objective_string = objective_string[4:-1]#Clearing out the directionality MIN{}
41-
#Building dictionary of variable names
42-
varnames = {}
43-
for variable in self.model.solver.variables:
44-
varnames[variable.name] = variable
45-
#Parsing the main body of the objective string
46-
terms = objective_string.split("|")
47-
for term in terms:
48-
coef = 1
49-
#Checking for coefficient
50-
if term[0:1] == "(":
51-
array = term.split(")")
52-
coef = float(array[0][1])
53-
term = array[1]
54-
#Applying the directionality sign
55-
coef = coef*sign
56-
#Checking for a +/- on term
57-
if term[0:1] == "+" or term[0:1] == "-":
58-
rxnid = term[1:]
59-
if rxnid not in self.model.reactions:
60-
logger.warning(rxnid+" in objective strin not found in model.")
61-
else:
62-
rxnobj = self.model.reactions.get_by_id(rxnid)
63-
if term[0:1] == "+":
64-
obj_coef[rxnobj.forward_variable] = coef
65-
elif term[0:1] == "-":
66-
obj_coef[rxnobj.reverse_variable] = coef
67-
#Checking if the term is just a reaction ID
68-
elif term in self.model.reactions:
69-
rxnobj = self.model.reactions.get_by_id(term)
70-
obj_coef[rxnobj.forward_variable] = coef
71-
obj_coef[rxnobj.reverse_variable] = -1*coef
72-
elif term in varnames:
73-
obj_coef[varnames[term]] = coef
74-
self.model.objective = self.objective_cache[objective_name]
75-
self.model.objective.set_linear_coefficients(obj_coef)
76-
print(self.model.objective)
58+
term_strings = objective_string.split("|")
59+
for term_string in term_strings:
60+
term = ObjectiveTerm.from_string(term_string)
61+
terms.append(term)
62+
return ObjectiveData(terms, sign)
63+
64+
def to_string(self):
65+
objective_string = ""
66+
if self.sign == 1:
67+
objective_string += "MAX{"
68+
else:
69+
objective_string += "MIN{"
70+
for term in self.terms:
71+
objective_string += term.to_string()+"|"
72+
objective_string = objective_string[:-1] + "}"
73+
return objective_string
7774

75+
def to_cobrapy_objective(self, model):
76+
#Creating empty objective
77+
objective = model.problem.Objective(Zero, direction="max")
78+
#Parsing the terms
79+
coefficients = {}
80+
for term in self.terms:
81+
if term.variable in model.reactions:
82+
coef = term.coefficient * self.sign
83+
rxnobj = model.reactions.get_by_id(term.variable)
84+
if term.direction == "+":
85+
coefficients[rxnobj.forward_variable] = coef
86+
elif term.direction == "-":
87+
coefficients[rxnobj.reverse_variable] = coef
88+
else:
89+
coefficients[rxnobj.forward_variable] = coef
90+
coefficients[rxnobj.reverse_variable] = -1*coef
91+
else:
92+
logger.warning("Reaction "+term.variable+" not found in model")
93+
model.objective = objective
94+
objective.set_linear_coefficients(coefficients)
95+
return objective
96+
97+
# Base class for FBA packages
98+
class ObjectivePkg(BaseFBAPkg):
99+
def __init__(self, model):
100+
BaseFBAPkg.__init__(self, model, "objective builder", {}, {})
101+
self.original_model_objective = None
102+
self.objective_name = None
103+
self.objective_data = None
104+
self.objective_data_cache = {}
105+
106+
def build_package(self,objective_or_string,objective_name=None,set_objective=True):
107+
#Caching the current objective
108+
self.original_model_objective = self.model.objective
109+
#check if input is a string or an ObjectiveData object
110+
if isinstance(objective_or_string, str):
111+
self.objective_data = ObjectiveData.from_string(objective_or_string)
112+
elif isinstance(objective_or_string, ObjectiveData):
113+
self.objective_data = objective_or_string
114+
else:
115+
raise TypeError("Input must be a string or an ObjectiveData object")
116+
#Setting default objective name if not provided
117+
self.objective_name = objective_name
118+
if objective_name == None:
119+
self.objective_name = self.objective_data.to_string()
120+
#Caching objective with name
121+
self.objective_data_cache[self.objective_name] = self.objective_data
122+
#Creating the objective in the model
123+
if set_objective:
124+
self.objective_data_cache[self.objective_name].to_cobrapy_objective(self.model)
125+
return objective_name
126+
78127
def restore_objective(self,name):
79-
if name in self.objective_cache:
80-
self.model.objective = self.objective_cache[name]
128+
self.original_model_objective = self.model.objective
129+
if name in self.objective_data_cache:
130+
self.model.objective = self.objective_data_cache[name].to_cobrapy_objective(self.model)
81131
else:
82132
logger.warning("Objective "+name+" not found in cache")

0 commit comments

Comments
 (0)