Repository: EPFL-LCSB/pytfa (language: python)

Issue:
optlang GLPK interface does not handle tolerance anymore

KeyError and potential solution in sampling tutorial
Dear Pierre and colleagues,

First of all, thank you for the great tool, the neat support in the docs and the informative articles.

A minor issue/suggestion: 
In _tutorial_sampling.py_, using model _iJO1366.json_, I got a KeyError  on line 72, while applying flux directionality. Going into the code in _/analysis/manipulation.py_, I found that if I change the end of the code on line 70 from
`backward_use.variable.lb = round(solution.x_dict[backward_use.name])`
to
`backward_use.variable.lb = round(solution.x_dict[backward_use.id])`
and similarly for the other three lines as well, I can enforce the use of the raw id name, without the "BU_" and "FU_" prefixes. 

Not sure if it was a common issue, but in case anyone else would run into the same, this solved my problem.

Keep up the great work!

Best, Adam
KeyError and potential solution in sampling tutorial
Dear Pierre and colleagues,

First of all, thank you for the great tool, the neat support in the docs and the informative articles.

A minor issue/suggestion: 
In _tutorial_sampling.py_, using model _iJO1366.json_, I got a KeyError  on line 72, while applying flux directionality. Going into the code in _/analysis/manipulation.py_, I found that if I change the end of the code on line 70 from
`backward_use.variable.lb = round(solution.x_dict[backward_use.name])`
to
`backward_use.variable.lb = round(solution.x_dict[backward_use.id])`
and similarly for the other three lines as well, I can enforce the use of the raw id name, without the "BU_" and "FU_" prefixes. 

Not sure if it was a common issue, but in case anyone else would run into the same, this solved my problem.

Keep up the great work!

Best, Adam


Discussion / hints:

Good day Adam!

Thank you for the kind words :) We appreciate that people read our prose and use our tools !

Indeed, I forgot to adapt this part of the code to the new fields of the solution attribute. The problem of your solution is that `backward_use.id` will yield for example `PGI` instead of `BU_PGI`, and `PGI` is the variable name of the reaction PGI -- which might create some issues since that would mean bounding an integer with continuous values ;) (This also explains why you do not get a key error)

However, i replaced `solution.x_dict` by  `solution.raw`, which yields the raw Series of variable values at solution - this should work. Try pulling from dev (commit  b9f14d27b5a2f616ba0917ae69b2f6d88f1abc11) - and tell me if that solves your problem :)

Cheers,
Pierre
Hi Pierre,

Appreciate the quick response and the update! You are right, I was binding the integers inappropriately... After pulling the update, it works like a charm, thanks. 

Subsequently, I found `x_dict`s to be replaced in `/analysis/variability.py` as well. Would the below changes make sense (as per your previous update)? 
77: `profiles[iter_count] = solution.x_dict` &rarr; `profiles[iter_count] = solution.raw`
207: `fluxes = solution.x_dict[reaction_id]` &rarr; `fluxes = solution.raw[reaction_id]`
210: `deltag = pd.Series({x.id:solution.x_dict[x.name]` &rarr; `deltag = pd.Series({x.id:solution.raw[x.name]`

In the case of line 207, I am a bit confused about the deprecated `x_dict`s from `cobrapy` (which were suggested to be changed to `fluxes`) and these `x_dict`s that should be changed to `raw`. Can you maybe suggest an intuitive way to differentiate them? This `pytfa - cobrapy` integration and tracking the attributes/variables gets a bit tricky for me at certain points... :)

Thanks ahead!
Adam


Good day Adam,

Thank you for pointing these out!
Yes, I kinda coded this in a hurry without proper documentation ... Here was my reasoning:

* `solution.fluxes` in `cobrapy` is a transformed version of the solver output, as it actually calculates the _net_ flux of each reaction by substracting the reverse variable value to the forward variable value. This should be used anytime one needs the actual flux value

* `solution.raw` is an attribute I added to get a clear copy of the solver output. From there one can access the value at solution for all the variables of the problem. However, looking for a reaction ID in there will only give the _forward_ flux. This should be used for any other variable than fluxes.

I did not want to remove fluxes from `solution.raw` because then, well, it would not be raw anymore ...
However if you have suggestions with respect to this I would gladly take them !
I should have corrected the last remnants of `x_dict` in the latest push (dev, commit a985ce897b3787f53e358cf73c314531b820454c)

