k1lib.eqn module

The idea of this module looks something like this:

from k1lib.imports import *
settings.eqn.eqnPrintExtras = False

s = k1lib.eqn.System("""
# comments like this are okay. This section's for buying things
10 cent -> 1 kWh
1.5 dollar -> 1000 H2O
12 dollar -> O2
1.4 dollar -> CH4

# conversions
100 cent -> 1 dollar
1 MJ -> 0.27 kWh
""");

# parsing extra equations down the line
s.parse("1 H2O + 16 MJ -> 0.11 H2 + 0.88 O2")

Essentially, you can create a system of equations. Here, all the chemicals are in kilograms, so you can buy 1 kg oxygen for 12 dollars. Then, you can get equations relating to a specific term:

s.O2

Output:

Consumers:


Producers:
1. H2O + 16 MJ -> 0.11 H2 + 0.88 O2
2. 12 dollar -> O2

Then, you can pick out a single equation that has another unique term “dollar”:

print(s.O2.dollar) # outputs "O2 -> 12 dollar"

This will return the flipped equation, going from “O2” to “dollar”, because the expression looks like s.O2.dollar. You can go in the reverse order if you want to:

print(s.dollar.O2) # outputs "12 dollar -> O2"

For complex case where s.dollar.O2 can have multiple answers, use Eqns.pick() instead. You can also do random math on these equations:

print(s.dollar.O2 * 5 + s.dollar.CH4) # outputs "61.4 dollar -> 5 O2 + CH4"

You can also “combine” multiple equations together like this:

print(s.cent.dollar @ s.dollar.O2) # outputs "100 cent -> 0.083 O2"

This looks for common terms between the 2 equations, in this case “dollar”. It then tries to add these 2 equations together so that “dollar” cancels out. “0.083” is a hideous number. To bring it to 1kg, you can multiply the equation by 1/0.083. This is sort of tedious, depends a lot on the original equation’s values, so you can just do it like this instead:

# outputs "1200 cent -> O2", rounds the last term to 1
print(round(s.cent.dollar @ s.dollar.O2))
# outputs "1200 cent -> O2", rounds specific term to 1
print(round(s.cent.dollar @ s.dollar.O2, "O2"))
# outputs "3600 cent -> 3 O2", rounds the last term to value
print(round(s.cent.dollar @ s.dollar.O2, 3))
# outputs "12000 cent -> 10 O2", rounds specific term to specific value
print(round(s.cent.dollar @ s.dollar.O2, ["O2", 10]))

So, using this module, you can do quick back-of-the-envelope calculations for anything you want, like comparing between making your own oxygen, or buying oxygen tanks from outside:

# outputs "12 dollar -> O2"
print(s.dollar.O2)
# outputs "0.493 dollar -> 0.125 H2 + O2"
print(round(s.dollar.cent @ s.cent.kWh @ s.kWh.MJ @ (s.dollar.H2O @ s.H2O.O2)))

So yeah, apparently, it’s much, much cheaper to make your own oxygen from electrolysis than buying it from outside. Makes me wonder why hospitals still buy oxygen tanks.

This module can definitely be improved. Right now, chemicals are implicitly in kilograms, but what if you want to convert between mol and kg? This module doesn’t really provide the facilities for that. Main reason is I’m lazy to implement, and it sounds more trouble than its worth. I still use this module for all kinds of chemical flow analysis, and it works out fine for me, so there’re really no incentives to do this.

class k1lib.eqn.Eqn(system: System)[source]

Bases: object

__init__(system: System)[source]

Creates a blank equation. Not expected to be instantiated by the end user.

save()[source]

Saves this (potentially new) equation to the system, so that it can be used directly later on

__contains__(x: str)[source]

Whether a term is in this equation

__getattr__(term: str)[source]

Gets the value of the term in this equation. Negative if on consumer side, positive if on producer side

__getitem__(idx: str)[source]

Same as __getattr__()

__iter__()[source]

Yields key:value pairs

__len__()[source]

Returns number of terms in this equation

__hash__()[source]

Return hash(self).

copy()[source]
sharedTerms(eqn: Eqn) List[str][source]

Gets a list of shared terms between this equation and the specified one.

join(eqn: Eqn, term: str) Eqn[source]

Tries to cancel out this equation with another equation at the specified term. Example:

s = eqn.System("""a + b -> c + d
c + 2e -> f""")
s.a.c.join(s.c.f, "c") # returns the equation "a + b + 2e -> d + f"

For simpler cases, where the shared term to be joined is obvious, use __matmul__() instead

__matmul__(eqn: Eqn) Eqn[source]

Convenience method that does the same thing as join(). Example:

s = eqn.System("""a + b -> c + d
c + 2e -> f""")
s.a.c @ s.c.f # returns the equation "a + b + 2e -> d + f"

Preference order of which term to join:

  1. If term is on producer side of self, and consumer side of eqn

  2. If term is on consumer side of self, and producer side of eqn

  3. Other cases

round(term: str, amount: float = 10) Eqn[source]

Rounds the equation off, so that the term’s value is the specified amount. For aesthetic purposes mainly. Example:

s = eqn.System("a + b -> 2c")
s.a.c.round("c", 5) # returns the equation "2.5a + 2.5b -> 5c"'
__round__(term: Optional[str] = None) Eqn[source]

Like round(), but more Pythonic?

Parameters

term

Can be any of these:

  • None

  • str

  • Union[int, float]

  • Tuple[str, float]

class k1lib.eqn.Eqns(system: System, eqns: List[Eqn], focusTerm: Optional[str] = None)[source]

Bases: object

__init__(system: System, eqns: List[Eqn], focusTerm: Optional[str] = None)[source]

Creates a new list of equations. Not expected to be instantiated by the end user.

Parameters
  • system – injected System

  • eqns – list of equations

  • focusTerm – if the list of equations are from the result of focusing in a single term, then use this parameter to prioritize certain search parameters.

__getitem__(idx: Union[int, str]) Optional[Eqn][source]

If int, return the equation with that index. Not really helpful for exploring the system of equations, but good for automated scripts

If string, then effectively the same as __getattr__()

__getattr__(term: str) Optional[Eqn][source]

Picks out a specific Eqn that has the specified term. Prefer shorter equations, and the returned Eqn always have the term on the products side. Meaning:

eqns = eqn.System("a + 2b -> c").b # gets an Eqns object with that single equation
eqns.a # gets the equation "c -> a + 2b" instead

This is a convenience way to search for equations. If you need more granularity, use pick() instead

pick(*terms: List[str]) Optional[Eqn][source]

Like the quick method (__getattr__()), but here, picks equations more carefully, with selection for multiple terms. Example:

s = eqn.System("""a + 2b -> c
b + c -> d
a -> 3d
a + b + c -> 2d""")
s.a.pick("b", "d") # returns last equation

As you can see, it’s impossible to pick out the last equation using __getattr__() alone, as they will all prefer the shorter equations, so this is where pick() can be useful.

__dir__()[source]

Returns the list of terms in every equation here. Useful for tab completion.

class k1lib.eqn.System(strToParse: Optional[str] = None)[source]

Bases: object

__init__(strToParse: Optional[str] = None)[source]

Creates a new system of equations.

Parameters

strToParse – if specified, then it gets feed into parse()

spellCheck()[source]

Runs a spell check to find out terms that are pretty similar to each other

__len__()[source]
__getitem__(idx: int) Eqn[source]

Picks out the i’th equation from the list of equations. Useful for automated scripts

__getattr__(term: str) Eqns[source]

Picks out equations that has the term

__dir__()[source]

Returns the list of terms in every equation here. Useful for tab completion.