pycomo 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pycomo-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Michael Predl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pycomo-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: pycomo
3
+ Version: 0.1.0
4
+ Summary: PyCoMo is a software package for generating and analysing compartmentalized community metabolic models
5
+ Home-page: https://github.com/univieCUBE/PyCoMo
6
+ Author: Michael Predl
7
+ Author-email: michael.predl@univie.ac.at
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+
15
+ # PyCoMo
16
+ ## What is PyCoMo?
17
+ PyCoMo is a python 3 package for the creation and analysis of community metabolic models. More specifically, PyCoMo generates compartmentalized community metabolic models with a structure allowing simulations under fixed growth rate, but variable abundance profile, or fixed abundance profile and variable community growth rate. The community metabolic models generated by PyCoMo can switch between these two structures and retain the original reaction bounds of the input member models. As also all metabolites, reactions, genes and compartments are directly attributable to their member of origin, PyCoMo community metabolic models are fully reusable.
18
+
19
+ The community models can be analysed with PyCoMo to predict all feasible exchange metabolites and cross-feeding interactions, for the whole space of growth rate and abundance profiles. The community models are COBRApy models and can therefor be directly used by other COBRA methods. It is also possible to save and load the community models in SBML format, allowing to share and reuse the models built with PyCoMo.
20
+
21
+ ## Installation
22
+ For installing PyCoMo download or clone the repository to your machine.
23
+ ```
24
+ git clone https://github.com/univieCUBE/PyCoMo
25
+ ```
26
+ Run pip install on the folder containing the PyCoMo repository.
27
+ ```
28
+ pip install path/to/PyCoMo
29
+ ```
30
+
31
+ ## Usage guide
32
+ PyCoMo can be imported in Python as any other package. Please look through the tutorial for a walkthrough of all the options generating and analysing community metabolic models (available as ipython notebook, python file and pdf).
33
+
34
+ PyCoMo can also be used via its command line interface. After installation, run ```pycomo -h``` or ```pycomo --help``` to see all options.
35
+ ## Citing PyCoMo
36
+ At the present moment we are still working on the final stages of the manuscript. Once it is made public, a citation note will be included at this place.
pycomo-0.1.0/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # PyCoMo
2
+ ## What is PyCoMo?
3
+ PyCoMo is a python 3 package for the creation and analysis of community metabolic models. More specifically, PyCoMo generates compartmentalized community metabolic models with a structure allowing simulations under fixed growth rate, but variable abundance profile, or fixed abundance profile and variable community growth rate. The community metabolic models generated by PyCoMo can switch between these two structures and retain the original reaction bounds of the input member models. As also all metabolites, reactions, genes and compartments are directly attributable to their member of origin, PyCoMo community metabolic models are fully reusable.
4
+
5
+ The community models can be analysed with PyCoMo to predict all feasible exchange metabolites and cross-feeding interactions, for the whole space of growth rate and abundance profiles. The community models are COBRApy models and can therefor be directly used by other COBRA methods. It is also possible to save and load the community models in SBML format, allowing to share and reuse the models built with PyCoMo.
6
+
7
+ ## Installation
8
+ For installing PyCoMo download or clone the repository to your machine.
9
+ ```
10
+ git clone https://github.com/univieCUBE/PyCoMo
11
+ ```
12
+ Run pip install on the folder containing the PyCoMo repository.
13
+ ```
14
+ pip install path/to/PyCoMo
15
+ ```
16
+
17
+ ## Usage guide
18
+ PyCoMo can be imported in Python as any other package. Please look through the tutorial for a walkthrough of all the options generating and analysing community metabolic models (available as ipython notebook, python file and pdf).
19
+
20
+ PyCoMo can also be used via its command line interface. After installation, run ```pycomo -h``` or ```pycomo --help``` to see all options.
21
+ ## Citing PyCoMo
22
+ At the present moment we are still working on the final stages of the manuscript. Once it is made public, a citation note will be included at this place.
pycomo-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
pycomo-0.1.0/setup.py ADDED
@@ -0,0 +1,32 @@
1
+ from setuptools import find_packages, setup
2
+
3
+ with open("README.md", "r") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="pycomo",
8
+ version="0.1.0",
9
+ description="PyCoMo is a software package for generating and analysing compartmentalized community metabolic models",
10
+ package_dir={"": "src"},
11
+ packages=find_packages(where="src"),
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/univieCUBE/PyCoMo",
15
+ author="Michael Predl",
16
+ author_email="michael.predl@univie.ac.at",
17
+ license="MIT",
18
+ classifiers=[
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3"
21
+ ],
22
+ entry_points={
23
+ 'console_scripts': [
24
+ 'pycomo = pycomo.pycomo_models:main'
25
+ ]
26
+ },
27
+ install_requires=["cobra >= 0.23.0", "pandas >= 1.5.3", "python-libsbml >= 5.20.1", "numpy >= 1.22.4"],
28
+ extra_require={
29
+ "dev": ["pytest >= 7.0", "twine >= 4.0.2"],
30
+ },
31
+ python_requires=">=3.9",
32
+ )
@@ -0,0 +1,16 @@
1
+ __author__ = "Michael Predl"
2
+ __version__ = "0.1.0"
3
+
4
+
5
+ from pycomo.pycomo_models import (
6
+ SingleOrganismModel,
7
+ CommunityModel,
8
+ doall
9
+ )
10
+ from pycomo.helper.utils import (
11
+ load_named_model,
12
+ load_named_models_from_dir,
13
+ read_medium_from_file,
14
+ read_abundance_from_file,
15
+ make_string_sbml_id_compatible,
16
+ )
File without changes
@@ -0,0 +1,127 @@
1
+ import os
2
+ import sys
3
+ import argparse
4
+
5
+ __description__ = ('A package for generating community metabolic models from single species/strain models.')
6
+ __author__ = 'Michael Predl'
7
+ __license__ = "MIT"
8
+ __version__ = "0.1.0"
9
+
10
+
11
+ def create_arg_parser():
12
+ parser = argparse.ArgumentParser(prog="PyCoMo")
13
+
14
+ parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version__}',
15
+ help="display PyCoMo version")
16
+
17
+ parser.add_argument('-i', '--input', nargs='+', type=str, required=True,
18
+ help="single species/strain models to combine, either as a directory or separate files")
19
+
20
+ parser.add_argument('-c', '--is-community', action='store_true',
21
+ help="Set this flag if the input model is already a community model.")
22
+
23
+ # All parameters regarding the generation and contextualisation of the community model
24
+ pg_com_model = parser.add_argument_group('Community model parameters')
25
+
26
+ pg_com_model.add_argument('-n', '--name', type=str, default='community_model',
27
+ help="the name for the new community model")
28
+
29
+ pg_com_model.add_argument('-m', '--match-via-annotation', type=str,
30
+ help="the metabolite annotation type to use for matching exchange metabolites of "
31
+ "different community members (e.g. metanetx.chemical)")
32
+
33
+ pg_linearisation = pg_com_model.add_mutually_exclusive_group()
34
+
35
+ pg_linearisation.add_argument('--growth-rate', type=float,
36
+ help="set abundances to be equal for all community members")
37
+
38
+ pg_linearisation.add_argument('--equal-abd', action='store_true',
39
+ help="set abundances to be equal for all community members")
40
+
41
+ pg_linearisation.add_argument('--abd-file', type=str,
42
+ help="a comma separated file containing the input model file names and their "
43
+ "abundance. No header should be used in the file.")
44
+
45
+ pg_com_model.add_argument('--medium', type=str,
46
+ help="the medium to be used in the community model, as a comma separated file "
47
+ "containing a column 'compounds' and a column 'maxFlux'.")
48
+
49
+ # All parameters regarding outputs to be produced
50
+ pg_output = parser.add_argument_group('Output parameters')
51
+
52
+ pg_output.add_argument('-o', '--output-dir', default=os.getcwd(), type=str,
53
+ help="the output directory to store results (default is the current working directory)")
54
+
55
+ pg_output.add_argument('--fba-flux', action='store_true',
56
+ help="run FBA on the community model and store the flux vector in a file")
57
+
58
+ pg_output.add_argument('--fva-flux', type=float,
59
+ help="run FVA on the exchange metabolites of the community model and store the flux vector "
60
+ "in a file. Set the threshold of the objective that needs to be achieved.")
61
+
62
+ pg_output.add_argument('--fba-interaction', action='store_true',
63
+ help="run FBA on the community model and store the flux of exchange metabolites and "
64
+ "whether they are cross-fed in a file")
65
+
66
+ pg_output.add_argument('--fva-interaction', action='store_true',
67
+ help="run FVA on the community model and store the flux of exchange metabolites and "
68
+ "whether they are cross-fed in a file. Set the threshold of the objective that needs "
69
+ "to be achieved.")
70
+
71
+ if len(sys.argv) == 1:
72
+ parser.print_help(sys.stderr)
73
+ sys.exit(1)
74
+
75
+ return parser
76
+
77
+
78
+ def check_args(args):
79
+ if not os.path.isdir(args.output_dir):
80
+ raise ValueError("The output-dir does not exist or is not a directory.")
81
+ if args.input is None:
82
+ raise ValueError("Please provide input to run PyCoMo (-i / --input)")
83
+ elif not all([os.path.exists(arg_path) for arg_path in args.input]):
84
+ raise ValueError("Not all input files / directories exist.")
85
+
86
+ if args.fva_flux is not None:
87
+ if not 0. <= args.fva_flux <= 1.:
88
+ raise ValueError("The fva-flux argument needs to be between 0. and 1. (inclusive).")
89
+
90
+ if args.fva_flux is not None:
91
+ if not 0. <= args.fva_flux <= 1.:
92
+ raise ValueError("The fva-flux argument needs to be between 0. and 1. (inclusive).")
93
+
94
+ if args.abd_file is not None and not os.path.isfile(args.abd_file):
95
+ raise ValueError("The abundance file is not a file or does not exist")
96
+
97
+ if args.growth_rate is not None and args.growth_rate < 0.:
98
+ raise ValueError(f"The specified growth rate ({args.growth_rate}) is negative.")
99
+
100
+ if args.medium is not None and not os.path.isfile(args.medium):
101
+ raise ValueError("The medium file is not a file or does not exist")
102
+
103
+ args.abundance = None
104
+ if args.equal_abd:
105
+ args.abundance = "equal"
106
+ elif args.abd_file is not None:
107
+ args.abundance = args.abd_file
108
+
109
+ args.fba_solution_path = None
110
+ if args.fba_flux:
111
+ args.fba_solution_path = os.path.join(args.output_dir, f"{args.name}_fba_flux.csv")
112
+
113
+ args.fva_solution_path = None
114
+ if args.fva_flux is not None:
115
+ args.fva_solution_path = os.path.join(args.output_dir, f"{args.name}_fva_{args.fva_flux}_flux.csv")
116
+
117
+ args.fba_interaction_path = None
118
+ if args.fba_interaction:
119
+ args.fba_interaction_path = os.path.join(args.output_dir, f"{args.name}_fba_flux.csv")
120
+
121
+ args.fva_interaction_path = None
122
+ if args.fva_interaction:
123
+ args.fva_interaction_path = os.path.join(args.output_dir, f"{args.name}_fva_{args.fva_interaction}_flux.csv")
124
+
125
+ args.sbml_output_path = os.path.join(args.output_dir, f"{args.name}.xml")
126
+
127
+ return args
@@ -0,0 +1,368 @@
1
+ """
2
+ This module contains some utility function related to cobrapy community models.
3
+ """
4
+ import pandas as pd
5
+ import cobra
6
+ import libsbml
7
+ import os
8
+ import re
9
+
10
+
11
+ def make_string_sbml_id_compatible(string, remove_ascii_escapes=False, remove_trailing_underscore=False):
12
+ """
13
+ This function
14
+ :param string:
15
+ :return:
16
+ """
17
+ alphanum_pattern = re.compile(r'\w')
18
+
19
+ for idx, character in enumerate(string):
20
+ if not alphanum_pattern.match(character):
21
+ string = string[:idx] + "_" + string[idx+1:]
22
+
23
+ if remove_ascii_escapes:
24
+ string = remove_ascii_escape_from_string(string)
25
+
26
+ if remove_trailing_underscore:
27
+ while string and string[-1] == "_":
28
+ string = string[:-1]
29
+
30
+ if string[0].isdigit():
31
+ string = "_" + string
32
+
33
+ return string
34
+
35
+
36
+ def remove_ascii_escape_from_string(text):
37
+ ascii_pattern = re.compile("__\d+__")
38
+ while re.search(ascii_pattern, text):
39
+ text = re.sub(ascii_pattern, remove_dunder_from_ascii_escape, text)
40
+ return text
41
+
42
+
43
+ def remove_dunder_from_ascii_escape(match_obj):
44
+ if match_obj.group() is not None:
45
+ print(match_obj.group())
46
+ return match_obj.group()[1:-1]
47
+
48
+
49
+ def read_medium_from_file(file, comp):
50
+ medium_df = pd.read_csv(file, sep=",")
51
+ medium_dict = {}
52
+ for idx, row in medium_df.iterrows():
53
+ met = row["compounds"]
54
+ flux = float(row["maxFlux"])
55
+ rxn = "EX_" + met + comp
56
+ medium_dict[rxn] = flux
57
+ return medium_dict
58
+
59
+
60
+ def read_abundance_from_file(file):
61
+ endings = {"sbml", "json", "mat", "yaml", "yml"}
62
+ abd_df = pd.read_csv(file, sep=",")
63
+ abd_dict = {}
64
+ assert len(abd_df.columns) == 2
65
+ abd_df.columns = ["model", "fraction"]
66
+ for idx, row in abd_df.iterrows():
67
+ model = row["model"]
68
+ if str(os.path.splitext(model)[1]) in endings:
69
+ model = model.replace(str(os.path.splitext(file)[1]), "")
70
+ fraction = float(row["fraction"])
71
+ abd_dict[model] = fraction
72
+ return abd_dict
73
+
74
+
75
+ def load_named_model(file, format="sbml"):
76
+ name = os.path.split(file)[1].replace(str(os.path.splitext(file)[1]), "")
77
+ if format == "sbml":
78
+ model = cobra.io.read_sbml_model(file)
79
+ elif format == "json":
80
+ model = cobra.io.load_json_model(file)
81
+ elif format == "mat":
82
+ model = cobra.io.load_matlab_model(file)
83
+ elif format in ["yaml", "yml"]:
84
+ model = cobra.io.load_yaml_model(file)
85
+ else:
86
+ raise ValueError(f"Incorrect format for model. Please use either sbml or json.")
87
+ return model, name
88
+
89
+
90
+ def load_named_models_from_dir(path, format="sbml"):
91
+ endings = {"sbml": [".xml"], "json": [".json"], "mat": [".mat"], "yaml": [".yaml", ".yml"]}
92
+ named_models = {}
93
+ files = os.listdir(path)
94
+ expected_ending = endings[format]
95
+ for file in files:
96
+ if not os.path.isfile(os.path.join(path, file)) or str(os.path.splitext(file)[1]) not in expected_ending:
97
+ continue
98
+ else:
99
+ model, name = load_named_model(os.path.join(path, file), format=format)
100
+ named_models[name] = model
101
+ return named_models
102
+
103
+
104
+ def close_to_zero(num, t=10**-10):
105
+ return -t < num < t
106
+
107
+
108
+ def get_model_biomass_compound(model, shared_compartment_name, expected_biomass_id="", generate_if_none=False):
109
+ """This will produce a biomass metabolite with a unique production reaction"""
110
+ objective = str(model.objective.expression).split("*")[1].split(' ')[0]
111
+ biomass_rxn = model.reactions.get_by_id(objective)
112
+ biomass_products = model.reactions.get_by_id(objective).products
113
+ biomass_met = None
114
+ if len(expected_biomass_id) > 0:
115
+ if expected_biomass_id in [met.id for met in biomass_products]:
116
+ biomass_met = model.metabolites.get_by_id(expected_biomass_id)
117
+ elif expected_biomass_id in [met.id for met in model.metabolites]:
118
+ print(f"WARNING: expected biomass id {expected_biomass_id} is not a product of the objective function.")
119
+ biomass_met = model.metabolites.get_by_id(expected_biomass_id)
120
+ else:
121
+ raise AssertionError(f"Expected biomass metabolite {expected_biomass_id} is not found in the model.")
122
+ elif len(biomass_products) == 0:
123
+ # No metabolites produced
124
+ if generate_if_none:
125
+ print(f"Note: no products in the objective function, adding biomass to it.")
126
+ biomass_met = cobra.Metabolite(f"cpd11416_{shared_compartment_name}", name='Biomass',
127
+ compartment=shared_compartment_name)
128
+ model.add_metabolites([biomass_met])
129
+ biomass_rxn.add_metabolites({biomass_met: 1.})
130
+ else:
131
+ raise AssertionError(f"No biomass compound could be found in objective\nObjective id: {objective}")
132
+ elif len(biomass_products) == 1:
133
+ biomass_met = biomass_products[0]
134
+ else:
135
+ # Multiple products in the objective, making biomass metabolites ambiguous
136
+ if generate_if_none:
137
+ print(f"Note: no products in the objective function, adding biomass to it.")
138
+ biomass_met = cobra.Metabolite(f"cpd11416_{shared_compartment_name}", name='Biomass',
139
+ compartment=shared_compartment_name)
140
+ model.add_metabolites([biomass_met])
141
+ biomass_rxn.add_metabolites({biomass_met: 1.})
142
+ else:
143
+ raise AssertionError(f"Multiple products in objective, biomass metabolite is ambiguous. Please set it "
144
+ f"manually.\nObjective id: {objective}")
145
+ return biomass_met
146
+
147
+
148
+ def make_model_ids_sbml_conform(model):
149
+ for met in model.metabolites:
150
+ if not met.name:
151
+ met.name = met.id
152
+ met.id = make_string_sbml_id_compatible(met.id, remove_ascii_escapes=True, remove_trailing_underscore=True)
153
+ met.compartment = make_string_sbml_id_compatible(met.compartment, remove_ascii_escapes=True, remove_trailing_underscore=True)
154
+ for rxn in model.reactions:
155
+ if not rxn.name:
156
+ rxn.name = rxn.id
157
+ rxn.id = make_string_sbml_id_compatible(rxn.id, remove_ascii_escapes=True, remove_trailing_underscore=True)
158
+ for group in model.groups:
159
+ if not group.name:
160
+ group.name = group.id
161
+ group.id = make_string_sbml_id_compatible(group.id, remove_ascii_escapes=True, remove_trailing_underscore=True)
162
+
163
+ rename_dict = {}
164
+ for gene in model.genes:
165
+ if not gene.name:
166
+ gene.name = gene.id
167
+ rename_dict[gene.id] = make_string_sbml_id_compatible(gene.id, remove_ascii_escapes=True, remove_trailing_underscore=True)
168
+
169
+ if rename_dict:
170
+ cobra.manipulation.modify.rename_genes(model, rename_dict)
171
+
172
+ model.repair()
173
+
174
+ return model
175
+
176
+
177
+ def get_metabolite_id_without_compartment(metabolite):
178
+ compartment_string = "_" + metabolite.compartment
179
+ if metabolite.id[-len(compartment_string):] == compartment_string:
180
+ return metabolite.id[:-len(compartment_string)]
181
+ else:
182
+ return metabolite.id
183
+
184
+
185
+ def list_contains_unique_strings(str_list):
186
+ return len(str_list) == len(list(set(str_list)))
187
+
188
+
189
+ def list_of_strings_is_self_contained(str_list):
190
+ self_contained = False
191
+ for idx, string in enumerate(str_list):
192
+ for other_idx in range(len(str_list)):
193
+ if idx == other_idx: continue
194
+ elif string in str_list[other_idx]:
195
+ self_contained = True
196
+ return self_contained
197
+
198
+
199
+ def list_without_element(list_var, element):
200
+ list_var = list_var.copy()
201
+ list_var.remove(element)
202
+ return list_var
203
+
204
+
205
+ def check_metabolite_equal_mass(met1, met2):
206
+ """
207
+ This function compares mass and charge of two metabolites. It returns True if the metabolites have equal mass and
208
+ charge and False if they do not.
209
+ """
210
+ test_reaction = cobra.Reaction()
211
+ test_reaction.add_metabolites({met1: -1., met2: 1.})
212
+ return not bool(test_reaction.check_mass_balance())
213
+
214
+
215
+ def get_exchange_metabolites(model):
216
+ exchange_metabolites = {}
217
+ for reaction in model.exchanges:
218
+ if len(reaction.metabolites) != 1:
219
+ print(f"Error: exchange reaction {reaction.id} has more than 1 metabolite")
220
+ exchange_met = list(reaction.metabolites.keys())[0]
221
+ exchange_metabolites[exchange_met.id] = exchange_met
222
+ return exchange_metabolites
223
+
224
+
225
+ def check_mass_balance_of_metabolites_with_identical_id(model_1, model_2):
226
+ exchg_mets_1 = get_exchange_metabolites(model_1)
227
+ exchg_mets_2 = get_exchange_metabolites(model_2)
228
+
229
+ unbalanced_metabolites = []
230
+
231
+ for met_id in set(exchg_mets_1) & set(exchg_mets_2):
232
+ equal_mass = check_metabolite_equal_mass(exchg_mets_1[met_id], exchg_mets_2[met_id])
233
+ if not equal_mass:
234
+ unbalanced_metabolites.append(met_id)
235
+
236
+ return unbalanced_metabolites
237
+
238
+
239
+ def create_parameter_in_sbml_model(sbml_model, pid, is_constant, value=None, as_name=False):
240
+ """
241
+ Helper function to set a parameter with ID and value to a SBML model.
242
+ """
243
+ parameter = sbml_model.createParameter()
244
+ parameter.setId(pid)
245
+ if value is not None:
246
+ if as_name:
247
+ parameter.setName(value)
248
+ else:
249
+ parameter.setValue(value)
250
+ parameter.setConstant(is_constant)
251
+
252
+
253
+ def create_abundance_parameter(sbml_model, member_id, abundance=None):
254
+ parameter_prefix = "Abundance_"
255
+ create_parameter_in_sbml_model(sbml_model=sbml_model, pid=parameter_prefix+member_id, is_constant=False, value=abundance)
256
+
257
+
258
+ def read_sbml_model_from_file(file_path):
259
+ sbml_doc = cobra.io.sbml._get_doc_from_filename(file_path)
260
+ return sbml_doc.getModel()
261
+
262
+
263
+ def get_abundance_parameters_from_sbml_doc(sbml_model):
264
+ parameter_prefix = "Abundance_"
265
+ abundance_dict = {}
266
+
267
+ for parameter in sbml_model.getListOfParameters():
268
+ parameter_id = parameter.getId()
269
+ if parameter_prefix in parameter_id[:len(parameter_prefix)]:
270
+ fraction = None
271
+ if parameter.isSetValue():
272
+ fraction = parameter.getValue()
273
+ abundance_dict[parameter_id[len(parameter_prefix):]] = fraction
274
+
275
+ return abundance_dict
276
+
277
+
278
+ def get_flags_and_muc_from_sbml_file(sbml_file):
279
+ sbml_model = read_sbml_model_from_file(sbml_file)
280
+
281
+ parameter_dict = {}
282
+
283
+ for parameter in sbml_model.getListOfParameters():
284
+ parameter_id = parameter.getId()
285
+ if parameter_id == "mu_c":
286
+ value = 1.
287
+ if parameter.isSetValue():
288
+ value = parameter.getValue()
289
+ parameter_dict["mu_c"] = value
290
+ if parameter_id == "fixed_abundance_flag":
291
+ value = 0
292
+ if parameter.isSetValue():
293
+ value = parameter.getValue()
294
+ value = value == 1 # Flags are stored as 1 and 0 for True and False. None is converted to False
295
+ parameter_dict["fixed_abundance_flag"] = value
296
+ if parameter_id == "shared_compartment_id":
297
+ if not parameter.isSetName():
298
+ raise ValueError("Error: Missing parameter shared_compartment_id (parameter name should contain ID of "
299
+ "the shared compartment)")
300
+ name = parameter.getName()
301
+ parameter_dict["shared_compartment_name"] = name
302
+ if parameter_id == "fixed_growth_rate_flag":
303
+ value = 0
304
+ if parameter.isSetValue():
305
+ value = parameter.getValue()
306
+ value = value == 1 # Flags are stored as 1 and 0 for True and False. None is converted to False
307
+ parameter_dict["fixed_growth_rate_flag"] = value
308
+
309
+ return parameter_dict
310
+
311
+
312
+ def get_abundance_parameters_from_sbml_file(sbml_file):
313
+ sbml_model = read_sbml_model_from_file(sbml_file)
314
+ return get_abundance_parameters_from_sbml_doc(sbml_model)
315
+
316
+
317
+ def find_matching_annotations(met1, met2):
318
+ shared_annotation_keys = set(met1.annotation) & set(met2.annotation)
319
+ matching_annotations = {}
320
+ for key in shared_annotation_keys:
321
+ if met1.annotation[key] == met2.annotation[key]:
322
+ matching_annotations[key] = met1.annotation[key]
323
+
324
+ return matching_annotations
325
+
326
+
327
+ def check_annotation_overlap_of_metabolites_with_identical_id(model_1, model_2):
328
+ exchg_mets_1 = get_exchange_metabolites(model_1)
329
+ exchg_mets_2 = get_exchange_metabolites(model_2)
330
+
331
+ metabolites_without_overlap = []
332
+
333
+ for met_id in set(exchg_mets_1) & set(exchg_mets_2):
334
+ matching_annotations = find_matching_annotations(exchg_mets_1[met_id], exchg_mets_2[met_id])
335
+ if not matching_annotations:
336
+ metabolites_without_overlap.append(met_id)
337
+
338
+ return metabolites_without_overlap
339
+
340
+
341
+ def relax_reaction_constraints_for_zero_flux(model):
342
+ """This function relaxes all constraints of a model to allow a flux of 0 in all reactions."""
343
+ for reaction in model.reactions:
344
+ if reaction.lower_bound > 0.:
345
+ reaction.lower_bound = 0.
346
+ if reaction.upper_bound < 0.:
347
+ reaction.upper_bound = 0.
348
+
349
+
350
+ def find_loops_in_model(model):
351
+ """This function finds loops in models. This is accomplished by setting the medium to contain nothing and relax
352
+ all constraints to allow a flux of 0. Then, FVA is run on all reactions"""
353
+ loop_model = model.copy()
354
+ loop_model.medium = {}
355
+ relax_reaction_constraints_for_zero_flux(loop_model)
356
+ no_medium = {}
357
+ max_flux_value = 1000.0
358
+ loops = []
359
+ for rxn in loop_model.reactions:
360
+ loop_model.objective = rxn.id
361
+ solution = loop_model.optimize("minimize")
362
+ min_flux = solution.objective_value if not solution.status == "infeasible" else 0.
363
+ solution = loop_model.optimize("maximize")
364
+ max_flux = solution.objective_value if not solution.status == "infeasible" else 0.
365
+ if min_flux != 0. or max_flux != 0.:
366
+ loops.append({"reaction": rxn.id, "min_flux": min_flux, "max_flux": max_flux})
367
+ loops_df = pd.DataFrame(loops)
368
+ return loops_df