Let me know if that solved your plroblem, and I'll close the issue :)

Cheers,
Pierre
Good day to you too, Pierre,

Thank you for the new update and the detailed explanation. The pace at which you addressed these issues is impressive :+1:. 

This thumb rule about the use cases of `fluxes` and `raw` makes good sense to me now. I also agree that for completeness, `solution.raw` should contain everything.

The changes seem to have done the trick, the code regained its flow.

Regarding suggestions, I would not make any now. It is a big project, and I could see that it takes quite some time to get a good enough overview of what is happening under the hood. Momentarily, it seems more feasible to assume that you made all the right choices during coding. :)

With that, please feel free to close this issue.

Keep it up,
Adam  
Good day Adam!

Thank you for the kind words :) We appreciate that people read our prose and use our tools !

Indeed, I forgot to adapt this part of the code to the new fields of the solution attribute. The problem of your solution is that `backward_use.id` will yield for example `PGI` instead of `BU_PGI`, and `PGI` is the variable name of the reaction PGI -- which might create some issues since that would mean bounding an integer with continuous values ;) (This also explains why you do not get a key error)

However, i replaced `solution.x_dict` by  `solution.raw`, which yields the raw Series of variable values at solution - this should work. Try pulling from dev (commit  b9f14d27b5a2f616ba0917ae69b2f6d88f1abc11) - and tell me if that solves your problem :)

Cheers,
Pierre
Hi Pierre,

Appreciate the quick response and the update! You are right, I was binding the integers inappropriately... After pulling the update, it works like a charm, thanks. 

Subsequently, I found `x_dict`s to be replaced in `/analysis/variability.py` as well. Would the below changes make sense (as per your previous update)? 
77: `profiles[iter_count] = solution.x_dict` &rarr; `profiles[iter_count] = solution.raw`
207: `fluxes = solution.x_dict[reaction_id]` &rarr; `fluxes = solution.raw[reaction_id]`
210: `deltag = pd.Series({x.id:solution.x_dict[x.name]` &rarr; `deltag = pd.Series({x.id:solution.raw[x.name]`

In the case of line 207, I am a bit confused about the deprecated `x_dict`s from `cobrapy` (which were suggested to be changed to `fluxes`) and these `x_dict`s that should be changed to `raw`. Can you maybe suggest an intuitive way to differentiate them? This `pytfa - cobrapy` integration and tracking the attributes/variables gets a bit tricky for me at certain points... :)

Thanks ahead!
Adam


Good day Adam,

Thank you for pointing these out!
Yes, I kinda coded this in a hurry without proper documentation ... Here was my reasoning:

* `solution.fluxes` in `cobrapy` is a transformed version of the solver output, as it actually calculates the _net_ flux of each reaction by substracting the reverse variable value to the forward variable value. This should be used anytime one needs the actual flux value

* `solution.raw` is an attribute I added to get a clear copy of the solver output. From there one can access the value at solution for all the variables of the problem. However, looking for a reaction ID in there will only give the _forward_ flux. This should be used for any other variable than fluxes.

I did not want to remove fluxes from `solution.raw` because then, well, it would not be raw anymore ...
However if you have suggestions with respect to this I would gladly take them !
I should have corrected the last remnants of `x_dict` in the latest push (dev, commit a985ce897b3787f53e358cf73c314531b820454c)

Let me know if that solved your plroblem, and I'll close the issue :)

Cheers,
Pierre
Good day to you too, Pierre,

Thank you for the new update and the detailed explanation. The pace at which you addressed these issues is impressive :+1:. 

This thumb rule about the use cases of `fluxes` and `raw` makes good sense to me now. I also agree that for completeness, `solution.raw` should contain everything.

The changes seem to have done the trick, the code regained its flow.

Regarding suggestions, I would not make any now. It is a big project, and I could see that it takes quite some time to get a good enough overview of what is happening under the hood. Momentarily, it seems more feasible to assume that you made all the right choices during coding. :)

With that, please feel free to close this issue.

Keep it up,
Adam  

