gsrap 0.8.0__py3-none-any.whl → 0.8.2__py3-none-any.whl

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.
@@ -0,0 +1,132 @@
1
+ import os
2
+ import contextlib
3
+ import importlib.metadata
4
+
5
+
6
+
7
+ import memote
8
+
9
+
10
+
11
+
12
+ def get_memote_results_dict(logger, model):
13
+
14
+
15
+ logger.info(f"Running selected modules of MEMOTE v{importlib.metadata.metadata('memote')['Version']}...")
16
+
17
+
18
+ # launch memote (only relevant modules)
19
+ with open(os.devnull, 'w') as devnull:
20
+ with contextlib.redirect_stdout(devnull), contextlib.redirect_stderr(devnull):
21
+ try: memote_report = memote.suite.api.test_model(model, exclusive=[
22
+ 'test_annotation',
23
+ 'test_sbo',
24
+ 'test_stoichiometric_consistency',
25
+ 'test_reaction_mass_balance',
26
+ 'test_reaction_charge_balance',
27
+ 'test_find_disconnected',
28
+ 'test_find_reactions_unbounded_flux_default_condition'], results=True)
29
+ except ValueError: memote_report = None
30
+
31
+
32
+ # parse memote's results
33
+ results_dict = {}
34
+ results_dict['version'] = importlib.metadata.version("memote")
35
+ test_results = dict(memote_report[1])['tests']
36
+ sections = {
37
+ 'consistency': [
38
+ ('test_stoichiometric_consistency', 3),
39
+ ('test_reaction_mass_balance', 1),
40
+ ('test_reaction_charge_balance', 1),
41
+ ('test_find_disconnected', 1),
42
+ ('test_find_reactions_unbounded_flux_default_condition', 1)
43
+ ],
44
+ 'annotation_M': [
45
+ ('test_metabolite_annotation_presence', 1),
46
+ ('test_metabolite_annotation_overview', 1),
47
+ ('test_metabolite_annotation_wrong_ids', 1),
48
+ ('test_metabolite_id_namespace_consistency', 1),
49
+ ],
50
+ 'annotation_R': [
51
+ ('test_reaction_annotation_presence', 1),
52
+ ('test_reaction_annotation_overview', 1),
53
+ ('test_reaction_annotation_wrong_ids', 1),
54
+ ('test_reaction_id_namespace_consistency', 1),
55
+ ],
56
+ 'annotation_G': [
57
+ ('test_gene_product_annotation_presence', 1),
58
+ ('test_gene_product_annotation_overview', 1),
59
+ ('test_gene_product_annotation_wrong_ids', 1),
60
+ ],
61
+ 'annotation_SBO': [
62
+ ('test_metabolite_sbo_presence', 1),
63
+ ('test_metabolite_specific_sbo_presence', 1),
64
+ ('test_reaction_sbo_presence', 1),
65
+ ('test_metabolic_reaction_specific_sbo_presence', 1),
66
+ ('test_transport_reaction_specific_sbo_presence', 1),
67
+ ('test_exchange_specific_sbo_presence', 1),
68
+ ('test_demand_specific_sbo_presence', 1),
69
+ ('test_sink_specific_sbo_presence', 1),
70
+ ('test_gene_sbo_presence', 1),
71
+ ('test_gene_specific_sbo_presence', 1),
72
+ ('test_biomass_specific_sbo_presence', 1),
73
+ ],
74
+ }
75
+ section_multipliers = {
76
+ 'consistency': 3,
77
+ 'annotation_M': 1,
78
+ 'annotation_R': 1,
79
+ 'annotation_G': 1,
80
+ 'annotation_SBO': 2,
81
+ }
82
+
83
+
84
+ numerator_total = 0
85
+ denominator_total = 0
86
+ for section, metrics in sections.items():
87
+ numerator = 0
88
+ denominator = 0
89
+ results_dict[section] = {}
90
+
91
+
92
+ # iterate metrics of this section:
93
+ for metric, metric_multiplier in metrics:
94
+ metric_raw = test_results[metric]['metric']
95
+
96
+
97
+ # no subcategories here:
98
+ if type(metric_raw) == float:
99
+ metric_percentage = ((1- metric_raw ) *100)
100
+ numerator = numerator + (metric_percentage * metric_multiplier)
101
+ denominator = denominator + metric_multiplier
102
+ results_dict[section][metric] = round(metric_percentage, 1)
103
+
104
+
105
+ # there are subcategories (like in the case of M/R/G/SBO annots)
106
+ else:
107
+ results_dict[section][metric] = {}
108
+ for key, value in metric_raw.items():
109
+ n_subcategories = len(metric_raw)
110
+ multiplier_corrected = metric_multiplier / n_subcategories
111
+ metric_percentage = ((1- value ) *100)
112
+ numerator = numerator + (metric_percentage * multiplier_corrected)
113
+ denominator = denominator + multiplier_corrected
114
+ results_dict[section][metric][key] = round(metric_percentage, 1)
115
+
116
+
117
+ # compute the subtotal:
118
+ sub_total = numerator / denominator
119
+ results_dict[section]['sub_total'] = int(round(sub_total, 0))
120
+
121
+
122
+ # compute the total:
123
+ denominator_total = denominator_total + section_multipliers[section] *denominator
124
+ numerator_total = numerator_total + section_multipliers[section] *numerator
125
+ total = numerator_total / denominator_total
126
+ results_dict['total'] = int(round(total, 0))
127
+
128
+
129
+ logger.info(f"Done! MEMOTE Total Score: {results_dict['total']}%.")
130
+
131
+
132
+ return results_dict
@@ -41,8 +41,15 @@ from ..commons import log_unbalances
41
41
  from ..commons import format_expansion
