Jak už název napovídá, balíček SymPy se používá pro symbolické zobrazení, úpravy a řešení rovnic. Tedy více odpovídá postupu, kterým bychom postupovali, kdybychom úlohu řešili ručně na papíře. Balíček SymPy nahrajeme stejně, jak jsme již zvyklí. Balíček můžeme načíst pod jakýmkoliv názvem, nicméně zde se budeme řídit konvencemi.
import sympy as sm
Klasicky jsme v Pythonu zvyklí, že než nějakou proměnnou použijeme, musíme ji zadefinovat. To většinou znamenalo, že jsme museli znát její hodnotu a priori. Obvykle se jedná o hodnoty ze zadání úlohy a podobně. Takové neznámé nemůžeme explicitně využít a lze je získat pouze řešením (bilančních) rovnic či výpočtem rovnice (například při hledání tlaku nasycených par z Antoineovy rovnice).
V případě balíčku SymPy je tomu jinak, protože zde můžeme používat i
proměnné, které jsou na počátku neznámé a v rovnicích budou následně
vystupovat díky symbolické reprezentaci. I zde je ale potřeba proměnnou
na začátku nadefinovat jako symbolickou. Pokud na začátku nezadefinujeme
proměnnou x
Python si s ní neporadí.
= x + 1 expr
> Traceback (most recent call last):
> NameError: name 'x' is not defined
Pokud však proměnnou definujeme pomocí příkazu
sm.symbols
jako symbol, problém již nenastane.
= sm.symbols('x')
x = x + 1
expr print(expr)
> x + 1
Pro vyčíslení funkce nebo výrazu pro nějakou hodnotu proměnné
x
, stačí použít příkaz subs
. Tento příkaz
nahradí symbolickou proměnnou zadanou hodnotou. Důležité je, že původní
výraz expr
zůstává nezměněný - subs
totiž
vytváří nový upravený výraz.
= expr.subs(x, 2)
exprVal print(exprVal)
print(expr)
> 3
> x + 1
Jedním ze zajímavých využití balíčku SymPy může být definice
libovolné funkce přímo symbolicky a její následná derivace podle námi
zvolené proměnné. Představme si, že máme následující rovnici popisující
enthalpickou bilanci pro ideálně míchaný průtočný reaktor s exotermní
reakcí 1. řádu a s recyklem (model CSTR1EXO), u které nás zajímá její
první derivace (řekněme podle proměnné theta
). Jednou z
možností je použít numerickou derivaci pomocí vícebodové aproximace.
Alternativně lze využít balíček SymPy a získat analytickou derivaci,
kterou je pak možné vyčíslit pouze v bodě, kde nás samotná derivace
zajímá.
= sm.symbols('a theta theta_c da b beta x gamma')
a, theta, theta_c, da, b, beta, x, gamma = -a*theta + da*b*(1 - x)*sm.exp(theta/(1 + theta/gamma)) - beta*(theta - theta_c)
funkce_t print(funkce_t)
= sm.diff(funkce_t, theta)
der print(der)
> b*da*(1 - x)*exp(theta/(1 + theta/gamma)) - a*theta - beta*(theta - theta_c)
> b*da*(1 - x)*(1/(1 + theta/gamma) - theta/(gamma*(1 + theta/gamma)**2))*exp(theta/(1 + theta/gamma)) - a - beta
Dalšími zajímavými funkcemi v balíčku SymPy jsou: -
simplify
: zjednodušení výrazu nebo funkce -
equals
: otestování, zda jsou dva výrazy totožné -
sympify
: převod proměnné ve formátu ‘string’ na SymPy výraz
- integrate
: výpočet integrálu výrazu podle zadané proměnné
- limit
: výpočet limity výrazu při určité hodnotě
proměnné.
Pro více informací, návodů a příkladů využití jednotlivých funkcí vás nasměruji na dokumentaci.
Řešme nyní soustavu lineárních rovnic ve tvaru Ax = b
.
Zde A
je matice soustavy, x
je vektor
neznámých a b
je vektor pravých stran. V rámci balíčku
SymPy budeme soustavu řešit pomocí funkce linsolve
. Nejprve
zapíšeme naše rovnice. Zde si všimněte možnosti dvojího zápisu. První
varianta předpokládá tvar a-b=0
, zatímco druhá využívá
příkaz Eq
a zapisuje rovnost ve tvaru a=b
= sm.symbols('x y')
x, y = 5*x + 1*y - 5
eq1_a = 1*x - 1*y - 4
eq2_a = sm.Eq(5*x + 1*y, 5)
eq1_b = sm.Eq(1*x - 1*y, 4)
eq2_b
= sm.linsolve([eq1_a, eq2_a], (x, y))
sol_a print(sol_a)
= sm.linsolve([eq1_b, eq2_b], (x, y))
sol_b print(sol_b)
> {(3/2, -5/2)}
> {(3/2, -5/2)}
Řešení komplikovanější soustavy lineárních algebraických rovnic si ukážeme na následujícím příkladu. Tento příklad použijeme k řešení bilančních rovnic stejné úlohy, která byla již vyřešena v minulé kapitole pomocí matic. > Při rektifikaci směsi fenolů obsahujících molárně 35 % fenolu, 40 % kresolu, 20 % xylenolu a 5 % těžších fenolů se získává destilát o molárním složení 95 % fenolu a 5 % kresolu. Destilát obsahuje 90 % z celkového látkového množství fenolu z nástřiku. Zjistěte látkové množství destilátu a zbytku vzhledem k nástřiku 100 kmol. Vypočtěte rovněž složení zbytku v molárních %.
Cílem úlohy je provést bilanci látkového množství v procesu
rektifikace. Jednotková operace má jeden vstupní proud (nástřik
F
) a dva výstupní proudy (destilát D
a zbytek
W
). V systému se nachází čtyři složky - (A
)
fenol, (B
) kresol, (C
) xylenol a
(D
) těžší fenoly. Ze zadání známe složení nástřiku a
destilátu (v mol. %), celkové množství nástřiku (v molech, základ
výpočtu) a obsah fenolu (A
) v destilátu (D
).
Naším úkolem je spočítat látkové množství destilátu D
a
zbytku W
a složení zbytku W
(v mol. %).
Z bilančního schématu je zřejmé, že máme celkem tři proudy, každý s
pěti neznámými. Celkem tedy 15 neznámých. Ze zadání známe složení a
množství nástřiku (5 proměnných) a složení destilátu (4 proměnné). Zbývá
nám tedy celkem 6 neznámých, které je třeba dopočítat. Jedná se o
množství destilátu nD
a zbytku nW
a složení
zbytku xAW
, xBW
, xCW
a
xDW
.
import sympy as sm
= .35, .40, .20, .05 # mol. %
xAF, xBF, xCF, xDF = .95, .05, .00, .00 # mol. %
xAD, xBD, xCD, xDD = 100. # kmol
nF
= sm.symbols('nD nW xAW xBW xCW xDW') nD, nW, xAW, xBW, xCW, xDW
Abychom mohli úlohu jednoznačně vyřešit, je potřeba sestavit 6
nezávislých bilančních rovnic. V našem případě se bude jednat o 4
složkové bilance (f1-f4
), podmínku na podíl fenolu v
destilátu (f5
) a poslední podmínkou (f6
) bude
o složení ve zbytku (suma zlomku se rovná 1).
= sm.Eq(xAD*nD + xAW*nW, xAF*nF)
f1 = sm.Eq(xBD*nD + xBW*nW, xBF*nF)
f2 = sm.Eq(xCD*nD + xCW*nW, xCF*nF)
f3 = sm.Eq(xDD*nD + xDW*nW, xDF*nF)
f4 = sm.Eq(xAD*nD , .9*xAF*nF)
f5 = sm.Eq(xAW + xBW + xCW + xDW, 1.)
f6 print(f1, f2, f3, f4, f5, f6)
> Eq(0.95*nD + nW*xAW, 35.0)
> Eq(0.05*nD + nW*xBW, 40.0)
> Eq(nW*xCW, 20.0)
> Eq(nW*xDW, 5.0)
> Eq(0.95*nD, 4.5)
> Eq(xAW + xBW + xCW + xDW, 1)
Jak je vidět, tímto způsobem jsme získali systém tzv. bilineárních
rovnic, který lineární řešitel linsolve
nedokáže vyřešit.
Je tedy nutné provést vhodnou substituci bilineárních členů – v tomto
případě nahradíme molární zlomky ve zbytku rovnou látkovým množstvím
dané složky ve zbytku (xiW * nW = niW
). Díky této
substituci dojde k přeformulování vazné podmínky na molární zlomky ve
zbytku a získáme celkovou látkovou bilanci zbytku. Dodefinujeme tedy
další symbolické proměnné a celou soustavu přepíšeme na tvar:
= sm.symbols('nAW nBW nCW nDW')
nAW, nBW, nCW, nDW
= sm.Eq(xAD*nD + nAW, xAF*nF)
ff1 = sm.Eq(xBD*nD + nBW, xBF*nF)
ff2 = sm.Eq(xCD*nD + nCW, xCF*nF)
ff3 = sm.Eq(xDD*nD + nDW, xDF*nF)
ff4 = sm.Eq(xAD*nD , .9*xAF*nF)
ff5 = sm.Eq(nAW + nBW + nCW + nDW, nW)
ff6 print(ff1, ff2, ff3, ff4, ff5, ff6)
> Eq(nAW + 0.95*nD, 35.0)
> Eq(nBW + 0.05*nD, 40.0)
> Eq(nCW, 20.0) Eq(nDW, 5.0)
> Eq(0.95*nD, 4.5)
> Eq(nAW + nBW + nCW + nDW, nW)
Soustavu opět vyřešíme funkcí linsolve
.
= sm.linsolve([ff1, ff2, ff3, ff4, ff5, ff6], (nD, nW, nAW, nBW, nCW, nDW))
sol print(sol)
= sol.args[0] nD, nW, nAW, nBW, nCW, nDW
> {(33.1578947368421, 66.8421052631579, 3.49999999999999, 38.3421052631579, 20.0, 5.0)}
Nyní zbývá pouze dopočítat molární zlomky v destilačním zbytku a zobrazit výsledky ve požadovaném formátu.
= nAW/nW, nBW/nW, nCW/nW, nDW/nW
xAW, xBW, xCW, xDW print(f"Destilát: {nD:.2f} kmol; Zbytek: {nW:.2f}")
print(f"Složení zbytku: {xAW*100:.1f} mol. % fenolu, {xBW*100:.1f} mol. % kresolu, {xCW*100:.1f} mol. % xylenolu, {xDW*100:.1f} mol. % těžších fenolů.")
> Destilát: 33.16 kmol; Zbytek: 66.84 kmol.
> Složení zbytku: 5.2 mol. % fenolu, 57.4 mol. % kresolu, 29.9 mol. % xylenolu, 7.5 mol. % těžších fenolů.