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

Issue:
Problem with pytfa.relax_dgo due to sympy
Hello, 
first of all big thanks for supporting the package! I had a problem with the function relax_dgo and also a possible solution:
from pytfa.optim.relaxation import relax_dgo
relaxed_model, slack_model, relax_table = relax_dgo(mytfa)

The follwing Error occours:

~\AppData\Roaming\Python\Python38\site-packages\pytfa\optim\utils.py in symbol_sum(variables)
    108     k=0
    109     # If we encounter a zero, which is a special type, increase k
--> 110     while isinstance(variables[k], **sympy.numbers.Zero**) and k<len(variables):
    111         k+=1
    112         if k == len(variables):

AttributeError: module 'sympy' has no attribute 'numbers'

By changing  sympy.numbers.Zero to **sympy.core.numbers.Zero** in ~\AppData\Roaming\Python\Python38\site-packages\pytfa\optim\utils.py the Error is gone. 
This seems like sympy changed something there. 

I'm using Python Python 3.8.5 and sympy version 1.6.2

Regards,
Emil
VER: release v0.9.1

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:

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/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)