42
42
  from ..commons import comparative_table
43
43
  from ..commons import download_keggorg
44
+ from ..commons import initialize_model
45
+ from ..commons import get_memote_results_dict
46
+
44
47
 
45
48
  from ..runsims.biosynth import biosynthesis_on_media
49
+ from ..runsims.simplegrowth import grow_on_media
50
+
51
+ from ..parsedb.cycles import verify_egc_all
52
+
46
53
 
47
54
 
48
55
 
@@ -100,7 +107,7 @@ def create_model_incore(params):
100
107
 
101
108
  ###### GAPFILLING
102
109
  # force inclusion of reactions:
103
- include_forced(logger, model, universe, args.force_inclusion)
110
+ include_forced(logger, model, universe, args.include)
104
111
 
105
112
  # remove missing conditional precursors + get the 'cond_col_dict' dict.
106
113
  # 'cond_col_dict' is str-to-str: {'pheme_c': 'M00868: 1/8; M00121: 2/12;', 'hemeO_c': 'gr_HemeO: 0/1'}
@@ -111,15 +118,15 @@ def create_model_incore(params):
111
118
  if response == 1: return 1
112
119
 
113
120
  # gap-fill based on media:
114
- df_B = gapfill_on_media(logger, model, universe, dbexp, args.gap_fill, cond_col_dict, args.exclude_orphans)
121
+ df_B = gapfill_on_media(logger, model, universe, dbexp, args.gapfill, cond_col_dict, args.excludeorp)
115
122
  if type(df_B)==int: return 1
116
123
 
117
124
  # force removal of reactions
118
- setattr(args, 'force_removal', '-') # experimental feature, not public. It's main purpose was to test gap-filling in biolog_on_media().
119
- remove_forced(logger, model, universe, args.force_removal)
125
+ setattr(args, 'remove', '-') # experimental feature, not public. It's main purpose was to test gap-filling in biolog_on_media().
126
+ remove_forced(logger, model, universe, args.remove)
120
127
 
121
128
  # perform Biolog(R) curation based on media
122
- df_P = biolog_on_media(logger, model, universe, dbexp, args.gap_fill, args.biolog, args.exclude_orphans, args.cnps)
129
+ df_P = biolog_on_media(logger, model, universe, dbexp, args.gapfill, args.biolog, args.excludeorp, args.cnps)
123
130
  if type(df_P)==int: return 1
124
131
 
125
132
 
@@ -139,9 +146,12 @@ def create_model_incore(params):
139
146
 
140
147
 
141
148
 
142
- ###### CHECKS
149
+ ###### CHECKS 1
150
+ # check erroneous EGCs
151
+ verify_egc_all(logger, model, args.outdir)
152
+
143
153
  # check blocked metabolites / dead-ends
