Git Product home page Git Product logo

chempy's Introduction

ChemPy

Github Actions CI status Woodpecker CI status PyPI version License Journal of Open Source Software DOI

ChemPy is a Python package useful for chemistry (mainly physical/inorganic/analytical chemistry). Currently it includes:

  • Numerical integration routines for chemical kinetics (ODE solver front-end)
  • Integrated rate expressions (and convenience fitting routines)
  • Solver for equilibria (including multiphase systems)
  • Relations in physical chemistry:
    • Debye-Hückel expressions
    • Arrhenius & Eyring equation
    • Einstein-Smoluchowski equation
  • Properties (pure python implementations from the literature)

The easiest way to get started is to have a look at the examples in this README, and also the jupyter notebooks. In addition there is auto-generated API documentation for the latest stable release here.

Simplest way to install ChemPy and its (optional) dependencies is to use the conda package manager:

$ conda install -c conda-forge chempy pytest
$ pytest -rs -W ignore::chempy.ChemPyDeprecationWarning --pyargs chempy

currently conda packages are only provided for Linux. On Windows and OS X you will need to use pip instead:

$ python3 -m pip install chempy pytest
$ python3 -m pytest -rs -W ignore::chempy.ChemPyDeprecationWarning --pyargs chempy

there will a few tests which will be skipped due to some missing optional backends in addition to those in SciPy (used for solving systems of non-linear equations and ordinary differential equations).

If you used conda to install ChemPy you can skip this section. But if you use pip the default installation is achieved by writing:

$ python3 -m pip install --user --upgrade chempy pytest
$ python3 -m pytest -rs --pyargs chempy

you can skip the --user flag if you have got root permissions. You may be interested in using additional backends (in addition to those provided by SciPy) for solving ODE-systems and non-linear optimization problems:

$ python3 -m pip install chempy[all]

Note that this option will install the following libraries (some of which require additional libraries to be present on your system):

  • pygslodeiv2: solving initial value problems, requires GSL. (>=1.16).
  • pyodeint: solving initial value problems, requires boost (>=1.65.0).
  • pycvodes: solving initial value problems, requires SUNDIALS (>=5.3.0).
  • pykinsol: solving non-linear root-finding, requires SUNDIALS (>=5.3.0).
  • pycompilation: python front-end for calling compilers, requires gcc/clang/icpc.
  • pycodeexport: package for code-generation, used when generating C++ code.

if you want to see what packages need to be installed on a Debian based system you may look at this Dockerfile.

If you have Docker installed, you may use it to host a jupyter notebook server:

$ ./scripts/host-env.sh host-notebook --port 8888

the first time you run the command, some dependencies will be downloaded. When the installation is complete there will be a link visible which you can open in your browser. You can also run the test suite using the same docker-image:

$ ./scripts/host-env.sh run-tests

there will be a few skipped test (due to some dependencies not being installed by default) and quite a few warnings.

See demonstration scripts in examples/, and some rendered jupyter notebooks. You may also browse the documentation for more examples. Below you will find a few code snippets:

>>> from chempy import Substance
>>> ferricyanide = Substance.from_formula('Fe(CN)6-3')
>>> ferricyanide.composition == {0: -3, 26: 1, 6: 6, 7: 6}  # 0 for charge
True
>>> print(ferricyanide.unicode_name)
Fe(CN)₆³⁻
>>> print(ferricyanide.latex_name + ", " + ferricyanide.html_name)
Fe(CN)_{6}^{3-}, Fe(CN)<sub>6</sub><sup>3-</sup>
>>> print('%.3f' % ferricyanide.mass)
211.955

as you see, in composition, the atomic numbers (and 0 for charge) is used as keys and the count of each kind became respective value.

>>> from chempy import balance_stoichiometry  # Main reaction in NASA's booster rockets:
>>> reac, prod = balance_stoichiometry({'NH4ClO4', 'Al'}, {'Al2O3', 'HCl', 'H2O', 'N2'})
>>> from pprint import pprint
>>> pprint(dict(reac))
{'Al': 10, 'NH4ClO4': 6}
>>> pprint(dict(prod))
{'Al2O3': 5, 'H2O': 9, 'HCl': 6, 'N2': 3}
>>> from chempy import mass_fractions
>>> for fractions in map(mass_fractions, [reac, prod]):
...     pprint({k: '{0:.3g} wt%'.format(v*100) for k, v in fractions.items()})
...
{'Al': '27.7 wt%', 'NH4ClO4': '72.3 wt%'}
{'Al2O3': '52.3 wt%', 'H2O': '16.6 wt%', 'HCl': '22.4 wt%', 'N2': '8.62 wt%'}

ChemPy can also balance reactions where the reacting species are more complex and are better described in other terms than their molecular formula. A silly, yet illustrative example would be how to make pancakes without any partially used packages:

>>> substances = {s.name: s for s in [
...     Substance('pancake', composition=dict(eggs=1, spoons_of_flour=2, cups_of_milk=1)),
...     Substance('eggs_6pack', composition=dict(eggs=6)),
...     Substance('milk_carton', composition=dict(cups_of_milk=4)),
...     Substance('flour_bag', composition=dict(spoons_of_flour=60))
... ]}
>>> pprint([dict(_) for _ in balance_stoichiometry({'eggs_6pack', 'milk_carton', 'flour_bag'},
...                                                {'pancake'}, substances=substances)])
[{'eggs_6pack': 10, 'flour_bag': 2, 'milk_carton': 15}, {'pancake': 60}]

ChemPy can even handle reactions with linear dependencies (underdetermined systems), e.g.:

>>> pprint([dict(_) for _ in balance_stoichiometry({'C', 'O2'}, {'CO2', 'CO'})])  # doctest: +SKIP
[{'C': x1 + 2, 'O2': x1 + 1}, {'CO': 2, 'CO2': x1}]

the x1 object above is an instance of SymPy's Symbol. If we prefer to get a solution with minimal (non-zero) integer coefficients we can pass underdetermined=None:

>>> pprint([dict(_) for _ in balance_stoichiometry({'C', 'O2'}, {'CO2', 'CO'}, underdetermined=None)])
[{'C': 3, 'O2': 2}, {'CO': 2, 'CO2': 1}]

note however that even though this solution is in some sense "canonical", it is merely one of an infinite number of solutions (x1 from earlier may be any integer).

>>> from chempy import Equilibrium
>>> from sympy import symbols
>>> K1, K2, Kw = symbols('K1 K2 Kw')
>>> e1 = Equilibrium({'MnO4-': 1, 'H+': 8, 'e-': 5}, {'Mn+2': 1, 'H2O': 4}, K1)
>>> e2 = Equilibrium({'O2': 1, 'H2O': 2, 'e-': 4}, {'OH-': 4}, K2)
>>> coeff = Equilibrium.eliminate([e1, e2], 'e-')
>>> coeff
[4, -5]
>>> redox = e1*coeff[0] + e2*coeff[1]
>>> print(redox)
32 H+ + 4 MnO4- + 20 OH- = 26 H2O + 4 Mn+2 + 5 O2; K1**4/K2**5
>>> autoprot = Equilibrium({'H2O': 1}, {'H+': 1, 'OH-': 1}, Kw)
>>> n = redox.cancel(autoprot)
>>> n
20
>>> redox2 = redox + n*autoprot
>>> print(redox2)
12 H+ + 4 MnO4- = 6 H2O + 4 Mn+2 + 5 O2; K1**4*Kw**20/K2**5

Functions and objects useful for working with units are available from the chempy.units module. Here is an example of how ChemPy can check consistency of units:

>>> from chempy import Reaction
>>> r = Reaction.from_string("H2O -> H+ + OH-; 1e-4/M/s")
Traceback (most recent call last):
...
ValueError: Unable to convert between units of "1/M" and "dimensionless"
>>> r = Reaction.from_string("H2O -> H+ + OH-; 1e-4/s")
>>> from chempy.units import to_unitless, default_units as u
>>> to_unitless(r.param, 1/u.minute)
0.006

right now the .units module wraps the quantities package with some minor additions and work-arounds. However, there is no guarantee that the underlying package will not change in a future version of ChemPy (there are many packages for dealing with units in the scientific Python ecosystem).

If we want to predict pH of a bicarbonate solution we simply just need pKa and pKw values:

>>> from collections import defaultdict
>>> from chempy.equilibria import EqSystem
>>> eqsys = EqSystem.from_string("""HCO3- = H+ + CO3-2; 10**-10.3
... H2CO3 = H+ + HCO3-; 10**-6.3
... H2O = H+ + OH-; 10**-14/55.4
... """)  # pKa1(H2CO3) = 6.3 (implicitly incl. CO2(aq)), pKa2=10.3 & pKw=14
>>> arr, info, sane = eqsys.root(defaultdict(float, {'H2O': 55.4, 'HCO3-': 1e-2}))
>>> conc = dict(zip(eqsys.substances, arr))
>>> from math import log10
>>> print("pH: %.2f" % -log10(conc['H+']))
pH: 8.30

here is another example for ammonia:

>>> from chempy import Equilibrium
>>> from chempy.chemistry import Species
>>> water_autop = Equilibrium({'H2O'}, {'H+', 'OH-'}, 10**-14)  # unit "molar" assumed
>>> ammonia_prot = Equilibrium({'NH4+'}, {'NH3', 'H+'}, 10**-9.24)  # same here
>>> substances = [Species.from_formula(f) for f in 'H2O OH- H+ NH3 NH4+'.split()]
>>> eqsys = EqSystem([water_autop, ammonia_prot], substances)
>>> print('\n'.join(map(str, eqsys.rxns)))  # "rxns" short for "reactions"
H2O = H+ + OH-; 1e-14
NH4+ = H+ + NH3; 5.75e-10
>>> init_conc = defaultdict(float, {'H2O': 1, 'NH3': 0.1})
>>> x, sol, sane = eqsys.root(init_conc)
>>> assert sol['success'] and sane
>>> print(', '.join('%.2g' % v for v in x))
1, 0.0013, 7.6e-12, 0.099, 0.0013

ChemPy collects equations and utility functions for working with concepts such as ionic strength:

>>> from chempy.electrolytes import ionic_strength
>>> ionic_strength({'Fe+3': 0.050, 'ClO4-': 0.150}) == .3
True

note how ChemPy parsed the charges from the names of the substances. There are also e.g. empirical equations and convenience classes for them available, e.g.:

>>> from chempy.henry import Henry
>>> kH_O2 = Henry(1.2e-3, 1800, ref='carpenter_1966')
>>> print('%.1e' % kH_O2(298.15))
1.2e-03

to get more information about e.g. this class, you may can look at the API documentation.

A common task when modelling problems in chemistry is to investigate the time dependence of a system. This branch of study is known as chemical kinetics, and ChemPy has some classes and functions for working with such problems:

>>> from chempy import ReactionSystem  # The rate constants below are arbitrary
>>> rsys = ReactionSystem.from_string("""2 Fe+2 + H2O2 -> 2 Fe+3 + 2 OH-; 42
...     2 Fe+3 + H2O2 -> 2 Fe+2 + O2 + 2 H+; 17
...     H+ + OH- -> H2O; 1e10
...     H2O -> H+ + OH-; 1e-4""")  # "[H2O]" = 1.0 (actually 55.4 at RT)
>>> from chempy.kinetics.ode import get_odesys
>>> odesys, extra = get_odesys(rsys)
>>> from collections import defaultdict
>>> import numpy as np
>>> tout = sorted(np.concatenate((np.linspace(0, 23), np.logspace(-8, 1))))
>>> c0 = defaultdict(float, {'Fe+2': 0.05, 'H2O2': 0.1, 'H2O': 1.0, 'H+': 1e-2, 'OH-': 1e-12})
>>> result = odesys.integrate(tout, c0, atol=1e-12, rtol=1e-14)
>>> import matplotlib.pyplot as plt
>>> fig, axes = plt.subplots(1, 2, figsize=(12, 5))
>>> for ax in axes:
...     _ = result.plot(names=[k for k in rsys.substances if k != 'H2O'], ax=ax)
...     _ = ax.legend(loc='best', prop={'size': 9})
...     _ = ax.set_xlabel('Time')
...     _ = ax.set_ylabel('Concentration')
>>> _ = axes[1].set_ylim([1e-13, 1e-1])
>>> _ = axes[1].set_xscale('log')
>>> _ = axes[1].set_yscale('log')
>>> _ = fig.tight_layout()
>>> _ = fig.savefig('examples/kinetics.png', dpi=72)

