From 50ebc192b07b387533c298f9e7a0a2cefe528ff5 Mon Sep 17 00:00:00 2001
From: Peter Schubert <Peter.Schubert@hhu.de>
Date: Tue, 19 Jul 2022 16:39:41 +0200
Subject: [PATCH] Initial base functionality

---
 modelpruner/core/model_pruner.py            | 199 ++++--
 modelpruner/core/protected_parts.py         |   6 +-
 modelpruner/models/fba_model.py             | 130 +++-
 modelpruner/problems/lp_problem.py          |  80 ++-
 sample_data/SBML_models/Deniz_model_fba.xml | 698 ++++++++++++++++++++
 sample_data/data/Deniz_model_fba_nrp2.xlsx  | Bin 0 -> 14046 bytes
 sample_prune_model.py                       |  50 ++
 7 files changed, 1030 insertions(+), 133 deletions(-)
 create mode 100644 sample_data/SBML_models/Deniz_model_fba.xml
 create mode 100644 sample_data/data/Deniz_model_fba_nrp2.xlsx
 create mode 100644 sample_prune_model.py

diff --git a/modelpruner/core/model_pruner.py b/modelpruner/core/model_pruner.py
index f2cd574..742cf3e 100644
--- a/modelpruner/core/model_pruner.py
+++ b/modelpruner/core/model_pruner.py
@@ -1,9 +1,11 @@
 
 
+import sys
 import os
 import re
 import time
 import numpy as np
+import pickle
 import sbmlxdf
 
 from modelpruner.models.fba_model import FbaModel
@@ -12,97 +14,172 @@ from modelpruner.core.protected_parts import ProtectedParts
 
 class ModelPruner:
 
-    def __init__(self, sbml_fname, protected_parts, reduced_fname=None):
+    def __init__(self, sbml_fname, protected_parts, reduced_fname=None, resume=False):
 
         if reduced_fname is None:
-            self.reduced_fname = re.sub('.xml', '_red.xml', sbml_fname)
-
-        if os.path.exists(sbml_fname):
-            self.sbml_model = sbmlxdf.Model(sbml_fname)
-            print(f'Protected data loaded: {sbml_fname} '
-                  f'(last modified: {time.ctime(os.path.getmtime(sbml_fname))})')
+            self.reduced_fname = re.sub(r'.xml$', '_red.xml', sbml_fname)
+        self._snapshot_sbml = re.sub(r'.xml$', '_snapshot.xml', sbml_fname)
+        self._snapshot_pkl = re.sub(r'.xml$', '_snapshot.pkl', sbml_fname)
+
+        if resume is True:
+            if os.path.exists(self._snapshot_sbml):
+                self.sbml_model = sbmlxdf.Model(self._snapshot_sbml)
+                print(f'Resume from snapshot from {self._snapshot_sbml} '
+                      f'(last modified: {time.ctime(os.path.getmtime(self._snapshot_sbml))})')
+            else:
+                print(f'{self._snapshot_sbml} not found!')
+                raise FileNotFoundError
         else:
-            print(f'{sbml_fname} not found!')
-            raise FileNotFoundError
+            if os.path.exists(sbml_fname):
+                self.sbml_model = sbmlxdf.Model(sbml_fname)
+                print(f'Full SBML model loaded from {sbml_fname} '
+                      f'(last modified: {time.ctime(os.path.getmtime(sbml_fname))})')
+            else:
+                print(f'{sbml_fname} not found!')
+                raise FileNotFoundError
 
         self.tolerance = 1e-8   # later accessible by set_params() ?
 
         self.fba_model = FbaModel(self.sbml_model)
         self.nrp = ProtectedParts(protected_parts)
+        self.protected_sids = self.nrp.initial_protected_sids
+
+        if resume is True and os.path.exists(self._snapshot_pkl):
+            with open(self._snapshot_pkl, 'rb') as f:
+                self.protected_rids = pickle.load(f)
+                print(f'snapshot restored at {len(self.fba_model.rids):4d} remaining reactions '
+                      f'from: {self._snapshot_pkl}')
+        else:
+            self.protected_rids = self.get_total_protected_rids(self.nrp.initial_protected_rids)
+
+        self.fba_model.update_flux_bounds(self.nrp.overwrite_bounds)
 
         self.base_obj_dir = self.fba_model.obj_dir
         self.base_flux_bounds = self.fba_model.flux_bounds.copy()
         self.base_coefs = self.fba_model.obj_coefs.copy()
-        self.protected_sids = set(self.nrp.initial_protected_sids)
-        self.protected_rids = self.get_total_protected_rids(set(self.nrp.initial_protected_rids))
-        self.essential_rids = self.protected_rids
-        self.free_rids = set(self.fba_model.rids).difference(self.essential_rids)
+
         self.set_function_optimum()
         self.check_protected_parts()
 
-        # overwrite bounds on fba
-        self.fba_model.update_flux_bounds(self.nrp.overwrite_bounds)
-
     def check_protected_parts(self):
 
         extra_p_rids = self.protected_rids.difference(set(self.fba_model.rids))
         extra_p_sids = self.protected_sids.difference(set(self.fba_model.sids))
         pf_rids = set()
         for pf in self.nrp.protected_functions.values():
-            pf_rids = pf_rids.union({rid for rid in pf.objective})
-            pf_rids = pf_rids.union({rid for rid in pf.overwrite_bounds})
+            pf_rids |= {rid for rid in pf.objective}
+            pf_rids |= {rid for rid in pf.overwrite_bounds}
             if pf.fba_success is False:
                 print(f'protected function {pf.obj_id} has unsuccessful FBA '
                       f' with optimal value {pf.optimal}')
 
         extra_pf_rids = pf_rids.difference(set(self.fba_model.rids))
+        pf_unprotected_rids = pf_rids.difference(set(self.protected_rids))
         if len(extra_p_rids) > 0:
             print('protected reactions that do not exist in model:', extra_p_rids)
         if len(extra_p_sids) > 0:
             print('protected metabolites that do not exist in model:', extra_p_sids)
         if len(extra_pf_rids) > 0:
-            print('reactions in protected function that do not exist in model:', extra_pf_rids)
+            print('reactions in protected functions that do not exist in model:', extra_pf_rids)
+        if len(pf_unprotected_rids) > 0:
+            print('reactions in protected functions that are not protected:', pf_unprotected_rids)
 
-        zero_flux_rids = self.get_blocked_reactions()
-        blocked_p_rids = self.protected_rids.intersection(zero_flux_rids)
-        blocked_pf_rids = pf_rids.intersection(zero_flux_rids)
+        blocked_rids = self.reaction_types_fva(self.fba_model.rids)['blocked_rids']
+        blocked_p_rids = self.nrp.initial_protected_sids.intersection(blocked_rids)
+        blocked_pf_rids = pf_rids.intersection(blocked_rids)
         if len(blocked_p_rids) > 0:
             print('protected reactions that are blocked (no flux):', blocked_p_rids)
         if len(blocked_pf_rids) > 0:
-            print('reactions in protected function that are blocked (no flux):', blocked_pf_rids)
-
-    def get_blocked_reactions(self):
-        # identify blocked reactions
-        # check that none of the blocked reactions are protected
-        # actually remove blocked reactions, even they are included in
-
-        fba_solution = self.fba_model.fba_optimize()
-        zero_flux_mask = np.abs(fba_solution['x']) < self.tolerance
-        zero_flux_rids = self.fba_model.rids[zero_flux_mask]
-
-        fva_solution = self.fba_model.fva_optimize(zero_flux_rids, fract_of_optimum=0.0)
-        flux_ranges = np.vstack((fva_solution['min'], fva_solution['max'])).T
-        zero_flux_mask = np.all(abs(flux_ranges) < self.tolerance, axis=1)
-        zero_flux_rids = set(fva_solution['rids'][zero_flux_mask])
-        return zero_flux_rids
-
-    def prune(self):
+            print('reactions in protected functions that are blocked (no flux):', blocked_pf_rids)
 
+    def prune(self, snapshot_freq=None):
+        print(f'initial S-matrix: {self.fba_model.s_mat.shape}; '
+              f' protected reactions: {len(self.protected_rids)}:', self.protected_rids)
         self.remove_parallel_reactions()
+        print('protected functions feasibility:', self.check_pf_feasibility())
 
-    def reaction_types_fva(self):
-
-        free_rids = list(set(self.fba_model.rids).difference(self.protected_rids))
+        if type(snapshot_freq) is int:
+            next_snapshot = max(0, len(self.fba_model.rids) - snapshot_freq)
+        else:
+            next_snapshot = None
+
+        while len(self.protected_rids) < self.fba_model.s_mat.shape[1]:
+            free_rids = list(set(self.fba_model.rids).difference(self.protected_rids))
+            reaction_types = self.reaction_types_fva(free_rids)
+            if len(reaction_types['blocked_rids']) > 0:
+                self.fba_model.remove_reactions(reaction_types['blocked_rids'], self.protected_rids)
+                print(f"{len(reaction_types['blocked_rids'])} blocked reaction(s) removed:",
+                      reaction_types['blocked_rids'])
+            if len(reaction_types['essential_rids']) > 0:
+                self.protected_rids |= reaction_types['essential_rids']
+                print(f"{len(reaction_types['essential_rids'])} reaction(s) added to protected reactions:",
+                      reaction_types['essential_rids'])
+
+            print(f"{len(reaction_types['candidate_rids_sorted'])} candidates:",
+                  reaction_types['candidate_rids_sorted'][:4], '...')
+            # try to remove one reaction while still satisfying all protected functions
+            feasible = False
+            essential_rids = set()
+            for rid in reaction_types['candidate_rids_sorted']:
+                orig_flux_bounds = self.fba_model.get_flux_bounds(rid)
+                self.fba_model.update_flux_bounds({rid: [0.0, 0.0]})
+                feasible = self.check_pf_feasibility()
+                self.fba_model.update_flux_bounds({rid: orig_flux_bounds})
+                if feasible:
+                    self.fba_model.remove_reactions({rid}, self.protected_rids)
+                    print(f'{rid} removed from the model')
+                    break
+                else:
+                    essential_rids.add(rid)
+            if len(essential_rids) > 0:
+                self.protected_rids |= essential_rids
+                print(f"{len(essential_rids)} reaction(s) added to protected reactions:", essential_rids)
+
+            if feasible is False:
+                print('no more reactions to remove')
+
+            # store intermediate results (snapshots)
+            print(f'snapshot check next: {next_snapshot}, rids: {len(self.fba_model.rids)}')
+            if (next_snapshot is not None) and (len(self.fba_model.rids) < next_snapshot):
+                self.export_pruned_model(self._snapshot_sbml)
+                with open(self._snapshot_pkl, 'wb') as f:
+                    pickle.dump(self.protected_rids, f)
+                next_snapshot = max(0, len(self.fba_model.rids) - snapshot_freq)
+                print('snapshot data saved.')
+
+            print(f'S-matrix: {self.fba_model.s_mat.shape}; '
+                  f'protected reactions: {len(self.protected_rids)}')
+
+        # check if blocked reactions exist in the finally pruned model
+        blocked_rids = self.reaction_types_fva(self.fba_model.rids)['blocked_rids']
+        if len(blocked_rids) > 0:
+            print(f'{len(blocked_rids)} blocked reaction(s) in pruned model', blocked_rids)
+
+        # remove any snapshot files
+        for fname in [self._snapshot_sbml, self._snapshot_pkl]:
+            if os.path.exists(fname):
+                os.remove(fname)
+
+    def reaction_types_fva(self, free_rids):
 
         flux_min_pfs = np.zeros((len(self.nrp.protected_functions), len(free_rids)))
         flux_max_pfs = np.zeros((len(self.nrp.protected_functions), len(free_rids)))
 
