|
9 | 9 | "In `FeOs`, we use [generalized dual numbers](https://www.frontiersin.org/articles/10.3389/fceng.2021.758090/full) to compute partial derivatives of the Helmholtz energy.\n", |
10 | 10 | "In this notebook, we take a closer look at how that works.\n", |
11 | 11 | "\n", |
12 | | - "We will make use of the `EquationOfState.python()` feature: we use a simple version of the Peng-Robinson equation of state implemented as Python class which is registered in `FeOs` as equation of state. \n", |
| 12 | + "We will make use of the `EquationOfState.python_residual()` feature: we use a simple version of the Peng-Robinson equation of state implemented as Python class which is registered in `FeOs` as equation of state. \n", |
13 | 13 | "If you want to learn how to implement an equation of state as Python class in conjunction with `FeOs`, take a look at the example implementation in the examples section.\n", |
14 | 14 | "In short - a class that implements a `helmholtz_energy` function that takes a `StateD` (or *any* state object, where the internal data types can be any generalized dual number)\n", |
15 | 15 | "and returns the Helmholtz energy (plus some minor functions), can be used with `FeOs`. We use the equation of state implemented in Python simply because we can add `print` statements and inspect the data types at runtime.\n", |
|
170 | 170 | "molar_weight = SIArray1(44.0962 * GRAM / MOL)\n", |
171 | 171 | "\n", |
172 | 172 | "# create an instance of our python class and hand it over to rust\n", |
173 | | - "eos = EquationOfState.python(PyPengRobinson(tc, pc, omega, molar_weight))" |
| 173 | + "eos = EquationOfState.python_residual(PyPengRobinson(tc, pc, omega, molar_weight))" |
174 | 174 | ] |
175 | 175 | }, |
176 | 176 | { |
|
237 | 237 | "A/kT : [5.06008546]\n" |
238 | 238 | ] |
239 | 239 | }, |
| 240 | + { |
| 241 | + "name": "stderr", |
| 242 | + "output_type": "stream", |
| 243 | + "text": [ |
| 244 | + "/tmp/ipykernel_3483/3728344996.py:1: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", |
| 245 | + " state.molar_helmholtz_energy(Contributions.Residual) / (RGAS * 300*KELVIN)\n" |
| 246 | + ] |
| 247 | + }, |
240 | 248 | { |
241 | 249 | "data": { |
242 | 250 | "text/plain": [ |
243 | | - "-6.554978406564657" |
| 251 | + "5.060085461999784" |
244 | 252 | ] |
245 | 253 | }, |
246 | 254 | "execution_count": 4, |
|
249 | 257 | } |
250 | 258 | ], |
251 | 259 | "source": [ |
252 | | - "state.helmholtz_energy() / (KB * 300*KELVIN)" |
| 260 | + "state.molar_helmholtz_energy(Contributions.Residual) / (RGAS * 300*KELVIN)" |
253 | 261 | ] |
254 | 262 | }, |
255 | 263 | { |
|
279 | 287 | "text": [ |
280 | 288 | "\n", |
281 | 289 | "data type : <class 'builtins.PyDual64'>\n", |
282 | | - "temperature: 300 + [0]ε\n", |
283 | | - "volume : 40744 + [1]ε\n", |
284 | | - "moles : [1 + [0]ε]\n", |
285 | | - "density : 0.000024543491066169253 + [-0.00000000060238295371513]ε\n", |
286 | | - "A/kT : 5.060085461999783 + [0.00000040024326944247174]ε\n" |
| 290 | + "temperature: 300 + 0ε\n", |
| 291 | + "volume : 40744 + 1ε\n", |
| 292 | + "moles : [1 + 0ε]\n", |
| 293 | + "density : 0.000024543491066169253 + -0.00000000060238295371513ε\n", |
| 294 | + "A/kT : 5.060085461999783 + 0.00000040024326944247174ε\n" |
| 295 | + ] |
| 296 | + }, |
| 297 | + { |
| 298 | + "name": "stderr", |
| 299 | + "output_type": "stream", |
| 300 | + "text": [ |
| 301 | + "/tmp/ipykernel_3483/2329888183.py:65: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", |
| 302 | + " ak = ((1.0 - np.sqrt(tr)) * self.kappa + 1.0)**2 * self.a_r\n" |
287 | 303 | ] |
288 | 304 | }, |
289 | 305 | { |
|
292 | 308 | "$100\\,\\mathrm{kPa}$" |
293 | 309 | ], |
294 | 310 | "text/plain": [ |
295 | | - "100.00005278190909 kPa" |
| 311 | + "100.00005278190908 kPa" |
296 | 312 | ] |
297 | 313 | }, |
298 | 314 | "execution_count": 5, |
|
323 | 339 | "text": [ |
324 | 340 | "\n", |
325 | 341 | "data type : <class 'builtins.PyDual64'>\n", |
326 | | - "temperature: 300 + [1]ε\n", |
327 | | - "volume : 40744 + [0]ε\n", |
328 | | - "moles : [1 + [0]ε]\n", |
329 | | - "density : 0.000024543491066169253 + [0]ε\n", |
330 | | - "A/kT : 5.060085461999783 + [-0.025513116823522065]ε\n" |
| 342 | + "temperature: 300 + 1ε\n", |
| 343 | + "volume : 40744 + 0ε\n", |
| 344 | + "moles : [1 + 0ε]\n", |
| 345 | + "density : 0.000024543491066169253 + 0ε\n", |
| 346 | + "A/kT : 5.060085461999783 + -0.025513116823522065ε\n" |
331 | 347 | ] |
332 | 348 | }, |
333 | 349 | { |
334 | | - "data": { |
335 | | - "text/latex": [ |
336 | | - "$118.14\\,\\mathrm{\\frac{ J}{molK}}$" |
337 | | - ], |
338 | | - "text/plain": [ |
339 | | - "118.13947975470876 J/mol/K" |
340 | | - ] |
341 | | - }, |
342 | | - "execution_count": 6, |
343 | | - "metadata": {}, |
344 | | - "output_type": "execute_result" |
345 | | - } |
346 | | - ], |
347 | | - "source": [ |
348 | | - "state.molar_entropy()" |
349 | | - ] |
350 | | - }, |
351 | | - { |
352 | | - "cell_type": "markdown", |
353 | | - "metadata": {}, |
354 | | - "source": [ |
355 | | - "Let's take a look at a more involved property.\n", |
356 | | - "For the Joule-Thomson coefficient, we need multiple second partial derivatives:\n", |
357 | | - "\n", |
358 | | - "$\\mu_{JT}=\\left(\\frac{\\partial T}{\\partial p}\\right)_{H,N_i} = -\\frac{1}{C_p} \\left(V + T \\left(\\frac{\\partial p}{\\partial T}\\right)_{\\mathbf{n}, V} \\left(\\frac{\\partial p}{\\partial V}\\right)_{\\mathbf{n}, T}^{-1} \\right)$\n", |
359 | | - "\n", |
360 | | - "We need three evaluations of the Helmholtz energy (that are actually occuring in the computation of $C_p$):\n", |
361 | | - "\n", |
362 | | - "1. $\\left(\\frac{\\partial p}{\\partial T}\\right)_{\\mathbf{n}, V} \\rightarrow -\\left(\\frac{\\partial A}{\\partial T \\partial V}\\right)_\\mathbf{n}$: second partial derivative w.r.t volume and temperature,\n", |
363 | | - "2. $\\left(\\frac{\\partial p}{\\partial V}\\right)_{\\mathbf{n}, T} \\rightarrow -\\left(\\frac{\\partial^2 A}{\\partial V^2}\\right)_{\\mathbf{n}, T}$: second derivative w.r.t volume,\n", |
364 | | - "3. $\\left(\\frac{\\partial^2 A}{\\partial T^2}\\right)_{\\mathbf{n}, V}$: 2nd partial derivative w.r.t temperature.\n", |
365 | | - "\n", |
366 | | - "Since we compute second partial derivatives, the data type is now `HyperDual64` (one real, 3 non-real values)." |
367 | | - ] |
368 | | - }, |
369 | | - { |
370 | | - "cell_type": "code", |
371 | | - "execution_count": 7, |
372 | | - "metadata": {}, |
373 | | - "outputs": [ |
374 | | - { |
375 | | - "name": "stdout", |
| 350 | + "name": "stderr", |
376 | 351 | "output_type": "stream", |
377 | 352 | "text": [ |
378 | | - "\n", |
379 | | - "data type : <class 'builtins.PyHyperDual64'>\n", |
380 | | - "temperature: 300 + [0]ε1 + [1]ε2 + [0]ε1ε2\n", |
381 | | - "volume : 40744 + [1]ε1 + [0]ε2 + [0]ε1ε2\n", |
382 | | - "moles : [1 + [0]ε1 + [0]ε2 + [0]ε1ε2]\n", |
383 | | - "density : 0.000024543491066169253 + [-0.00000000060238295371513]ε1 + [0]ε2 + [0]ε1ε2\n", |
384 | | - "A/kT : 5.060085461999783 + [0.00000040024326944247174]ε1 + [-0.025513116823522065]ε2 + [-0.0000000023037315630585307]ε1ε2\n", |
385 | | - "\n", |
386 | | - "data type : <class 'builtins.PyDual2_64'>\n", |
387 | | - "temperature: 300 + [0]ε1 + [0]ε1²\n", |
388 | | - "volume : 40744 + [1]ε1 + [0]ε1²\n", |
389 | | - "moles : [1 + [0]ε1 + [0]ε1²]\n", |
390 | | - "density : 0.000024543491066169253 + [-0.00000000060238295371513]ε1 + [0.00000000000002956916128583988]ε1²\n", |
391 | | - "A/kT : 5.060085461999783 + [0.00000040024326944247174]ε1 + [-0.000000000019592452372041545]ε1²\n", |
392 | | - "\n", |
393 | | - "data type : <class 'builtins.PyDual2_64'>\n", |
394 | | - "temperature: 300 + [1]ε1 + [0]ε1²\n", |
395 | | - "volume : 40744 + [0]ε1 + [0]ε1²\n", |
396 | | - "moles : [1 + [0]ε1 + [0]ε1²]\n", |
397 | | - "density : 0.000024543491066169253 + [0]ε1 + [0]ε1²\n", |
398 | | - "A/kT : 5.060085461999783 + [-0.025513116823522065]ε1 + [0.0001919137873859282]ε1²\n" |
| 353 | + "/tmp/ipykernel_3483/2329888183.py:65: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", |
| 354 | + " ak = ((1.0 - np.sqrt(tr)) * self.kappa + 1.0)**2 * self.a_r\n" |
399 | 355 | ] |
400 | 356 | }, |
401 | 357 | { |
402 | 358 | "data": { |
403 | 359 | "text/latex": [ |
404 | | - "$-1.4939\\times10^{-4}\\,\\mathrm{\\frac{ms^{2}K}{kg}}$" |
| 360 | + "$21.566\\,\\mathrm{\\frac{ J}{molK}}$" |
405 | 361 | ], |
406 | 362 | "text/plain": [ |
407 | | - "-1.4938695195180555e-4 m kg^-1 s^2 K" |
| 363 | + "21.566465412067362 J/mol/K" |
408 | 364 | ] |
409 | 365 | }, |
410 | | - "execution_count": 7, |
| 366 | + "execution_count": 6, |
411 | 367 | "metadata": {}, |
412 | 368 | "output_type": "execute_result" |
413 | 369 | } |
414 | 370 | ], |
415 | 371 | "source": [ |
416 | | - "state.joule_thomson()" |
| 372 | + "state.molar_entropy(Contributions.Residual)" |
417 | 373 | ] |
418 | 374 | }, |
419 | 375 | { |
|
430 | 386 | ], |
431 | 387 | "metadata": { |
432 | 388 | "kernelspec": { |
433 | | - "display_name": "Python 3", |
| 389 | + "display_name": "Python 3 (ipykernel)", |
434 | 390 | "language": "python", |
435 | 391 | "name": "python3" |
436 | 392 | }, |
|
444 | 400 | "name": "python", |
445 | 401 | "nbconvert_exporter": "python", |
446 | 402 | "pygments_lexer": "ipython3", |
447 | | - "version": "3.8.6" |
| 403 | + "version": "3.9.12" |
448 | 404 | } |
449 | 405 | }, |
450 | 406 | "nbformat": 4, |
|
0 commit comments