https://raw.githubusercontent.com/bjodah/chempy/master/examples/kinetics.png

One of the fundamental tasks in science is the careful collection of data about the world around us. ChemPy contains a growing collection of parametrizations from the scientific literature with relevance in chemistry. Here is how you use one of these formulations:

>>> from chempy import Substance
>>> from chempy.properties.water_density_tanaka_2001 import water_density as rho
>>> from chempy.units import to_unitless, default_units as u
>>> water = Substance.from_formula('H2O')
>>> for T_C in (15, 25, 35):
...     concentration_H2O = rho(T=(273.15 + T_C)*u.kelvin, units=u)/water.molar_mass(units=u)
...     print('[H2O] = %.2f M (at %d °C)' % (to_unitless(concentration_H2O, u.molar), T_C))
...
[H2O] = 55.46 M (at 15 °C)
[H2O] = 55.35 M (at 25 °C)
[H2O] = 55.18 M (at 35 °C)

If you make use of ChemPy in e.g. academic work you may cite the following peer-reviewed publication:

Journal of Open Source Software DOI

Depending on what underlying solver you are using you should also cite the appropriate paper (you can look at the list of references in the JOSS article). If you need to reference, in addition to the paper, a specific point version of ChemPy (for e.g. reproducibility) you can get per-version DOIs from the zenodo archive:

Zenodo DOI

The source code is Open Source and is released under the very permissive "simplified (2-clause) BSD license". See LICENSE for further details.

Contributors are welcome to suggest improvements at https://github.com/bjodah/chempy (see further details here).

Björn I. Dahlgren, contact:
  • gmail address: bjodah

chempy's People

Contributors

adelq avatar bjodah avatar cbporch avatar cclauss avatar daankoning avatar dniiboy avatar jeremyagray avatar kyleniemeyer avatar matecsaj avatar montmorill avatar smsaladi avatar thebicpen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chempy's Issues

Stoichiometry with substances on both sides

Hi,

I would like to calculate the stoichiometry of reactions like the one bellow:

reac, prod = balance_stoichiometry({'H4P2O7', 'HPO3', 'H2O'}, {'H4P2O7', 'HPO3'})

Answer would be:

H4P2O7 + 4 HPO3 + H2O <-> 2 H4P2O7 + 2 HPO3

OR

2 HPO3 + H2O = H4P2O7

Any idea on how this would be performed? At the moment I receive an error: "Stoichiometry with substances on both sides".

How to get molar masses of elements?

Thanks for putting this package together, it is useful!

I'm trying to get the mass fraction of each constituent element of a substance, which I had hoped I could do from the substance object itself but haven't figured that one out.

More generally, how can I access the text name or attributes of an element.

I could do this using the dict from Substance.composition, but it returns a dict with keys of z number rather than element name, which seems to be required by chempy.mass_fractions

any way to map z number back to the text element name? it seems like this dictionary probably exits in the code somewhere to return the Substance.composition dictionary in the first place..

Thanks,

pycvodes/pykinsol failing to compile with python 3.9.1

I just upgraded my debian bullseye system to current, which pulled in python 3.9.1 and subsequently triggered a bunch of warnings like:

    In file included from pycvodes/_cvodes.cpp:668:
    external/anyode/include/anyode/anyode_numpy.hpp:205:61: warning: ‘PyObject* PyEval_CallObjectWithKeywords(PyObject*, PyObject*, PyObject*)’ is deprecated [-Wdeprecated-declarations]
      205 |         PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_jac, py_arglist, this->py_kwargs);

in pycvodes/anyode and pykinsol during compilation.

Python references: here and here.

This is just informational. Without further reading, I don't really now how serious this is so this may be fixable by ignoring the deprecation warning.

balance_stoichiometry raises "ValueError: Component '0' not among products" when using electrons

For the newer versions (I tested chempy versions 0.7.8 & 0.6.8), when I try to balance semi-reactions, I get the exception "ValueError: Component '0' not among products": :

from chempy import balance_stoichiometry
from pprint import pprint
reac, prod = balance_stoichiometry({'Zn+2', 'e-'}, {'Zn'})
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.6/site-packages/chempy/chemistry.py", line 1207, in balance_stoichiometry
raise ValueError("Component '%s' not among products" % ck)
ValueError: Component '0' not among products

This snippet worked fine for chempy 0.4.1:

from chempy import balance_stoichiometry
from pprint import pprint
reac, prod = balance_stoichiometry({'Zn+2', 'e-'}, {'Zn'})
pprint(dict(reac))
{'Zn+2': 1, 'e-': 2}
pprint(dict(prod))
{'Zn': 1}

Strangely, for 0.6.8 & 0.7.8, if I put the electrons on the wrong side, the function works, but returns negative coefficients:

reac, prod = balance_stoichiometry({'Zn+2'}, {'Zn','e-'})
pprint(dict(reac))
{'Zn+2': -1}
pprint(dict(prod))
{'Zn': -1, 'e-': 2}

I'm running python on OpenSuse Leap 15.0 which all the python packages updated (except for chempy for the version tests) using pip3.

