[ANN] ChemEquations.jl: write and balance chemical equations elegantly and efficiently

Afte a few days of honest work and one Discourse thread, I’ve completed a package dedicated for chemical equations.

Edit: It’s presented comprehensively on my blog.

It is mostly based on the syntax of https://www.webqc.org/balance.php, but it is more flexible. It uses ce"H2 + O2 = H2O" syntax for equations and cc"H2O" for compounds.

Here are some examples:

julia> equation = ce"Fe + Cl2 = FeCl3"
ce"Fe + Cl2 = FeCl3"
julia> balance(equation)
ce"2 Fe + 3 Cl2 = 2 FeCl3"

julia> balance(ce"Cr2O7{-2} + H{+} + {-} = Cr{+3} + H2O")
ce"Cr2O7{-2} + 14 H{+} + 6 e = 2 Cr{+3} + 7 H2O"

julia> cc"CH3CH2CH2CH2CH2OH" == cc"C5H12O"
true

Just like Julia, it supports unicode, too:

julia> ce"⏣H + Cl2 = ⏣Cl + HCl"
ce"⏣H + Cl2 = ⏣Cl + HCl"

julia> ce"C + α = O + γ" # a reaction from triple-α process
ce"C + α = O + γ"

… and more examples in the documentation.

I want to hear the opinions on package’s API and syntax. Are they intuitive? Do they follow the best conventions?

After another round of review, I’ll write a post on my blog about the package. Maybe there’ll be another blog post, sharing my experience with the automated tests and documentation. The package is 100% documented and 100% tested (100% coverage) after all. :smile:

17 Likes

Nice package!, the API seems concise enough. a few feature requests on my part :face_with_hand_over_mouth::

  • an option to display a fractional balance? balance(chemeq,int=false) that fixes the the least relative abundant element to 1 mol and gives a float result?
  • molecular_weight(chemelem) ?
  • is there a way to query if the compound has a ring? (in the webhc example, they do something with this, using this example: (C6H5C2H5 + O2 = C6H5OH + CO2 + H2O )
3 Likes

nice package, would be good for pedagogical use, or make a cool web app for students.
Would be nice if it could dump to LaTeX maybe. Just a passing thought, but yea great concept, execution looks usable to me!

5 Likes

If you want bells and whistles, you can add support for building a ReactionNetwork from a collection of ChemEquations haahah (I’m gonna check on how can it be done and do a PR)

3 Likes

Should be pretty straightforward. With the utilities already made, but let us know!

Do you want it just to be displayed that way, or also for data to be stored as Rational? Currently Compound and ChemEquation structs work with Ints only, but it can be reworked. That would even support compounds with fractional or even real indexes and charges (doesn’t make sense chemically, but it does mathematically).

To be discussed in https://github.com/zdroid/ChemEquations.jl/issues/2. :wink:

Do you have an idea how they check it? We can try to write some basic function that checks for “C6H12”, “C6H6”, “C6H5…” and unicode characters that are rings. Although more advanced verification would require adding oxidation numbers. It’s a nice feature to have nevertheless.

I had the same ideas in mind, I’m glad that you recognized that. :slight_smile:
LaTeX output can be done nicely by just replacing = with \to arrow, {digits} with _{digits} and {+-charge} ^{+-charge}. Is there some more generic way to convert the output to LaTex, though?

3 Likes

Yeah it would be cool to use this with Catalyst.jl somehow

4 Likes

Since this is possible:

@eval rn = @reaction_network begin
    α, $(Meta.parse("2H → H2"))
    β, $(Meta.parse("2H2 + O2 → 2H2O"))
end α β

a macro could be written to Meta.parse(string(::ChemEquation)). Or maybe add conversion-promotion from ChemEquation to Expr?

1 Like

Respecting to the detection of aromatic rings, I don’t thibk is good to detect automatically, but using the ring unicode seems good! (one more point for Julia and its unicode support)

1 Like

It’s impossible to detect aromaticity from complex chemical formulas. Too many possible isomers. That’s okay though, this is a great tool without it.

I have some ideas for extending this into another tool using Optim that some other flavors of chemist might like.

2 Likes

Although it is fairly easy if your formula is written with a unicode character representing a ring (as suggested above).

That’s great. If there is something to do to help you with the integration, please suggest it. I’m currently abstracting compounds and chemequations to work on any number, not just integer. (If constructed manually of course, parsing that isn’t easy…)

2 Likes

Yes if you are using a ring as a unicode it does tell you something about aromaticity. Is this using emipirical formulas(IE:C2H6) or just anything someone types in(IE: CH3CH3)? I may be misunderstanding the scope. It’s actually difficult to create a symbolic language to display chemicals uniquely. One that I use on computers is SMILES.

All I’d need from it would be the output :). So a collection of maps like the following: compound_name => stoich. Maybe one for each rxn in a network. But maybe we could work on it together, it’d be a pretty short and sweet application.

