# \[Resolved\] Julia, Cantera, PyCall

**URL:** <https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510>\
**Category:** General Usage\
**Tags:** question\
**Created:** [March 3, 2023, 5:23pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510 "2023-03-03T17:23:01Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 5:23pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/1 "2023-03-03T17:23:01Z")

</div>

Hello,

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

```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](https://cantera.org/tutorials/python-tutorial.html) with PyCall, I would like to use the equivalent of II (class ‘list’) in a statement like:

julia\> g.net\_rates\_of\_progress[II]

Thank you in advance for your suggestions and explanations about II = …  
Thierry

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [March 3, 2023, 5:32pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/2 "2023-03-03T17:32:57Z")

</div>

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

```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.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 3, 2023, 5:40pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/3 "2023-03-03T17:40:26Z")

</div>

> [@aquarelleX332](#):
>
> ```julia
> II = [i for i,r in enumerate(g.reactions())
> if 'CO' in r.reactants and 'CO2' in r.products]
> 
> ```

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.

```julia
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).

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 6:18pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/4 "2023-03-03T18:18:12Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 3, 2023, 7:43pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/5 "2023-03-03T19:43:22Z")

</div>

> [@stevengj](#):
>
> 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).

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

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 7:47pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/6 "2023-03-03T19:47:18Z")

</div>

I am going to look at PythonCall.jl  
Many thanks 👍

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 8:04pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/7 "2023-03-03T20:04:23Z")

</div>

This syntax:

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

```

returns:

```julia
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:

```julia
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:

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

```

appears to introduce a problem. Why?

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 8:23pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/8 "2023-03-03T20:23:42Z")

</div>

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:

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

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 3, 2023, 8:44pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/9 "2023-03-03T20:44:52Z")

</div>

> [@aquarelleX332](#):
>
> The vector is constructed. Since I use gri30.yaml from cantera, there is 325 elements (what is right).  
> The part:
> 
> ```julia
> if "CO" in r.reactants && "CO2" in r.products
> 
> ```
> 
> appears to introduce a problem. Why?

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

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 9:00pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/10 "2023-03-03T21:00:12Z")

</div>

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

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

```julia
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{}

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [March 3, 2023, 9:05pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/11 "2023-03-03T21:05:29Z")

</div>

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

```julia
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?

---

<div class="post-metadata">

**Author:** ![aquarelleX332](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aquarellex332/32/21048_2.png) [@aquarelleX332](https://discourse.julialang.org/u/aquarelleX332)\
**Post date:** [March 3, 2023, 9:35pm UTC](https://discourse.julialang.org/t/resolved-julia-cantera-pycall/95510/12 "2023-03-03T21:35:00Z")

</div>

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

```julia
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.