I can do what I want with 0.4.1 (it's a huge time-saver!), but you should correct this bug.

Yours,
Bob Burrow

[TODO] Re-enable comp

  • _test_get_odesys__Eyring_1st_order_linearly_ramped_temperature
  • _test_get_odesys__Eyring_2nd_order_linearly_ramped_temperature

Remove units logic from get_odesys

The current implementation is too complicated.
Better: provide mechanism in pyodesys for early preprocessing whith dedimensionalisation,
then in extra from get_odesys provide a "dedimensionalisation" callback.

This is ongoing in the provisional API: chempy.kinetics.ode._create_odesys.

Why is my kinetics chempy code not running?

from chempy import ReactionSystem # The rate constants below are arbitrary
rsys = ReactionSystem.from_string("""Li + Li2CO3 -> Li+ + Li2CO3; 1e-4
2Li2CO3 -> 4Li+ + 2CO2 + O2; 17
Li2O2 -> 2Li+ + O2-; 2.5
Li -> Li+; 1e-7
4Li + 2C + 3O2 -> 2Li2CO3; 1e3
2Li+ + O2 -> Li2O2; 2.5""") # "[Li2O2]" = 7.9e-3 (actually 0.025 at RT)
from chempy.kinetics.ode import get_odesys
odesys, extra = get_odesys(rsys)
from collections import defaultdict
import numpy as np
tout = sorted(np.concatenate((np.linspace(0, 23), np.logspace(-8, 1))))
c0 = defaultdict(float, {'Li': 0.05, 'O2': 1e-12, 'Li2CO3': 1e-2, 'Li2O2': 2.5e-10, 'CO2': 1e-12})
result = odesys.integrate(tout, c0, atol=1e-12, rtol=1e-14)
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
for ax in axes:
_ = result.plot(names=[k for k in rsys.substances if k != 'Li2O2'], ax=ax)
_ = ax.legend(loc='best', prop={'size': 9})
_ = ax.set_xlabel('Time (hours)')
_ = ax.set_ylabel('Concentration')
_ = axes[1].set_ylim([1e-13, 1e-1])
_ = axes[1].set_xscale('log')
_ = axes[1].set_yscale('log')
_ = fig.tight_layout()
_ = fig.savefig('examples/attempt.png', dpi=72)

underdetermined set of equations giving negative numbers

in the code this comment states what happens when setting the underdertermined = none

underdetermined : bool
Allows to find a non-unique solution (in addition to a constant factor
across all terms). Set to False to disallow (raise ValueError) on
e.g. "C + O2 -> CO + CO2". Set to None if you want the symbols replaced
so that the coefficients are the smallest possible positive (non-zero) integers.

yet i ran several examples where i get back -ive values
example 1 of a result :

{'C10H15N5O10P2': -57, 'CH4NO5P': 70, 'C3H6O3': 35, 'H3PO4': -88}
{'C3H7NO2': 5, 'H2O': 5, 'HCO3': 30, 'C10H16N5O13P3': -44}

example 2:

{'C10H14N5O7P': -6, 'C3H6O3': 8, 'H4P2O7': 15, 'C4H8N2O3': 43}
{'C3H7NO2': 8, 'H2O': 64, 'C4H7NO4': 8, 'C10H16N5O13P3': 8}

Support for units in equilirbria

IUPAC defines: c⦵ = 1 mol/dm³
which by convention makes equiblirium constants unitless (ignorant chemists...)
Equilibrium needs to handle both unitless K (post-user-dedimensionalization) and K with units, e.g. by passing a kwarg:
c0=None => c⦵ = 1 mol/dm³
c0=False => K must carry appropriate units.

This has implications for scaling when "lambdifying" symbolic expressions.

How to plot sum of concentrations (question)?

I found this wonderful package ideal for modeling the kinetics of well known iodine clock reaction - https://arxiv.org/pdf/1808.06010.pdf
"""
H+ + OH- -> H2O; 1.4e+11
H2O -> H+ + OH-; 2.5e-5
I- + H2O2 -> HIO + OH-; 0.0115
I- + HIO -> I2 + OH-; 3.6e+9
I2 + OH- -> I- + HIO; 1.8e+11
I2 + I- -> I3-; 6.2e+9
I3- -> I2 + I-; 8.5e+6
I2 + C6H8O6 -> 2 H+ + 2 I- + C6H6O6; 1.2e+5
I3- + C6H8O6 -> 2 H+ + 3 I- + C6H6O6; 5.9e+5
"""
However, due to reversibility in the reaction I- + I2 <-> I3- it is much more convenient to plot the total concentration iodine as sum of I2 and I3-
it is possible to plot not only the concentration of individual reagents, but also the sum of concentration or difference?
Best Vladimir.

Deprecate chempy.util._expr?

The code is quite complicated and most (perhaps all?) functionality would be possible
to get through using e.g. SymPy directly. Related: #47

Conda install instructions don't work on macOS

Hi! This is related to openjournals/joss-reviews#565

The conda installation instructions don't work for me on macOS (although they do on Ubuntu). The error I receive is

$ conda create -n chempy -c bjodah chempy pytest
Solving environment: failed

PackagesNotFoundError: The following packages are not available from current channels:

  - chempy
  - pyneqsys[version='>=0.5.1']
  - sym[version='>=0.3.1']
  - chempy
  - pyodesys[version='>=0.11.6']

Current channels:

  - https://conda.anaconda.org/bjodah/osx-64
  - https://conda.anaconda.org/bjodah/noarch
  - https://repo.continuum.io/pkgs/main/osx-64
  - https://repo.continuum.io/pkgs/main/noarch
  - https://repo.continuum.io/pkgs/free/osx-64
  - https://repo.continuum.io/pkgs/free/noarch
  - https://repo.continuum.io/pkgs/r/osx-64
  - https://repo.continuum.io/pkgs/r/noarch
  - https://repo.continuum.io/pkgs/pro/osx-64
  - https://repo.continuum.io/pkgs/pro/noarch
  - https://conda.anaconda.org/conda-forge/osx-64
  - https://conda.anaconda.org/conda-forge/noarch

You should list in the documentation/README which platforms are supported.

Non-integer subscripts in formulas

Hi,

Is it possible in ChemPy to parse formulas for substances that have non-integer subscripts?

e.g.

'Ca2.832Fe0.6285Mg2.5395(CO3)6'

If so, how would you format the formula above to achieve the desired result?

Right now, I get the following:

cp.Substance.from_formula('Ca2.832Fe0.6285Mg2.5395(CO3)6')

Ca2⋅832Fe0⋅6285Mg2⋅5395(CO3)6

Using formulas like this is a common need when working with natural minerals that behave as a solid solution. I thought about just increasing the subscripts until they are all integers, but would then have a molar mass that is incorrect.

image

pytest -rs --pyargs chempy ERROR

I've been unable to pip install chempy on my Mac running Python 3.7. After I run the pip install command. I run purest -rs -pyargs chempy and get the following error:

====================================== test session starts =======================================
platform darwin -- Python 3.6.6, pytest-3.7.4, py-1.6.0, pluggy-0.7.1
rootdir: /Users/skmcneill, inifile:
plugins: remotedata-0.3.0, openfiles-0.3.0, doctestplus-0.1.3, arraydiff-0.2

================================== no tests ran in 0.00 seconds ==================================
ERROR: file or package not found: chempy (missing __init__.py?)

balance_stoichiometry fails for larger problems

Thanks to Colin Lam for reporting this:

>>> from chempy import balance_stoichiometry
>>> reac, prod = balance_stoichiometry( { "C3H5NO", "H2", } , { "CH4", "NH3", "H2O", } )
>>> reac
{'H2': 6, 'C3H5NO': 1}
>>> prod
{'H2O': 1, 'NH3': 1, 'CH4': 3}
>>> reac, prod = balance_stoichiometry( { "C3H5NO", "CH4", "NH3", "H2O", } , { "C2H6", "CH4O", "CH5N", "CH3N", } )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/Work/psi4conda/envs/my-rdkit-env/lib/python3.6/site-packages/chempy/chemistry.py", line 1159, in balance_stoichiometry
    nullsp, = A.nullspace()
ValueError: too many values to unpack (expected 1)

Larger matrices have multiple

Install fails for conda

Running conda install -c bjodah chempy pytest., I get the following error:

(base) CHEM-67816:~ n00002621$ conda install -c bjodah chempy pytest
Collecting package metadata: done
Solving environment: failed

PackagesNotFoundError: The following packages are not available from current channels:

  - chempy -> pulp[version='>=1.6.8']
  - chempy -> pyodesys[version='>=0.12.4'] -> pycvodes[version='>=0.11.9']
  - chempy -> pyodesys[version='>=0.12.4'] -> pygslodeiv2[version='>=0.9.1']
  - chempy -> pyodesys[version='>=0.12.4'] -> pyodeint[version='>=0.10.1']
  - chempy -> pyodesys[version='>=0.12.4'] -> pysym
  - chempy -> pyodesys[version='>=0.12.4'] -> python-symengine
  - chempy -> pyodesys[version='>=0.12.4'] -> symcxx
  - chempy -> quantities[version='>=0.12.1']

Expected type 'bool', got 'None' instead.

Code such as the following triggers a warning in PyCharm: Expected type 'bool', got 'None' instead.

b = balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'}, underdetermined=None)

I don't know if this is truly an issue, mentioning this just in case it is helpful. Closing this issue would not hurt my feelings.

examples/demo_kinetics.py throws TypeError

This is related to the review: openjournals/joss-reviews#565

In trying to verify the functionality of the code, I ran into an error running examples/demo_kinetics.py. Running on Ubuntu 16.04, I installed ChemPy 0.6.5 from conda in a Python 3.6 environment, then cloned this repository, then

cd examples
python demo_kinetics.py

which results in

Traceback (most recent call last):
  File "demo_kinetics.py", line 37, in <module>
    import argh
ModuleNotFoundError: No module named 'argh'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "demo_kinetics.py", line 39, in <module>
    main()
  File "demo_kinetics.py", line 25, in main
    expr = f(*args, **kwargs)
TypeError: pseudo_irrev() got an unexpected keyword argument 'exp'

AttributeError: type object 'Substance' has no attribute 'from_formula'

While trying to replicate the sample application at http://hera.physchem.kth.se/~chempy/branches/master/examples/aqueous_radiolysis.html I installed a fresh copy of Anaconda (linux, x64), installed chempy via

conda install -c https://conda.anaconda.org/bjodah chempy

and ran a slightly modified version of the radiolysis example code.

This failed with AttributeError: type object 'Substance' has no attribute 'from_formula' so I reduced the sample code to a minimal viable example (attached)

c2.zip

which is:

from chempy.chemistry import Substance

_species = ['H', 'H+', 'H2', 'e-(aq)', 'HO2-', 'HO2', 'H2O2', 'HO3', 'O-', 'O2', 'O2-', 'O3', 'O3-', 'OH', 'OH-', 'H2O']

species = [Substance.from_formula(s) for s in _species]

Strangely, the later code which builds the reactions list of Reaction() objects from the _reactions dict seems to work, indicating that there is not a generic problem resolving method names in the chempy.chemistry namespace. The problem seems isolated to chempy.chemistry.Substance

Also, I was able to get this part of the sample code to work successfully using my distribution's stock python installation, though the sample code would fail later for other reasons which is why I installed Anaconda.

Check if reaction is stoichiometry balanced

Is there are why to check if a chemical equation is stoichiometry balanced?

For example, "C5H6N2O5 + H + HO4P = C4H6N1O4 + C1H2N1O5P1" is balanced and should return True. "C5H6N2O5 + HO4P = C4H6N1O4 + C1H2N1O5P1" is unbalanced and should return false.

Balancing half-reactions

It is quite trivial but would still be useful to provide functions to balance half reactions, e.g.:

MnO4- + 8H+ + 5e- -> Mn2+ + 4H2O
Fe2+ -> Fe3+ + e-

perhaps:

>>> balance([permanganate, ferrous], 'e-')
[1, 5]

need to contemplate if this the most general (and canonical) way to represent this.

Equilibrium root solver fails when some concentrations are 0

Hi! First of all, thanks so much for this package, I really appreciate it!

This is a minor issue: I'm using the Eqsys.root function to solve for equilibrium concentrations in order to analyze a titration curve measured by UV-VIS. I'm trying to estimate extinction coefficients of unknown complexes by simulating these concentrations and noticed that the solver fails when one of my concentrations is 0 i.e. the beginning point of my titration before any addition. While I can work around it, maybe it would be possible to catch this exception before passing the values to the solver and just return the input concentration? Might make more sense than returning a warning and some non-physical concentrations.

Here's an example:

Example_root_fail.txt

atomic weight concerns

The atomic weights for Cn and Fl are too low. Also, as indicated by the crash, an atomic weight appears to be absent.

from chempy import Substance
from chempy.util.periodic import symbols

for symbol in symbols:
        print(symbol + ' ' + '%.4f' % Substance.from_formula(symbol).mass)

H 1.0080
He 4.0026
Li 6.9400
Be 9.0122
B 10.8100
C 12.0110
N 14.0070
O 15.9990
F 18.9984
Ne 20.1797
Na 22.9898
Mg 24.3050
Al 26.9815
Si 28.0850
P 30.9738
S 32.0600
Cl 35.4500
Ar 39.9480
K 39.0983
Ca 40.0780
Sc 44.9559
Ti 47.8670
V 50.9415
Cr 51.9961
Mn 54.9380
Fe 55.8450
Co 58.9332
Ni 58.6934
Cu 63.5460
Zn 65.3800
Ga 69.7230
Ge 72.6300
As 74.9216
Se 78.9710
Br 79.9040
Kr 83.7980
Rb 85.4678
Sr 87.6200
Y 88.9058
Zr 91.2240
Nb 92.9064
Mo 95.9500
Tc 98.0000
Ru 101.0700
Rh 102.9055
Pd 106.4200
Ag 107.8682
Cd 112.4140
In 114.8180
Sn 118.7100
Sb 121.7600
Te 127.6000
I 126.9045
Xe 131.2930
Cs 132.9055
Ba 137.3270
La 138.9055
Ce 140.1160
Pr 140.9077
Nd 144.2420
Pm 145.0000
Sm 150.3600
Eu 151.9640
Gd 157.2500
Tb 158.9254
Dy 162.5000
Ho 164.9303
Er 167.2590
Tm 168.9342
Yb 173.0450
Lu 174.9668
Hf 178.4900
Ta 180.9479
W 183.8400
Re 186.2070
Os 190.2300
Ir 192.2170
Pt 195.0840
Au 196.9666
Hg 200.5920
Tl 204.3800
Pb 207.2000
Bi 208.9804
Po 209.0000
At 210.0000
Rn 222.0000
Fr 223.0000
Ra 226.0000
Ac 227.0000
Th 232.0377
Pa 231.0359
U 238.0289
Np 237.0000
Pu 244.0000
Am 243.0000
Cm 247.0000
Bk 247.0000
Cf 251.0000
Es 252.0000
Fm 257.0000
Md 258.0000
No 259.0000
Lr 266.0000
Rf 267.0000
Db 268.0000
Sg 269.0000
Bh 270.0000
Hs 269.0000
Mt 278.0000
Ds 281.0000
Rg 282.0000
Cn 12.0110
Uut 286.0000
Fl 18.9984
Uup 289.0000
Traceback (most recent call last):
File "/Users/matecsaj/Dropbox (Personal)/Projects/electrochemistry/main.py", line 5, in
sub = Substance.from_formula(symbol)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/chempy/chemistry.py", line 180, in from_formula
composition=formula_to_composition(formula),
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/chempy/util/parsing.py", line 281, in formula_to_composition
comp = _parse_stoich(stoich)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/chempy/util/parsing.py", line 209, in _parse_stoich
in _get_formula_parser().parseString(stoich)}
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pyparsing.py", line 1955, in parseString
raise exc
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pyparsing.py", line 3342, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected {Re:('A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|S[bcegimnr]?|T[abcehilm]|Uu[bhopqst]|U|V|W|Xe|Yb?|Z[nr]') | Group:({{Suppress:("(") : ...} Suppress:(")")})}, found 'L' (at char 0), (line:1, col:1)

Process finished with exit code 1

NumLinSys

Bad due to shifting:
C1*C2/(K*C3) - 1

better:
C1*C2 - K*C3

2 tests fail on FreeBSD

============================= test session starts ==============================
platform freebsd11 -- Python 2.7.15, pytest-3.6.4, py-1.6.0, pluggy-0.6.0
rootdir: /usr/ports/science/py-chempy/work-py27/chempy-0.7.2, inifile: setup.cfg
collected 304 items

chempy/electrochemistry/tests/test_nernst.py ..                          [  0%]
chempy/kinetics/tests/test__native.py ssssssssss                         [  3%]
chempy/kinetics/tests/test__rates.py ..................                  [  9%]
chempy/kinetics/tests/test_arrhenius.py ...                              [ 10%]
chempy/kinetics/tests/test_eyring.py ...                                 [ 11%]
chempy/kinetics/tests/test_integrated.py ....                            [ 13%]
chempy/kinetics/tests/test_ode.py ..........................ssssssss.sss [ 25%]
ssssss                                                                   [ 27%]
chempy/kinetics/tests/test_rates.py ................                     [ 32%]
chempy/printing/tests/test_numbers.py ......                             [ 34%]
chempy/printing/tests/test_str.py .                                      [ 35%]
chempy/printing/tests/test_table.py .                                    [ 35%]
chempy/properties/tests/test_sulfuric_acid_density_myhre_1998.py ...     [ 36%]
chempy/properties/tests/test_water_density_tanaka_2001.py .              [ 36%]
chempy/properties/tests/test_water_diffusivity_holz_2000.py .            [ 37%]
chempy/properties/tests/test_water_permittivity_bradley_pitzer_1979.py . [ 37%]
.                                                                        [ 37%]
chempy/properties/tests/test_water_viscosity_korson_1969.py .            [ 38%]
chempy/tests/test__equilibrium.py ....                                   [ 39%]
chempy/tests/test_chemistry.py ......................F.F....             [ 49%]
chempy/tests/test_core.py .                                              [ 49%]
chempy/tests/test_einstein_smoluchowski.py ..                            [ 50%]
chempy/tests/test_electrolytes.py .......                                [ 52%]
chempy/tests/test_equilibria.py .........                                [ 55%]
chempy/tests/test_henry.py ...                                           [ 56%]
chempy/tests/test_reactionsystem.py ..................                   [ 62%]
chempy/tests/test_solution.py .....                                      [ 63%]
chempy/tests/test_units.py .............................                 [ 73%]
chempy/tests/test_util.py ..                                             [ 74%]
chempy/thermodynamics/tests/test_expressions.py .......                  [ 76%]
chempy/util/tests/test_arithmeticdict.py ..............                  [ 80%]
chempy/util/tests/test_expr.py ................................          [ 91%]
chempy/util/tests/test_graph.py ss                                       [ 92%]
chempy/util/tests/test_numutil.py .                                      [ 92%]
chempy/util/tests/test_parsing.py .....                                  [ 94%]
chempy/util/tests/test_periodic.py .....                                 [ 95%]
chempy/util/tests/test_pyutil.py ..                                      [ 96%]
chempy/util/tests/test_regression.py ..                                  [ 97%]
chempy/util/tests/test_stoich.py ....                                    [ 98%]
chempy/util/tests/test_table.py ..sss                                    [100%]