Fix patch:
diff --git a/CLAI b/CLAI
new file mode 100644
index 0000000..b298bf6
--- /dev/null
+++ b/CLAI
@@ -0,0 +1,134 @@
+                    EPFL / LCSB / Lausanne / CH
+     Individual Contributor License Agreement ("Agreement") V2.0
+                   http://www.apache.org/licenses/
+
+Thank you for your interest in software from EPFL/LCSB (Lab).
+In order to clarify the intellectual property license
+granted with Contributions from any person or entity, the Lab
+must have a Contributor License Agreement ("CLA") on file that has
+been signed by each Contributor, indicating agreement to the license
+terms below. This license is for your protection as a Contributor as
+well as the protection of the Lab and its users; it does not
+change your rights to use your own Contributions for any other purpose.
+If you have not already done so, please complete and sign, then scan
+and email a pdf file of this Agreement to softwares.lcsb@epfl.ch.
+
+Please read this document carefully before
+signing and keep a copy for your records.
+
+  Full name: ______________________________________________________
+
+  (optional) Public name: _________________________________________
+
+  Mailing Address: ________________________________________________
+
+                   ________________________________________________
+
+  Country:   ______________________________________________________
+
+  Telephone: ______________________________________________________
+
+  E-Mail:    ______________________________________________________
+
+  Github account: _________________________________________________
+
+You accept and agree to the following terms and conditions for Your
+present and future Contributions submitted to the Lab. In
+return, the Lab shall not use Your Contributions in a way that
+is contrary to the public benefit or inconsistent with its nonprofit
+status and bylaws in effect at the time of the Contribution. Except
+for the license granted herein to the Lab and recipients of
+software distributed by the Lab, You reserve all right, title,
+and interest in and to Your Contributions.
+
+1. Definitions.
+
+   "You" (or "Your") shall mean the copyright owner or legal entity
+   authorized by the copyright owner that is making this Agreement
+   with the Lab. For legal entities, the entity making a
+   Contribution and all other entities that control, are controlled
+   by, or are under common control with that entity are considered to
+   be a single Contributor. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "Contribution" shall mean any original work of authorship,
+   including any modifications or additions to an existing work, that
+   is intentionally submitted by You to the Lab for inclusion
+   in, or documentation of, any of the products owned or managed by
+   the Lab (the "Work"). For the purposes of this definition,
+   "submitted" means any form of electronic, verbal, or written
+   communication sent to the Lab or its representatives,
+   including but not limited to communication on electronic mailing
+   lists, source code control systems, and issue tracking systems that
+   are managed by, or on behalf of, the Lab for the purpose of
+   discussing and improving the Work, but excluding communication that
+   is conspicuously marked or otherwise designated in writing by You
+   as "Not a Contribution."
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this Agreement, You hereby grant to the Lab and to
+   recipients of software distributed by the Lab a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare derivative works of,
+   publicly display, publicly perform, sublicense, and distribute Your
+   Contributions and such derivative works.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this Agreement, You hereby grant to the Lab and to
+   recipients of software distributed by the Lab a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have
+   made, use, offer to sell, sell, import, and otherwise transfer the
+   Work, where such license applies only to those patent claims
+   licensable by You that are necessarily infringed by Your
+   Contribution(s) alone or by combination of Your Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If any
+   entity institutes patent litigation against You or any other entity
+   (including a cross-claim or counterclaim in a lawsuit) alleging
+   that your Contribution, or the Work to which you have contributed,
+   constitutes direct or contributory patent infringement, then any
+   patent licenses granted to that entity under this Agreement for
+   that Contribution or Work shall terminate as of the date such
+   litigation is filed.
+
+4. You represent that you are legally entitled to grant the above
+   license. If your employer(s) has rights to intellectual property
+   that you create that includes your Contributions, you represent
+   that you have received permission to make Contributions on behalf
+   of that employer, that your employer has waived such rights for
+   your Contributions to the Lab, or that your employer has
+   executed a separate Corporate CLA with the Lab.
+
+5. You represent that each of Your Contributions is Your original
+   creation (see section 7 for submissions on behalf of others).  You
+   represent that Your Contribution submissions include complete
+   details of any third-party license or other restriction (including,
+   but not limited to, related patents and trademarks) of which you
+   are personally aware and which are associated with any part of Your
+   Contributions.
+
+6. You are not expected to provide support for Your Contributions,
+   except to the extent You desire to provide support. You may provide
+   support for free, for a fee, or not at all. Unless required by
+   applicable law or agreed to in writing, You provide Your
+   Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+   OF ANY KIND, either express or implied, including, without
+   limitation, any warranties or conditions of TITLE, NON-
+   INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
+
+7. Should You wish to submit work that is not Your original creation,
+   You may submit it to the Lab separately from any
+   Contribution, identifying the complete details of its source and of
+   any license or other restriction (including, but not limited to,
+   related patents, trademarks, and license agreements) of which you
+   are personally aware, and conspicuously marking the work as
+   "Submitted on behalf of a third-party: [named here]".
+
+8. You agree to notify the Lab of any facts or circumstances of
+   which you become aware that would make these representations
+   inaccurate in any respect.
+
+Please sign: __________________________________ Date: ________________
diff --git a/README.rst b/README.rst
index a40b316..2198b2e 100644
--- a/README.rst
+++ b/README.rst
@@ -183,8 +183,8 @@ Here is an example script :
    :target: http://pytfa.readthedocs.io/en/latest/?badge=latest
 .. |license| image:: http://img.shields.io/badge/license-APACHE2-blue.svg
    :target: https://github.com/EPFL-LCSB/pytfa/blob/master/LICENSE.txt