144
- df_S = biosynthesis_on_media(logger, model, dbexp, args.gap_fill, args.biosynth)
154
+ df_S = biosynthesis_on_media(logger, model, dbexp, args.gapfill, args.biosynth)
145
155
  if type(df_S)==int: return 1
146
156
 
147
157
 
@@ -149,6 +159,16 @@ def create_model_incore(params):
149
159
  ###### POLISHING 3
150
160
  # reset growth environment befor saving the model
151
161
  gempipe.reset_growth_env(model)
162
+
163
+ # initialize model
164
+ response = initialize_model(logger, model, dbexp, args.initialize, args.gapfill)
165
+ if response==1: return 1
166
+
167
+
168
+
169
+ ###### CHECKS 2
170
+ # compute Memote metrics
171
+ memote_results_dict = get_memote_results_dict(logger, model)
152
172
 
153
173
 
154
174
 
@@ -159,7 +179,7 @@ def create_model_incore(params):
159
179
  cobra.io.write_sbml_model(model, f'{args.outdir}/{model.id}.xml') # SBML # groups are saved only to SBML
160
180
  logger.info(f"'{args.outdir}/{model.id}.xml' created!")
161
181
  force_id_on_sbml(f'{args.outdir}/{model.id}.xml', model.id) # force introduction of the 'id=""' field
162
- sheets_dict = write_excel_model(model, f'{args.outdir}/{model.id}.mkmodel.xlsx', args.nofigs, None, df_B, df_P, df_S)
182
+ sheets_dict = write_excel_model(model, f'{args.outdir}/{model.id}.mkmodel.xlsx', args.nofigs, memote_results_dict, None, df_B, df_P, df_S)
163
183
  logger.info(f"'{args.outdir}/{model.id}.mkmodel.xlsx' created!")
164
184
 
165
185
 
gsrap/mkmodel/mkmodel.py CHANGED
@@ -41,8 +41,15 @@ from ..commons import log_unbalances
41
41
  from ..commons import format_expansion
42
42
  from ..commons import comparative_table
43
43
  from ..commons import download_keggorg
44
+ from ..commons import initialize_model
45
+ from ..commons import get_memote_results_dict
46
+
44
47
 
45
48
  from ..runsims.biosynth import biosynthesis_on_media
49
+ from ..runsims.simplegrowth import grow_on_media
50
+
51
+ from ..parsedb.cycles import verify_egc_all
52
+
46
53
 
47
54
 
48
55
 
@@ -100,7 +107,7 @@ def create_model_incore(params):
100
107
 
101
108
  ###### GAPFILLING
102
109
  # force inclusion of reactions:
103
- include_forced(logger, model, universe, args.force_inclusion)
110
+ include_forced(logger, model, universe, args.include)
104
111
 
105
112
  # remove missing conditional precursors + get the 'cond_col_dict' dict.
106
113
  # 'cond_col_dict' is str-to-str: {'pheme_c': 'M00868: 1/8; M00121: 2/12;', 'hemeO_c': 'gr_HemeO: 0/1'}
@@ -111,15 +118,15 @@ def create_model_incore(params):
111
118
  if response == 1: return 1
112
119
 
113
120
  # gap-fill based on media:
114
- df_B = gapfill_on_media(logger, model, universe, dbexp, args.gap_fill, cond_col_dict, args.exclude_orphans)
121
+ df_B = gapfill_on_media(logger, model, universe, dbexp, args.gapfill, cond_col_dict, args.excludeorp)
115
122
  if type(df_B)==int: return 1
116
123
 
117
124
  # force removal of reactions
118
- setattr(args, 'force_removal', '-') # experimental feature, not public. It's main purpose was to test gap-filling in biolog_on_media().
119
- remove_forced(logger, model, universe, args.force_removal)
125
+ setattr(args, 'remove', '-') # experimental feature, not public. It's main purpose was to test gap-filling in biolog_on_media().
126
+ remove_forced(logger, model, universe, args.remove)
120
127
 
121
128
  # perform Biolog(R) curation based on media
122
- df_P = biolog_on_media(logger, model, universe, dbexp, args.gap_fill, args.biolog, args.exclude_orphans, args.cnps)
129
+ df_P = biolog_on_media(logger, model, universe, dbexp, args.gapfill, args.biolog, args.excludeorp, args.cnps)
123
130
  if type(df_P)==int: return 1
124
131
 
125
132
 