=================================== FAILURES ===================================
_________________ test_balance_stoichiometry__impossible[None] _________________

underdet = None

    @requires('sympy')
    @pytest.mark.parametrize('underdet', [False, None, True])
    def test_balance_stoichiometry__impossible(underdet):
        with pytest.raises(ValueError):
>           r1, p1 = balance_stoichiometry({'CO'}, {'CO2'}, underdetermined=underdet)

chempy/tests/test_chemistry.py:309: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
chempy/chemistry.py:1262: in balance_stoichiometry
    sol = Tuple(*[Integer(x) for x in _solve_balancing_ilp_pulp(A)])
chempy/chemistry.py:1111: in _solve_balancing_ilp_pulp
    prob.solve()
/usr/local/lib/python2.7/site-packages/pulp/pulp.py:1664: in solve
    status = solver.actualSolve(self, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pulp.solvers.GLPK_CMD instance at 0x8145aa878>
lp = chempy balancing problem:
MINIMIZE
1*x0 + 1*x1 + 0
SUBJECT TO
_C1: - x0 + x1 = 0

_C2: - x0 + 2 x1 = 0

VARIABLES
1 <= x0 Integer
1 <= x1 Integer


    def actualSolve(self, lp):
        """Solve a well formulated lp problem"""
        if not self.executable(self.path):
            raise PulpSolverError("PuLP: cannot execute "+self.path)
        if not self.keepFiles:
            uuid = uuid4().hex
            tmpLp = os.path.join(self.tmpDir, "%s-pulp.lp" % uuid)
            tmpSol = os.path.join(self.tmpDir, "%s-pulp.sol" % uuid)
        else:
            tmpLp = lp.name+"-pulp.lp"
            tmpSol = lp.name+"-pulp.sol"
        lp.writeLP(tmpLp, writeSOS = 0)
        proc = ["glpsol", "--cpxlp", tmpLp, "-o", tmpSol]
        if not self.mip: proc.append('--nomip')
        proc.extend(self.options)
    
        self.solution_time = clock()
        if not self.msg:
            proc[0] = self.path
            pipe = open(os.devnull, 'w')
            if operating_system == 'win':
                # Prevent flashing windows if used from a GUI application
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                rc = subprocess.call(proc, stdout = pipe, stderr = pipe,
                                     startupinfo = startupinfo)
            else:
                rc = subprocess.call(proc, stdout = pipe, stderr = pipe)
            if rc:
                raise PulpSolverError("PuLP: Error while trying to execute "+self.path)
        else:
            if os.name != 'nt':
                rc = os.spawnvp(os.P_WAIT, self.path, proc)
            else:
                rc = os.spawnv(os.P_WAIT, self.executable(self.path), proc)
            if rc == 127:
                raise PulpSolverError("PuLP: Error while trying to execute "+self.path)
        self.solution_time += clock()
    
        if not os.path.exists(tmpSol):
>           raise PulpSolverError("PuLP: Error while executing "+self.path)
E           PulpSolverError: PuLP: Error while executing glpsol

/usr/local/lib/python2.7/site-packages/pulp/solvers.py:400: PulpSolverError
----------------------------- Captured stdout call -----------------------------
GLPSOL: GLPK LP/MIP Solver, v4.65
Parameter(s) specified in the command line:
 --cpxlp /tmp/471fe6bdf4d242a594faa531c5d307b9-pulp.lp -o /tmp/471fe6bdf4d242a594faa531c5d307b9-pulp.sol
Reading problem data from '/tmp/471fe6bdf4d242a594faa531c5d307b9-pulp.lp'...
2 rows, 2 columns, 4 non-zeros
2 integer variables, none of which are binary
13 lines were read
GLPK Integer Optimizer, v4.65
2 rows, 2 columns, 4 non-zeros
2 integer variables, none of which are binary
Preprocessing...
Assertion failed: q->lb < q->ub
Error detected in file npp/npp3.c at line 556
_________________ test_balance_stoichiometry__underdetermined __________________

    @requires('sympy', 'pulp')
    def test_balance_stoichiometry__underdetermined():
        with pytest.raises(ValueError):
            balance_stoichiometry({'C2H6', 'O2'}, {'H2O', 'CO2', 'CO'}, underdetermined=False)
        reac, prod = balance_stoichiometry({'C2H6', 'O2'}, {'H2O', 'CO2', 'CO'})
    
        r1 = {'C7H5O3-', 'O2', 'C21H27N7O14P2-2', 'H+'}
        p1 = {'C7H5O4-', 'C21H26N7O14P2-', 'H2O'}  # see https://github.com/bjodah/chempy/issues/67
        bal1 = balance_stoichiometry(r1, p1, underdetermined=None)
        assert bal1 == ({'C21H27N7O14P2-2': 1, 'H+': 1, 'C7H5O3-': 1, 'O2': 1},
                        {'C21H26N7O14P2-': 1, 'H2O': 1, 'C7H5O4-': 1})
    
        with pytest.raises(ValueError):
            balance_stoichiometry({'C3H4O3', 'H3PO4'}, {'C3H6O3'}, underdetermined=None)
    
        for underdet in [False, True, None]:
            with pytest.raises(ValueError):
>               balance_stoichiometry({'C3H6O3'}, {'C3H4O3'}, underdetermined=underdet)

chempy/tests/test_chemistry.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
chempy/chemistry.py:1262: in balance_stoichiometry
    sol = Tuple(*[Integer(x) for x in _solve_balancing_ilp_pulp(A)])
chempy/chemistry.py:1111: in _solve_balancing_ilp_pulp
    prob.solve()
/usr/local/lib/python2.7/site-packages/pulp/pulp.py:1664: in solve
    status = solver.actualSolve(self, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pulp.solvers.GLPK_CMD instance at 0x8145aa878>
lp = chempy balancing problem:
MINIMIZE
1*x0 + 1*x1 + 0
SUBJECT TO
_C1: - 6 x0 + 4 ... + 3 x1 = 0

_C3: - 3 x0 + 3 x1 = 0

VARIABLES
1 <= x0 Integer
1 <= x1 Integer


    def actualSolve(self, lp):
        """Solve a well formulated lp problem"""
        if not self.executable(self.path):
            raise PulpSolverError("PuLP: cannot execute "+self.path)
        if not self.keepFiles:
            uuid = uuid4().hex
            tmpLp = os.path.join(self.tmpDir, "%s-pulp.lp" % uuid)
            tmpSol = os.path.join(self.tmpDir, "%s-pulp.sol" % uuid)
        else:
            tmpLp = lp.name+"-pulp.lp"
            tmpSol = lp.name+"-pulp.sol"
        lp.writeLP(tmpLp, writeSOS = 0)
        proc = ["glpsol", "--cpxlp", tmpLp, "-o", tmpSol]
        if not self.mip: proc.append('--nomip')
        proc.extend(self.options)
    
        self.solution_time = clock()
        if not self.msg:
            proc[0] = self.path
            pipe = open(os.devnull, 'w')
            if operating_system == 'win':
                # Prevent flashing windows if used from a GUI application
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                rc = subprocess.call(proc, stdout = pipe, stderr = pipe,
                                     startupinfo = startupinfo)
            else:
                rc = subprocess.call(proc, stdout = pipe, stderr = pipe)
            if rc:
                raise PulpSolverError("PuLP: Error while trying to execute "+self.path)
        else:
            if os.name != 'nt':
                rc = os.spawnvp(os.P_WAIT, self.path, proc)
            else:
                rc = os.spawnv(os.P_WAIT, self.executable(self.path), proc)
            if rc == 127:
                raise PulpSolverError("PuLP: Error while trying to execute "+self.path)
        self.solution_time += clock()
    
        if not os.path.exists(tmpSol):
>           raise PulpSolverError("PuLP: Error while executing "+self.path)
E           PulpSolverError: PuLP: Error while executing glpsol

/usr/local/lib/python2.7/site-packages/pulp/solvers.py:400: PulpSolverError
----------------------------- Captured stdout call -----------------------------
GLPSOL: GLPK LP/MIP Solver, v4.65
Parameter(s) specified in the command line:
 --cpxlp /tmp/ef48038bc32a4666ba31005b7360f341-pulp.lp -o /tmp/ef48038bc32a4666ba31005b7360f341-pulp.sol
Reading problem data from '/tmp/ef48038bc32a4666ba31005b7360f341-pulp.lp'...
6 rows, 7 columns, 25 non-zeros
7 integer variables, none of which are binary
27 lines were read
GLPK Integer Optimizer, v4.65
6 rows, 7 columns, 25 non-zeros
7 integer variables, none of which are binary
Preprocessing...
6 rows, 7 columns, 25 non-zeros
7 integer variables, none of which are binary
Scaling...
 A: min|aij| =  1.000e+00  max|aij| =  2.700e+01  ratio =  2.700e+01
GM: min|aij| =  4.429e-01  max|aij| =  2.258e+00  ratio =  5.099e+00
EQ: min|aij| =  1.961e-01  max|aij| =  1.000e+00  ratio =  5.099e+00
2N: min|aij| =  2.500e-01  max|aij| =  1.688e+00  ratio =  6.750e+00
Constructing initial basis...
Size of triangular part is 5
Solving LP relaxation...
GLPK Simplex Optimizer, v4.65
6 rows, 7 columns, 25 non-zeros
*     0: obj =   7.000000000e+00 inf =   0.000e+00 (0)
OPTIMAL LP SOLUTION FOUND
Integer optimization begins...
Long-step dual simplex will be used
+     0: mip =     not found yet >=              -inf        (1; 0)
+     0: >>>>>   7.000000000e+00 >=   7.000000000e+00   0.0% (1; 0)
+     0: mip =   7.000000000e+00 >=     tree is empty   0.0% (0; 1)
INTEGER OPTIMAL SOLUTION FOUND
Time used:   0.0 secs
Memory used: 0.1 Mb (54667 bytes)
Writing MIP solution to '/tmp/ef48038bc32a4666ba31005b7360f341-pulp.sol'...
GLPSOL: GLPK LP/MIP Solver, v4.65
Parameter(s) specified in the command line:
 --cpxlp /tmp/69137e31be2c4309ae5d4d9c4fa46f02-pulp.lp -o /tmp/69137e31be2c4309ae5d4d9c4fa46f02-pulp.sol
Reading problem data from '/tmp/69137e31be2c4309ae5d4d9c4fa46f02-pulp.lp'...
3 rows, 2 columns, 6 non-zeros
2 integer variables, none of which are binary
14 lines were read
GLPK Integer Optimizer, v4.65
3 rows, 2 columns, 6 non-zeros
2 integer variables, none of which are binary
Preprocessing...
Assertion failed: q->lb < q->ub
Error detected in file npp/npp3.c at line 556
=========================== short test summary info ============================
SKIP [1] chempy/kinetics/tests/test_ode.py:539: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] chempy/kinetics/tests/test__native.py:108: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:497: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] chempy/util/tests/test_table.py:46: latex not installed? (pdflatex command missing)
SKIP [1] chempy/kinetics/tests/test_ode.py:569: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:858: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] chempy/kinetics/tests/test__native.py:89: Unfulfilled requirements. Missing modules: pygslodeiv2.
SKIP [1] chempy/util/tests/test_graph.py:30: graphviz not installed? (dot command missing)
SKIP [1] chempy/kinetics/tests/test_ode.py:757: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] chempy/kinetics/tests/test__native.py:49: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:675: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:949: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:808: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:915: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/util/tests/test_table.py:58: latex not installed? (pdflatex command missing)
SKIP [4] chempy/kinetics/tests/test_ode.py:475: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [3] chempy/kinetics/tests/test__native.py:181: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:710: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test__native.py:156: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/kinetics/tests/test_ode.py:634: Unfulfilled requirements. Missing modules: pygslodeiv2.
SKIP [1] chempy/kinetics/tests/test_ode.py:510: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] chempy/util/tests/test_graph.py:48: graphviz not installed? (dot command missing)
SKIP [1] chempy/kinetics/tests/test_ode.py:655: Unfulfilled requirements. Missing modules: pyodeint.
=============================== warnings summary ===============================
chempy/kinetics/tests/test_ode.py::test_get_ode__Radiolytic__units__multi
  /usr/local/lib/python2.7/site-packages/pyodesys/core.py:519: UserWarning: 'adaptive' mode with SciPy's integrator (vode/lsoda) may overshoot (itask=2)
    warnings.warn("'adaptive' mode with SciPy's integrator (vode/lsoda) may overshoot (itask=2)")
  /usr/local/lib/python2.7/site-packages/pyodesys/core.py:520: UserWarning: 'adaptive' mode with SciPy's integrator is unreliable, consider using e.g. cvode
    warnings.warn("'adaptive' mode with SciPy's integrator is unreliable, consider using e.g. cvode")

