# [Resolved] Julia, Cantera, PyCall

Hello,

This is a bit of a stupid question because I don’t know the Python language.
How to convert these instructions to Julia?

``````>>> II = [i for i,r in enumerate(g.reactions())
...       if 'CO' in r.reactants and 'CO2' in r.products]
>>> for i in II:
...     print(g.reaction(i).equation)
``````

Indeed, as I learn to use Cantera with PyCall, I would like to use the equivalent of II (class ‘list’) in a statement like:

julia> g.net_rates_of_progress[II]

Thierry

I would make this more explicit personally than a list comprehension. Here is the conversion for Julia:

``````II = []

for (i, r) in enumerate(g.reactions())
if in("CO", r.reactants) && in("CO2", r.products)
push!(II, i)
end
end

for i in II
println(g.reaction(i).equation)
end
``````

For your second question on `g.net_rates_of_progress[II]`, I think syntax like this should work: `g.net_rates_of_progress[II...]` although it’s a bit hard to tell without knowing what the data types are.

Hope that helps, let me know if you have any questions!

P.S. Also, not a stupid question at all! List comprehensions can be hard to read which is why I expanded it in my conversion above.

1 Like

Doesn’t this syntax literally work in Julia if you put parens around `(i,r)`, change single quotes to double quotes, and change `and` to `&&`? i.e.

``````II = [i for (i,r) in enumerate(g.reactions())
if "CO" in r.reactants && "CO2" in r.products]
``````

is valid Julia syntax for a comprehension. (And `in` is a valid infix operator in Julia too!)

I just noticed that PyCall does not currently overload `in` for Python objects, which I should probably fix. In the meantime, you can call `r.reactants.__contains__("CO")` to call the underlying Python `__contains__` method (equivalent to Python’s `in` operator).

1 Like

TheCedarPrince and stevengj,

Thank you very much for your replies.

The first formulation (TheCedarPrince) is very understandable for me because I am familiar with the separation between the creation of an empty table and the instructions for filling it.

@stevengj: thank you in particular for the last part of your reply which helps me a lot.

1 Like

Note also that PythonCall.jl (a more modern rewrite of PyCall.jl) already handles this.

I am going to look at PythonCall.jl
Many thanks

This syntax:

``````II = [i for (i,r) in enumerate(g.reactions())
if "CO" in r.reactants && "CO2" in r.products]
``````

returns:

``````ERROR: AbstractDict collections only contain Pairs;
Either look for e.g. A=>B instead, or use the `keys` or `values`
function if you are looking for a key or value respectively.
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] in(p::String, a::Dict{Any, Any})
@ Base ./abstractdict.jl:28
[3] (::var"#8#10")(::Tuple{Int64, PyObject})
@ Main ./none:0
[4] iterate
@ ./iterators.jl:472 [inlined]
[5] iterate
@ ./generator.jl:44 [inlined]
[6] grow_to!(dest::Vector{Int64}, itr::Base.Generator{Base.Iterators.Filter{var"#8#10", Base.Iterators.Enumerate{Vector{PyObject}}}, var"#7#9"})
@ Base ./array.jl:860
[7] collect(itr::Base.Generator{Base.Iterators.Filter{var"#8#10", Base.Iterators.Enumerate{Vector{PyObject}}}, var"#7#9"})
@ Base ./array.jl:784
[8] top-level scope
@ REPL[6]:1
``````

If I just do:

``````II = [i for (i,r) in enumerate(g.reactions())]
``````

The vector is constructed. Since I use gri30.yaml from cantera, there is 325 elements (what is right).
The part:

``````if "CO" in r.reactants && "CO2" in r.products
``````

appears to introduce a problem. Why?

I still can’t extract the reactions with CO and CO2 (all the reactions of gri30.yaml are displayed), there is a small modification to make on the indices because in Julia we start at 1:

``````for i in II
println(g.reaction(i-1).equation)
end
``````

What are the types of `r.reactants` and `r.products`?

r.reactants and r.products are Dict{}.

For exemple, if I consider the first reaction in gri30.yaml:

``````using PyCall
ct = pyimport("cantera")
g = ct.Solution("gri30.yaml")
r = g.reaction(1)
PyObject H + O + M <=> OH + M    <ThreeBodyReaction(Arrhenius)>
r.reactants
Dict{Any, Any} with 2 entries:
"H" => 1.0
"O" => 1.0
``````

And, of course, H and O are the keys of the Dict{}

Ah thanks for sharing the data type. This should work better:

``````II = []

for (i, r) in enumerate(g.reactions())
if in("CO", keys(r.reactants)) && in("CO2", keys(r.products))
push!(II, i)
end
end

for i in II
println(g.reaction(i - 1).equation)
end
``````

Does that solve the problems you were running into?

This is indeed the solution.
I tried to use keys(r.reactants) on the half of the code, i.e. just with “CO” but I missed the push! to fill table II.
Now, everything is clear.

Many thanks to you both, TheCedarPrice and stevengj.

Just to complete our discussion. I tried this (for fun):

``````II = []

for (i, r) in enumerate(g.reactions())
if "CO" ∈ keys(r.reactants) && "CO2" ∈ keys(r.products)
push!(II, i)
end
end

for i in II
println(g.reaction(i - 1).equation)
end
``````

With the same (correct) results.

1 Like