-.. |Build Status| image:: https://travis-ci.org/EPFL-LCSB/pytfa.svg?branch=master
-   :target: https://travis-ci.org/EPFL-LCSB/pytfa
+.. |Build Status| image:: https://travis-ci.com/EPFL-LCSB/pytfa.svg?branch=master
+   :target: https://travis-ci.com/EPFL-LCSB/pytfa
 .. |Codecov| image:: https://img.shields.io/codecov/c/github/EPFL-LCSB/pytfa.svg
    :target: https://codecov.io/gh/EPFL-LCSB/pytfa
 .. |Codacy branch grade| image:: https://img.shields.io/codacy/grade/d8fd67ee134d46a69115c9b39c19be26/master.svg
diff --git a/doc/conf.py b/doc/conf.py
index 332eeed..854f173 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -95,7 +95,7 @@ def __getattr__(cls, name):
 version = '0.9'
 # The full version, including alpha/beta/rc tags.
 
-release = '0.9.1'
+release = '0.9.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docker/requirements.txt b/docker/requirements.txt
index 0c1b848..6232adb 100644
--- a/docker/requirements.txt
+++ b/docker/requirements.txt
@@ -1,5 +1,7 @@
 bokeh>=0.12.1
 cobra>0.13
+equilibrator-api
+equilibrator-cache
 ipdb
 lxml
 networkx
diff --git a/models/README.rst b/models/README.rst
index 755da0b..46212d2 100644
--- a/models/README.rst
+++ b/models/README.rst
@@ -46,12 +46,12 @@ Publications
 
 Some models have been published:
 
-+-----------------+-------------------------------------------------------------------------+
-| small_ecoli.mat | Ataman, M., Gardiol, D. F. H., Fengos, G., & Hatzimanikatis, V. (2017). |
-|                 | redGEM: Systematic reduction and analysis of genome-scale metabolic     |
-|                 | reconstructions for development of consistent core metabolic models.    |
-|                 | PLoS computational biology, 13(7), e1005444.                            |
-+-----------------+-------------------------------------------------------------------------+
++-----------------+--------------------------------------------------------------------------------+
+| small_ecoli.mat | Ataman, M., Hernandez Gardiol, D. F., Fengos, G., & Hatzimanikatis, V. (2017). |
+|                 | redGEM: Systematic reduction and analysis of genome-scale metabolic            |
+|                 | reconstructions for development of consistent core metabolic models.           |
+|                 | PLoS computational biology, 13(7), e1005444.                                   |
++-----------------+--------------------------------------------------------------------------------+
 
 
 
diff --git a/pytfa/io/base.py b/pytfa/io/base.py
index 847c825..0ef75ed 100644
--- a/pytfa/io/base.py
+++ b/pytfa/io/base.py
@@ -189,8 +189,7 @@ def import_matlab_model(path, variable_name=None):
         comp = comps[i]
         comp['membranePot'] = {}
         for j in range(num_comp):
-            comp['membranePot'][comps[j]['symbol']] = \
-            CompartmentDB['membranePot'][0][i, j]
+            comp['membranePot'][comps[j]['symbol']] = float(CompartmentDB['membranePot'][0][i, j])
 
     cobra_model.compartments = Compartments
 