chempy/kinetics/tests/test_rates.py::test_RateExpr__subclass_from_callback
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/kinetics/tests/test_rates.py:52: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use from_callback instead.
    lambda v, a, backend: a[0]*v['H2']*v['Br2']**(3/2) / (v['Br2'] + a[1]*v['HBr'])

chempy/kinetics/tests/test_rates.py::test_MassAction__subclass_from_callback
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/kinetics/tests/test_rates.py:100: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use MassAction.from_callback instead.
    rate_coeff, cls_attrs=dict(parameter_keys=('temperature',), nargs=2))
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/kinetics/rates.py:180: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use from_callback instead.
    _RateExpr = super(MassAction, cls).subclass_from_callback(cb, cls_attrs=cls_attrs)

chempy/tests/test__equilibrium.py::test_solve_equilibrium_1
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/chemistry.py:859: RuntimeWarning: divide by zero encountered in double_scalars
    tot *= conc**nr

chempy/tests/test_henry.py::test_Henry__with_units
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/tests/test_henry.py:23: ChemPyDeprecationWarning: get_kH_at_T is deprecated since (not including) 0.3.1, it will be missing in 0.5.0. Use __call__ instead.
    assert allclose(kH_H2.get_kH_at_T(

chempy/tests/test_reactionsystem.py::test_ReactionSystem__html_tables
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/tests/test_reactionsystem.py:101: ChemPyDeprecationWarning: unimolecular_html_table is deprecated since (not including) 0.5.7, it will be missing in 0.8.0. Use chempy.printing.tables.UnimolecularTable instead.
    ut, unc = rs.unimolecular_html_table()
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/tests/test_reactionsystem.py:106: ChemPyDeprecationWarning: bimolecular_html_table is deprecated since (not including) 0.5.7, it will be missing in 0.8.0. Use chempy.printing.tables.BimolecularTable instead.
    bt, bnc = rs.bimolecular_html_table()

chempy/util/tests/test_expr.py::test_mk_Poly
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/tests/test_expr.py:165: ChemPyDeprecationWarning: _mk_Poly is deprecated. Use create_Poly instead.
    Poly = mk_Poly('T', reciprocal=True)

chempy/util/tests/test_expr.py::test_PiecewisePoly
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/tests/test_expr.py:208: ChemPyDeprecationWarning: _mk_PiecewisePoly is deprecated. Use create_Piecewise instead.
    TPiecewisePoly = mk_PiecewisePoly('temperature')

chempy/util/tests/test_parsing.py::test_formula_to_composition
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/tests/test_parsing.py:16: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    assert formula_to_composition('Fe/3+') == {0: 3, 26: 1}
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/tests/test_parsing.py:16: ChemPyDeprecationWarning: 'Fe/3+' deprecated, use e.g. 'Fe+3'
    assert formula_to_composition('Fe/3+') == {0: 3, 26: 1}

chempy/util/tests/test_parsing.py::test_formula_to_latex
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/parsing.py:435: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    formula, prefixes, infixes, **kwargs)
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/parsing.py:435: ChemPyDeprecationWarning: 'Fe/3+' deprecated, use e.g. 'Fe+3'
    formula, prefixes, infixes, **kwargs)

chempy/util/tests/test_periodic.py::test_mass_from_composition__formula
  /usr/ports/science/py-chempy/work-py27/chempy-0.7.2/chempy/util/tests/test_periodic.py:44: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    Fminus = mass_from_composition(formula_to_composition('F/-'))

-- Docs: http://doc.pytest.org/en/latest/warnings.html
======== 2 failed, 270 passed, 32 skipped, 16 warnings in 10.23 seconds ========

Version 0.7.2

square brackets support

I tried to parse "[Fe(CN)6]-4" and got ParseException:

In [2]: import chempy.util.parsing as chem

In [3]: comp = chem.formula_to_composition("[Fe(CN)6]-4")
---------------------------------------------------------------------------
ParseException                            Traceback (most recent call last)
<ipython-input-3-da99372ece8d> in <module>()
----> 1 comp = chem.formula_to_composition("[Fe(CN)6]-4")

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/chempy/util/parsing.py in formula_to_composition(formula, prefixes, suffixes)
    247         else:
    248             m, stoich = _get_leading_integer(stoich)
--> 249         comp = _parse_stoich(stoich)
    250         for k, v in comp.items():
    251             if k not in tot_comp:

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/chempy/util/parsing.py in _parse_stoich(stoich)
    175         return {}
    176     return {symbols.index(k)+1: n for k, n
--> 177             in _get_formula_parser().parseString(stoich)}
    178
    179 _greek_letters = (

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseString(self, instring, parseAll)
   1630             else:
   1631                 # catch and re-raise exception from here, clears out pyparsing internal stack trace
-> 1632                 raise exc
   1633         else:
   1634             return tokens

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseString(self, instring, parseAll)
   1620             instring = instring.expandtabs()
   1621         try:
-> 1622             loc, tokens = self._parse( instring, 0 )
   1623             if parseAll:
   1624                 loc = self.preParse( instring, loc )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1377             if self.mayIndexError or loc >= len(instring):
   1378                 try:
-> 1379                     loc,tokens = self.parseImpl( instring, preloc, doActions )
   1380                 except IndexError:
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3715     def parseImpl( self, instring, loc, doActions=True ):
   3716         if self.expr is not None:
-> 3717             return self.expr._parse( instring, loc, doActions, callPreParse=False )
   3718         else:
   3719             raise ParseException("",loc,self.errmsg,self)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1377             if self.mayIndexError or loc >= len(instring):
   1378                 try:
-> 1379                     loc,tokens = self.parseImpl( instring, preloc, doActions )
   1380                 except IndexError:
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3846         if check_ender:
   3847             try_not_ender(instring, loc)
-> 3848         loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
   3849         try:
   3850             hasIgnoreExprs = (not not self.ignoreExprs)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1377             if self.mayIndexError or loc >= len(instring):
   1378                 try:
-> 1379                     loc,tokens = self.parseImpl( instring, preloc, doActions )
   1380                 except IndexError:
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3715     def parseImpl( self, instring, loc, doActions=True ):
   3716         if self.expr is not None:
-> 3717             return self.expr._parse( instring, loc, doActions, callPreParse=False )
   3718         else:
   3719             raise ParseException("",loc,self.errmsg,self)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1377             if self.mayIndexError or loc >= len(instring):
   1378                 try:
-> 1379                     loc,tokens = self.parseImpl( instring, preloc, doActions )
   1380                 except IndexError:
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3376         # pass False as last arg to _parse for first element, since we already
   3377         # pre-parsed the string as part of our And pre-parsing
-> 3378         loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False )
   3379         errorStop = False
   3380         for e in self.exprs[1:]:

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1377             if self.mayIndexError or loc >= len(instring):
   1378                 try:
-> 1379                     loc,tokens = self.parseImpl( instring, preloc, doActions )
   1380                 except IndexError:
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3543             if maxException is not None:
   3544                 maxException.msg = self.errmsg
-> 3545                 raise maxException
   3546             else:
   3547                 raise ParseException(instring, loc, "no defined alternatives to match", self)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   3528         for e in self.exprs:
   3529             try:
-> 3530                 ret = e._parse( instring, loc, doActions )
   3531                 return ret
   3532             except ParseException as err:

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in _parseNoCache(self, instring, loc, doActions, callPreParse)
   1381                     raise ParseException( instring, len(instring), self.errmsg, self )
   1382             else:
-> 1383                 loc,tokens = self.parseImpl( instring, preloc, doActions )
   1384
   1385         tokens = self.postParse( instring, loc, tokens )

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py in parseImpl(self, instring, loc, doActions)
   2792         result = self.re.match(instring,loc)
   2793         if not result:
-> 2794             raise ParseException(instring, loc, self.errmsg, self)
   2795
   2796         loc = result.end()

ParseException: Expected {Re:('A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|S[bcegimnr]?|T[abcehilm]|Uu[bhopqst]|U|V|W|Xe|Yb?|Z[nr]') | Group:({Suppress:("(") Forward: ... Suppress:(")")})} (at char 0), (line:1, col:1)

The problem is in square brackets I guess, without them parsing works well:

In [5]: chem.formula_to_composition("Fe(CN)6-4")
Out[5]: {26: 1, 6: 6, 7: 6, 0: -4}

trouble with get_odesys()

I can't seem to run one of the tests. Is there an issue with the version I am running or my setup?

~ ipython
Python 3.5.2 |Anaconda custom (x86_64)| (default, Jul  2 2016, 17:52:12)
Type "copyright", "credits" or "license" for more information.

IPython 5.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]:

In [1]:

In [1]:

In [1]: from chempy.arrhenius import ArrheniusParam
   ...: from chempy.chemistry import Substance, Reaction, ReactionSystem
   ...: from chempy.units import (
   ...:     SI_base_registry, get_derived_unit, allclose, units_library,
   ...:     to_unitless, default_units as u
   ...: )
   ...:
/Users/saladi/anaconda3/lib/python3.5/site-packages/chempy/arrhenius.py:13: ChemPyDeprecationWarning: use .kinetics.arrhenius instead of .arrhenius
  warnings.warn("use .kinetics.arrhenius instead of .arrhenius", ChemPyDeprecationWarning)

In [2]: from chempy.kinetics.ode import get_odesys

In [3]: def test_get_odesys_1():
   ...:     k = .2
   ...:     a = Substance('A')
   ...:     b = Substance('B')
   ...:     r = Reaction({'A': 1}, {'B': 1}, param=k)
   ...:     rsys = ReactionSystem([r], [a, b])
   ...:     odesys = get_odesys(rsys, include_params=True)[0]
   ...:     c0 = {
   ...:         'A': 1.0,
   ...:         'B': 3.0,
   ...:     }
   ...:     t = np.linspace(0.0, 10.0)
   ...:     xout, yout, info = odesys.integrate(t, rsys.as_per_substance_array(c0))
   ...:     yref = np.zeros((t.size, 2))
   ...:     yref[:, 0] = np.exp(-k*t)
   ...:     yref[:, 1] = 4 - np.exp(-k*t)
   ...:     assert np.allclose(yout, yref)
   ...:

In [4]: test_get_odesys_1()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-e26790a55311> in <module>()
----> 1 test_get_odesys_1()

<ipython-input-3-89f86ce7db8a> in test_get_odesys_1()
      5     r = Reaction({'A': 1}, {'B': 1}, param=k)
      6     rsys = ReactionSystem([r], [a, b])
----> 7     odesys = get_odesys(rsys, include_params=True)[0]
      8     c0 = {
      9         'A': 1.0,

/Users/saladi/anaconda3/lib/python3.5/site-packages/chempy/kinetics/ode.py in get_odesys(rsys, include_params, substitutions, SymbolicSys, unit_registry, output_conc_unit, output_time_unit, **kwargs)
    227         dydt, len(substance_keys),
    228         len(param_keys) + (0 if include_params else len(unique_keys)),
--> 229         **kwargs), param_keys, unique_keys, p_units