2 Likes

It’s actually just a character which is recognized as a unique entity (i.e. “⏣” from “⏣Cl” is treated as an “element”, as is “Cl”).

1 Like

Whats the output of “⏣ + O2 -> CO2 + H2O” ?
Good unit test to have :smiley:

2 Likes

v0.2.0 announcement

  1. Instead of loading “heavy” packages AbstractAlgebra for one method and LinearAlgebra for I (identity matrix), ChemEquations now uses LinearAlgebraX. It calculates nullspace and other matrix operations exactly if that possible, generically.

  2. ce and cc are removed from Base.show() methods, so that copy-paste from REPL is easier.

julia> ce"⏣H + Cl2 = ⏣Cl + HCl"
⏣H + Cl2 = ⏣Cl + HCl
  1. Non-integer (<: Real) coefficients are supported:
julia> ChemEquation{Rational}("1//2 H2 + 1//2 Cl2 → HCl")
1//2 H2 = H

julia> ChemEquation{Float64}("0.5 H2 + 0.5 Cl2 = HCl")
0.5 H2 + 0.5 Cl2 = HCl

Supported!

julia> balance(ce"H2 + Cl2 = HCl", fractions=true)
1//2 H2 + 1//2 Cl2 = HCl

Although not 100% percent in the way you wanted, since

julia> balance(ce"C2H4 + O2 = CO2 + H2O", fractions=true)
1//2 C2H4 + 3//2 O2 = CO2 + H2O

But on the other side, this is the “neatest” solution, as the number of coefficients equal to 1 is maximal. If you still want scaling to 1, which way would you prefer it to called?

3 Likes

Hi, I just wanted to point to this related package:https://github.com/rafaqz/UnitfulMoles.jl It is nice to see these tools in Julia :slight_smile:

3 Likes

Does the package support balancing redox equation?

There is nothing inherently special about redox reactions.

Even some chemists overlook it, but if the system of equations is specified correctly (matrix of coefficients is correct and system of linear equations can be solved) then the chemical reaction can be balanced regardless of it’s “chemical” nature.

After all it’s just about balancing numbers of atoms on the both sides, you don’t even have to really know chemistry if (correct) equation(s) to balance are written.

2 Likes

julia> satans_kimchi=cc"FOOF"
F2O2
julia> ce"satans_kimchi + H2O = HF +O3"
satanskimchi + H2O = HF + O3

Which is the nearest I am ever getting to this stuff.

3 Likes

v0.2.1

adds support for state symbols, i.e. (s), (l), (g), (aq). They don’t change the balancing of the equation, so they’re just stripped.


Thanks! I was looking for a package to interface the stoichiometric calculations in ChemEquations, so I’ll try this one, too.

Absolutely, try any equation! If you find some that doesn’t work, please file an issue.

It has to be written this way:

julia> ChemEquation("$satans_kimchi + H2O = HF + O3")
F2O2 + H2O = HF + O3

But I don’t know why macro doesn’t work with the same syntax.

3 Likes