diff --git a/pytfa/io/dict.py b/pytfa/io/dict.py
index 0ca760f..ffd2cb6 100644
--- a/pytfa/io/dict.py
+++ b/pytfa/io/dict.py
@@ -18,6 +18,8 @@
 from ..optim.constraints import ReactionConstraint, MetaboliteConstraint, ModelConstraint
 from ..optim.utils import symbol_sum
 
+from ..thermo.metabolite import MetaboliteThermo
+
 from optlang.util import expr_to_json, parse_expr
 
 from copy import copy
@@ -215,7 +217,7 @@ def model_to_dict(model):
 
         if is_thermo and not is_peptide: # peptides have no thermo
             the_met = model.metabolites.get_by_id(the_met_id)
-            _add_thermo_metabolite_info(the_met, rxn_dict)
+            _add_thermo_metabolite_info(the_met, met_dict)
             met_dict['kind'] = 'Metabolite'
 
     # Relaxation info
@@ -425,6 +427,9 @@ def init_thermo_model_from_dict(new, obj):
 
         if 'thermo' in met_dict:
             new._prepare_metabolite(met)
+            met.thermo = MetaboliteThermo(metData=None, pH=7,ionicStr=0)
+            for k,v in met_dict['thermo'].items():
+                setattr(met.thermo,k,v)
     return new
 
 def rebuild_compositions(new, compositions_dict):
diff --git a/pytfa/io/json.py b/pytfa/io/json.py
index 253a640..d863ba6 100644
--- a/pytfa/io/json.py
+++ b/pytfa/io/json.py
@@ -54,3 +54,27 @@ def load_json_model(filepath):
 
     model = model_from_dict(obj)
     return model
+
+
+def json_dumps_model(model):
+    """
+    Returns a JSON dump as a string
+
+    :param model:
+    :return:
+    """
+
+    obj = model_to_dict(model)
+
+    return json.dumps(obj,cls=MyEncoder)
+
+
+def json_loads_model(s):
+    """
+    Loads a model from a string JSON dump
+
+    :param s: JSON string
+    :return:
+    """
+    obj = json.loads(s)
+    return model_from_dict(obj)
\ No newline at end of file
diff --git a/pytfa/optim/utils.py b/pytfa/optim/utils.py
index 5eb8fd3..2283576 100644
--- a/pytfa/optim/utils.py
+++ b/pytfa/optim/utils.py
@@ -16,6 +16,8 @@
 import sympy
 from cobra.core.solution import Solution
 
+from sympy.core.numbers import Zero
+
 from numbers import Number
 
 from .constraints import GenericConstraint
@@ -107,7 +109,7 @@ def symbol_sum(variables):
     
     k=0
     # If we encounter a zero, which is a special type, increase k
-    while isinstance(variables[k], sympy.numbers.Zero) and k<len(variables):
+    while isinstance(variables[k], Zero) and k<len(variables):
         k+=1
         if k == len(variables):
             # everything is 0
diff --git a/pytfa/optim/variables.py b/pytfa/optim/variables.py
index f97bd91..578cb48 100644
--- a/pytfa/optim/variables.py
+++ b/pytfa/optim/variables.py
@@ -568,7 +568,6 @@ class NegSlackLC(MetaboliteVariable):
 
     prefix = 'NegSlackLC_'
 
-
 class LinearizationVariable(ModelVariable):
     """
     Class to represent a product A*B when performing linearization of the
diff --git a/pytfa/redgem/lumpgem.py b/pytfa/redgem/lumpgem.py
index 2a9fdb3..a07d8ec 100644
--- a/pytfa/redgem/lumpgem.py
+++ b/pytfa/redgem/lumpgem.py
@@ -22,6 +22,7 @@
 GUROBI = 'optlang-gurobi'
 GLPK = 'optlang-glpk'
 
+DEFAULT_EPS = 1e-5
 
 # Transforms (OnePerBBB --> oneperbbb), (one_per_bbb --> oneperbbb), etc ...
 disambiguate = lambda s:s.lower().replace('_','')
@@ -292,7 +293,11 @@ def compute_lumps(self, force_solve=False, method='OnePerBBB'):
         self._tfa_model.convert()
         # self._tfa_model.objective_direction = 'min'
 
-        epsilon = self._tfa_model.solver.configuration.tolerances.feasibility
+        try:
+            epsilon = self._tfa_model.solver.configuration.tolerances.feasibility
+        except AttributeError as e:
+            self._tfa_model.logger.error('{} does not support tolerance settings.'.format(self._tfa_model.solver))
+            epsilon = DEFAULT_EPS
 
         the_method = disambiguate(method)
         print('Lumping method detected: {}'.format(the_method))
@@ -391,7 +396,11 @@ def _lump_min_plus_p(self, met_BBB, sink, p, force_solve):
         :return:
         """
 