/Users/saladi/anaconda3/lib/python3.5/site-packages/pyodesys/symbolic.py in from_callback(cls, cb, ny, nparams, **kwargs)
    117         An instance of :class:`SymbolicSys`.
    118         """
--> 119         be = Backend(kwargs.pop('backend', None))
    120         x, = be.real_symarray('x', 1)
    121         y = be.real_symarray('y', ny)

TypeError: object() takes no parameters

In [5]: import chempy

In [6]: chempy.__version__
Out[6]: '0.4.1'

running an example in README.rst triggers a warning and a diagnostic dump

from chempy import balance_stoichiometry
from pprint import pprint

pprint([dict(_) for _ in balance_stoichiometry({'C', 'O2'}, {'CO2', 'CO'}, underdetermined=None)])

/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pulp/pulp.py:1190: UserWarning: Spaces are not permitted in the name. Converted to ''
warnings.warn("Spaces are not permitted in the name. Converted to '
'")
Welcome to the CBC MILP Solver
Version: 2.9.0
Build Date: Feb 12 2015

command line - /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pulp/apis/../solverdir/cbc/osx/64/cbc /var/folders/jy/txq0svb52s5drzcmw93j87m00000gn/T/a513b22ee4b14893a06dbdbd9d0b79f3-pulp.mps branch printingOptions all solution /var/folders/jy/txq0svb52s5drzcmw93j87m00000gn/T/a513b22ee4b14893a06dbdbd9d0b79f3-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 7 COLUMNS
At line 26 RHS
At line 29 BOUNDS
At line 34 ENDATA
Problem MODEL has 2 rows, 4 columns and 6 elements
Coin0008I MODEL read with 0 errors
Continuous objective value is 5.5 - 0.00 seconds
Cgl0003I 0 fixed, 3 tightened bounds, 0 strengthened rows, 0 substitutions
Cgl0004I processed model has 1 rows, 3 columns (3 integer (0 of which binary)) and 3 elements
Cutoff increment increased from 1e-05 to 0.9999
Cbc0012I Integer solution of 8 found by DiveCoefficient after 0 iterations and 0 nodes (0.00 seconds)
Cbc0031I 1 added rows had average density of 1
Cbc0013I At root node, 1 cuts changed objective from 7 to 8 in 3 passes
Cbc0014I Cut generator 0 (Probing) - 0 row cuts average 0.0 elements, 1 column cuts (1 active) in 0.000 seconds - new frequency is 1
Cbc0014I Cut generator 1 (Gomory) - 1 row cuts average 1.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is 1
Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is -100
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is -100
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is -100
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is -100
Cbc0001I Search completed - best objective 8, took 0 iterations and 0 nodes (0.00 seconds)
Cbc0035I Maximum depth 0, 0 variables fixed on reduced cost
Cuts at root node changed objective from 7 to 8
Probing was tried 3 times and created 1 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Gomory was tried 3 times and created 1 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Knapsack was tried 3 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Clique was tried 3 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
MixedIntegerRounding2 was tried 3 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
FlowCover was tried 3 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
TwoMirCuts was tried 1 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)

Result - Optimal solution found

Objective value: 8.00000000
Enumerated nodes: 0
Total iterations: 0
Time (CPU seconds): 0.00
Time (Wallclock seconds): 0.00

Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.01

[{'C': 3, 'O2': 2}, {'CO': 2, 'CO2': 1}]

passing reaction params through to get_odesys

In the process of setting up a system, I specify a param to reactions, but don't see them passed through when create the ODE. See below. I think I have misunderstood the API here. Could you lend a hand please?

In [56]: def mono_rev_single():
    ...:     '''Reversible mechanism with one central complex (pg 36)
    ...:     '''
    ...:     R1, R3 = sp.symbols('R_1 R_3')
    ...:
    ...:     # reactants and products
    ...:     E, A, P = map(Substance, ['E', 'A', 'P'])
    ...:
    ...:     # enzyme complexes
    ...:     X = Substance('X')
    ...:
    ...:     reactions = r1, r3 = [
    ...:         Equilibrium({'E', 'A'}, {'X'}, param=R1),
    ...:         Equilibrium({'X'}, {'E', 'P'}, param=R3)
    ...:     ]
    ...:     return ReactionSystem(reactions, (E, A, P, X))
    ...:

In [57]: rsys = mono_rev_single()

In [58]: rode = get_odesys(rsys)[0]

In [59]: rode.params
Out[59]: array([], dtype=object)

In [60]: rsys.params()
Out[60]: [R_1, R_3]

In [65]: rode = get_odesys(rsys, include_params=False)[0]

In [66]: rode.params
Out[66]: array([], dtype=object)

In [69]: print(rode.get_jac())
Matrix([[-R_1*y_1, -R_1*y_0, 0, R_3], [-R_1*y_1, -R_1*y_0, 0, 0], [0, 0, 0, R_3], [R_1*y_1, R_1*y_0, 0, -R_3]])

Which version of Anaconda can run “conda install -c bjodah chempy pytest” correctly

I tried to build the image with the dockerfile below

FROM continuumio/anaconda3:5.3.0
RUN conda install -y -c bjodah chempy pytest &&  \
    conda clean -y -all

it will downgrade the python version to 2.7

The following packages will be DOWNGRADED:

    gstreamer:                     1.14.0-hb453b48_1              --> 1.14.0-hb31296c_0      
    numpy:                         1.15.1-py37h1d66e8a_0          --> 1.9.2-py27_0           
    python:                        3.7.0-hc3d631a_0               --> 2.7.18-h15b4118_1      
    scipy:                         1.1.0-py37hfa4b5c9_1           --> 0.16.0-np19py27_0      


Downloading and Extracting Packages
Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done

Traceback (most recent call last):
  File "/opt/conda/bin/conda", line 7, in <module>
    from conda.cli import main
ImportError: No module named conda.cli
The command '/bin/sh -c conda install -y -c bjodah chempy pytest &&      conda clean -y -all' returned a non-zero code: 1

then I change the base image

FROM continuumio/anaconda3:latest
RUN conda install -y -c bjodah chempy pytest &&  \
    conda clean -y -all

also fail

Step 1/2 : FROM continuumio/anaconda3:latest
 ---> 472a925c4385
Step 2/2 : RUN conda install -y -c bjodah chempy pytest &&      conda clean -y -all
 ---> Running in 58f5bbc542f4
Collecting package metadata (current_repodata.json): ...working... done
Solving environment: ...working... failed with initial frozen solve. Retrying with flexible solve.
Solving environment: ...working... failed with repodata from current_repodata.json, will retry with next repodata source.
Collecting package metadata (repodata.json): ...working... done
Solving environment: ...working... failed with initial frozen solve. Retrying with flexible solve.
                                                                                   
Found conflicts! Looking for incompatible packages.
This can take several minutes.  Press CTRL-C to abort.
failed

UnsatisfiableError: The following specifications were found
to be incompatible with the existing python installation in your environment:

Specifications:

  - chempy -> python[version='2.7.*|3.5.*|3.6.*|3.4.*']

Your python: python=3.8

If python is on the left-most side of the chain, that's the version you've asked for.
When python appears to the right, that indicates that the thing on the left is somehow
not available for the python version you are constrained to. Note that conda will not
change your python version to a different minor version unless you explicitly specify
that.

The following specifications were found to be incompatible with each other:

Output in format: Requested package -> Available versions

Package pyparsing conflicts for:
pytest -> packaging -> pyparsing[version='>=2.0.2']
chempy -> pyparsing[version='>=2.0.3']

The command '/bin/sh -c conda install -y -c bjodah chempy pytest &&      conda clean -y -all' returned a non-zero code: 1

I've also tried using the dockerfile in './scripts/environment/Dockerfile', but after build the image, it can't run "python3 -m pip install chempy[all]" correctly ,There are several error like this

  ERROR: Command errored out with exit status 1:
   command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-vku0voi6/pykinsol/setup.py'"'"'; __file__='"'"'/tmp/pip-install-vku0voi6/pykinsol/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-p9d_6em1
       cwd: /tmp/pip-install-vku0voi6/pykinsol/
  Complete output (66 lines):
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  /tmp/tmpi_wq0ceh/compiler_test_source.cpp:4:8: error: #error "INFO: Sundials 3+ was not configured to use lapack"
       #  error "INFO: Sundials 3+ was not configured to use lapack"
          ^~~~~
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  /tmp/tmpbphvu9ui/compiler_test_source.cpp:4:10: error: #error "INFO: KLU was not enabled for this sundials build"
           #error "INFO: KLU was not enabled for this sundials build"
            ^~~~~
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  /tmp/tmpt3680vk4/compiler_test_source.cpp:3:26: error: #error INFO: SUNDIALS_INT32_T is not defined
                           #error INFO: SUNDIALS_INT32_T is not defined
                            ^~~~~
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build/lib.linux-x86_64-3.5
  creating build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/core.py -> build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/_util.py -> build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/_config.py -> build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/__init__.py -> build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/_release.py -> build/lib.linux-x86_64-3.5/pykinsol
  creating build/lib.linux-x86_64-3.5/pykinsol/tests
  copying pykinsol/tests/test_kinsol_numpy.py -> build/lib.linux-x86_64-3.5/pykinsol/tests
  copying pykinsol/tests/__init__.py -> build/lib.linux-x86_64-3.5/pykinsol/tests
  running egg_info
  writing dependency_links to pykinsol.egg-info/dependency_links.txt
  writing requirements to pykinsol.egg-info/requires.txt
  writing pykinsol.egg-info/PKG-INFO
  writing top-level names to pykinsol.egg-info/top_level.txt
  reading manifest file 'pykinsol.egg-info/SOURCES.txt'
  reading manifest template 'MANIFEST.in'
  writing manifest file 'pykinsol.egg-info/SOURCES.txt'
  copying pykinsol/_kinsol_numpy.cpp -> build/lib.linux-x86_64-3.5/pykinsol
  copying pykinsol/_kinsol_numpy.pyx -> build/lib.linux-x86_64-3.5/pykinsol
  creating build/lib.linux-x86_64-3.5/pykinsol/include
  copying pykinsol/include/kinsol_cxx.hpp -> build/lib.linux-x86_64-3.5/pykinsol/include
  copying pykinsol/include/kinsol_numpy.hpp -> build/lib.linux-x86_64-3.5/pykinsol/include
  copying pykinsol/include/kinsol_numpy.pxd -> build/lib.linux-x86_64-3.5/pykinsol/include
  copying pykinsol/include/sundials_cxx.hpp -> build/lib.linux-x86_64-3.5/pykinsol/include
  running build_ext
  building 'pykinsol._kinsol_numpy' extension
  creating build/temp.linux-x86_64-3.5
  creating build/temp.linux-x86_64-3.5/pykinsol
  x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fdebug-prefix-map=/build/python3.5-wzfkdC/python3.5-3.5.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DPYKINSOL_NO_KLU=1 -DPYKINSOL_NO_LAPACK=1 -I/usr/local/lib/python3.5/dist-packages/numpy/core/include -Ipykinsol/include -I/usr/include/python3.5m -c pykinsol/_kinsol_numpy.cpp -o build/temp.linux-x86_64-3.5/pykinsol/_kinsol_numpy.o -DVERSION_INFO="0.1.5" --std=c++11
  cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
  In file included from /usr/local/lib/python3.5/dist-packages/numpy/core/include/numpy/ndarraytypes.h:1832:0,
                   from /usr/local/lib/python3.5/dist-packages/numpy/core/include/numpy/ndarrayobject.h:12,
                   from /usr/local/lib/python3.5/dist-packages/numpy/core/include/numpy/arrayobject.h:4,
                   from pykinsol/_kinsol_numpy.cpp:631:
  /usr/local/lib/python3.5/dist-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
   #warning "Using deprecated NumPy API, disable it with " \
    ^~~~~~~
  In file included from pykinsol/include/kinsol_numpy.hpp:8:0,
                   from pykinsol/_kinsol_numpy.cpp:638:
  pykinsol/include/kinsol_cxx.hpp: In member function ‘void kinsol_cxx::Solver::set_linear_solver_to_dense(int)’:
  pykinsol/include/kinsol_cxx.hpp:298:44: error: ‘SUNLapackDense’ was not declared in this scope
                   LS_ = SUNLapackDense(y_, A_);
                                              ^
  error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
  ----------------------------------------
  ERROR: Failed building wheel for pykinsol

four symbols need updating

from chempy.util.periodic import symbols


print(symbols)

('H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Uut', 'Fl', 'Uup', 'Lv', 'Uus', 'Uuo')

Compare with: https://en.wikipedia.org/wiki/Periodic_table.
See atomic numbers 113, 115, 117 and 118.

[Feature] Significant Figures

It would be nice if there was a feature for adding significant figures for numbers when adding units and all future calculations would take the sig figs into account.

beginners question on sympy/chempy

I am getting started learning how to implement chempy into some of our metabolomics workflows and have hit an area where I need to learn more. I'm looking for direction in how to best evaluate the output of balance_stoichiometry.

For this scenario:

balance_stoichiometry({u'C7H5O3-', u'O2', u'C21H27N7O14P2-2', u'H+'},
           {u'C7H5O4-', u'C21H26N7O14P2-', u'H2O'})

The following is returned:

({u'C21H27N7O14P2-2': x1, u'C7H5O3-': 2, u'H+': x1, u'O2': x1/2 + 1},
 {u'C21H26N7O14P2-': x1, u'C7H5O4-': 2, u'H2O': x1})

Looking at the dtypes, I see that they are SymPy types:

<class 'sympy.core.numbers.One'>
<class 'sympy.core.numbers.Integer'>
<class 'sympy.core.symbol.Symbol'>

And quickly heading over to SymPy, it seems that there is a great deal of things you can do regarding SymPy expressions, evaluations, and substitutions.

I'm looking to convert these coefficients into a SymPy expression and find the solution that satisfies the requirement that all coefficients are integers. My naive idea is to make a SymPy expression and eval it for x1:=[1:Inf] until equality.

In the example above, x1 would be equal to 2.

I'm guessing that you have an excellent way to do this that is better than my idea, but its not immediately coming to me. Any help is much appreciated.

Error using pip install

Preparing wheel metadata ... error ERROR: Command errored out with exit status 1: command: 'c:\users\matias muñoz\appdata\local\programs\python\python38-32\python.exe' 'c:\users\matias muñoz\appdata\local\programs\python\python38-32\lib\site-packages\pip\_vendor\pep517\_in_process.py' prepare_metadata_for_build_wheel 'C:\Users\MATIAS~1\AppData\Local\Temp\tmpq3_54r0e' cwd: C:\Users\Matias Muñoz\AppData\Local\Temp\pip-install-3nquu0w2\amply Complete output (13 lines): running dist_info creating C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info writing C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\PKG-INFO writing dependency_links to C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\dependency_links.txt writing requirements to C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\requires.txt writing top-level names to C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\top_level.txt writing manifest file 'C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\SOURCES.txt' reading manifest file 'C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\SOURCES.txt' writing manifest file 'C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.egg-info\SOURCES.txt' creating 'C:\Users\Matias Muñoz\AppData\Local\Temp\pip-modern-metadata-pt381q33\amply.dist-info' Error in sitecustomize; set PYTHONVERBOSE for traceback: SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0xf1 in position 0: unexpected end of data (sitecustomize.py, line 7) error: invalid command 'bdist_wheel' ---------------------------------------- ERROR: Command errored out with exit status 1: 'c:\users\matias muñoz\appdata\local\programs\python\python38-32\python.exe' 'c:\users\matias muñoz\appdata\local\programs\python\python38-32\lib\site-packages\pip\_vendor\pep517\_in_process.py' prepare_metadata_for_build_wheel 'C:\Users\MATIAS~1\AppData\Local\Temp\tmpq3_54r0e' Check the logs for full command output.

I dont know how to fix this error

Add formulae for physical chemistry concepts

chempy:

chempy.thermochemistry

  • Shomate equation
  • Data equivalent to NIST-JANAF.

chempy.electrochemistry

chempy.radiation

chempy.kinetics:

balance_stoichiometry raises "ValueError: Component '0' not among reactants" when using the examples provided

Hi Bjørn

I've used chempy for quite some time (v. 0.4.1). Now I've updated the package as I contemplate including the package in a course I'm teaching. I am, however, now faced with the following issue:
I can no longer use balance_stoichiometry in my script. I get the following return:
""
File ".....\Python\Python35\site-packages\chempy\chemistry.py", line 1209, in balance_stoichiometry
raise ValueError("Component '%s' not among reactants" % ck)"

ValueError: Component '0' not among reactants
""

I've tried with a completely reduced script with photosynthesis:
""
import chempy
from chempy import balance_stoichiometry
from chempy import Substance

substrates = ['CO2','H2O']
products = ['O2','C6H12O6']

bal_reac, bal_prod = balance_stoichiometry(substrates, products)
""

Still the same ValueError is raised. I've tried with executing the examples provided in 'chemistry.py' but I get the same error. I'm using Spyder 3.2.6 and Python 3.6.4. I reverted to v. 0.4.1 and there it works just fine.

I'll look more into it next week, but I just wanted to let you know that there might be a small issue here (maybe the issue is me and not something in the package :))

Have a nice weekend,
Andreas

syntax error in chempy.kinetics.ode

I installed chempy to my debian linux system using
$ python -m pip install --user --upgrade chempy pytest

Installation seems successful

Successfully installed MarkupSafe-1.0 Send2Trash-1.5.0 atomicwrites-1.2.1 attrs-18.2.0 backports-abc-0.5 backports.functools-lru-cache-1.5 backports.shutil-get-terminal-size-1.0.0 bleach-2.1.4 chempy-0.7.1 configparser-3.5.0 cycler-0.10.0 defusedxml-0.5.0 entrypoints-0.2.3 funcsigs-1.0.2 functools32-3.2.3.post2 futures-3.2.0 html5lib-1.0.1 ipykernel-4.9.0 ipython-5.8.0 ipython-genutils-0.2.0 ipywidgets-7.4.2 jinja2-2.10 jsonschema-2.6.0 jupyter-1.0.0 jupyter-client-5.2.3 jupyter-console-5.2.0 jupyter-core-4.4.0 kiwisolver-1.0.1 matplotlib-2.2.3 mistune-0.8.3 more-itertools-4.3.0 mpmath-1.0.0 nbconvert-5.4.0 nbformat-4.4.0 notebook-5.6.0 pandocfilters-1.4.2 pathlib2-2.3.2 pexpect-4.6.0 pickleshare-0.7.4 pluggy-0.7.1 prometheus-client-0.3.1 prompt-toolkit-1.0.15 ptyprocess-0.6.0 pulp-1.6.8 py-1.6.0 pygments-2.2.0 pyneqsys-0.5.4 pyodesys-0.12.3 pyparsing-2.2.0 pytest-3.8.0 pytz-2018.5 pyzmq-17.1.2 qtconsole-4.4.1 quantities-0.12.2 scandir-1.9.0 scipy-1.1.0 simplegeneric-0.8.1 singledispatch-3.4.0.3 subprocess32-3.5.2 sym-0.3.4 sympy-1.3 terminado-0.8.1 testpath-0.3.1 tornado-5.1 traitlets-4.3.2 wcwidth-0.1.7 webencodings-0.5.1 widgetsnbextension-3.4.2

I tried to run the chemical kinetics demo script found here []https://pypi.org/project/chempy/(url) using commandline:

`$ python
Python 2.7.13 (default, Nov 24 2017, 17:33:09)
[GCC 6.3.0 20170516] on linux2
Type "help", "copyright", "credits" or "license" for more information.

from chempy import ReactionSystem # The rate constants below are arbitrary
rsys = ReactionSystem.from_string("""2 Fe+2 + H2O2 -> 2 Fe+3 + 2 OH-; 42
... 2 Fe+3 + H2O2 -> 2 Fe+2 + O2 + 2 H+; 17
... H+ + OH- -> H2O; 1e10
... H2O -> H+ + OH-; 1e-4""") # "[H2O]" = 1.0 (actually 55.4 at RT)
from chempy.kinetics.ode import get_odesys
Traceback (most recent call last):
File "", line 1, in
File "/home/bob/.local/lib/python2.7/site-packages/chempy/kinetics/ode.py", line 556
validate(dict(**c, **p))
^
SyntaxError: invalid syntax
`

CompilationError: Error executing in get_native(rsys, odesys, 'cvode')

Hi,

I have general problems (I will point out more once I will be sure the problem is not on my side) with this specific line (4th) in this example notebook but this one I got right now on Win10:

native = get_native(rsys, odesys, 'cvode')

and I am getting:

CompilationError: Error executing 'C:\Users\janka.WISMAIN\AppData\Local\Continuum\anaconda3\Library\mingw-w64\bin\g++.exe -fwrapv -pthread -c -std=c++11 -Wall -Wextra -fPIC -O2 -ffast-math -funroll-loops -fopenmp -fno-strict-aliasing -o .\_cvode_wrapper.obj -DPYCVODES_NO_KLU=1 -DPYCVODES_NO_LAPACK=1 -DANYODE_NO_LAPACK=1 -IC:\Users\janka.WISMAIN\AppData\Local\Continuum\anaconda3\lib\site-packages\numpy\core\include -IC:\Users\janka.WISMAIN\AppData\Local\Continuum\anaconda3\lib\site-packages\pyodesys\native\sources -Ic:\users\janka.wismain\appdata\local\continuum\anaconda3\lib\site-packages\pycvodes\include -IC:\Users\janka.WISMAIN\AppData\Local\Continuum\anaconda3\include .\_cvode_wrapper.cpp' in C:\Users\JANKA~1.WIS\AppData\Local\Temp\tmp0j3jigxs. Command exited with status 1 after givning the following output: .\_cvode_wrapper.cpp:1:0: warning: -fPIC ignored for target (all code is position independent)
 /* Generated by Cython 0.29.14 */
 ^
In file included from .\_cvode_wrapper.cpp:622:0:
c:\users\janka.wismain\appdata\local\continuum\anaconda3\lib\site-packages\pycvodes\include/cvodes_cxx.hpp:19:38: fatal error: sundials/sundials_config.h: No such file or directory
compilation terminated.

What is going on and how can I fix it?

I am using:

CPython 3.7.3
IPython 7.17.0
chempy 0.7.12
chempy._release 0.7.12
sympy 1.6.2
numpy 1.16.2
pytest 6.1.1
scipy 1.5.0
pycodeexport 0.1.2

Numerical instability when solving equilibrium problems

I'm working on a biological system with the following reactions:

1. 50 A <-> A_50
2. A_50 + B <-> A_50 B

I want to determine [A_50 B] as a function of [A]_initial.

I have setup a series of Equilibrium objects and defined an EqSystem. Everything works as expected, however the results are numerically unstable for some combinations of equilibrium constants and concentrations.

I think the issue is the massive difference between the equilibrium constants for the two reactions (K_1 = 1e233, k_2=1e6). For some combinations of initial concentrations, this produces bogus results and gives Too much of at least one component errors.

I'm not sure if it's possible to improve the numerical stability, but it would really help if something could be done.

Here is a minimal example that gives this problem:

import numpy as np
from chempy import Substance
from chempy import Equilibrium
from chempy.equilibria import EqSystem
from matplotlib import pyplot as pp
from collections import OrderedDict

Km = 1 / 1e-6
CMC = 30e-6
N = 50
K = CMC**-N / N
print(K)

substances = OrderedDict([
    ('P', Substance('P', composition={'protein': 1}, latex_name='[P]')),
    ('S', Substance('S', composition={'surfactant': 1}, latex_name='[S]')),
    ('S50', Substance('S50', composition={'surfactant': N}, latex_name='[S50]')),
    ('PS50', Substance('PS50', composition={'surfactant': N, 'protein': 1}, latex_name='[PS50]')),
])

eq1 = Equilibrium({'S': N}, {'S50': 1}, K)
eq2 = Equilibrium({'S50': 1, 'P': 1}, {'PS50': 1}, Km)
eqsys = EqSystem([eq1, eq2], substances=substances)

prot_added = np.logspace(-6, 0, 500, base=10)

def prot_to_concs(conc):
    init_conc = {'P': conc, 'S': 100e-6, 'S50': 0, 'PS50': 0, }
    x, sol, sane = eqsys.root(init_conc)
    return x

results = [prot_to_concs(c) for c in prot_added]
prot = np.array([r[0] for r in results])
surf = np.array([r[1] for r in results])
mic = np.array([r[2] for r in results])
prot_mic = np.array([r[3] for r in results])

pp.plot(prot_added, surf*1e6, label='[S]')
pp.plot(prot_added, mic*1e6, label='[M]')
pp.plot(prot_added, prot_mic*1e6, label='[PM]')
pp.xscale('log')
pp.legend();
pp.xlabel("Protein Acid Added (M)")
pp.ylabel("Concentration (µM)")
pp.show()

Tests fail with version 0.7.3 with installed extras

Thanks for fixing some other failures yesterday. Today I installed extras, and some tests fail again:

================================================================================= FAILURES ==================================================================================
____________________________________________________________ test_get_native__a_substance_no_composition[solve0] ____________________________________________________________

solve = ()

    @pytest.mark.veryslow
    @requires('pygslodeiv2', 'pyodesys')
    @pytest.mark.parametrize('solve', [(), ('H2O',)])
    def test_get_native__a_substance_no_composition(solve):
        rsys = ReactionSystem.from_string('\n'.join(['H2O -> H2O+ + e-(aq); 1e-8', 'e-(aq) + H2O+ -> H2O; 1e10']))
        odesys, extra = get_odesys(rsys)
        c0 = {'H2O': 0, 'H2O+': 2e-9, 'e-(aq)': 3e-9}
        if len(solve) > 0:
            from pyodesys.symbolic import PartiallySolvedSystem
            odesys = PartiallySolvedSystem(odesys, extra['linear_dependencies'](solve))
>       odesys = get_native(rsys, odesys, 'gsl')

/usr/local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:99: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

rsys = <chempy.reactionsystem.ReactionSystem object at 0x812b501d0>, odesys = <pyodesys.symbolic.SymbolicSys object at 0x8162bda90>, integrator = 'gsl', skip_keys = (0,)
steady_state_root = False, conc_roots = None

    def get_native(rsys, odesys, integrator, skip_keys=(0,), steady_state_root=False, conc_roots=None):
        comp_keys = Substance.composition_keys(rsys.substances.values(), skip_keys=skip_keys)
        if PartiallySolvedSystem is None:
>           raise ValueError("Failed to import 'native_sys' from 'pyodesys.native'")
E           ValueError: Failed to import 'native_sys' from 'pyodesys.native'

/usr/local/lib/python2.7/site-packages/chempy/kinetics/_native.py:113: ValueError
____________________________________________________________ test_get_native__a_substance_no_composition[solve1] ____________________________________________________________

solve = ('H2O',)

    @pytest.mark.veryslow
    @requires('pygslodeiv2', 'pyodesys')
    @pytest.mark.parametrize('solve', [(), ('H2O',)])
    def test_get_native__a_substance_no_composition(solve):
        rsys = ReactionSystem.from_string('\n'.join(['H2O -> H2O+ + e-(aq); 1e-8', 'e-(aq) + H2O+ -> H2O; 1e10']))
        odesys, extra = get_odesys(rsys)
        c0 = {'H2O': 0, 'H2O+': 2e-9, 'e-(aq)': 3e-9}
        if len(solve) > 0:
            from pyodesys.symbolic import PartiallySolvedSystem
            odesys = PartiallySolvedSystem(odesys, extra['linear_dependencies'](solve))
>       odesys = get_native(rsys, odesys, 'gsl')

/usr/local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:99: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

rsys = <chempy.reactionsystem.ReactionSystem object at 0x816468610>, odesys = <pyodesys.symbolic.PartiallySolvedSystem object at 0x8130a33d0>, integrator = 'gsl'
skip_keys = (0,), steady_state_root = False, conc_roots = None

    def get_native(rsys, odesys, integrator, skip_keys=(0,), steady_state_root=False, conc_roots=None):
        comp_keys = Substance.composition_keys(rsys.substances.values(), skip_keys=skip_keys)
        if PartiallySolvedSystem is None:
>           raise ValueError("Failed to import 'native_sys' from 'pyodesys.native'")
E           ValueError: Failed to import 'native_sys' from 'pyodesys.native'

/usr/local/lib/python2.7/site-packages/chempy/kinetics/_native.py:113: ValueError
========================================================================== short test summary info ==========================================================================
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:808: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [3] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:181: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:915: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:49: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:108: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/util/tests/test_graph.py:30: graphviz not installed? (dot command missing)
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:569: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/util/tests/test_table.py:58: latex not installed? (pdflatex command missing)
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:710: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:539: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/util/tests/test_graph.py:48: graphviz not installed? (dot command missing)
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:858: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:497: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:675: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:510: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:757: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test__native.py:156: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [1] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:949: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [4] ../../../../../local/lib/python2.7/site-packages/chempy/kinetics/tests/test_ode.py:475: Unfulfilled requirements. Missing modules: pycvodes.
SKIP [2] ../../../../../local/lib/python2.7/site-packages/chempy/util/tests/test_table.py:46: latex not installed? (pdflatex command missing)
============================================================================= warnings summary ==============================================================================
chempy/kinetics/tests/test_ode.py::test_get_ode__Radiolytic__units__multi
  /usr/local/lib/python2.7/site-packages/pyodesys/core.py:519: UserWarning: 'adaptive' mode with SciPy's integrator (vode/lsoda) may overshoot (itask=2)
    warnings.warn("'adaptive' mode with SciPy's integrator (vode/lsoda) may overshoot (itask=2)")
  /usr/local/lib/python2.7/site-packages/pyodesys/core.py:520: UserWarning: 'adaptive' mode with SciPy's integrator is unreliable, consider using e.g. cvode
    warnings.warn("'adaptive' mode with SciPy's integrator is unreliable, consider using e.g. cvode")

chempy/kinetics/tests/test_rates.py::test_RateExpr__subclass_from_callback
  /usr/local/lib/python2.7/site-packages/chempy/kinetics/tests/test_rates.py:52: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use from_callback instead.
    lambda v, a, backend: a[0]*v['H2']*v['Br2']**(3/2) / (v['Br2'] + a[1]*v['HBr'])

chempy/kinetics/tests/test_rates.py::test_MassAction__subclass_from_callback
  /usr/local/lib/python2.7/site-packages/chempy/kinetics/tests/test_rates.py:100: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use MassAction.from_callback instead.
    rate_coeff, cls_attrs=dict(parameter_keys=('temperature',), nargs=2))
  /usr/local/lib/python2.7/site-packages/chempy/kinetics/rates.py:180: ChemPyDeprecationWarning: subclass_from_callback is deprecated. Use from_callback instead.
    _RateExpr = super(MassAction, cls).subclass_from_callback(cb, cls_attrs=cls_attrs)

chempy/tests/test__equilibrium.py::test_solve_equilibrium_1
  /usr/local/lib/python2.7/site-packages/chempy/chemistry.py:859: RuntimeWarning: divide by zero encountered in double_scalars
    tot *= conc**nr

chempy/tests/test_henry.py::test_Henry__with_units
  /usr/local/lib/python2.7/site-packages/chempy/tests/test_henry.py:23: ChemPyDeprecationWarning: get_kH_at_T is deprecated since (not including) 0.3.1, it will be missing in 0.5.0. Use __call__ instead.
    assert allclose(kH_H2.get_kH_at_T(

chempy/tests/test_reactionsystem.py::test_ReactionSystem__html_tables
  /usr/local/lib/python2.7/site-packages/chempy/tests/test_reactionsystem.py:101: ChemPyDeprecationWarning: unimolecular_html_table is deprecated since (not including) 0.5.7, it will be missing in 0.8.0. Use chempy.printing.tables.UnimolecularTable instead.
    ut, unc = rs.unimolecular_html_table()
  /usr/local/lib/python2.7/site-packages/chempy/tests/test_reactionsystem.py:106: ChemPyDeprecationWarning: bimolecular_html_table is deprecated since (not including) 0.5.7, it will be missing in 0.8.0. Use chempy.printing.tables.BimolecularTable instead.
    bt, bnc = rs.bimolecular_html_table()

chempy/util/tests/test_expr.py::test_mk_Poly
  /usr/local/lib/python2.7/site-packages/chempy/util/tests/test_expr.py:165: ChemPyDeprecationWarning: _mk_Poly is deprecated. Use create_Poly instead.
    Poly = mk_Poly('T', reciprocal=True)

chempy/util/tests/test_expr.py::test_PiecewisePoly
  /usr/local/lib/python2.7/site-packages/chempy/util/tests/test_expr.py:208: ChemPyDeprecationWarning: _mk_PiecewisePoly is deprecated. Use create_Piecewise instead.
    TPiecewisePoly = mk_PiecewisePoly('temperature')

chempy/util/tests/test_parsing.py::test_formula_to_composition
  /usr/local/lib/python2.7/site-packages/chempy/util/tests/test_parsing.py:16: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    assert formula_to_composition('Fe/3+') == {0: 3, 26: 1}
  /usr/local/lib/python2.7/site-packages/chempy/util/tests/test_parsing.py:16: ChemPyDeprecationWarning: 'Fe/3+' deprecated, use e.g. 'Fe+3'
    assert formula_to_composition('Fe/3+') == {0: 3, 26: 1}

chempy/util/tests/test_parsing.py::test_formula_to_latex
  /usr/local/lib/python2.7/site-packages/chempy/util/parsing.py:435: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    formula, prefixes, infixes, **kwargs)
  /usr/local/lib/python2.7/site-packages/chempy/util/parsing.py:435: ChemPyDeprecationWarning: 'Fe/3+' deprecated, use e.g. 'Fe+3'
    formula, prefixes, infixes, **kwargs)

chempy/util/tests/test_periodic.py::test_mass_from_composition__formula
  /usr/local/lib/python2.7/site-packages/chempy/util/tests/test_periodic.py:44: ChemPyDeprecationWarning: / depr. (before 0.5.0): use 'Fe+3' over 'Fe/3+'
    Fminus = mass_from_composition(formula_to_composition('F/-'))

-- Docs: http://doc.pytest.org/en/latest/warnings.html
====================================================== 2 failed, 274 passed, 28 skipped, 16 warnings in 11.71 seconds =======================================================
*** Error code 1

Chempy install errors using pip

pip install chempy does not work out of the box on my Ubuntu system because compilation from source requires a few system packages that I didn't have. It would be nice to either distribute pre-compiled binaries for common systems, or to at least document which system packages are required. Even though the packages I needed were common (python-devel and gfortran), many new users get tripped up by this sort of thing.

Parsing oddities

Parsing of strings silently accept some insane strings. Instead, it should raise an error. Examples:

>>> from chempy import ReactionSystem as RS
>>> RS.from_string("CO -> O + C ->").string()  # should raise!
'CO -> C + O\n'
>>> RS.from_string("COqasza -> Oqasd + Cqagdsfxxds").string()  # should raise!
'COqasza -> Cqagdsfxxds + Oqasd\n'

Root finding failed

When trying to find the equilibria of the following system, I get "root finding failed" and "too much of at least one component" warnings.

eqsys = EqSystem.from_string("""

CO2 + H2O = H2CO3; 1.7e-3
H2CO3 = H+ + HCO3-; 10**-6.3
HCO3- = H+ + CO3-2; 10**-10.3

HPO4-2 + H2O = H2PO4- + OH-; 10**-6.79
Na2HPO4 = 2 Na+ + HPO4-2; 10**5
KH2PO4 = K+ + H2PO4-; 10**5

HCl = H+ + Cl-; 10**5.9
CH3CO2H = CH3CO2- + H+; 10**-4.756

H2O = H+ + OH-; 10**-14
""")


arr, info, sane = eqsys.root(defaultdict(float, \
    {'H2O': 55.4,
     'CO2': 3, 
     "KH2PO4": 0.0422 * 1,
     "Na2HPO4": 0.0578 * 1,
     "HCl": 0,
     "CH3CO2H": 0.0,
     "CH3CO2-": 0
    }))
conc = dict(zip(eqsys.substances, arr))
print(conc)

Even with somewhat simplified versions of this system, I still get the errors at times.

Pretty printing with big uncertainties

>>> from chempy.printing import number_to_scientific_unicode
>>> number_to_scientific_unicode(0.05, 5.5)
...
ValueError: unsupported format character '-' (0x2d) at index 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.