@@ -139,9 +146,12 @@ def create_model_incore(params):
139
146
 
140
147
 
141
148
 
142
- ###### CHECKS
149
+ ###### CHECKS 1
150
+ # check erroneous EGCs
151
+ verify_egc_all(logger, model, args.outdir)
152
+
143
153
  # check blocked metabolites / dead-ends
144
- df_S = biosynthesis_on_media(logger, model, dbexp, args.gap_fill, args.biosynth)
154
+ df_S = biosynthesis_on_media(logger, model, dbexp, args.gapfill, args.biosynth)
145
155
  if type(df_S)==int: return 1
146
156
 
147
157
 
@@ -149,6 +159,16 @@ def create_model_incore(params):
149
159
  ###### POLISHING 3
150
160
  # reset growth environment befor saving the model
151
161
  gempipe.reset_growth_env(model)
162
+
163
+ # initialize model
164
+ response = initialize_model(logger, model, dbexp, args.initialize, args.gapfill)
165
+ if response==1: return 1
166
+
167
+
168
+
169
+ ###### CHECKS 2
170
+ # compute Memote metrics
171
+ memote_results_dict = get_memote_results_dict(logger, model)
152
172
 
153
173
 
154
174
 
@@ -159,7 +179,7 @@ def create_model_incore(params):
159
179
  cobra.io.write_sbml_model(model, f'{args.outdir}/{model.id}.xml') # SBML # groups are saved only to SBML
160
180
  logger.info(f"'{args.outdir}/{model.id}.xml' created!")
161
181
  force_id_on_sbml(f'{args.outdir}/{model.id}.xml', model.id) # force introduction of the 'id=""' field
162
- sheets_dict = write_excel_model(model, f'{args.outdir}/{model.id}.mkmodel.xlsx', args.nofigs, None, df_B, df_P, df_S)
182
+ sheets_dict = write_excel_model(model, f'{args.outdir}/{model.id}.mkmodel.xlsx', args.nofigs, memote_results_dict, None, df_B, df_P, df_S)
163
183
  logger.info(f"'{args.outdir}/{model.id}.mkmodel.xlsx' created!")
164
184
 
165
185
 