+        n_pf = len(self.nrp.protected_functions)
         for idx, pf in enumerate(self.nrp.protected_functions.values()):
             self.set_temp_func_conditions(pf)
             res = self.fba_model.fva_optimize(free_rids, fract_of_optimum=0.0)
             self.restore_base_conditions()
+            if res['success'] is False:
+                print('FVA unsuccessful')
+                return {'blocked_rids': [], 'essential_rids': [], 'candidate_rids_sorted': []}
+
             flux_min_pfs[idx] = res['min']
             flux_max_pfs[idx] = res['max']
+            sys.stdout.write('\rFVA[{}{}] {:3.0f}%'.format(
+                '=' * int(idx+1), ' ' * int(n_pf - idx-1), (idx+1)/n_pf*100))
+            sys.stdout.flush()
+        print()
 
         flux_min_pfs[np.abs(flux_min_pfs) < self.tolerance] = 0.0
         flux_max_pfs[np.abs(flux_max_pfs) < self.tolerance] = 0.0
@@ -144,18 +221,17 @@ class ModelPruner:
                 drop_group_rids = set(group_rids) - {reversible}
             else:
                 drop_group_rids = set(group_rids[1:])
-            drop_rids = drop_rids.union(drop_group_rids)
+            drop_rids |= drop_group_rids
 
         self.fba_model.remove_reactions(drop_rids, self.protected_sids)
-        print(f'{len(drop_rids)} parallel reactions dropped:', drop_rids)
+        print(f'{len(drop_rids)} parallel reaction(s) dropped:', drop_rids)
 
     def set_function_optimum(self):
         """using FBA to set optimal value for protected functions"""
-        for obj_id in self.nrp.protected_functions:
-            pf = self.nrp.protected_functions[obj_id]
+        for obj_id, pf in self.nrp.protected_functions.items():
             self.set_temp_func_conditions(pf)
-
             res = self.fba_model.fba_optimize(short_result=True)
+            self.restore_base_conditions()
 
             pf.optimal = res['fun']
             pf.fba_success = res['success']
@@ -165,8 +241,6 @@ class ModelPruner:
                 else:
                     pf.set_target_lb(pf.target_frac * pf.optimal)
 
-            self.restore_base_conditions()
-
     def get_total_protected_rids(self, protected_rids):
         # identify reactions that need protection due to protected metabolites
         #  over and above the protected rids provided in protected parts
@@ -179,11 +253,12 @@ class ModelPruner:
         # identify rids in protected functions and ensure they are protected
         pf_rids = set()
         for pf in self.nrp.protected_functions.values():
-            pf_rids = pf_rids.union({rid for rid in pf.objective})
-            pf_rids = pf_rids.union({rid for rid in pf.overwrite_bounds})
+            pf_rids |= {rid for rid in pf.objective}
+            pf_rids |= {rid for rid in pf.overwrite_bounds}
 
-        total_protect_rids = protected_rids.union(protected_sid_rids).union(pf_rids)
-        return total_protect_rids
+        protected_rids |= protected_sid_rids
+        protected_rids |= pf_rids
+        return protected_rids
 
     def check_pf_feasibility(self):
         """check that current model still satisfies the protected functions
@@ -217,8 +292,8 @@ class ModelPruner:
 
     def restore_base_conditions(self):
         self.fba_model.obj_dir = self.base_obj_dir
-        self.fba_model.flux_bounds = self.base_flux_bounds
-        self.fba_model.obj_coefs = self.base_coefs
+        self.fba_model.flux_bounds = self.base_flux_bounds.copy()
+        self.fba_model.obj_coefs = self.base_coefs.copy()
 
     def get_reduction_status(self):
         """ Return status in model reduction process
@@ -231,12 +306,18 @@ class ModelPruner:
         status = self.fba_model.get_model_status()
         status['n_protected_rids'] = len(self.protected_rids)
         status['n_protected_sids'] = len(self.protected_sids)
-        status['essential_rids'] = len(self.essential_rids)
         return status
 
     def print_status(self):
 
         status = self.get_reduction_status()
         print("reactions removed/remaining/protected: "
-              f"{status['n_full_rids' ] -status['n_red_rids']:4d}/{status['n_red_rids']:4d}/"
-              f"{status['essential_rids']:4d}; dof: {status['dof']:3d}")
+              f"{status['n_full_rids' ] - status['n_rids']:4d}/{status['n_rids']:4d}/"
+              f"/{len(self.protected_rids):4d}; dof: {status['dof']:3d}")
+
+    def export_pruned_model(self, pruned_sbml):
+        success = self.fba_model.export_pruned_model(pruned_sbml)
+        if success is True:
+            print('pruned model exported to', pruned_sbml)
+        else:
+            print('pruned model could not be exported')
diff --git a/modelpruner/core/protected_parts.py b/modelpruner/core/protected_parts.py
index 71de8b2..b35343a 100644
--- a/modelpruner/core/protected_parts.py
+++ b/modelpruner/core/protected_parts.py
@@ -14,7 +14,7 @@ class ProtectedParts:
         if type(protected_parts) == str:
             if os.path.exists(protected_parts):
                 nrp = self.load_raw_protected_data(protected_parts)