-        epsilon = self._tfa_model.solver.configuration.tolerances.integrality
+        try:
+            epsilon = self._tfa_model.solver.configuration.tolerances.integrality
+        except AttributeError as e:
+            self._tfa_model.logger.error('{} does not support tolerance settings.'.format(self._tfa_model.solver))
+            epsilon = DEFAULT_EPS
 
         try:
             max_lumps =self._param_dict['max_lumps_per_BBB']
@@ -475,8 +484,13 @@ def _build_lump(self, met_BBB, sink):
         :return:
         """
 
-        epsilon_int = self._tfa_model.solver.configuration.tolerances.integrality
-        epsilon_flux = self._tfa_model.solver.configuration.tolerances.feasibility
+        try:
+            epsilon_int = self._tfa_model.solver.configuration.tolerances.integrality
+            epsilon_flux = self._tfa_model.solver.configuration.tolerances.feasibility
+        except AttributeError:
+            self._tfa_model.logger.error('{} does not support tolerance settings.'.format(self._tfa_model.solver))
+            epsilon_int = DEFAULT_EPS
+            epsilon_flux = DEFAULT_EPS
 
         sigma = sink.flux
         lump_dict = dict()
@@ -521,4 +535,4 @@ def sum_reactions(rxn_dict, id_ = 'summed_reaction', epsilon = 1e-9):
                subnetwork = {x.id:v for x,v in rxn_dict.items()},
                gene_reaction_rule=gpr)
 
-    return new
\ No newline at end of file
+    return new
diff --git a/pytfa/redgem/redgem.py b/pytfa/redgem/redgem.py
index 3a8b9d4..adfaee6 100644
--- a/pytfa/redgem/redgem.py
+++ b/pytfa/redgem/redgem.py
@@ -109,10 +109,16 @@ def run(self):
 
         force_solve = self.params["force_solve"]
         timeout = self.params["timeout"]
-        self._gem.solver.configuration.tolerances.feasibility = self.params["feasibility"]
-        self._gem.solver.configuration.tolerances.integrality = self.params["feasibility"]
-        self._source_gem.solver.configuration.tolerances.feasibility = self.params["feasibility"]
-        self._source_gem.solver.configuration.tolerances.integrality = self.params["feasibility"]
+        try:
+            self._gem.solver.configuration.tolerances.feasibility = self.params["feasibility"]
+            self._gem.solver.configuration.tolerances.integrality = self.params["feasibility"]
+        except AttributeError as e:
+            self.logger.error('Solver {} is not compatible with tolerance parameters'.format(self._gem.solver))
+        try:
+            self._source_gem.solver.configuration.tolerances.feasibility = self.params["feasibility"]
+            self._source_gem.solver.configuration.tolerances.integrality = self.params["feasibility"]
+        except AttributeError as e:
+            self.logger.error('Solver {} is not compatible with tolerance parameters'.format(self._source_gem.solver))
 
         self.logger.info("Computing network expansion...")
         expander = NetworkExpansion(self._gem, core_subsystems, extracellular_system,
diff --git a/pytfa/thermo/equilibrator.py b/pytfa/thermo/equilibrator.py
index b68e146..8a00d9c 100644
--- a/pytfa/thermo/equilibrator.py
+++ b/pytfa/thermo/equilibrator.py
@@ -40,7 +40,8 @@ def build_thermo_from_equilibrator(model, T=TEMPERATURE_0):
         ccache = create_compound_cache_from_quilt()
         logger.debug("eQuilibrator compound cache is loaded.")
 
-    cc = ComponentContribution(temperature=Q_(str(T) + "K"))
+    cc = ComponentContribution()
+    cc.temperature=Q_(str(T) + "K")
 
     thermo_data = {"name": "eQuilibrator", "units": "kJ/mol", "cues": {}}
     met_to_comps = compat.map_cobra_metabolites(ccache, model.metabolites)
diff --git a/pytfa/thermo/utils.py b/pytfa/thermo/utils.py
index f0fca94..96e1044 100644
--- a/pytfa/thermo/utils.py
+++ b/pytfa/thermo/utils.py
@@ -76,7 +76,7 @@ def check_reaction_balance(reaction, proton = None):
                 'Cd', 'W', 'Hg', 'R', 'Mo', 'X'].index(atom[0])
             except ValueError:
                 # Jorge Carrasco: Can we use warnings here?
-                reaction.model.logger.warn(
+                reaction.model.logger.warning(
                     'Warning : ' + metabolite.formula + '/' + atom[0]
                 )
                 continue
diff --git a/setup.py b/setup.py
index c9020f4..e1590fb 100644
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@
 #     return reqs
 
 
-version_tag = '0.9.1'
+version_tag = '0.9.3'
 
 setup(name='pytfa',
       version=version_tag,


Test patch:
diff --git a/tests/test_equilibrator.py b/tests/test_equilibrator.py
index 3f9d64f..4215e3a 100644
--- a/tests/test_equilibrator.py
+++ b/tests/test_equilibrator.py
@@ -3,22 +3,20 @@
 """Tests for the equilibrator integration."""
 import pytest
 import sys
+from pytfa.io.json import json_dumps_model, json_loads_model
 
 
-t_model = None
-
 
 # previous version aren't compatible for the required equlibrator_cache version
 # this annotation is not necessary since this file shoud be ignored in the test
 # collection phase for previous versions, but it makes it more clear.
 @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires >= python3.6")
-def test_load_with_equi_thermo():
+def test_load_with_equi_thermo(request):
     """Build thermo data structure with equilibrator."""
+    t_model = request.config.cache.get('model', None)
     from pytfa.thermo.equilibrator import build_thermo_from_equilibrator
     from pytfa import ThermoModel
     from settings import cobra_model
-
-    global t_model
     cmodel = cobra_model.copy()
     for met in cmodel.metabolites:
         # normalize but don't overwrite
@@ -26,25 +24,35 @@ def test_load_with_equi_thermo():
     thermo_data = build_thermo_from_equilibrator(cmodel)
     t_model = ThermoModel(thermo_data, cmodel)
     t_model.solver = "optlang-glpk"
+    request.config.cache.set('model', json_dumps_model(t_model))
+
 
 
+@pytest.mark.dependency(depends=['test_load_with_equi_thermo'])
 @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires >= python3.6")
-def test_equilibrator_preparation():
+def test_equilibrator_preparation(request):
     """Prepare using equilibrator_api."""
-    global t_model
+    t_model = json_loads_model(request.config.cache.get('model',None))
+    assert t_model is not None
     t_model.prepare()
+    request.config.cache.set('model', json_dumps_model(t_model))
 
 
+@pytest.mark.dependency(depends=['test_equilibrator_preparation'])
 @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires >= python3.6")
-def test_equilibrator_conversion():
+def test_equilibrator_conversion(request):
     """Convert by the usual method in `ThermoModel`."""
-    global t_model
+    t_model = json_loads_model(request.config.cache.get('model',None))
+    assert t_model is not None
     t_model.convert()
+    request.config.cache.set('model', json_dumps_model(t_model))
 
 
+@pytest.mark.dependency(depends=['test_equilibrator_conversion'])
 @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires >= python3.6")
-def test_equilibrator_optim():
+def test_equilibrator_optim(request):
     """LP optimization by the usual method in `ThermoModel`."""
-    global t_model
+    t_model = json_loads_model(request.config.cache.get('model',None))
+    assert t_model is not None
     solution = t_model.optimize()
     return sum(solution.fluxes)
diff --git a/tests/test_io.py b/tests/test_io.py
index 74261cc..44eee8d 100644
--- a/tests/test_io.py
+++ b/tests/test_io.py
@@ -54,7 +54,7 @@ def test_io():
     sol_orig = small_tmodel.slim_optimize()
     sol_new = new.slim_optimize()
 
-    epsilon = new.solver.configuration.tolerances.optimality
+    epsilon = 1e-5 # new.solver.configuration.tolerances.optimality is deprecated in GLPK now, see opencobra/optlang/issues/223
 
     assert(abs(sol_new - sol_orig) < epsilon)