@@ -0,0 +1,128 @@
1
+ import warnings
2
+ import os
3
+ import logging
4
+
5
+
6
+ import cobra
7
+ import gempipe
8
+
9
+
10
+ from ..commons import fba_no_warnings
11
+ from ..commons import get_optthr
12
+
13
+
14
+
15
+ def verify_egc(logger, model, mid, outdir):
16
+
17
+
18
+ # changes as not permament:
19
+ found_egc = False
20
+ with model:
21
+
22
+ # close (0; 0) all the exchange reactions:
23
+ gempipe.close_boundaries(model)
24
+
25
+
26
+ # create a dissipation reaction:
27
+ dissip = cobra.Reaction(f'__dissip__{mid}')
28
+ model.add_reactions([dissip])
29
+ dissip = model.reactions.get_by_id(f'__dissip__{mid}')
30
+
31
+
32
+ # define the dissipation reaction:
33
+ modeled_mids = [m.id for m in model.metabolites]
34
+ if mid == 'atp':
35
+ dissip_string = 'atp_c + h2o_c --> adp_c + pi_c + h_c'
36
+ elif mid == 'ctp':
37
+ dissip_string = 'ctp_c + h2o_c --> cdp_c + pi_c + h_c'
38
+ elif mid == 'gtp':
39
+ dissip_string = 'gtp_c + h2o_c --> gdp_c + pi_c + h_c'
40
+ elif mid == 'utp':
41
+ dissip_string = 'utp_c + h2o_c --> udp_c + pi_c + h_c'
42
+ elif mid == 'itp':
43
+ dissip_string = 'itp_c + h2o_c --> idp_c + pi_c + h_c'
44
+ elif mid == 'nadh':
45
+ dissip_string = 'nadh_c --> nad_c + h_c'
46
+ elif mid == 'nadph':
47
+ dissip_string = 'nadph_c --> nadp_c + h_c'
48
+ elif mid == 'fadh2':
49
+ dissip_string = 'fadh2_c --> fad_c + 2.0 h_c'
50
+ elif mid == 'accoa':
51
+ dissip_string = 'accoa_c + h2o_c --> ac_c + coa_c + h_c'
52
+ elif mid == 'glu__L':
53
+ dissip_string = 'glu__L_c + h2o_c --> akg_c + nh4_c + 2.0 h_c'
54
+ elif mid == 'q8h2':
55
+ dissip_string = 'q8h2_c --> q8_c + 2.0 h_c'
56
+ dissip.build_reaction_from_string(dissip_string)
57
+
58
+
59
+ # set the objective and optimize:
60
+ model.objective = f'__dissip__{mid}'
61
+ res, obj_value, status = fba_no_warnings(model)
62
+
63
+
64
+ # apply the threshold:
65
+ obj_value = res.objective_value
66
+ status = res.status
67
+ if status == 'optimal' and obj_value >= get_optthr():
68
+ found_egc = True
69
+
70
+
71
+ # get suspect !=0 fluxes
72
+ fluxes = res.fluxes
73
+ # get interesting fluxes (get_optthr() tries to take into account the approximation in glpk and cplex solvers)
74
+ fluxes_interesting = fluxes[(fluxes > get_optthr()) | (fluxes < -get_optthr())]
75
+
76
+
77
+ # create a model for escher, remove Rs not beloning to the cycle
78
+ model_copy = model.copy()
79
+ all_rids = [r.id for r in model_copy.reactions]
80
+ to_delete = set(all_rids) - set(fluxes_interesting.index)
81
+
82
+
83
+ # trick to avoid the WARNING "cobra/core/group.py:147: UserWarning: need to pass in a list"
84
+ # triggered when trying to remove reactions that are included in groups.
85
+ with warnings.catch_warnings(): # temporarily suppress warnings for this block
86
+ warnings.simplefilter("ignore") # ignore all warnings
87
+ cobra_logger = logging.getLogger("cobra.util.solver")
88
+ old_level = cobra_logger.level
89
+ cobra_logger.setLevel(logging.ERROR)
90
+
91
+ # triggering code
92
+ model_copy.remove_reactions(to_delete) # should work also with IDs
93
+
94
+ # restore original behaviour:
95
+ cobra_logger.setLevel(old_level)
96
+
97
+
98
+ # save JSON to direct import in Escher:
99
+ outfile = os.path.join(outdir, f'EGC_{mid}.json')
100
+ cobra.io.save_json_model(model_copy, outfile)
101
+
102
+
103
+ # log some messages
104
+ rid_labels = []
105
+ for rid, flux in fluxes_interesting.to_dict().items():
106
+ rid_label = "'" + rid + "'"
107
+ # mark reversible reactions composing the cycle:
108
+ r = model.reactions.get_by_id(rid)
109
+ if r.lower_bound < 0 and r.upper_bound > 0:
110
+ rid_label = rid_label + '(<=>)'
111
+ rid_labels.append(rid_label)
112
+ logger.warning(f"Found erroneous EGC (N={len(model_copy.reactions)}) for '{mid}' (f={obj_value}): [{', '.join(rid_labels)}]. EGC saved to '{outfile}' to be inspected with Escher-FBA.")
113
+
114
+
115
+ return found_egc
116
+
117
+
118
+
119
+ def verify_egc_all(logger, model, outdir='./', mids_to_check=['atp','ctp','gtp','utp','itp','nadh','nadph','fadh2','accoa','glu__L','q8h2']):
120
+
121
+
122
+ all_results = []
123
+ for mid in mids_to_check:
124
+ all_results.append(verify_egc(logger, model, mid, outdir))
125
+ if any(all_results)==False:
126
+ logger.info("Found 0 erroneous energy-generating cycles (EGCs).")
127
+
128
+
@@ -287,15 +287,7 @@ def introduce_transporters(logger, db, model, idcollection_dict, kegg_reaction_t
287
287
  r = model.reactions.get_by_id(f'EX_{mid_e}')
288
288
  r.name = f"Exchange for {model.metabolites.get_by_id(mid_e).name}"
289
289
  r.build_reaction_from_string(f'{mid_e} --> ')
290
- if mid_e in [
291
- # basics:
292
- 'glc__D_e', 'nh4_e', 'pi_e', 'so4_e', 'h2o_e', 'h_e', 'o2_e', 'co2_e',
293
- # metals:
294
- 'cu2_e', 'mobd_e', 'fe2_e', 'cobalt2_e',
295
- ]:
296
- r.bounds = (-1000, 1000)
297
- else:
298
- r.bounds = (0, 1000)
290
+ r.bounds = (0, 1000)
299
291
 
300
292
  # add SBO annotation
301
293
  r.annotation['sbo'] = ['SBO:0000627'] # exchange reaction
@@ -19,6 +19,33 @@ def get_rids_with_mancheck_gpr():
19
19
  return rids_mancheck_gpr
20
20
 
21
21
 
22
+ def get_rids_with_mancheck_balancing():
23
+ rids_mancheck_bal = [ # same reactions involving ATP can be reversible
24
+
25
+ # SECTION "reversible both in KEGG and MetaCyc"
26
+ 'PGK', 'SUCOAS', 'ADK1', 'GK1', 'NNATr', 'CYTK1', 'ACKr',
27
+ 'DGK1', 'PPAKr', 'ATPSr', 'NDPK10',
28
+
29
+ ### SECTION "reversible in KEGG but not in MetaCyc" ###
30
+ 'CYTK2', # clearly reversible in KEGG but not in MetaCyc (RXN-7913)
31
+ 'DADK', # clearly reversible in KEGG but not in MetaCyc (DEOXYADENYLATE-KINASE-RXN)
32
+ 'UMPK', # clearly reversible in KEGG but not in MetaCyc (RXN-12002)
33
+ 'NDPK1', # clearly reversible in KEGG but not in MetaCyc (GDPKIN-RXN)
34
+ 'NDPK2', # clearly reversible in KEGG but not in MetaCyc (UDPKIN-RXN)
35
+ 'NDPK3', # clearly reversible in KEGG but not in MetaCyc (CDPKIN-RXN)
36
+ 'NDPK4', # clearly reversible in KEGG but not in MetaCyc (DTDPKIN-RXN)
37
+ 'NDPK5', # clearly reversible in KEGG but not in MetaCyc (DGDPKIN-RXN)
38
+ 'NDPK6', # clearly reversible in KEGG but not in MetaCyc (DUDPKIN-RXN)
39
+ 'NDPK7', # clearly reversible in KEGG but not in MetaCyc (DCDPKIN-RXN)
40
+ 'NDPK8', # clearly reversible in KEGG but not in MetaCyc (DADPKIN-RXN)
41
+ 'NDPK9', # clearly reversible in KEGG but not in MetaCyc (RXN-14120)
42
+
43
+ ### SECTION "missing reversibility info" ###
44
+ 'LPHERA',
45
+ ]
46
+ return rids_mancheck_bal
47
+
48
+
22
49
 
23
50
  def get_manual_sinks():
24
51
 
@@ -18,6 +18,9 @@ from ..commons import adjust_biomass_precursors
18
18
  from ..commons import count_undrawn_rids
19
19
  from ..commons import format_expansion
20
20
  from ..commons import download_keggorg
21
+ from ..commons import initialize_model
22
+ from ..commons import get_memote_results_dict
23
+
21
24
 
22
25
  from .introduce import introduce_metabolites
23
26
  from .introduce import introduce_reactions
@@ -35,6 +38,8 @@ from ..runsims.biosynth import biosynthesis_on_media
35
38
 
36
39
  from ..mkmodel.polishing import remove_disconnected
37
40
 
41
+ from .cycles import verify_egc_all
42
+
38
43
 
39
44
 
40
45
 
@@ -173,41 +178,59 @@ def main(args, logger):
173
178
 
174
179
 
175
180
  ###### CHECKS 2
176
- # check growth on minmal media
177
- df_G = grow_on_media(logger, universe, dbexp, args.media, '-', True)
178
- if type(df_G)==int: return 1
181
+ # check erroneous EGCs
182
+ verify_egc_all(logger, universe, args.outdir)
183
+
179
184
 
180
- # check blocked biomass precursors
181
- cond_col_dict = adjust_biomass_precursors(logger, universe, universe, 1.0)
182
- df_E = precursors_on_media(logger, universe, universe, dbexp, args.media, cond_col_dict, args.precursors)
183
- if type(df_E)==int: return 1
185
+ if not args.justparse:
186
+
187
+
188
+ ###### CHECKS 3
189
+ # check growth on minmal media
190
+ df_G = grow_on_media(logger, universe, dbexp, args.media, '-', True)
191
+ if type(df_G)==int: return 1
184
192
 
185
- # check blocked metabolites / dead-ends
186
- df_S = biosynthesis_on_media(logger, universe, dbexp, args.media, args.biosynth)
187
- if type(df_S)==int: return 1
193
+ # check blocked biomass precursors
194
+ cond_col_dict = adjust_biomass_precursors(logger, universe, universe, 1.0)
195
+ df_E = precursors_on_media(logger, universe, universe, dbexp, args.media, cond_col_dict, args.precursors)
196
+ if type(df_E)==int: return 1
197
+
198
+ # check blocked metabolites / dead-ends
199
+ df_S = biosynthesis_on_media(logger, universe, dbexp, args.media, args.biosynth)
200
+ if type(df_S)==int: return 1
188
201
 
189
202
 
190
203
 
191
- ###### POLISHING 2
192
- # reset growth environment befor saving the model
193
- gempipe.reset_growth_env(universe)
194
-
204
+ ###### POLISHING 2
205
+ # reset growth environment befor saving the model
206
+ gempipe.reset_growth_env(universe)
207
+
208
+ # initialize model
209
+ response = initialize_model(logger, universe, dbexp, args.initialize, args.media)
210
+ if response==1: return 1
211
+
212
+
213
+
214
+ ###### CHECKS 4
215
+ # compute Memote metrics
216
+ memote_results_dict = get_memote_results_dict(logger, universe)
217
+
195
218
 
196
219
 
197
- # output the universe
198
- logger.info("Writing universal model...")
199
- cobra.io.save_json_model(universe, f'{args.outdir}/universe.json')
200
- logger.info(f"'{args.outdir}/universe.json' created!")
201
- cobra.io.write_sbml_model(universe, f'{args.outdir}/universe.xml') # groups are saved only to SBML
202
- logger.info(f"'{args.outdir}/universe.xml' created!")
203
- force_id_on_sbml(f'{args.outdir}/universe.xml', 'universe') # force introduction of the 'id=""' field
204
- sheets_dict = write_excel_model(universe, f'{args.outdir}/universe.parsedb.xlsx', args.nofigs, df_E, None, None, df_S, df_C)
205
- logger.info(f"'{args.outdir}/universe.parsedb.xlsx' created!")
220
+ # output the universe
221
+ logger.info("Writing universal model...")
222
+ cobra.io.save_json_model(universe, f'{args.outdir}/universe.json')
223
+ logger.info(f"'{args.outdir}/universe.json' created!")
224
+ cobra.io.write_sbml_model(universe, f'{args.outdir}/universe.xml') # groups are saved only to SBML
225
+ logger.info(f"'{args.outdir}/universe.xml' created!")
226
+ force_id_on_sbml(f'{args.outdir}/universe.xml', 'universe') # force introduction of the 'id=""' field
227
+ sheets_dict = write_excel_model(universe, f'{args.outdir}/universe.parsedb.xlsx', args.nofigs, memote_results_dict, df_E, None, None, df_S, df_C)
228
+ logger.info(f"'{args.outdir}/universe.parsedb.xlsx' created!")
206
229
 
207
230
 
208
231
 
209
- ###### CHECKS 3
210
- # check if universal escher map os updated:
232
+ ###### CHECKS 4
233
+ # check if universal escher map is updated:
211
234
  count_undrawn_rids(logger, universe, lastmap)
212
235
 
213
236
 
@@ -4,6 +4,7 @@ import cobra
4
4
 
5
5
  from .manual import get_deprecated_kos
6
6
  from .manual import get_rids_with_mancheck_gpr
7
+ from .manual import get_rids_with_mancheck_balancing
7
8
 
8
9
 
9
10
 
@@ -138,6 +139,14 @@ def add_reaction(logger, model, rid, row, kr_ids, kegg_reaction_to_others, addty
138
139
  return 1
139
140
 
140
141
 
142
+ # check if reversible and using ATP
143
+ if r.lower_bound < 0 and r.upper_bound > 0:
144
+ for m in r.metabolites:
145
+ if m.id.rsplit('_', 1)[0] == 'atp':
146
+ if rid not in get_rids_with_mancheck_balancing():
147
+ logger.warning(f"Reaction '{rid}' involves ATP and is reversible: are you sure?")
148
+
149
+
141
150
  return 0
142
151
 
143
152