-                print(f'Protected data loaded: {protected_parts} '
+                print(f'Protected data loaded from {protected_parts} '
                       f'(last modified: {time.ctime(os.path.getmtime(protected_parts))})')
             else:
                 print(f'{protected_parts} not found!')
@@ -23,8 +23,8 @@ class ProtectedParts:
             assert type(protected_parts) == dict, 'expected filename or a dict'
             nrp = protected_parts
 
-        self.initial_protected_rids = nrp.get('reactions', [])
-        self.initial_protected_sids = nrp.get('metabolites', [])
+        self.initial_protected_rids = set(nrp.get('reactions', []))
+        self.initial_protected_sids = set(nrp.get('metabolites', []))
         if 'bounds' in nrp:
             num_cols = ['fbcLb', 'fbcUb']
             nrp['bounds'][num_cols] = nrp['bounds'][num_cols].applymap(
diff --git a/modelpruner/models/fba_model.py b/modelpruner/models/fba_model.py
index 8e8ae17..3496c3a 100644
--- a/modelpruner/models/fba_model.py
+++ b/modelpruner/models/fba_model.py
@@ -1,5 +1,6 @@
 
 
+import re
 import math
 import numpy as np
 import sbmlxdf
@@ -11,10 +12,10 @@ class FbaModel:
 
     def __init__(self, sbml_model):
 
-        model_dict = sbml_model.to_df()
-        df_reactions = model_dict['reactions']
-        df_species = model_dict['species']
-        df_fbc_objectives = model_dict['fbcObjectives']
+        self.model_dict = sbml_model.to_df()
+        df_reactions = self.model_dict['reactions']
+        df_species = self.model_dict['species']
+        df_fbc_objectives = self.model_dict['fbcObjectives']
 
         self.full_model_shape = {'n_full_rids': len(df_reactions),
                                  'n_full_sids': len(df_species)}
@@ -105,6 +106,7 @@ class FbaModel:
         if lp is None:
             return {'success': False, 'message': 'not an FBA model'}
         res = lp.solve(short_result)
+        del lp
         if short_result is False:
             res['x'] = self.combine_fwd_bwd(res['x'])
             res['reduced_costs'] = self.combine_fwd_bwd(res['reduced_costs'])
@@ -125,40 +127,42 @@ class FbaModel:
             return {'success': False, 'message': 'not an FBA model'}
         res = lp.solve(short_result=True)
 
+        if res['success'] is False:
+            del lp
+            return {'success': False, 'message': res['generic_status']}
+
         if rids is None:
             rids = self.rids
-
         flux_min = np.zeros(len(rids))
         flux_max = np.zeros(len(rids))
-        if res['success']:
-            # fix FBA objective as constraint
-            fwd_bwd_obj_coefs = np.hstack((self.obj_coefs, -self.obj_coefs[self.reversible]))
-            if self.obj_dir == 'maximize':
-                row_bds = np.array([res['fun'] * fract_of_optimum, np.nan])
+        # fix FBA objective as constraint
+        fwd_bwd_obj_coefs = np.hstack((self.obj_coefs, -self.obj_coefs[self.reversible]))
+        if self.obj_dir == 'maximize':
+            row_bds = np.array([res['fun'] * fract_of_optimum, np.nan])
+        else:
+            if fract_of_optimum > 0.0:
+                row_bds = np.array([np.nan, res['fun'] / fract_of_optimum])
             else:
-                if fract_of_optimum > 0.0:
-                    row_bds = np.array([np.nan, res['fun'] / fract_of_optimum])
-                else:
-                    row_bds = np.array([np.nan, 1000.0])
-            lp.add_constraint(fwd_bwd_obj_coefs, row_bds)
-
-            n_cols = len(self.rids)
-            for idx, rid in enumerate(rids):
-                # select reaction maximize/minimize flux
-                new_obj_coefs = np.zeros(n_cols)
-                new_obj_coefs[self.rid2idx[rid]] = 1
-                new_fwd_bwd_obj_coefs = np.hstack((new_obj_coefs, -new_obj_coefs[self.reversible]))
-                lp.set_obj_coefs(new_fwd_bwd_obj_coefs)
-
-                lp.set_obj_dir('minimize')
-                res = lp.solve(short_result=True)
-                flux_min[idx] = res['fun'] if res['success'] else np.nan
-
-                lp.set_obj_dir('maximize')
-                res = lp.solve(short_result=True)
-                flux_max[idx] = res['fun'] if res['success'] else np.nan
-
-        return {'rids': np.array(rids), 'min': flux_min, 'max': flux_max}
+                row_bds = np.array([np.nan, 1000.0])
+        lp.add_constraint(fwd_bwd_obj_coefs, row_bds)
+
+        n_cols = len(self.rids)
+        for idx, rid in enumerate(rids):
+            # select reaction maximize/minimize flux
+            new_obj_coefs = np.zeros(n_cols)
+            new_obj_coefs[self.rid2idx[rid]] = 1
+            new_fwd_bwd_obj_coefs = np.hstack((new_obj_coefs, -new_obj_coefs[self.reversible]))
+            lp.set_obj_coefs(new_fwd_bwd_obj_coefs)
+
+            lp.set_obj_dir('minimize')
+            res = lp.solve(short_result=True)
+            flux_min[idx] = res['fun'] if res['success'] else np.nan
+
+            lp.set_obj_dir('maximize')
+            res = lp.solve(short_result=True)
+            flux_max[idx] = res['fun'] if res['success'] else np.nan
+        del lp
+        return {'success': True, 'rids': np.array(rids), 'min': flux_min, 'max': flux_max}
 
     def update_flux_bounds(self, flux_bounds):
         """selectivly set lower/upper flux bounds for given reactions.
@@ -169,6 +173,7 @@ class FbaModel:
         :type flux_bounds: dict (key: reaction id, value: list with two float values for lower/upper bound)
         """
         for rid, (lb, ub) in flux_bounds.items():
+            assert rid in self.rids
             if math.isfinite(lb):
                 self.flux_bounds[0, self.rid2idx[rid]] = lb
                 if lb < 0:
@@ -176,6 +181,10 @@ class FbaModel:
             if math.isfinite(ub):
                 self.flux_bounds[1, self.rid2idx[rid]] = ub
 
+    def get_flux_bounds(self, rid):
+        assert rid in self.rids
+        return [self.flux_bounds[0, self.rid2idx[rid]], self.flux_bounds[1, self.rid2idx[rid]]]
+
     def remove_reactions(self, drop_rids, protected_sids):
         # remove_reactions
         # also remove corresponding metabolite columns
@@ -242,8 +251,8 @@ class FbaModel:
         :rtype: dict
         """
         status = {'dof': self.s_mat.shape[1] - np.linalg.matrix_rank(self.s_mat),
-                  'n_red_rids': len(self.rids),
-                  'n_red_sids': len(self.sids)}
+                  'n_rids': len(self.rids),
+                  'n_sids': len(self.sids)}
         status.update(self.full_model_shape)
         return status
 
@@ -257,3 +266,54 @@ class FbaModel:
             self.__obj_dir = 'minimize'
         else:
             self.__obj_dir = 'maximize'
+
+    def export_pruned_model(self, pruned_sbml):
+
+        pruned_mdict = self.model_dict.copy()
+        pruned_mdict['reactions'] = self.model_dict['reactions'].loc[self.rids]
+        pruned_mdict['species'] = self.model_dict['species'].loc[self.sids]
+
+        # update model attributes (ids, names and modification date)
+        pruned_mdict['modelAttrs']['id'] += '_pruned'
+        pruned_mdict['modelAttrs']['name'] += '_pruned'
+        pruned_mdict['modelAttrs']['metaid'] += '_pruned'
+        pruned_mdict['modelAttrs']['modifiedHistory'] += '; localtime'
+
+        # clean groups table
+        keep_idxs = []
+        if 'groups' in pruned_mdict:
+            df_groups_pruned = pruned_mdict['groups'].copy()
+            for idx, row in df_groups_pruned.iterrows():
+                members_pruned = []
+                for member in sbmlxdf.misc.record_generator(row['members']):
+                    params = sbmlxdf.misc.extract_params(member)
+                    if 'idRef' in params:
+                        if params['idRef'] in self.rids:
+                            members_pruned.append(member)
+                    else:
+                        members_pruned.append(member)
+                df_groups_pruned.at[idx, 'members'] = '; '.join(members_pruned)
+                if len(members_pruned) > 0:
+                    keep_idxs.append(idx)
+            pruned_mdict['groups'] = df_groups_pruned.loc[keep_idxs]
+
+        # clean gene products
+        gp_ids = set()
+        if 'fbcGeneProdAssoc' in pruned_mdict['reactions']:
+            for gpr in pruned_mdict['reactions']['fbcGeneProdAssoc']:
+                if type(gpr) is str:
+                    if 'assoc=' in gpr:
+                        gpr = re.sub('assoc=', '', gpr)
+                        gpr = re.sub(r'[()]', ' ', gpr)
+                        gpr = re.sub(r'and', ' ', gpr)
+                        gpr = re.sub(r'or', ' ', gpr)
+                    gp_ids = gp_ids.union(set(re.findall(r'\w+', gpr)))
+        if 'fbcGeneProducts' in pruned_mdict:
+            pruned_mdict['fbcGeneProducts'] = pruned_mdict['fbcGeneProducts'].loc[list(gp_ids)]
+
+        pruned_model = sbmlxdf.Model()
+        pruned_model.from_df(pruned_mdict)
+        err = pruned_model.validate_sbml()
+        if len(err) > 0:
+            print('SBML validation result: %s', ', '.join([k + ': ' + str(v) for k, v in err.items()]))
+        return pruned_model.export_sbml(pruned_sbml)
diff --git a/modelpruner/problems/lp_problem.py b/modelpruner/problems/lp_problem.py
index b52d803..3bc4932 100644
--- a/modelpruner/problems/lp_problem.py
+++ b/modelpruner/problems/lp_problem.py
@@ -14,43 +14,43 @@ import swiglpk as glpk
 # status reported
 simplex_status = {
     0: 'LP problem instance successfully solved',
-    1: 'Unable to start search, initial basis specified in the '
+    glpk.GLP_EBADB: 'Unable to start search, initial basis specified in the '
        'problem object is invalid. Number basic variables is not same as the number rows.',
-    2: 'Unable to start search, basis matrix corresponding to initial basis is singular.',
-    3: 'Unable to start search, basis matrix corresponding to initial basis is ill-conditioned.',
-    4: 'Unable to start search, some double-bounded variables have incorrect bounds.',
-    5: 'Search prematurely terminated: solver failure.',
-    6: 'Search prematurely terminated: objective function being '
+    glpk.GLP_ESING: 'Unable to start search, basis matrix corresponding to initial basis is singular.',
+    glpk.GLP_ECOND: 'Unable to start search, basis matrix corresponding to initial basis is ill-conditioned.',
+    glpk.GLP_EBOUND: 'Unable to start search, some double-bounded variables have incorrect bounds.',
+    glpk.GLP_EFAIL: 'Search prematurely terminated: solver failure.',
+    glpk.GLP_EOBJLL: 'Search prematurely terminated: objective function being '
        'maximized reached its lower limit and continues decreasing.',
-    7: 'Search prematurely terminated: objective function being '
+    glpk.GLP_EOBJUL: 'Search prematurely terminated: objective function being '
        'minimized reached its upper limit and continues increasing.',
-    8: 'Search prematurely terminated: simplex iteration limit exceeded.',
-    9: 'Search prematurely terminated: time limit exceeded.',
-    10: 'LP problem instance has no primal feasible solution.',
-    11: 'LP problem instance has no dual feasible solution.'}
-
-generic_status = {1: 'solution is undefined',
-                  2: 'solution is feasible',
-                  3: 'solution is infeasible',
-                  4: 'problem has no feasible solution', 
-                  5: 'solution is optimal',
-                  6: 'problem has unbounded solution'}
-
-dual_status = {1: 'dual solution is undefined',
-               2: 'dual solution is feasible',
-               3: 'dual solution is infeasible',
-               4: 'no dual feasible solution exists'}
-
-prim_status = {1: 'primal solution is undefined',
-               2: 'primal solution is feasible',
-               3: 'primal solution is infeasible',
-               4: 'no primal feasible solution exists'}
-
-var_status = {1: 'basic variable',
-              2: 'non-basic variable on its lower bound',
-              3: 'non-basic variable on its upper bound',
-              4: 'non-basic free (unbounded) variable',
-              5: 'non-basic fixed variable'}
+    glpk.GLP_EITLIM: 'Search prematurely terminated: simplex iteration limit exceeded.',
+    glpk.GLP_ETMLIM: 'Search prematurely terminated: time limit exceeded.',
+    glpk.GLP_ENOPFS: 'LP problem instance has no primal feasible solution.',
+    glpk.GLP_ENODFS: 'LP problem instance has no dual feasible solution.'}
+
+generic_status = {glpk.GLP_UNDEF: 'solution is undefined',
+                  glpk.GLP_FEAS: 'solution is feasible',
+                  glpk.GLP_INFEAS: 'solution is infeasible',
+                  glpk.GLP_NOFEAS: 'problem has no feasible solution',
+                  glpk.GLP_OPT: 'solution is optimal',
+                  glpk.GLP_UNBND: 'problem has unbounded solution'}
+
+dual_status = {glpk.GLP_UNDEF: 'dual solution is undefined',
+               glpk.GLP_FEAS: 'dual solution is feasible',
+               glpk.GLP_INFEAS: 'dual solution is infeasible',
+               glpk.GLP_NOFEAS: 'no dual feasible solution exists'}
+
+prim_status = {glpk.GLP_UNDEF: 'primal solution is undefined',
+               glpk.GLP_FEAS: 'primal solution is feasible',
+               glpk.GLP_INFEAS: 'primal solution is infeasible',
+               glpk.GLP_NOFEAS: 'no primal feasible solution exists'}
+
+var_status = {glpk.GLP_BS: 'basic variable',
+              glpk.GLP_NL: 'non-basic variable on its lower bound',
+              glpk.GLP_NU: 'non-basic variable on its upper bound',
+              glpk.GLP_NF: 'non-basic free (unbounded) variable',
+              glpk.GLP_NS: 'non-basic fixed variable'}
 
 scaling_options = {'GM': glpk.GLP_SF_GM,
                    'EQ': glpk.GLP_SF_EQ,
@@ -69,6 +69,8 @@ class LpProblem:
         self.lp = glpk.glp_create_prob()
         self.ncols = 0
         self.nrows = 0
+        self.msg_lev = glpk.GLP_MSG_ERR
+        self.tm_lim = 10 * 1000
         self.res = {}
 
     @staticmethod
@@ -245,12 +247,18 @@ class LpProblem:
         :return: result information from the optimization stored in a dictionary
         :rtype: dict
         """
+        # configure options
+        smcp = glpk.glp_smcp()
+        glpk.glp_init_smcp(smcp)
+        smcp.msg_lev = self.msg_lev
+        smcp.tm_lim = self.tm_lim
+
         if scaling is not None:
             opt = scaling_options.get(scaling, glpk.GLP_SF_AUTO)
             glpk.glp_scale_prob(self.lp, opt)
         # sjj = [glpk.glp_get_sjj(self.lp, 1+i) for i in range(glpk.glp_get_num_cols(self.lp))]
         # rii = [glpk.glp_get_rii(self.lp, 1+i) for i in range(glpk.glp_get_num_rows(self.lp))]
-        simplex_result = glpk.glp_simplex(self.lp, None)
+        simplex_result = glpk.glp_simplex(self.lp, smcp)
         return self.results(simplex_result, short_result)
 
     def get_row_value(self, row):
@@ -279,6 +287,7 @@ class LpProblem:
         self.res = {
             'fun': glpk.glp_get_obj_val(self.lp),
             'success': glpk.glp_get_status(self.lp) == glpk.GLP_OPT,
+            'generic_status': generic_status[glpk.glp_get_status(self.lp)],
         }
         if short_result is False:
             self.res['x'] = np.array([glpk.glp_get_col_prim(self.lp, 1 + i) for i in range(self.ncols)])
@@ -286,7 +295,6 @@ class LpProblem:
             self.res['shadow_prices'] = np.array([glpk.glp_get_row_dual(self.lp, 1 + i) for i in range(self.nrows)])
             self.res['simplex_result'] = simplex_result
             self.res['simplex_message'] = simplex_status[simplex_result],
-            self.res['generic_status'] = generic_status[glpk.glp_get_status(self.lp)]
             self.res['primal_status'] = prim_status[glpk.glp_get_prim_stat(self.lp)]
             self.res['dual_status'] = dual_status[glpk.glp_get_dual_stat(self.lp)]
         return self.res
diff --git a/sample_data/SBML_models/Deniz_model_fba.xml b/sample_data/SBML_models/Deniz_model_fba.xml
new file mode 100644
index 0000000..e3b9f55
--- /dev/null
+++ b/sample_data/SBML_models/Deniz_model_fba.xml
@@ -0,0 +1,698 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created by sbmlxdf version 0.2.7 on 2022-07-18 14:55 with libSBML version 5.19.0. -->
+<sbml xmlns="http://www.sbml.org/sbml/level3/version2/core" xmlns:fbc="http://www.sbml.org/sbml/level3/version1/fbc/version2" xmlns:groups="http://www.sbml.org/sbml/level3/version1/groups/version1" level="3" version="2" fbc:required="false" groups:required="false">
+  <model metaid="Deniz_model_fba" id="Deniz_model_fba" name="Deniz_model_fba" substanceUnits="substance" timeUnits="time" extentUnits="substance" fbc:strict="true">
+    <notes>
+      <body xmlns="http://www.w3.org/1999/xhtml">
+        <h2>FBA model based on Deinz&apos;s model 11_12, presented Nov. 2020. </h2>
+        <p> including biomass reaction, external metabolites, ATPM maintenance reaction, oxidative and fermenative phenotypes</p>
+        <p>Based on Deniz Sezer </p>
+      </body>
+    </notes>
+    <annotation>
+      <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+        <rdf:Description rdf:about="#Deniz_model_fba">
+          <dcterms:creator>
+            <rdf:Bag>
+              <rdf:li rdf:parseType="Resource">
+                <vCard4:hasName rdf:parseType="Resource">
+                  <vCard4:family-name>Schubert</vCard4:family-name>
+                  <vCard4:given-name>Peter</vCard4:given-name>
+                </vCard4:hasName>
+                <vCard4:hasEmail>Peter.Schubert@hhu.de</vCard4:hasEmail>
+                <vCard4:organization-name>HHU-CCB</vCard4:organization-name>
+              </rdf:li>
+            </rdf:Bag>
+          </dcterms:creator>
+          <dcterms:created rdf:parseType="Resource">
+            <dcterms:W3CDTF>2022-07-18T14:55:18+02:00</dcterms:W3CDTF>
+          </dcterms:created>
+          <dcterms:modified rdf:parseType="Resource">
+            <dcterms:W3CDTF>2022-07-18T14:55:18+02:00</dcterms:W3CDTF>
+          </dcterms:modified>
+        </rdf:Description>
+      </rdf:RDF>
+    </annotation>
+    <listOfUnitDefinitions>
+      <unitDefinition id="mmol_per_gDW_per_hr" name="Millimoles per gram (dry weight) per hour">
+        <listOfUnits>
+          <unit kind="mole" exponent="1" scale="-3" multiplier="1"/>
+          <unit kind="gram" exponent="-1" scale="0" multiplier="1"/>
+          <unit kind="second" exponent="-1" scale="0" multiplier="3600"/>
+        </listOfUnits>
+      </unitDefinition>
+      <unitDefinition id="substance" name="Millimoles per gram (dry weight)">
+        <listOfUnits>
+          <unit kind="mole" exponent="1" scale="-3" multiplier="1"/>
+          <unit kind="gram" exponent="-1" scale="0" multiplier="1"/>
+        </listOfUnits>
+      </unitDefinition>
+      <unitDefinition id="time" name="Hour">
+        <listOfUnits>
+          <unit kind="second" exponent="1" scale="0" multiplier="3600"/>
+        </listOfUnits>
+      </unitDefinition>
+    </listOfUnitDefinitions>
+    <listOfCompartments>
+      <compartment id="c" name="cytosol" spatialDimensions="3" size="1e-06" units="litre" constant="true"/>
+      <compartment id="e" name="extracellular space" spatialDimensions="3" size="1" units="litre" constant="true"/>
+    </listOfCompartments>
+    <listOfSpecies>
+      <species id="M_aa_c" name="M_aa_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_aapre_c" name="M_aapre_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_accoa_c" name="M_accoa_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_adp_c" name="M_adp_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_atp_c" name="M_atp_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_co2_c" name="M_co2_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_for_c" name="M_for_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_glc__D_c" name="M_glc__D_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_lac_c" name="M_lac_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_nh4_c" name="M_nh4_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_o2_c" name="M_o2_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_aa_e" name="M_aa_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_co2_e" name="M_co2_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_for_e" name="M_for_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_glc__D_e" name="M_glc__D_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false">
+        <notes>
+          <body xmlns="http://www.w3.org/1999/xhtml"> main carbon source </body>
+        </notes>
+      </species>
+      <species id="M_lac_e" name="M_lac_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_nh4_e" name="M_nh4_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_o2_e" name="M_o2_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="M_rib_c" name="M_rib_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
+      <species id="Biomass" name="Biomass" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false">
+        <notes>
+          <body xmlns="http://www.w3.org/1999/xhtml"> this is a note </body>
+        </notes>
+      </species>
+    </listOfSpecies>
+    <listOfParameters>
+      <parameter id="default_lb" name="default_lb" value="-1000" units="mmol_per_gDW_per_hr" constant="true"/>
+      <parameter id="default_ub" name="default_ub" value="1000" units="mmol_per_gDW_per_hr" constant="true"/>
+      <parameter id="zero_bound" name="zero_bound" value="0" units="mmol_per_gDW_per_hr" constant="true"/>
+      <parameter id="R_EX_glc__D_e_lb" name="R_EX_glc__D_e_lb" value="-10" units="mmol_per_gDW_per_hr" constant="true"/>
+      <parameter id="R_ATPM_lb" name="R_ATPM_lb" value="3.15" units="mmol_per_gDW_per_hr" constant="true"/>
+    </listOfParameters>
+    <listOfReactions>
+      <reaction id="R_EX_aa" name="R_EX_aa" reversible="true" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_aa_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_co2" name="R_EX_co2" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_co2_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_for" name="R_EX_for" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_for_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_glc__D" name="R_EX_glc__D" reversible="true" fbc:lowerFluxBound="R_EX_glc__D_e_lb" fbc:upperFluxBound="default_ub">
+        <notes>
+          <body xmlns="http://www.w3.org/1999/xhtml"> carbon source transporter </body>
+        </notes>
+        <listOfReactants>
+          <speciesReference species="M_glc__D_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_lac" name="R_EX_lac" reversible="true" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_lac_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_nh4" name="R_EX_nh4" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_nh4_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_EX_o2" name="R_EX_o2" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_o2_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_DM_Biomass" name="R_DM_Biomass" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="Biomass" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+      </reaction>
+      <reaction id="R_aa_imp" name="R_aa_imp" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_aa_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:and>
+            <fbc:geneProductRef fbc:geneProduct="G_b2215"/>
+            <fbc:geneProductRef fbc:geneProduct="G_b2836"/>
+          </fbc:and>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_co2_tex" name="R_co2_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_co2_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_co2_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+      </reaction>
+      <reaction id="R_for_tex" name="R_for_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_for_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_for_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:or>
+            <fbc:geneProductRef fbc:geneProduct="G_b1377"/>
+            <fbc:geneProductRef fbc:geneProduct="G_b0929"/>
+          </fbc:or>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_for_texi" name="R_for_texi" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_for_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_for_e" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+      </reaction>
+      <reaction id="R_glc__D_tex" name="R_glc__D_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_glc__D_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:or>
+            <fbc:geneProductRef fbc:geneProduct="G_b1377"/>
+            <fbc:geneProductRef fbc:geneProduct="G_b0929"/>
+          </fbc:or>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_lac_tex" name="R_lac_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_lac_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_lac_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:or>
+            <fbc:and>
+              <fbc:geneProductRef fbc:geneProduct="G_b3670"/>
+              <fbc:geneProductRef fbc:geneProduct="G_b3671"/>
+            </fbc:and>
+            <fbc:and>
+              <fbc:geneProductRef fbc:geneProduct="G_b0077"/>
+              <fbc:geneProductRef fbc:geneProduct="G_b0078"/>
+            </fbc:and>
+          </fbc:or>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_nh4_imp" name="R_nh4_imp" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_nh4_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b1377"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_o2_tex" name="R_o2_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_o2_e" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_o2_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b2215"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mr2" name="R_mr2" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b0929"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_ppp1" name="R_ppp1" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_rib_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b0241"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_ppp2" name="R_ppp2" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_rib_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4034"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mrOx" name="R_mrOx" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_accoa_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_adp_c" stoichiometry="4" constant="true"/>
+          <speciesReference species="M_o2_c" stoichiometry="2" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_atp_c" stoichiometry="4" constant="true"/>
+          <speciesReference species="M_co2_c" stoichiometry="2" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4033"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mrFerm" name="R_mrFerm" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_accoa_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_adp_c" stoichiometry="2" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_atp_c" stoichiometry="2" constant="true"/>
+          <speciesReference species="M_for_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4032"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mr5" name="R_mr5" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_aapre_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4035"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mr7" name="R_mr7" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_aapre_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4036"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mr9" name="R_mr9" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_lac_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b4213"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_mr11" name="R_mr11" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/>
+          <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b2835"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_ATPM" name="R_ATPM" reversible="false" fbc:lowerFluxBound="R_ATPM_lb" fbc:upperFluxBound="default_ub">
+        <listOfReactants>
+          <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+        <fbc:geneProductAssociation>
+          <fbc:geneProductRef fbc:geneProduct="G_b2836"/>
+        </fbc:geneProductAssociation>
+      </reaction>
+      <reaction id="R_Biomass_core" name="R_Biomass_core" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub">
+        <notes>
+          <body xmlns="http://www.w3.org/1999/xhtml"> biomass reaction </body>
+        </notes>
+        <listOfReactants>
+          <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/>
+          <speciesReference species="M_atp_c" stoichiometry="3" constant="true"/>
+        </listOfReactants>
+        <listOfProducts>
+          <speciesReference species="M_adp_c" stoichiometry="3" constant="true"/>
+          <speciesReference species="Biomass" stoichiometry="1" constant="true"/>
+        </listOfProducts>
+      </reaction>
+    </listOfReactions>
+    <fbc:listOfObjectives fbc:activeObjective="obj">
+      <fbc:objective fbc:id="obj" fbc:type="maximize">
+        <fbc:listOfFluxObjectives>
+          <fbc:fluxObjective fbc:reaction="R_Biomass_core" fbc:coefficient="1"/>
+        </fbc:listOfFluxObjectives>
+      </fbc:objective>
+    </fbc:listOfObjectives>
+    <fbc:listOfGeneProducts>
+      <fbc:geneProduct metaid="G_b1377" fbc:id="G_b1377" fbc:label="b1377">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b1377">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P77747"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0004608"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG13375"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/946313"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16129338"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b2215" fbc:id="G_b2215" fbc:label="b2215">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b2215">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P06996"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0007321"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10670"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/946716"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130152"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b0929" fbc:id="G_b0929" fbc:label="b0929">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b0929">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P02931"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0003153"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10671"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/945554"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16128896"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b0241" fbc:id="G_b0241" fbc:label="b0241">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b0241">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P02932"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0000823"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10729"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/944926"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16128227"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4034" fbc:id="G_b4034" fbc:label="b4034">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4034">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P0AEX9"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013200"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10554"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948538"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131860"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4033" fbc:id="G_b4033" fbc:label="b4033">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4033">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P02916"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013195"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10555"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948532"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131859"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4032" fbc:id="G_b4032" fbc:label="b4032">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4032">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P68183"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013193"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10556"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948530"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131858"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4035" fbc:id="G_b4035" fbc:label="b4035">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4035">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P68187"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013216"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10558"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948537"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131861"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4036" fbc:id="G_b4036" fbc:label="b4036">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4036">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P02943"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013218"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10528"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948548"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131862"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b4213" fbc:id="G_b4213" fbc:label="b4213">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b4213">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P08331"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013782"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10160"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/948729"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16132035"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b2835" fbc:id="G_b2835" fbc:label="b2835">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b2835">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P39196"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0009300"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG12455"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/947317"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130739"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b2836" fbc:id="G_b2836" fbc:label="b2836">
+        <annotation>
+          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
+            <rdf:Description rdf:about="#G_b2836">
+              <bqbiol:is>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/uniprot/P31119"/>
+                </rdf:Bag>
+              </bqbiol:is>
+              <bqbiol:isEncodedBy>
+                <rdf:Bag>
+                  <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0009302"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ecogene/EG11679"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigene/947315"/>
+                  <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130740"/>
+                </rdf:Bag>
+              </bqbiol:isEncodedBy>
+            </rdf:Description>
+          </rdf:RDF>
+        </annotation>
+      </fbc:geneProduct>
+      <fbc:geneProduct metaid="G_b3670" fbc:id="G_b3670" fbc:label="b3670"/>
+      <fbc:geneProduct metaid="G_b3671" fbc:id="G_b3671" fbc:label="b3671"/>
+      <fbc:geneProduct metaid="G_b0077" fbc:id="G_b0077" fbc:label="b0077"/>
+      <fbc:geneProduct metaid="G_b0078" fbc:id="G_b0078" fbc:label="b0078"/>
+    </fbc:listOfGeneProducts>
+    <groups:listOfGroups>
+      <groups:group groups:id="g1" groups:name="Carbon metabolism" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_mr2"/>
+          <groups:member groups:idRef="R_mr5"/>
+          <groups:member groups:idRef="R_mr7"/>
+          <groups:member groups:idRef="R_mr9"/>
+          <groups:member groups:idRef="R_mr11"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g2" groups:name="Fermentation" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_mrFerm"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g3" groups:name="Maintenance" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_ATPM"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g4" groups:name="Oxidative Phosphorylation" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_mrOx"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g5" groups:name="Glucolysis" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_mr2"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g6" groups:name="Pentose Phosphate Pathway" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_ppp1"/>
+          <groups:member groups:idRef="R_ppp2"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g7" groups:name="Transporter" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_co2_tex"/>
+          <groups:member groups:idRef="R_glc__D_tex"/>
+          <groups:member groups:idRef="R_lac_tex"/>
+          <groups:member groups:idRef="R_o2_tex"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g8" groups:name="Transport, Exporter" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_for_texi"/>
+        </groups:listOfMembers>
+      </groups:group>
+      <groups:group groups:id="g9" groups:name="Transport, Importer" groups:kind="partonomy">
+        <groups:listOfMembers>
+          <groups:member groups:idRef="R_aa_imp"/>
+          <groups:member groups:idRef="R_nh4_imp"/>
+        </groups:listOfMembers>
+      </groups:group>
+    </groups:listOfGroups>
+  </model>
+</sbml>
diff --git a/sample_data/data/Deniz_model_fba_nrp2.xlsx b/sample_data/data/Deniz_model_fba_nrp2.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..24fcb71c4eff84d47b7f7e513c08bb9dc728d027
GIT binary patch
literal 14046
zcmWIWW@Zs#U}NB5U|>*W@NjfGRmjM|uz`(%L70JqDcU(duOv0EBtE3FAhkGFuOc@`
zVG#?H#sUT=hEXu0L*UHR(|Lys1YEyQ{l!0VduY-E{!r0ISNYRoE59(`sc_Pfx&29k
ztNwmx<%(570!%A&l2qsI{GRgU&X@M?DD~nGE7qKhG=IH;#V2v8(d0R`J@J>SrzHru
zapm$p$>BWF)_l7AU&TuX;a6reHfynbKVT^)dWFgO^??h1nYFr1cYkq~FPhQC`5|t3
z@p0~KrWq63+?siR`3H2(<U9GfNp8J|O-9Pe$E!AcJT<exZ?zMP($iw)`khO%+k32%
zdBaXU6iqa^_v*r%Ke-#;RWH6+p{gj!DIe$RD^auX=k%|a{j-<f%$q3Y6+LYgLuPlR
zukeWrl>$QA4FRup)<x=0xmoc3L;ukek1yF8dVH=6{Nd1dZ)QQve9q%%XBOP~C+xn!
zhCy!qaruiHo1Z%*MPwZauAdfIGQU{W=EV00nwI{2AA+mJ*`J;|5#2szgB;)dLuyX5
z-(8s~@MA%O^rGCvEu~9J?5f^ec(R}6=A*NZ|EGQ6WnlRKpP2zQr)~{3Q2xTez~IBg
zz`)JG!4zMVnp3Q=2O>vv=!njkaL=!q<m-RffT!)f$Dj5S->=@CAmHR(?&z^wF{G}+
z^A7*k-o+}?clWEy2!u#AGrQOCt^0H8_FbFaS0@y|Ju+F-EhJ*Cysdm~+w@<BrR-X2
zMbq30*E~ETSa$X6)o(N1y(%@z_V#P7-mzk#_sRRpZkIp0Y!o=J7cRvqyi}%5%ywpD
z)G^=4dr3dmF<x~Fe!I49!*=rpuO^?LU4MPrvX{K}9hN?CF3l7@QJj>1b6w4e9L29c
zr9y)<EHgJG%`37}s*F#`Iq=zK_I}~NHM)LtP6R2L2>mcR?|xi$!PokzEhZmN&#gRS
z&UtwCi4P~6y5(2gW5kw8Y$F<~1Q;0@-mx$+NHK6QR^;eI@=1AqQFc;(em1B=gXI}U
zjRg#h@W30Pj6GBCX0;dyw7%!AWj|TlyUSiT_3^zahwg2=&CX#Z73r&D{P*KqYmT*|
z)4sUv;q+;n^Z6;y$%j9#ZoSA}kuxV$saIIAQ8(u1ji}D;pFiJi?wGJexAe7+h9N^&
za$R+CMPrz-ywl|`5*xc*Re1vcYp9B`S@mvweXwYG(Z0(^mab1--`%~=G0k(+^ndHy
zPNdJexH9C>e6Mfz{I=Hr*S}r0O7p=<(f6_uyOI<yX7E4NuG}ZLX~Q?u2|}CS&gg92
z!?!t7=-K6;+)@m)9O|}D2;8}u^WYu*Sv<VD{bgM#Yn;E`JQbOuIHM$Wh6&5&c%il*
zng>m$Z=CX2D&@vnnG<><C(2T1yyfqi^Ec?HIRm!z)V-->{%U3hhL0Q!4EziX3>7*0
z$SG+}=<U2E76P^Lng1C-`t|H=c5*wrbn8T;#TvIK+qmT!9OO#4He<=+jDNMKzw|C~
zU!0Q`eYfm)S<R|fAwU0|UY5H>U5oA5?ug^*%hp|7_dxZvT+-j)S(z-m%sehlUGh-2
z_p7+oi+H_%e;)st*?rvfdA}4xiSwH4LEi+OoC7aU<4NDMTy{-J%odkJ0X7R3icgv1
z?7K2YI%Vdv_NYTvwcP@p{RtmdC9$5ZFx<9fa<qV>v{K{0fX#I`7H>W3{vb+8?)W{8
zirdUbW|{iz^I5R#n%Kps&c|6J%@e+{?Y`x9;Ci#X*}{$A%nx2V8LF$zB443;o<S<t
zbN-Ef-<FTk;yHe`9}HF4Ie#4q;o)D?^4&A+9^+~2ro_+NFB*7cU-Du(_giIF^@FsR
z;?G61U%U<5CH3-m{*us%StV-~o6mTwWlFxb<N0Y^Ypi(8PBM~t^^GqZ*DmgEp0mC+
z>{3@xZ%pHa?;kc?{Ooi()Y?&^+b!s#pl9$s&kGOFt-F8qPRrbDG9v5lUZ{6d(@+t6
z!JX4OO++w~|GLYyTN<+>-0!m+$bA(#v7pwAN5fZm$t2<0#X(=qx5#kc2o&<X;Irk4
ziSxha9M6R{jRz#758eL%UY2=}>z3)ywu(so*?(r8f=&Hobq+C)MR%TlTH4iCdtX%P
z(D(H(4{bEI=I?2J9_9APdZysBx}+cT@^`Mx)q7*|gYg)LzLte#>fXzZO?{>8O?8{T
zOwIYGeWS-MOfGZJtlj+!f0duDd)sal5xOVZZGQXWq$>fp?`&N6<ow2Wm3$IkH_7i(
z>zTKFt995ZsT`g|kMG1!vAyCTvD=S9+EekSaO}j=HLE`<>9`tSi{Go`D}GJMq(<G7
z_jYNG^2R+^4DMd}SYyuLWH9H&LdpFrFEE{7d3~3rWkh)4is!3XwCi{8Qd25z-l4P6
zD1V=Lm+6JZO;*+q&hSON=id@C$+TUF%m2tnJ{xPJ)LVc0vflW;YgkpR@O01q<g>@u
z%{hE~^WEU8MSJhEW^$cKyXt$3Yhz|BSAX7<s|w5IggP7RHzw=V7?<!EZ;w69>2#Vy
z=C)Cg`sSkR=QrCFPQ7)-Tl}y5*Sb^BGyb<|O*0iNQuX-A!T+9jt&ovJm5-t0fpR~y
z!sa7e8W(Lo;(hqw!c3LU=Ms6_w#3S(XTH<xPnr7js`~2OhGmzhoDkHSsO5CoBIs?A
zdG(An+k~wb?p>*J(RbOOeb#?h_nyAZpSR~!`1kVDD_Q<dxpFOY)m$fAQJ1OG;{Tbj
z<?Cx3FZ|19Wnfq#&cGmn$k)Xgsi`H!`e4cgR8`EG;@f}AK%njY)L-&C_l4EfSUNaX
zGj-pH-umkJO$)w_9gnY=$**4Zf3L9cjj1~hc(q*joROYpvwo)C+>l<rLlG-y%N$Q^
znV7LdFJ<%DkLTrAC-h7#U85PXR(j%N^Tdyr-@KmwGH~|XwJil49KB}?pT^7<OsZ5B
z*}O2%l{w!lSERTgd)tw1ZW|7<ubg#3!>coC)4_D7w0DUwHcg4qF}<&Safk7dj#-u~
z?uTvKDn9Gc_Xi@zyKAKj)<!wxr0CwA-JZ0h;@hU&Es3+Ge%T%6eEcHif%8MDm`gvN
zo6nr`#4S2@m%xc9F`sjP3mYl+#CvZ4d%v|)IO&LE8FP*M#q(1|_}A<F;*+isniiLM
zw`^vPez(m3SYNm1=SMT-T)IuTE}1MmSg|99<?zX_OFt{-shvMvUty-J;<Mv5mt^3x
zAJKExJ~mX_ywd!FzGmOI!&0SncQW?Aj@#ofk3%5rN>lkc2c9PiVvE)l?^&i<@vDD%
zS+>I@?az;Y9g45yuQ$&)ye8tsukAW9tnIAlycE5*t-rh4Ze8o0Fur+AcV;VT-<x)*
zs6bOh_>vy)byI_f(waBtni)u1SK7{-XwvexBgpDVhVq)|lYAY5_oJ6zRn7IcGEVTB
z$5@rPB=L^|%dy9b3v5$987RM$Twt3la_>IdpG)Bkx7?2xTF70<5U}4Mx#8<Prdz>n
z{8tQKEq45!@!*an+mGpAW;*`9p|Wdt-J{x%@}PtTt~I7a-0r*O!1Gsh%6BGVy=p-P
zQMJT6=D;O(xvN=oZ|~1<OX#n)>723s>c>vo<H_pncVp_~^vvsb%`KaKKE7UU{_Rtt
zlV88Te0=Ng*MGOp`cr!P^3T7=?dx~W)0f|#YdZhh)x6@qEAJ=nUi&KR(!=X~e{bpD
zN&B|-_L+11r|g_(W}iA3^lIs{AM-W|w7v@W%`}R<XR>r_<by5lryC}>oblXoYE8D{
z_l>VlU9Ro9le=|axq!5X;l;;NQW=_Gxui6-&n(ZsVZ(J^?)tT~pmn9El<kDy80&Qg
z?tQ8E>E%6_yg8>VVsA)yUEIjeQNbd`6lA?Hbgj;hvj;cG872xeT-u>?L1L$~l;$xh
z!4DiJEGoSP<!g01xC~<j8iICc1xV~Pd1$b&x~tb`$I=;`(?yMh{SxPW@bQ{cP@|UO
zH!J>MsbjWM`HN4Q$ENo;xGzvls5p@7X#HS@0f$5EfA7X)CscbY{)!~`g6*Dk>=*03
zubY(oES_;Q1qkhafB*9Pl_z8;c^FRXfM{+{bqwiv*~BpA&~~QQiQd+G8qV7sbt}6O
zbcXLV*G$=E2JZ{hRNooc2^*>VZeCpV_!;Zjos(bw+h{HGgwdeG+~e?x4_d2NS+J-j
zPw1Ey_2gF-|CRbly&+s`D?2LML9#oHRizkz-!igncG+@6DErxxVkYAq&lgBkXPi(x
zY*>6=CMzm=LYQodo$0x&H#YhtPQSNzm$GWx8PDbvt0W!C%bv4AmYi79f7@uXv4=~y
zZeg0Am8$Z!3gcz!8;{L3Oi#IX{Ljplf+~xaPKz+PzI*o7yP47}YcGUmt`+>)&(e9h
z;M7F^ty<Y}i4iI%l%ldbpBL?2`Le$7Ab*1``_VAw=Mx^!kY5|3l547@`&{u{pe%3Q
z^46G}Uyfe>tMM`2@0RLi{>K+)%sY0c!sJ=c8xy;dkmm;aKNVhQZB+F;>-jw8h}8OH
zUsSH`Fn!kZX3LscKkFn<tG)h~l>TzR>3?vG8=T-bX?pnhfA!VzI)6qx?1ZPU_Qp`7
z3&!C=8mD~DH*EC&#C>4VGA}OApQ@@qcN)aGWhHrK33jBfSu$bT{CK9vFK*v_G5xuC
zs(AX6Wz%keTHu(y-GE-V#g@zr47b^F7SN{P0(!D{|7-(+<L{^blGV9i$(GUN6t+7t
z@OxNj?HpNS7HzX-+dfBx{(UEVB02mN>spqcji&o&e*BaEvmj`a%z=o7yPMqVJUB~Y
zt}nVR_T_p1soB9vvahq$O4(U!S-s`M=G*J`-+Gh7{eVNrD*D2;op$_8r=tVT{Myyd
z=P#8jQv4%(TjN{a4OJyUMjeL@xJ86zZH0@BTNloDE6^~#ul?eVWRinX`_J29U%q(-
z*Kqk6Zod6p?)obCBW@lK<J>h=yPoUc_LJT=G2*+XmxQ`{vwzR~H&SVL_IEE~F~9Zd
z*iR3>bC&m~@8=L>d~ANH_U~kaKp_c>j)jf+D}E$vy1wic-G4Amz9rTCMnSCF{q9{0
z>QAQx+O;RXO5LpW@sz&jE8BC6cZR!Oy}YYu@rD||{}-=r61(wmp;poY%@@g2rWZ@C
znYOsDj?>~+dA8As!o^?nEGiBfC<^I#JZLuHa4v2z^@zN%oJI6+ddefA#)G{->S~vU
zFK*Z`xhYZXTGT(Wt=WY^1p!$ZH{y1^|Mf%k$hOiQvlACK2YvdrmF0Bcq8VNC{E0iJ
zoj9T&m?U8uB4OS8t-|eCvHj^u2HnA4W(l#XY)Yy%`E$dr?-0Kt+2kvwn4v%6%KUe|
zv66c)#_ciu+d1*0kbC@=>tBTJXHBa!h&x&jPJLjaXObgdvx0!@`lPgY!7Iz83|3YN
zI{C)jTqbe*-oEA#Rv)SIv}cX^{|=PiFHP2VkNo)juc$3+K*twnKB@hucovzTU80pg
zf7|6%(r%2NXBIp<wIKX3SAsxdY;x(Uh=V6X3K!LR<W9WexWeD0TIq9Vng52V3M=;R
zT6{9G{JItYlC!o<n<^Ix`!YRT`r4<KuSeIZF+pUV!n1;i3mce<)OSA6->SMMO=IVm
z3kml^v$-Xn?$X|M_qpezYy93)*QWEhWO+xNG+E|&<?N+6+uTI?yGOUbD&&d!Q$N4g
zvN>Yc+r}LJf86l;h4$I)=t7-`$CwxxyjXE&H)BM06KVYpY7f2dtW?$1NSShc!MSy>
zs<Y3iMjV`R>s)fk+4}daQ<emoUG!);bF}8U-0zByC1JdlO&f!@OY*AOsb1iUJykaS
zqWt~h_rj{zK5l!`o2R&VeZtGjpB}d#^_;zTEu)O1Q?_xG_4TDak%7&R$`)B?eDq%0
z`-m_8Y~Rgu4EpW!Lk>ILpKo|H)!W@EOD^%ns!2@SqGUw2<h4j->^S@{SJ%3Fq2@;p
zzrxM-CG#$<a&}Raei_HEq3X3ne|MO4oXLydGDl)2Y(A)c#Cq2Wp0C!bs;w8p!t<I^
z%)Zs++b429e&n_5`+otoj+u>%-<W@B<GO!6$YAfLL()1`>N#btUr(oAYSuCSRX%IY
z^F+Dh_tg5Y-P7tbkNaffzsJ{ze_r_NpINd$@=JDVsJ<(B-zBlaXcyO|QjeQOhEJX^
zZ<+iz-$zD&pCj`j#w?a8Uq0$=x%XW-e!~;xi+sXWO5%QUiOjyu2_;$od!!P7S>NZ=
zdZV>D=FTzklQkY+#htnG5*N>o-&A7y>d{Nhg^${g)m~kGUZH&J;a5C&zkl>ReO))A
zap%!GwdS4+yha~Ar@T2Yf2?o5JF~J;mwKScuRsAmP0a(lV!nBJ9XV*VR`1Z?LmL>s
z@gpU*a~{6Bg`PT|TQ+N+(ht|z6lxR_q@m|=Mw@NZnw}{lT0Fi&lh+1m#Rhdx4*u+w
zGnMB{&y+VglN`Nd9kX($HmquRbkyOZk+pNM^j{-u>Axu}gw#8Pl2ugQ3PgW7Xg~bM
z<g0iqZbH-sZ^3U;i*IrVFXAuZxgk~C5)P>dz?qwf1TXeXjje6jGqq^~3CosDm=Yf}
zX@0k}k+P|w>Q)YwOj4?vptP#2AQlFOa{>$uypX;?aY<!PYB4CmpNWXgzilQ`E5G5t
z14s3oMf~1fDL3?9sW5Ss9BoKB(2&Z%#pR}pX3&BcA@4T)-{&2)X{zeR*(JHp*rS)6
zI9zOB!T<8+>~(1``c6#avMX;gzVFx~zGz8}ZF$}9lXtH;u06Eph);o0dD)WEdpdQ0
z60fb2RlY9mB*MgT@7&zpptDy#-%4`Mx-+k{t)L>9GcuXgcjvcTZbC8Js)Ek$x$s?h
z{jn+5Vu?}W@k~L<KMQZ~Xk8(_=0%s$sxr3Ij-`gR+n+f-5@lW$EAWu1)KSU#%E?5*
z%fA!m&JqjSvM4CM;dSS#MXZ$`=AVuD#o1LH>Lh~%s_QDwfAiZXp!rE<=VGgxb4kax
zm&UCY6HPiT+ERSK_08;Z)e^Od%{d`@E}V709;=)C*Zw~*WVhY^{vs*6?I-NsKX_cx
zJ@dM<e%jOr?W_zZe-!_k*zIp~ef^tNRacU()wz|=aP(hc$aS#Ag=-VD7;g#xanp^;
zdIpQNJxkMDvrm<6yM4x(^J@QU&6eu|o3wAd7xk&0y0u62{yDuH(_iT>&d%F=F;Du&
ziq<8)m-K#o6RTFcAs}?>hTX~}53Rc!XB_+&?D9!QYDN9No2CLWd`l0po$RqTtkV6c
z$MINy{sk^K@3Tp@hJGt=ICj0-^8Ng|%{t!AD!!L>X0ZgOe9E}WzkH91Y~x%8X~*T?
zPVi(zZc^8<Kd2xhvEydsw7JVqKDpDcHd)2<?~hY|{ZuynzrcG;`fbuSp-nAUq+fY)
zoZBATEvY<}N#ASNy%+JSQa|E2moDhjOH)0XebDf7om^T^degRso-?vr86_pWSng?E
z4CnAa{QZUcaVfsGLnjwH6em;`tucL87+tmafGca_n<(ke)urqIm0ex&t!dXQ-KD2D
z)V18bXdgBIM%%Zm5@(b86O{|Del83Qe=A<`?hON1t-bWaWr@3&p6sym@?Usx%|W*t
zQtZ<53o@Txh!BewpW^)5G;EWN&&#zZ7c5Z-%w9J;Ls{i7&y=m(L@zk5Q7OIp`Eb#Z
zjq5!gu?k8&{rse2&3yS-k9msy4~~4iT3K>vV^nG8!es8yLmEe3vxZIIe_Zp+@g0wk
zK5;Coy>U%jskm6JKBwDO`FUS}G5eYS%9{iK{@rJ3x=!L);jeR7({HX0o!~EIZob5L
zYIyg#jVA53e_LYydP}qg*{zUxam_>Kz31b&ClmH@oSDmSrF+vOGc5Yl1CQ?=I!tLd
zWm~RYuXcHvJ%8eZzghzCCxuj;zH;{8(>s<n|DC*ctyL8L6}bPB^+KK_cPwr8Ur(Q{
za51}K`@QW23XSnwrk1?>Q@YjWyx#gx7kvMwzH$>5+8w!PPs!sKNtKl|)9YTu|FPXw
zQ+M?0<vUXylbSMa8SPkb>fVicf4(lvf7c^5ZQn{Ihw3x7+`|8JWq#z@ePlNGDtL8h
zrfJz{3l9Cs8I_{XKXz3AIi$VnJ!|9MH(8%PhFqLwaH>6mS5WG^(}V?;M>z63TFQSY
zCw&ABA7Zw1Wt(s7gflTPTxMlp5P=px8Hq)yDZwR0nR)5pGN>fPH~+Q)&tCo;^#V1w
z8dUr)Z`J6!arf1_7l{YE*07cdr_Ag#jgX)ELht2z`OkqV8|ExN>Hh2G#Qx8-x8~i{
z{;^=q$w`|}nj|JHSvw^%_2lQve}8BtGPbUC$>vQt$>vlvx&Ds<KcnQ;Srb%w7wz`D
ztRQ_s$!86-^UPaSx)bdFYVn?HUg5;tu**07^2^j!twx-aw;9h+oi5J#x~J;pf*b7T
z)=#RD3$Z(1b8^3}t===uXP--?Z@Rw@`ls>RF<H>bKH|)c8Jx-oZU=d8`}9rU?2Od5
zEbB~Wo|3vgEB&9}@7G-QI`{t9kJweBMtzf+WD;f7Ic(<?oSS>}Lv*U=^#e=43f_K{
z^Ltn0cGsFN{gyrVd0A(C-u6o6(uJGL7n`{U+I^4I*)jLy3iYjy6F5W{|9p4L^^?4!
zRKS#Np%S)-4tp=RDOEhW?Fygm+z%38jh)=TPWtDka;%pt<L|Qbv6ro`zbdH<&#Scv
zji3Eydd@B(o4y;TQu-Snxc7)zS65WXi64F2aq-#eviURjH19X_HD9T;tvun1w0h8m
zPrF3!R9sL0Bk|fQ$^9?WTjS2FGxt8(t<Umyjd<PR6H;qbq(d7+1GJrYEn3QW@jxT%
z!dUK{@8#E$FQqq1i%-0sw029?>*L#(Kd`b;eCaabI`fZkk0P@dqSoCy&%d9(+`nHx
zv?YJ{*YltFbLUm{@~&UC(C+4>|L+$I&XrrS`9$vRc%dnG{B~DxEtx7Qq-Zaa|NiRA
zOOKjjc3525%qr{1qRiu)w$r?kt@$FSwY-VtT)`!K?H2`Go%(4Hu42)r7Y~XH?DJ!1
zU^u1#S_l9h=`P7g%}vz@lZK$W@J-~|ZfSFo{o=pud5X*bm_F`Tz4zdJ&g8o)DRZaD
zY~A)IFwEF!(jIAHCRNL?K^jYzPIt<;opQ;6nRoA(eomDwFXb<)pS_{H_NmJ39IvP{
z-fs7QdzUZ&KCiCobIHd`duLR+&)_~ZX<GcgfKRq_dTz{6xjd~t+d{*n#@}N~k6<ci
z-cN3wKi;?J*A)rop5!=wgZZaR&cbQ(o|d<@#Ex>bP26!H&7$$XTF-HhL(22Tj?2h4
z-<u((y)<a8gz}^xaZO19)wM>7%5Qk5dev92+~fbyaH11$kH-D*RU1_oZhU-rlT%0Q
zi=cP1inl%`&NH-@dsKbtyM~ZZUB*w<zt6?D>x*3sQ*(LB7U`U~;J?vMpM;u-i^ZQ_
z9?ZI=z|kC4y)>SAMa=Q9um48v-*#y^V}?o7)MeXlSiPQ_HzAQX;MdiKyTjjyo))`z
zAwG8VPT5uct=AWX)Ng2$=h^kZm3zITrd`b8_PJ@x(_dd-AQB&a_`3JoxPa|nvIAT^
zi*~f>wQkKS-+4<k<<N^h;d?yIj(aC*o)4VByZTK~uup`N`O~PLcSd#$4NO5Qo{vr*
z<?+A3v1v!QhUJ4Jw^c852<5j4sU_?P(d6|C{juJz@!vA#u5AWJOQyyqbVf)AeXB@1
za?)qpAMFKK%@y?L*;fDi@bYu}e%tylFMb}5-?Ve9mUkYzYH;wotNro+|Na%f7r+1C
zhgToXUtUrFc<9LfKR-`T-~a!k)z=M=?uhE|tNrxp<z(^uwO^n6_sib8Eh9VQgJS2#
z59=N#zgO!{Y~=g>k<otc5d)_qlN<zJl&yGo`UF#4w8E=Hr5oNJx-AoNjCGIA{_LB(
z<Uj6I>6^LUZ6oWQ<eoB#a;pccW}W`<p<tfR-N~PKcJA)HFfsd`p4S?N@6U}Fgg*+p
z*}Eb28E=<R=Q4|fmxA|ibncN|z1z9t>2+J9M?M=mzq}Jz-`sIo)ww7^HAruZb5h6Y
zL#YZaw?3V{m%IM(itMYlH7Ux8(F(5&>N-|FJ$5=nFYrx}rHi4d?7r@lCq_l}2i?`y
z>8rR@b!mS7m8{d^{miQUtCZt@E}qAHzvW_&m`(C7dU0uIw}kOT+p-(I0XD_Ad?uYZ
zQm4kJ=sK~y*;Blx|E9Hzt+PzPCc``1Ikz49o2Imb^-0#ODe4`+QqRe5+|x62ue^9k
z(}wQUTQ=KnY(3j)Y;E;<<<widC4x(Oj!9H+w=0ggkZ64R`Ob^sN(obC-*~OjK5;Je
z>zo9})JdzduW1%d=g?05{&7m~hV=ZxrV25QPQm+2eoDFA^@_E4Iwz!RZXciT15>7_
z@=v-iYhGu4VK8a;e$Usv9FghMtHj>b#1?4R<!?To5P#41?LHRCnk-3$Z`;3}6$s33
zvPo>?;NlU#+teZYFUDo=>ZqJ|Q)b_oZN|?m;uf}7c=D6pqvty}ssAZIC0EW^%*NUu
z{qgq{tJxZ<&s`cfbAFfC$*#(ncjrLCq&3CYDnpCouKxJ{dsW)yIJ@<!=dx#hjym@B
z+gHb|nR{v)PpQ5C^kSa>o{dk96{oyA&3bgVQ+X}Ro{K%~1}km{?o!{G#duEX$1Kws
zI(Mch>pm`2vsX8|)qjB}$~pD!fjM11UqdR4)fOZ#X)o0loqu8NZ@<qK{k@m{(s!=e
zHESg|>!hC*nlZi3)4i6xh_ZcuZ|#-WbuU87Bd4=z+`KH2=#wTLSSk@1&*vl(m);V0
zb7sWFl8B1|ucj}Iaw~kwWsoZ>TJiMT1J}Jz+Z}H?wx!8Td7dn~e9O!|J?ps6&2+1p
zY`k(NV?dI=u|j`I@XcL?m06{ktM<As(N*p#+1zVz$)HL6p^o+0X}=?n?V8zs#mIkW
z(oX3j<5#`>g4wQ0>N^_M)PgS@vCjLsHH%}rma<9qPvI95hp$(#?Nq)JExsstN13GO
zyu2UfiPI#-H?_M=v5J}OcZ$t$;*9DkM|9(L>!<RXT$EXqs~z4TU>EgxmV>tZ>_UMO
z>k}152bDA1?n(sC^OJ9U7`(3F->RecW!I?OlsU9lzPEobv(<BR!6gw9`r7P$oeka1
zMX$~pHqPE4I`Qex@<$1$GOq9Gx@0+}PuZ$Fpls2MlzE3tJJ+4L=&vm=;eT`1!G*8&
zY-X%t{<hcuuDSfxQ^jnV&(fD2)p~WMWb(^)>nFUcKRI97q_s5fub*zwx@^6jd_q?%
zz4ko3QrpdZ&_H+o86)4wJ${F~Hg68Ry~S9EgUA1h*^k^(Hw)X3>(uQ}%smrT>Y(%5
zYwnL<r|#^zn#Nw<Xj4^CZ?Qb+MSxTObY+cSm!wZjF-z#aGdZ%C<Nh8?b>07UnmO!u
zy>94EkxJYz5Yim9P;lGo)-s8((|<R_yeie=e~>USFoy5)>tBp9i5FbUGS?r!uJ`Wo
zQ{A(zg5BLd@n^)hKK%7)U(C&#!uNs-bAN9=wIDLLEUPtbtE~phUcbc`-dwY4jawW2
zp111mhKn*bANDHq^4+?c7x3DgYirT=XpSt^)VjryE7+%cS+CeG{dd7%1?}X7r<%1X
z`xw4w?cN3%Mn$iFpYxs+`^&_@aGnk401UVeo-_Gm-)+zm57u9NP4D`0{LQ2b*(USO
zjSeia)Hagko~RQZWF}JkJ?NYf^OFk+45yc<eo)T*H_JwTt5Wp_zgf!`c<L`>kxKNs
znZxz#@oz1u;72z3Uh90AS#28^pTB&5z8ZV-_H}F@SU7vNGv3zDHh$*9J8Sp6%!LOh
zpNcqZTX&1Mcr9yOnPb#OlfHJJ;+q$Y6}R^r_=I{gax0ffN!7PS1l&pfyKdUr-Iumh
zuq@ws_uVZIiMvWkx>LU_=kw|m`}umA;k5Ol-)en+#Y|XyRQ>4J65q>j@26f0ig@hv
zwAv%>`K<T1&qo~Fv18)9-}M}mzszji%fnpbyi{IWQ}Ry4OX)W+n>Re<6|0+mwtSP$
zi}^n_VtLa}t4Q%5&onvlt2;wXe^SPk_`c0gHL5!=|BSbqJ4HxcW|xxKs-}G^KXV*z
zmRx%Ie0fWA_4COJ$F~~ZF8I)unWSP7pwMK%;l8=uQX}+2<Slp4|L+zYbXVDJc>j0!
zzg3ezgnM7RbMVfJ_)T9-?}_fwQVpqD{>Qxd2UAgS^&Q!y)sEVgl~E1C8bLbCrni|K
z(nxtUJ3~@WI;%(Cx_XDg#!aRc`zJNJbl$Mc|6+QdwUF=F$C85?`Ujie)E``<{Xibv
z90L<mCPi~K8wj{=7uwYDEb$1tVL+j>BJYn>Z{=-wYt@ha;@Ea&zrmb`KF|6})^4p`
zT)p<m-(L>jxr}@6_?Gj<sUClEvc>G_)2k8bIoHEy%(Do)RPp7_O|Ena$GeWD0XmaY
z0w&GiT**9Xy}-HjmWE=n<~R8p)0GS5I+=rdf;kn7Hs<^CMc7J5cAt=%&~Lgle$AtX
ztDB>>mrpcR=vlY@%(wru^Umt3On3Nwenu_-9A+2G=X+8t;+$+1H+*6BT*P^zC?_B6
z4KP8(5DtY?1myy6E?;J1U|5Jd%NT*0t#dBN=g&3}VEFLe^EcnL`v)Bw)ijoP-Ew<<
z+a&ky=H$Kv*5tI=Y8SV>-)9^-$*;0g>*MhokIKsn=Gndp^}cr?V&(2h#}ZvQOPs<t
z-JbI0`1eqQg-5LJh3>g3JLL<*G`lnQ^|Rh?xp|B2fS`y~^rd4v_a&U^ViVcE@ZG}0
zQ=_Dh%G%B1-Mp7Eu2+BMG}XW<#z#}#`<)W=3`=6Y8U3SpxU9-uc(&Y0{I_l5UfWEs
zJBKv$wmHjltgTYHwrT1+gGEy`0{7%t<^I@pbl3O7Q>-_oZG>#z`z0Rxx!S{vYw_B3
zu^mR%&uU`-P4Sf!Pk*`V-|QI{sciR_{BIQ3`WfL>Shj5vciDGG9sA^e#WRy8gnHKd
zZ+dRAH$KI2Qd*=yYNnWv`c0FHI6J+|B|Ck>yMOS{J+IYSe<R$L=Zb3Vk)uY-bL^yl
zKMy^0ZlAR9{l4tA+v^^(9(K`)$Z-5|OyS%0|7_OEpKqqtFoh?duQN?Kt84S>+wwIr
z&9j-myRn+eM}7Bwzpvqr3152gnzQEafwJ=saJ}*BkyEn^T^Mch(QC?=-Q2Ug-XH({
zPQFt9Sf$icPq&j&Yoa~%gOu1_Z?QW1RYZF2esFRE6H_jEa~)O?Xn45#gv^3tCbg`h
zQAL6qox(Fa6*KeiOX{rb_uVx4;c<W0m-6R&=SucYJ6xRB%*1$Rzt8)^tJe<PTV%Lx
zZ@ty?wXfY-udZX$)m<8%C7RG$+<)1#+wGI5cV7L;wLY(wKJd`fd$;M<(y)VyMdu}@
zxfo^63u*u6<=VQk=+u;eDPEf$7n+6Y%@pbiI{15ioncn+QjqYe!e*!3RULCUJ&%0&
zFaC##f#Lsuu%l_85ghh6_^)GRV3@$nz#zcD!I+Yt98i>BP^_O=PyosR3X51qm%1@9
zFwB{JFb`aJGJoT1d)Ip>Pq)n>%y5ZxRm4UH&)vq#mp2AP8o3<v`1WOT?v>JON$qz%
z{_TJ1zhp^XfA49lo|I<&BTtpYC98zJUVm?`xKp?Phmuj!2h|-9HVUY#6t1?f3wSLN
z)sr)E)0IQPidoOfG*xFxNiT4nR2#HPK>ERyV|#f*Q+e;P7H3@8yyw(s=}vdI_@v5<
ze!NF-6yE&!-%Pjp<<A$H)mg$D!s2gVi)5bTw^DvhXQR{6o6HigEd%D(T1%C9oy*lK
z%)217=;`EhQ|BztN<Q#V>#tMz1CFURx+|(%>T0q#Ox*IS;^xAK0>+#6UuIbEvW@wW
z+!_H{7w&n&3Hj2K70YxBuk3fKStd2n^JI!dx3k6fbIHFW<N4b}=1Bi$Q}z%#C%Ao@
z?4|Qryc6#$?wcpf&!halBe_=eS&4AEi^1PL=WnQQb!>|*nD()DuhozGn!ViY@%v;?
z89Xo9v!8DZmoN9fDJ`1668{?8Zo6H;-lK1Aqrb|RSMc<fZ<)t~9q%sx>U!Ng^6G={
z5&J6cas9npEhRr=-=4@P^FNi$OfyxDpFimzGbrnzw~sW-yP^Ua85pXW7#IYR(|B@z
zQR-+SM=V~gnH1=MSV5%qzV9D?-uJpoXBSV}=6&VONmt?R88ylUy9J{&7~Xz+zw~Wb
z_A<Lemfs)Gk-l_vpH{QKL0HyQMWLe_3`e(4$eO+IY?b7>zy#Ugrm0bmZF~tAmG<xP
zsPx@w_93N&_n7B~Ms-c^%DD^Y7MmWu@kuu1jN841dm=J|4WIX?b!Q*De^abL=(urA
zV{(&aTXaGMze|Y=mz~0E{@^l(gN9QA8jHjxcRa{n&m4R+p4}?-$(kkBSN2^td^bb4
zLZ9#5-l@$SGiR-sSz=gI*AySle?InVz^bxvnSgm*emfbTZ8;e}NjkUmVbk$N6J(lt
zStmc5xcF|`$u-eGkBSQ&dHZnv!s5mCE48oLvnzcF%#44tWRK6gch|kAs24gp3ALwe
zQZx3-dm&nxUHoT8{^IZAR}IhpKcdpJ-r4AVN3hl0z7BPVd5<=~pHvTC?f^=Mj7+)=
zxOU7S0v^J`vVSJP8`S{Rh5Qh05ONhW0|R8aGI)tHx<=H+%@8dR64YzJz0?`q9P~x4
z5HmoeJSTYI4|$0zx@Pp1b0BRH{GA7`8N7fFc^w_PDd@{PASQsw8A4c10WbDIH{~eW
ztTjkK1hb1_H3d9}jcyA1{2#;w5XmeFHU%*oh^`fV^d6)Mf*WKZTG7Yy(M>=fc7^B$
zk(>%(6Tri-NQ1EG2B3EwL0TcWLmg}YC^C`b1Kf>7HwC>d4KV>k?$*L;3b<vBZVGBc
x1f&~=kLWTmpf^j<b)y#XFr6TJg#nUoSb-nl&B_Ln<YVAtn8D1zU|<a50RYavoH_si

literal 0
HcmV?d00001

diff --git a/sample_prune_model.py b/sample_prune_model.py
new file mode 100644
index 0000000..134b319
--- /dev/null
+++ b/sample_prune_model.py
@@ -0,0 +1,50 @@
+# sample_reduce_model.py
+# sample script for network reduction of a SBML fbc model
+
+import sys
+import os
+# import logging
+
+import modelpruner
+
+
+def prune_model():
+
+    # activate logging
+    #    logfile = './log/nr.log'
+    #    logformat = '%(asctime)s %(levelname)s:%(name)s %(message)s'
+    #    logdir = os.path.dirname(logfile)
+    #    if not os.path.exists(logdir):
+    #        os.mkdir(logdir)
+    #    logging.basicConfig(filename=logfile, filemode='w',
+    #                        level=logging.INFO, format=logformat)
+    #    logging.info('Started')
+
+    # print some environment information
+    print('Python version:     {:d}.{:d}.{:d}'.format(sys.version_info.major,
+                                                      sys.version_info.minor,
+                                                      sys.version_info.micro))
+    print('modelpruner version: {:s}'.format(modelpruner.__version__))
+    print('working directory :', os.getcwd())
+
+    # file names
+    full_sbml = 'sample_data/SBML_models/Deniz_model_fba.xml'
+    pruned_sbml = 'sample_data/SBML_models/Deniz_model_fba_reduced.xml'
+    protected_fname = 'sample_data/data/Deniz_model_fba_nrp2.xlsx'
+
+    # load the original model
+    mp = modelpruner.ModelPruner(full_sbml, protected_fname)
+
+    print('---- network reduction -----')
+    mp.prune()
+
+    # export pruned model in sbml format
+    mp.export_pruned_model(pruned_sbml)
+    print('pruned model converted to SBML:', pruned_sbml)
+    print('finally protected reactions:', mp.protected_rids)
+    print(mp.print_status())
+    # logging.info('Finished')
+
+
+if __name__ == '__main__':
+    prune_model()
-- 
GitLab