You are on page 1of 112

Treball de Fi de Grau

Grau en Enginyeria en Tecnologies Industrials

Simulació i parametrització de cicles


termodinàmics amb Python

MEMÒRIA

Autor: Jordi Ramon Arasa


Director: Jose Luis Martin Godoy
Convocatòria: Juny 2020

Escola Tècnica Superior


d’Enginyeria Industrial de Barcelona
1

Resum
Aquest TFG consisteix en el disseny d’una aplicació per ordinador feta
amb Python per simular diversos cicles termodinàmics predefinits. El
programa és una calculadora capaç de resoldre els estats que conformen
el cicle, obtenir les potències i rendiments, obtenir els diagrames T-s i
P-h i, en alguns casos, realitzar l’anàlisi exergètica. A més a més, un
cop realitzat el càlcul, el programa permet canviar el valor d’alguna de
les dades inicials i veure com variacions d’aquest paràmetre afecten la
resta de resultats. Tota l’aplicació s’ha dissenyant amb la finalitat de
complementar la docència dels cicles termodinàmics, de manera que el
professorat pugui fer més dinàmiques les sessions i els estudiants puguin
verificar els resultats dels exercicis.
2

Sumari

Resum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Índex de figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Llistat de codis Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

1 Introducció 7
1.1 Motivació . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 Objectius . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Abast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4 Metodologia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5 Marc teòric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.5.1 Propietats de les substàncies pures . . . . . . . . . . . . . . . . . . 11
1.5.2 Diagrames P-h i T-s . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.5.3 Criteri de signes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5.4 Dispositius de flux estacionari . . . . . . . . . . . . . . . . . . . . . 14
1.5.5 Programari utilitzat . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2 Cicles termodinàmics d’estudi 19


2.1 Cicle bàsic de Rankine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Cicle de Rankine amb reescalfament intermedi . . . . . . . . . . . . . . . . 22
2.3 Cicle de Brayton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.4 Cicle de refrigeració per compressió de vapor . . . . . . . . . . . . . . . . . 25
2.5 Cicle de refrigeració per gas . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3 Modelització mitjançant CoolProp 29


3.1 Funció PropsSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2 Classe PropertyPlot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.3 Classe StateContainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4 Funció draw_process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3

4 Implementació de l’aplicació amb Python 37


4.1 Disseny general de l’aplicació amb Tkinter . . . . . . . . . . . . . . . . . . 38
4.2 Disseny específic del solucionador . . . . . . . . . . . . . . . . . . . . . . . 61
4.3 Disseny específic dels diagrames termodinàmics . . . . . . . . . . . . . . . 80

5 Manual d’usuari 82
5.1 Introducció de dades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.2 Resolució del cicle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.3 Visualització de resultats i diagrames termodinàmics . . . . . . . . . . . . 86

6 Anàlisi de resultats 87
6.1 Exemple 1: Cicle bàsic de Rankine . . . . . . . . . . . . . . . . . . . . . . 87
6.2 Exemple 2: Cicle de Rankine amb reescalfament intermedi . . . . . . . . . 91
6.3 Exemple 3: Cicle de Brayton . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.4 Exemple 4: Cicle de refrigeració per compressió de vapor . . . . . . . . . . 98
6.5 Exemple 5: Cicle de refrigeració per gas . . . . . . . . . . . . . . . . . . . 101

Pressupost 104

Impacte ambiental 107

Conclusions i Línies futures 108

Bibliografia 110
4

Índex de figures

1.1 Diagrama P-h d’una substància pura . . . . . . . . . . . . . . . . . . . . . 12


1.2 Diagrama T-s d’una substància pura . . . . . . . . . . . . . . . . . . . . . 13
1.3 Esquema d’una caldera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.4 Esquema d’una vàlvula . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.5 Esquema d’un compressor . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.6 Esquema d’una turbina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.7 Esquema d’una bomba . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.1 Esquema d’un cicle bàsic de Rankine . . . . . . . . . . . . . . . . . . . . . 20


2.2 Diagrama T-s d’un cicle bàsic real de Rankine . . . . . . . . . . . . . . . . 21
2.3 Cicle de Rankine amb rescalfament intermedi . . . . . . . . . . . . . . . . 23
2.4 Diagrama T-s d’un cicle ideal de Rankine amb rescalfament intermedi . . . 23
2.5 Esquema del cicle bàsic de turbina de gas en règim tancat . . . . . . . . . 24
2.6 Diagrama T-s del cicle ideal de Brayton en règim tancat . . . . . . . . . . 25
2.7 Esquema d’un cicle de refrigeració per compressió de vapor . . . . . . . . . 26
2.8 Diagrames T-s i P-h d’un cicle real de refrigeració per compressió de vapor 27
2.9 Esquema d’un cicle de refrigeració per gas . . . . . . . . . . . . . . . . . . 27
2.10 Diagrama T-s d’un cicle ideal de refrigeració per gas . . . . . . . . . . . . . 28

3.1 Logotip de la llibreria CoolProp . . . . . . . . . . . . . . . . . . . . . . . . 29


3.2 Exemple de Python d’utilització de la funció PropsSI . . . . . . . . . . . . 30
3.3 Taula de string inputs de la funció PropsSI que utilitzarà l’aplicació . . . . 31
3.4 Taula de fluids de treball de la llibreria CoolProp que utilitzarà l’aplicació 32
3.5 Exemple de Python d’utilització de la classe PropertyPlot . . . . . . . . . 32
3.6 Diagrama P-h del R-134a obtingut utilitzant la classe Property Plot . . . . 33
3.7 Exemple de Python d’utilització de la classe PropertyPlot . . . . . . . . . 33
3.8 Diagrama T-s d’un cicle de Rankine amb reescalfament intermedi . . . . . 36

4.1 Pantalla principal del cicle bàsic de Rankine . . . . . . . . . . . . . . . . . 42


4.2 Visualitzador del cicle bàsic de Rankine . . . . . . . . . . . . . . . . . . . . 46
4.3 Pantalla de selecció del fluid . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4 Pantalla d’introducció de les propietats termodinàmiques, el flux màssic i
la potència neta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.5 Pantalles d’introducció de dades als dispositius de flux estacionari . . . . . 55
4.6 Pantalla d’anàlisi exergètica . . . . . . . . . . . . . . . . . . . . . . . . . . 75

5.1 Pantalla inicial de l’aplicació . . . . . . . . . . . . . . . . . . . . . . . . . . 82


5.2 Selecció del fluid de treball . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5

5.3 Introducció de propietats termodinàmiques, flux màssic i potència neta . . 84


5.4 Introducció de treballs, calors i rendiments als dispositius . . . . . . . . . . 85
5.5 Error provocat per no haver introduït suficients dades . . . . . . . . . . . . 85
5.6 Pantalla de realització de l’anàlisi exergètica . . . . . . . . . . . . . . . . . 86

6.1 Esquema del cicle bàsic de Rankine de l’enunciat . . . . . . . . . . . . . . 87


6.2 Introducció de dades del cicle bàsic de Rankine . . . . . . . . . . . . . . . 88
6.3 Visualització dels resultats obtinguts un cop realitzat el càlcul . . . . . . . 88
6.4 Diagrama T-s obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 89
6.5 Ampliació del diagrama T-s per diferenciar els Estats 1 i 2 . . . . . . . . . 89
6.6 Diagrama P-h obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 90
6.7 Visualització dels resultats de l’anàlisi exergètica . . . . . . . . . . . . . . . 90
6.8 Esquema del cicle de Rankine amb reescalfament intermedi de l’enunciat . 91
6.9 Introducció de dades del cicle de Rankine amb reescalfament . . . . . . . . 92
6.10 Visualització dels resultats obtinguts un cop realitzat el càlcul . . . . . . . 92
6.11 Diagrama T-s obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 93
6.12 Diagrama P-h obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 93
6.13 Visualització dels resultats de l’anàlisi exergètica . . . . . . . . . . . . . . . 94
6.14 Esquema del cicle de Brayton de l’enunciat . . . . . . . . . . . . . . . . . . 95
6.15 Introducció de dades del cicle de Brayton . . . . . . . . . . . . . . . . . . . 96
6.16 Visualització dels resultats obtinguts un cop realitzat el càlcul . . . . . . . 96
6.17 Diagrama T-s obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 97
6.18 Esquema del cicle de refrigeració per compressió de vapor de l’enunciat . . 98
6.19 Introducció de dades al cicle de refrigeració per compressió de vapor . . . . 98
6.20 Visualització dels resultats obtinguts un cop realitzat el càlcul . . . . . . . 99
6.21 Diagrama T-s obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 99
6.22 Diagrama P-h obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 100
6.23 Esquema del cicle de refrigeració per gas . . . . . . . . . . . . . . . . . . . 101
6.24 Introducció de dades del cicle de Brayton . . . . . . . . . . . . . . . . . . . 102
6.25 Visualització dels resultats obtinguts un cop realitzat el càlcul . . . . . . . 102
6.26 Diagrama T-s obtingut per l’aplicació . . . . . . . . . . . . . . . . . . . . . 103
6.27 Diagrama de Gantt del projecte . . . . . . . . . . . . . . . . . . . . . . . . 104
6

Llistat de codis Python

3.1 Codi Python necessari per representar un diagrama T-s . . . . . . . . . . . 34


4.1 Inicialització de totes les variables de l’aplicació amb Python. . . . . . . . . 39
4.2 Programació de l’esquema gràfic d’un cicle termodinàmic. . . . . . . . . . . 43
4.3 Mètodes associats als botons de l’esquema gràfic del cicle termodinàmic . . 45
4.4 Codi Python del visualitzador del cicle bàsic de Rankine . . . . . . . . . . 46
4.5 Codi Python del menú . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.6 Codi Python de la classe LlistaFluid . . . . . . . . . . . . . . . . . . . . . 50
4.7 Codi Python de la classe TaulaProps . . . . . . . . . . . . . . . . . . . . . 52
4.8 Mètodes associats a la selecció del fluid i a la taula de propietats . . . . . . 54
4.9 Codi Python de les classes Caldera, Turbina, Bomba i Condensador . . . . 56
4.10 Codi Python de la primera etapa del solucionador . . . . . . . . . . . . . . 62
4.11 Codi Python de la funció ResoldreEstat . . . . . . . . . . . . . . . . . . . . 64
4.12 Codi Python de la funció CalcularAltres . . . . . . . . . . . . . . . . . . . 64
4.13 Codi Python de la funció CalcularFlux . . . . . . . . . . . . . . . . . . . . 65
4.14 Codi Python de la funció CalcularPotencies . . . . . . . . . . . . . . . . . 66
4.15 Codi Python de la funció RendimentTurbina . . . . . . . . . . . . . . . . . 66
4.16 Codi Python de la funció RendimentBomba . . . . . . . . . . . . . . . . . 67
4.17 Codi Python de la funció CalcularEntalpies . . . . . . . . . . . . . . . . . 68
4.18 Codi Python de la tercera etapa del solucionador . . . . . . . . . . . . . . 72
4.19 Codi Python de la quarta etapa del solucionador . . . . . . . . . . . . . . . 73
4.20 Codi Python de la classe AnalisiExergetic . . . . . . . . . . . . . . . . . . 74
4.21 Codi Python de la representació dels diagrames T-s i P-h . . . . . . . . . . 80
Capítol 1 7

Capítol 1

Introducció

La resolució de problemes relacionats amb cicles termodinàmics generalment no és un


procés difícil. Malgrat això en alguns casos pot acabar resultant un procés costós pel
que fa a temps. No existeix un algoritme ni tampoc una recepta exacta que permeti
resoldre tots els cicles de la mateixa manera, almenys a escala humana. Un altre dels
inconvenients que es presenten a l’hora de plantejar la resolució és haver de consultar les
taules termodinàmiques per acabar de resoldre els estats.

Amb l’arribada de les noves tecnologies la majoria d’aquestes problemàtiques s’han so-
lucionat incorporant les taules termodinàmiques dintre dels softwares que per si sols són
capaços de fer les interpolacions i resoldre els estats. D’aquests n’hi ha de més generalistes
com el Engineering Equation Solver (EES) o més específics com el CyclePad. Tanmateix,
tot avantatge té els seus inconvenients, i en aquest cas en trobem dos de molt rellevants.
Per una banda, molts d’aquests softwares són de pagament, com és el cas de l’EES, i això
fa que perdi molts punts en la seva utilització com a eina docent perquè pot ser que la
universitat no hagi comprat la llicència o que aquesta llicència sigui vàlida tan sols en
les xarxes de la universitat. En general la majoria d’aquests programaris no s’utilitzen
per a la realització dels exàmens, sinó que s’utilitzen a l’aula per a clarificar conceptes,
exemplificar continguts... Per altra banda la manipulació d’aquests softwares moltes ve-
gades requereix una formació específica, és a dir, són complicats de fer servir, i per tant
inicialment cal dedicar-los-hi un temps per aprendre a fer-los servir, bastant més temps
que el que pot costar consultar les taules termodinàmiques. Per tant, malgrat que a la
llarga evidentment són molt més ràpids que les taules (i també molt més útils), no tothom
està disposat a fer aquest esforç inicial si l’objectiu de la utilització del software només és
la verificació d’uns resultats o la de complementar la docència.

En aquest projecte es desenvoluparà un software que pugui superar els inconvenients


anteriorment mencionats, és a dir, es dissenyarà un software gratuït i senzill que, malgrat
que segur que no serà perfecte, sí que sigui adequat per l’ús educatiu.
8 Capítol 1

1.1 Motivació
La principal motivació per la realització d’aquest projecte és donar una alternativa l’EES,
que és el software que s’utilitza actualment com a complement a la docència a l’assignatura
Termodinàmica impartida a l’ETSEIB.

Aquest software presenta els dos grans inconvenients mencionats a la introducció. Per
una banda, malgrat que la UPC té la llicència del software pagada, aquest només es
pot fer servir utilitzant l’eduroam de la UPC. Això és un problema perquè perjudica el
treball autònom de l’estudiant, que si volgués, per exemple, verificar algun càlcul amb el
software en algun entorn fora de la universitat, no podria. Per altra banda, l’EES no és
un programari fàcil de manipular, cal aprendre el seu llenguatge per saber introduir les
dades i saber manipular les equacions. En aquesta situació els estudiants es troben que
de manera autònoma han de fer un treball addicional per aprendre a manipular aquest
software per només poder verificar resultats o entendre millor els conceptes. Amb aquest
programa es busca deixar això enrere i que d’una manera molt senzilla l’estudiant pugui
verificar resultats, veure què passa quan canvia un paràmetre d’un cicle, veure com afecta
el fluid de treball a les potències calorífiques del sistema, entre d’altres.

1.2 Objectius
L’objectiu principal d’aquest projecte és desenvolupar una aplicació informàtica que per-
meti la resolució de cicles termodinàmics predefinits a partir d’un conjunt de dades inicials
que siguin suficients perquè el problema tingui solució única.

En aquesta línia, es defineixen les següents especificacions de disseny que el programa ha


de satisfer.

• Se li han de poder introduir com a dada qualsevol de les propietats termodinàmi-


ques més utilitzades en cicles termodinàmics (P, T, h, s) per qualsevol dels n estats
termodinàmics que formin el cicle (1, 2, ..., n).

• Se li ha de poder introduir com a dada el títol x per cada un dels estats si en tenen.

• S’ha de poder definir el fluid de treball del sistema.

• Se li han de poder introduir les potències associades a les calors Q̇ i les potències
associades als treballs Ẇ dels dispositius que formen el cicle.

• Se li han de poder introduir els rendiments isentròpics η dels dispositius que formen
el cicle si és que estan definits

• Se li ha de poder introduir o la potència neta del sistema o el flux màssic o ambdues.

• Un cop realitzat el càlcul, després d’haver introduït un conjunt de les variables


anteriors definides com a dades inicials, totes les variables que poden introduir-se al
sistema, s’han de poder visualitzar com a resultats.
Capítol 1 9

• Un cop realitzat el càlcul s’han de poder modificar les dades inicials i el seu valor
per veure com el canvi de valor d’un paràmetre afectaria el cicle.

• Un cop realitzat el càlcul s’han de poder obtenir els diagrames T-s i P-h.

• Un cop realitzat el càlcul s’ha de poder visualitzar el rendiment tèrmic o el COP


del cicle estudiat.

• Un cop realitzat el càlcul s’ha de poder fer una anàlisi exergètica del sistema per ob-
tenir els treballs perduts dels dispositius del sistema, els seus rendiments exergètics
i el rendiment exergètic de la instal·lació.

1.3 Abast
L’abast d’aquest projecte és el disseny d’una aplicació per ordinador amb Python que
permeti resoldre cicles termodinàmics predefinits complint totes les especificacions de
disseny definides en l’apartat anterior.

Aquesta aplicació no podrà treballar amb els cicles termodinàmics que no s’hagin pre-
definit en el seu disseny. En aquest projecte se’n dissenyaran uns quants i l’usuari, és a
dir, la persona que farà servir l’aplicació, podrà treballar amb aquests. L’usuari no podrà
definir els cicles termodinàmics perquè el software no és una aplicació de disseny de cicles
termodinàmics sinó de resolució.

Per altra banda, l’aplicació no permet l’elaboració de taules paramètriques però sí que
permet anar canviant el valor d’alguna de les variables definides anteriorment com si fos
un paràmetre i tornar a resoldre el cicle per veure com aquest canvi de valor afecta el
cicle.

Finalment comentar que malgrat que el programa podria utilitzar-se per estudiar aquests
mateixos cicles en l’àmbit industrial, no es recomana, ja que l’aplicació s’ha pensat per
complementar la docència.

1.4 Metodologia
L’elaboració d’aquesta aplicació s’ha estructurat en quatre fases: fase creativa, fase de
recerca bibliogràfica, fase de programació de l’aplicació i finalment una fase de testeig a
la recerca d’errors. Cal dir que aquestes fases no han sigut seqüencials. Per descomptat
la fase creativa s’ha iniciat abans que la fase de recerca bibliogràfica, la de recerca biblio-
gràfica abans que la de programació, etc. però en el moment en què una fase s’iniciava no
tenia per què acabar-se’n una. Mentre es feia la programació de l’aplicació, per exemple,
podien anar sorgint noves idees i per implementar-les calia fer una recerca bibliogràfica,
quan s’acabava una part del codi convenia fer-li una prova per solucionar errors, ja que
detectar-los el més aviat millor permetia solucionar-los de la forma més òptima.
10 Capítol 1

En primer lloc es va començar el projecte amb la fase creativa. En aquesta etapa es


va fer un primer esbós de com havia de ser l’aplicació a primer cop d’ull, és a dir, com
s’havien de visualitzar les dades, com s’havien d’introduir, quins botons havia de tenir
l’aplicació... Aquest esbós va ser molt útil al llarg del procés creatiu perquè va anar
canviant al llarg de les setmanes, adaptant-se a les idees que anaven sorgint, tot amb
l’objectiu de fer l’aplicació la més ergonòmica possible.

En segon lloc es va iniciar la fase de recerca bibliogràfica, en paral·lel amb la fase


creativa. En aquesta etapa el que es va fer és buscar com traslladar totes aquelles idees
esquematitzades a l’esbós en una realitat. Des d’un principi ja es va decidir que el llen-
guatge de programació seria Python, per tots els coneixements previs d’aquest llenguatge
que s’havien adquirit al llarg del grau. Però malgrat que aquests coneixements previs
són generalistes, no són suficients per fer l’aplicació sinó que permeten tenir la base per
aprendre el necessari per dissenyar-la. Aquesta fase de recerca bibliogràfica és això, un
procés d’aprenentatge constant sobre com programar una aplicació. Es va donar resposta
a preguntes com per exemple: Com aconseguir que s’obri una finestra en compilar el codi?
Com fer que aparegui un botó? Com donar-li una funcionalitat al botó? Com transfor-
mar l’aplicació de Python a un fitxer executable? Totes aquestes preguntes i moltes més
anaven sorgint al llarg de l’elaboració de l’aplicació i es buscaven respostes. Cal remar-
car que aquesta fase conviu amb la resta de fases, per exemple per solucionar un error,
que formaria part de l’etapa de testeig, normalment cal adquirir nous coneixements per
aprendre a resoldre’l.

En tercer lloc s’inicia la fase de programació de l’aplicació. La programació de


l’aplicació tampoc s’ha fet de manera estrictament vertical, sinó que prèviament es va
fer un esbós de com havia de ser el cicle més bàsic i es va aprendre a programar-lo.
Aleshores es va començar a programar la part de l’aplicació que correspondria a aquest
cicle. I la major part del temps d’aquesta etapa es va dedicar a la programació d’aquest
cicle. Perquè un cop acabat de programar es va utilitzar com a plantilla, de manera que
replicant-lo i introduint-li canvis es podien obtenir la resta de cicles. D’aquesta manera
cada cicle termodinàmic es troba en un arxiu Python diferent, es podria dir que tots es
troben en un mateix nivell. Finalment es va dissenyar la pantalla inicial de l’aplicació que
condueix a l’usuari al cicle que desitgi, que es trobaria en un nivell superior.

Finalment la darrera etapa és la fase de testeig que s’inicia just quan s’acaba de pro-
gramar la primera part del codi del cicle “plantilla”. Aquesta etapa consisteix a detectar,
localitzar i donar una solució a tots els problemes que poden sorgir durant la programació
de l’aplicació. La majoria d’aquests errors són informàtics, per exemple, no haver declarat
alguna variable, deixar-se un parèntesi... Però també es busquen errors en els resultats
numèrics que ha de donar el programa per verificar que no hi hagi errors en les fórmules
matemàtiques que s’han traslladat a codi Python. Aquesta etapa resulta fonamental per-
què qualsevol aplicació inicialment pot presentar errors que el programador pot no haver
detectat i com més verificacions i més testos es facin més possibilitats de solucionar els
errors, que normalment acostumen a ser de fàcil correcció i són fruit d’algun factor que
no s’ha tingut en compte.
Capítol 1 11

Seguir aquesta metodologia ha sigut molt útil per l’elaboració de l’aplicació gràcies a
la flexibilitat que permet la convivència de les fases entre elles. A més a més, un gran
avantatge d’aquest mètode és que les etapes s’alimenten entre elles de manera que els errors
es converteixen en oportunitats que poden donar peu a idees per millorar l’aplicació.

1.5 Marc teòric


1.5.1 Propietats de les substàncies pures
Les característiques d’un estat termodinàmic s’anomenen propietats i en són un exemple
la pressió, la temperatura, l’entalpia o la massa. Aquestes propietats les podem diferenciar
entre propietats intensives, que són independents de la massa, o propietats extensives el
valor de les quals sí que depèn de la massa del sistema. En els cicles termodinàmics ens és
convenient treballar amb independència de la massa, per aquest motiu es treballarà amb
les propietats intensives i amb les propietats extensives per unitat de massa. Aquestes
darreres les anomenarem propietats específiques. En particular treballarem amb la pressió
P , la temperatura T , l’entalpia específica h i l’entropia específica s.

Per altra banda, s’anomena substància pura a tota substància que té una composició
química fixa. Una substància pura no té per què ser un sol element o component químic,
pot ser una barreja de diversos, com per exemple l’aire, que és una barreja de diversos
gasos. L’únic requisit és que la composició química sigui uniforme. Una barreja de dues o
més fases d’una substància pura, com per exemple una barreja entre aigua líquida i vapor
d’aigua, se segueix considerant una substància pura sempre que la composició química de
les fases sigui la mateixa.

En l’estudi dels cicles termodinàmics s’acostuma a treballar amb substàncies pures en les
regions vapor, líquid-vapor i líquid. Quan el fluid es troba en fase líquida es diu que és
líquid comprimit o subrefredat, que significa que no està a punt d’evaporar-se.

Si d’aquest fluid es modifiquen les seves propietats, per exemple augmentant la tempera-
tura en cas de l’aigua, s’arriba a la saturació en què el líquid passa a ser líquid saturat i
s’entra a la regió líquid-vapor. En aquesta regió el líquid es va evaporant mantenint la
temperatura constant. Quan ja quasi s’ha completat l’evaporació, el vapor que es té és un
vapor que es troba al límit de la fase líquida. S’anomena vapor saturat que és un vapor
que està a punt de condensar-se.

En aquesta regió es defineix el títol, que es representa com x, és el quocient entre la massa
que hi ha de fase vapor i la massa total del sistema:
mv
x=
m
El títol és una magnitud compresa entre 0 (líquid saturat) i 1 (vapor saturat). Amb el
títol es pot trobar l’entalpia o l’entropia especifiques d’una substància pura que es trobi
dins la regió líquid-vapor.
h = x · hvs + (1 − x) · hls
12 Capítol 1

s = x · svs + (1 − x) · sls
La quantitat 1 − x s’anomena humitat.

Finalment, a l’entrar a la regió vapor es té un vapor sobreescalfat, que és un vapor que


no està a punt de condensar-se.

Des d’un punt de vista pràctic, en les regions de líquid o vapor cal conèixer dues propietats
termodinàmiques per resoldre un estat. En canvi, en la regió líquid-vapor, coneixent una
propietat i el títol és suficient. Això és de gran importància a l’hora d’introduir les dades
a l’aplicació, per tal que aquestes siguin suficients per resoldre el cicle.

1.5.2 Diagrames P-h i T-s


A la Figura 1.1 es pot observar un exemple de diagrama P-h per a una substància pura, en
què es representa la pressió versus l’entalpia. Bàsicament el diagrama està format per dos
eixos principals i tres zones delimitades per la corba de saturació. En l’eix d’ordenades es
té el valor de la pressió P graduat en escala logarítmica. En l’eix d’abscisses es té el valor
de l’entalpia específica h. La regió interior a la corba de saturació s’anomena campana
de saturació, i el punt crític correspon al punt més elevat. A partir d’aquest punt, per
molt que s’augmenti la pressió no es pot condensar el gas. A dintre de la campana hi ha
l’equilibri líquid-vapor.

Figura 1.1: Diagrama P-h d’una substància pura

Al diagrama també es poden visualitzar diferents tipus de línies isotermes; les subcrítiques
passen a través de la campana i a dintre d’ella són paral·leles a l’eix d’entalpia (si la
substància és pura, el canvi de fase es produeix a pressió constant). La isoterma crítica
presenta un punt d’inflexió al punt crític i les isotermes supercrítiques no passen a través
de la campana de saturació.
Capítol 1 13

A banda de la regió líquid-vapor, en aquest diagrama també apareix la regió líquida


que es troba a l’esquerra de la corba de saturació (on el líquid és saturat). A la dreta
de la mateixa corba (on el vapor és saturat) la substància es troba en estat vapor (a
temperatures subcrítiques) i gas (a temperatures supercrítiques).

En aquest diagrama també es poden observar altres corbes com del que uneixen els punts
amb el mateix títol (discontinues i situades a l’interior de la campana de saturació), les
isentròpiques i les isocòriques.

A la Figura 1.2 es té un exemple d’un diagrama T-s per a una substància pura, en què es
representa la temperatura versus l’entropia. Novament en el diagrama es poden distingir
les tres regions en què acostumen a treballar els cicles termodinàmics separades per la
corba de saturació. Dins de la campana de saturació es troba la zona d’equilibri líquid-
vapor i les corbes que uneixen punts amb el mateix títol. A l’esquerra de la campana i per
sota de la temperatura crítica es troba la zona de líquid, i a la dreta i també per sota de la
temperatura crítica, la zona de vapor. En aquest diagrama també es poden veure altres
corbes com les isobàriques, les isentàlpiques, les isocòriques i el grau de reescalfament
constant (T − Tsat ).

Figura 1.2: Diagrama T-s d’una substància pura

Ambdós diagrames seran els que es podran obtenir a través de l’aplicació. En els obtinguts,
es veuran les línies isotermes i les de títol constant en el cas del diagrama P-h i les línies
isòbares i les de títol constant en el diagrama T-s.

1.5.3 Criteri de signes


En aquesta aplicació se segueix el criteri de signes propi de l’assignatura de Termodinàmica
de l’ETSEIB.
14 Capítol 1

Per tant es consideraran positives (Ẇ > 0 i Q̇ > 0) les entrades d’energia al sistema
mentre que les sortides es consideraran negatives (Ẇ < 0 i Q̇ < 0).

1.5.4 Dispositius de flux estacionari


Es consideren dispositius de flux estacionari tots aquells dispositius que operen durant
llargs períodes de temps en les mateixes condicions. Els processos que tenen lloc en
aquests dispositius són estacionaris, de manera que les propietats del fluid, com l’energia
i la massa, es mantenen constants durant tot el procés dins el volum de control definit.

Els cicles termodinàmics estan formats per aquests dispositius i es plantegen balanços
de massa i energia en cadascun d’aquests dispositius per obtenir les equacions que ens
permeteren calcular les propietats termodinàmiques dels estats i resoldre el cicle.

Es pot plantejar tant un balanç de potència com un d’energia. En la nostra aplicació es


treballarà sempre amb potències. Per tant es parteix del següent balanç general:
 
1
ṁ · (hs − he ) + · (~ 2 2
vs − v~e ) + g · (zs − ze ) = Q̇ + Ẇ (1.1)
2

En la majoria de dispositius, i en particular, en tots els que utilitza l’aplicació que s’està
dissenyant, es pot simplificar l’equació 1.1 negligint l’energia potencial per unitat de temps
i l’energia cinètica per unitat de temps. De manera que se simplifica com:

ṁ · (hs − he ) = Q̇ + Ẇ (1.2)

On el subíndex e indica l’estat termodinàmic d’entrada al dispositiu i el subíndex s fa


referència a l’estat de sortida.

A continuació s’explicaran els diferents dispositius que formen part dels cicles termodi-
nàmics amb els quals treballarà l’aplicació i se’n presentarà, per cadascun d’ells els seus
balanços d’energia i en alguns casos l’expressió del rendiment isentròpic. Aquestes equaci-
ons posteriorment s’hauran d’introduir al programa amb Python de manera que es puguin
resoldre.

Intercanviador de calor

Figura 1.3: Esquema d’una caldera


Capítol 1 15

L’intercanviador de calor és un dispositiu en què un fluid en estat líquid, vapor o gas rep
o cedeix calor, segons si és una caldera o un condensador. A la Figura 1.3 es té l’esquema
d’una caldera. En aquest sistema es considera, a part de les suposicions fetes inicialment,
que no hi ha intercanvi de potència mecànica. Per tant l’equació 1.2 se simplifica de la
següent manera:
ṁ · (hs − he ) = Q̇ (1.3)
En els casos en què el cicle termodinàmic presenti reescalfament es dóna el cas que per la
caldera hi circulen més d’un corrents D’aquesta manera el balanç d’energia de l’equació
1.3 s’ha de generalitzar com:
k
X k
X
ṁi · (hs − he )i = Q̇i (1.4)
i=1 i=1

D’aquesta manera només cal fer la suma dels termes de l’equació 1.3 per cada un dels
corrents.

Per altra banda, l’exergia (energia disponible) de la caldera es pot calcular amb l’equació:
 
T0
Ex (Q̇) = Q̇ · 1 − (1.5)
TF
On T0 és la temperatura de l’entorn i TF és la temperatura de la font de la calor que rep
la caldera. En canvi, l’exergia del condensador és nula.

Vàlvula o dispositiu d’estrangulament


La vàlvula provoca una disminució de la pressió del fluid que passa a través d’ella (Ps <
Pe ). A la Figura 1.4 es mostra un esquema d’aquest dispositiu.

Figura 1.4: Esquema d’una vàlvula

En aquest dispositiu se suposa que no hi ha ni intercanvi de calor ni de treball. D’aquesta


manera l’equacio 1.2 queda simplificada com:

hs − he = 0 →
− hs = he (1.6)

És a dir, que la vàlvula és isoentàlpica.

Compressor
Un compressor augmenta la pressió d’un gas o d’un vapor (Ps > Pe ). Perquè això sigui
possible cal subministrar potència mecànica. A la Figura 1.5 es pot veure un esquema
d’aquest dispositiu.
16 Capítol 1

En els compressors utilitzats en molts cicles termodinàmics, i en particular en tots els de


l’aplicació dissenyada, es considera que no hi ha intercanvi de calor. Aleshores l’equació
1.2 queda com:
Ẇ = ṁ · (hs − he ) (1.7)

Figura 1.5: Esquema d’un compressor

Per altra banda, el rendiment isentròpic d’un compressor real es defineix com el quocient
entre la potència isentròpica i la real.

Ẇiso h2s − h1
ηC = = (1.8)
Ẇreal h2 − h1
On l’Estat 1 correspon a l’entrada, l’Estat 2 a la sortida i l’Estat 2s correspon a la sortida
que tindria el mateix compressor si fos isentròpic (reversible i adiabàtic) en el qual es
verifica que s2s = s1 i P2s = P2 .

Turbina
A la turbina entra un fluid en estat vapor o gas a elevada pressió que genera una potència
mecànica. Això provoca una disminució de la pressió del fluid (Ps < Pe ). Es pot veure
un esquema de la turbina a la Figura 1.6.

Figura 1.6: Esquema d’una turbina

Des d’un punt de vista pràctic les suposicions són les mateixes fetes en el compressor.
Per tant les equacions són les mateixes i l’únic que canviarà serà el signe de la potència
mecànica. Cal dir que el rendiment isentròpic tampoc serà el mateix.
Capítol 1 17

Es defineix el rendiment isentròpic d’una turbina real com el quocient entre la potència
real i la isentròpica.

Ẇreal h2 − h1
ηT = = (1.9)
Ẇiso h2s − h1
On l’Estat 1 correspon a l’entrada, l’Estat 2 a la sortida i l’Estat 2s correspon a la sortida
que tindria la mateixa turbina si fos isentròpica (reversible i adiabàtica) en el qual es
verifica que s2s = s1 i P2s = P2 .

Bomba
La bomba serveix per impulsar un líquid i augmentar al mateix temps la seva pressió
(Ps > Pe ). Per fer-ho cal subministrar-li potència mecànica. A la Figura 1.7 hi ha un
esquema d’una bomba.

Figura 1.7: Esquema d’una bomba

Pel seu estudi se suposa que és adiabàtica, de manera que l’equacio 1.2 queda de la següent
manera:
W˙B = ṁ · (hs − he ) (1.10)
El rendiment isentròpic d’una bomba es defineix com el quocient entre la potència isen-
tròpica i la potència real:
Ẇiso h2s − h1
ηB = = (1.11)
Ẇreal h2 − h1
On l’Estat 1 correspon a l’entrada, l’Estat 2 a la sortida i l’Estat 2s correspon a la sortida
que tindria el mateix compressor si fos isentròpic (reversible i adiabàtic) en el qual es
verifica que s2s = s1 i P2s = P2 .

En alguns casos la potència subministrada a la bomba és negligible en comparació a la


potència del sistema. En aquests casos el rendiment de la bomba no té sentit i no s’ha de
donar.

Finalment fer notar que l’exergia de la turbina, del compressor i de la bomba és directa-
ment el seu treball.

1.5.5 Programari utilitzat


Per la realització d’aquest projecte s’ha utilitzat la plataforma Anaconda per treballar amb
Python des de Windows. El codi s’ha escrit mitjançant l’entorn IDLE i s’han utilitzat
18 Capítol 1

les llibreries Tkinter, pel disseny de tota la interfície gràfica; CoolProp, per realitzar el
càlcul de propietats termodinàmiques i també els diagrames P-h i T-s; Matplotlib per
complementar la representació dels diagrames i PyInstaller per convertir el codi Python
en un arxiu executable.

S’ha consultat [1] i [2] com a fonts bibliogràfiques per la redacció d’aquest capítol.
Totes les figures i equacions utilitzades d’aquest capítol han sigut extretes de [2].
Capítol 2 19

Capítol 2

Cicles termodinàmics d’estudi

En aquest capítol s’explicarà el fonament teòric dels cicles termodinàmics que s’imple-
mentaran amb el software. En primer lloc s’explicarà breument el funcionament del cicle
i també quins són els fluids que hi circulen habitualment. En segon lloc, es detallaran
quins dels dispositius de flux estacionari configuren els cicles i a quins estats corresponen
les seves entrades i sortides. També s’explicarà quins han de ser els sentits de les potèn-
cies mecàniques i tèrmiques del cicle. A més a més, es donaran altres equacions lligades
estrictament a cada un dels cicles, com poden ser el rendiment tèrmic o el coeficient de
funcionament. A continuació, es mostraran els diagrames termodinàmics típics perquè el
lector pugui veure’n la forma i sobretot perquè ens servirà per comprovar que els diagra-
mes obtinguts amb l’aplicació són coherents. Finalment, pels cicles de Rankine, es farà
l’anàlisi exergètica del cicle i se’n donaran les equacions, ja que aquesta funcionalitat de
l’aplicació només està disponible en aquests cicles.

2.1 Cicle bàsic de Rankine


El cicle bàsic de Rankine modelitza els cicles de potència de turbina de vapor, en aquests
cicles el fluid de treball s’evapora i es condensa alternadament. Aquests cicles es fan servir
en centrals tèrmiques o nuclears. Habitualment s’empra com a fluid de treball l’aigua per
la seva disponibilitat, el seu baix cost i l’elevada entalpia de vaporització. L’objectiu
fonamental del cicle és transformar la calor (generada per una combustió o una reacció
nuclear) en energia elèctrica. Els dispositius que conformen aquest cicle són: bomba,
caldera, turbina i condensador. Es pot veure un esquema d’aquest cicle a la Figura 2.1.

El fluid de treball entra a la bomba (Estat 1) en forma de líquid saturat o comprimit i


es condensa fins a la pressió d’operació de la caldera. En aquest procés l’augment de la
temperatura és molt petit i per aquest motiu en la representació del diagrama T-s els
estats 1 i 2 es trobaran molt a prop, per veure bé que són estats diferents caldrà ampliar
el gràfic. Per fer-ho caldrà subministrar una potència mecànica a la bomba, donada per
la següent equació:
ẆB = ṁ · (h2 − h1 ) (2.1)
20 Capítol 2

Figura 2.1: Esquema d’un cicle bàsic de Rankine

A continuació, el fluid de treball entra a la caldera (Estat 2) i rep la calor generada en les
cambres de combustió o reactors nuclears de la central, a pressió constant. A la sortida el
vapor sortirà en forma de vapor saturat o sobreescalfat. El flux de calor que rep el fluid
ve donat per la següent equació:

Q̇H = ṁ · (h3 − h2 ) (2.2)

Tot seguit, el fluid entra a la turbina (Estat 3) on s’expandeix i produeix treball al fer-
la girar l’eix connectat a un generador elèctric. La potència mecànica generada per la
turbina és:
ẆT = ṁ · (h4 − h3 ) (2.3)
Finalment, el fluid entra al condensador (Estat 4) on cedeix calor a un medi de refredament
(el riu, el mar, l’atmosfera...) i condensa completament a pressió constant. Aquesta calor
es pot calcular a partir de la següent equació:

Q̇L = ṁ · (h1 − h4 ) (2.4)

En aquest cicle s’han de tenir en compte l’efecte de les irreversibilitats tant de la turbina
com de la bomba, que tenen un rendiment isentròpic determinat. El cabal màssic serà el
mateix per tot el cicle i la potència neta serà la suma de les potències de la turbina i la
bomba.
ẆN = ẆB + ẆT (2.5)
El rendiment tèrmic del cicle serà el quocient entre la potència neta i la calor subministrada
a la caldera en valor absolut.


N
ηt = (2.6)
Q̇H
Capítol 2 21

Aquesta equació es pot reescriure com:



ẆT + ẆB (h4 − h3 ) + (h2 − h1 )

ηt = =
(h3 − h2 ) (2.7)
Q̇H

El diagrama T-s característic d’aquest cicle es pot veure a la figura 2.2:

Figura 2.2: Diagrama T-s d’un cicle bàsic real de Rankine

El balanç d’exergia d’aquest cicle es pot fer calculant l’exergia específica per cadascun
dels estats i segons l’equació:

exi = (hi − h0 ) − T0 · (si − s0 ) (2.8)

On T0 i h0 són les propietats termodinàmiques de l’entorn.

Per altra banda considerant les exergies de cada un dels dispositius del cicle es pot calcular
el treball perdut com: X X
Ẇp = Ėxe − Ėxs (2.9)

D’aquesta manera els treballs perduts per cadascun dels dispositius seran:

ẆpT = Ėx3 − Ėx4 − Ėx (ẆT ) = ṁ · (ex3 − ex4 ) − ẆT (2.10)


ẆpB = Ėx1 − Ėx2 + Ėx (ẆB ) = ṁ · (ex1 − ex2 ) + ẆB (2.11)

 
T0
ẆpH = Ėx2 − Ėx3 + Ėx (Q̇H ) = ṁ · (ex2 − ex3 ) + Q̇H · 1 − (2.12)

TF


ẆpL = Ėx4 − Ėx1 + Ėx (Q̇L ) = ṁ · (ex4 − ex1 ) + 0 (2.13)

On TF és la temperatura de la font de la calor que rep la caldera.


22 Capítol 2

I finalment també es pot calcular el rendiment exergètic de la caldera, la turbina, la bomba


i de la instal·lació segons les següents expressions:

Ėx (ẆT )
ηexT = (2.14)
Ėx3 − Ėx4

Ėx1 − Ėx2
ηexB = (2.15)
Ėx (ẆB )

Ėx (Q̇H )
ηexC = (2.16)
Ėx2 − Ėx3

Ėx (ẆN )
ηex = (2.17)
Ėx (Q̇H )

La majoria d’aquestes equacions s’utilitzaran en el cicle de Rankine amb rescalfament


canviant petites coses.

2.2 Cicle de Rankine amb reescalfament intermedi


El reescalfament és una forma d’augmentar el rendiment del cicle bàsic de Rankine. Per
introduir el reescalfament s’expandeix el vapor en una turbina en dues etapes i entre aques-
tes etapes es reescalfa el vapor a la caldera. D’aquesta manera s’aconsegueix augmentar la
temperatura mitjana a la qual s’absorbeix calor i també s’augmenta el rendiment tèrmic.

Per tant el cicle de Rankine amb rescalfament intermedi no és més que una ampliació del
cicle de Rankine bàsic, per aquest motiu totes les equacions presentades anteriorment són
igualment vàlides en aquest cicle però tenint en compte a les fórmules que la numeració
dels estats ara és un altre. Es pot observar un esquema d’aquest cicle en la Figura 2.3.

En aquesta millora el fluid de treball passa dos cops per la caldera. Després de passar-hi
per primer cop (Estat 3) entra a la turbina a alta pressió i s’expandeix per primer cop.
Tot seguit torna a entrar novament a la caldera (Estat 4) i es reescalfa per entrar a la
turbina de baixa pressió (Estat 5). La turbina de baixa pressió i la d’alta pressió estan
acoblades i produeixen un treball mecànic.

Finalment, el fluid surt de la turbina i entra al condensador per refrigerar-se. En aquest


cicle, en una primera aproximació, es considera que no hi ha pèrdues de pressió ni en la
caldera ni en el condensador.

En aquest cicle, la potència neta produïda serà:

ẆN = ẆB + ẆT1 + ẆT2 = ṁ(h2 − h1 ) + ṁ(h4 − h3 ) + ṁ(h6 − h5 ) (2.18)


Capítol 2 23

Figura 2.3: Cicle de Rankine amb rescalfament intermedi

I el flux de calor que cal cedir a la caldera es pot calcular amb la següent expressió:
Q̇H = Q̇H1 + Q̇H2 = ṁ(h3 − h2 ) + ṁ(h5 − h4 ) (2.19)
I el rendiment tèrmic d’aquest cicle és:

ẆN

ηt = (2.20)
Q̇H
El diagrama T-s característic d’aquest cicle es pot veure a la Figura 2.4:

Figura 2.4: Diagrama T-s d’un cicle ideal de Rankine amb rescalfament intermedi

Respecte a l’anàlisi exergètica és el mateix que en el Rankine bàsic ampliant les equacions
a la resta de dispositius i calculant les exergies específiques per cada estat.
24 Capítol 2

2.3 Cicle de Brayton


L’aplicació també considerà el cicle de Brayton en règim tancat, que és un tipus de cicle de
turbina de gas en règim tancat. El cicle està format per un compressor, dos intercanviadors
de calor i una turbina. Es pot veure un esquema d’aquest cicle a la Figura 2.5.

Figura 2.5: Esquema del cicle bàsic de turbina de gas en règim tancat

En aquest cicle el fluid de treball acostuma a ser aire, i habitualment es considera gas
ideal. Malgrat això, l’aplicació, en treballar amb CoolProp, considerarà l’aire com un fluid
amb unes propietats determinades. Per tant el càlcul que es farà dels treballs i calors serà
a través d’entalpies i no de temperatures. Això que a priori pot semblar un inconvenient,
ja que els resultats numèrics no seran exactament iguals als teòrics, suposa també un
avantatge perquè permet molt fàcilment considerar com a fluid de treball l’heli o el diòxid
de carboni, que són altres gasos que també s’utilitzen en aquests cicles.

En aquest cicle el fluid de treball entra al compressor (Estat 1) al qual se li ha de submi-


nistrar un treball perquè comprimeixi el gas. Tot seguit, el gas s’envia a un intercanviador
de calor (Estat 2) on rep calor a pressió constant i s’escalfa. Després, el gas passa per una
turbina (Estat 3) en la qual s’expandeix, generant una potència mecànica. Finalment, el
gas es refreda al segon intercanviador de calor on cedeix calor cap a l’ambient.

Les equacions que relacionen les entalpies dels estats, el flux màssic i les potències tèr-
miques i mecàniques obtingudes són les de cada un dels dispositius que formen part del
cicle.
Capítol 2 25

La potència neta del cicle és la suma de la potència subministrada al compressor (que


serà positiva) i la potència generada per la turbina (que serà negativa):

ẆN = ẆC + ẆT (2.21)

I per tant, el rendiment tèrmic del cicle de Brayton:



ẆN

h4 − h1
ηt = = 1 − (2.22)
Q̇H h3 − h2

Els diagrames T-s característics d’aquest cicle es pot veure a la figura 2.6:

Figura 2.6: Diagrama T-s del cicle ideal de Brayton en règim tancat

2.4 Cicle de refrigeració per compressió de vapor


El cicle de refrigeració per compressió de vapor és el procés típic que es dóna a neveres,
congeladors i sistemes de condicionament d’aire. Aquest cicle funciona com una màquina
frigorífica: absorbeix calor d’un focus fred i cedeix calor a un focus calent, i per funcionar
necessita rebre un treball. A la Figura 2.7 es pot veure un esquema d’aquest tipus de
cicle de refrigeració, que està format per un condensador, un evaporador, una vàlvula i
un compressor. Tant el condensador com l’evaporador es modelitzen com intercanviadors
de calor. Els fluids de treball que circulen per aquests cicles són refrigerants, com per
l’exemple el R-134a o el R-12.

El refrigerant entra al compressor com a vapor (Estat 1) i es comprimeix rebent una


potència subministrada i augmentant la seva pressió i la seva temperatura. La potència
que cal subministrar, considerant el compressor adiabàtic, és:

ẆC = ṁ · (h2 − h1 ) (2.23)


26 Capítol 2

Figura 2.7: Esquema d’un cicle de refrigeració per compressió de vapor

Tot seguit, el refrigerant entra al condensador (Estat 2), on perd calor i es condensa
completament. La calor que perd és:

Q̇H = ṁ · (h1 − h4 ) (2.24)

A continuació, el refrigerant s’estrangula fins a la pressió de l’evaporador a través d’una


vàlvula d’expansió isentàlpica (Estat 3) on la disminució de la pressió provoca que una
part del líquid s’evapori, de manera que a la sortida es té vapor humit. Finalment, el fluid
passa a través d’un evaporador (Estat 4) on absorbeix calor i s’evapora completament.
La calor que cal subministrar per produir aquesta evaporació és:

Q̇L = ṁ · (h3 − h2 ) (2.25)

Cal considerar que el software té en compte que en els cicles de refrigeració hi pot haver
pèrdues de pressió en el condensador i l’evaporador. Per aquest motiu cal introduir totes
les pressions dels estats termodinàmics, encara que, per exemple, la pressió de l’estat 2
sigui la mateixa que la del estat 3.

Noti’s també que en tots els processos de refrigeració el sentit del cicle és antihorari. Això
a nivell pràctic no té cap implicació, però és una diferència respecte als cicles de potència.

Considerant que el cabal màssic és el mateix per tot el circuit, aleshores el coeficient de
funcionament (COP) del cicle de refrigeració respon a l’equació:

Q̇L h1 − h4
COPM F = = (2.26)
ẆC h2 − h1
Capítol 2 27

Finalment, es pot veure a la Figura 2.8 els diagrames T-s i P-h d’aquests cicles de refri-
geració a escala qualitativa. En els cicles de potència, no s’ha mostrat el diagrama P-h
perquè no és habitual treballar amb ell, malgrat que l’aplicació permet calcular-lo. En
canvi en aquest cicle sí que és més habitual.

Figura 2.8: Diagrames T-s i P-h d’un cicle real de refrigeració per compressió de vapor

2.5 Cicle de refrigeració per gas


El cicle de refrigeració per gas no és res més que el cicle invers de Brayton. A la Figura
2.9 es pot veure un esquema d’aquest cicle.

Figura 2.9: Esquema d’un cicle de refrigeració per gas


28 Capítol 2

El fluid de treball és un gas que en primer lloc es comprimeix a l’entrar al compressor


(Estat 1), a continuació el gas a pressió i a una temperatura més elevada passa per un
intercanviador de calor (Estat 2) on es refreda a pressió constant. Tot seguit s’expandeix
en una turbina (Estat 3) on la temperatura del gas disminueix i finalment passa per un
altre intercanviador (Estat 4) on absorbeix la calor del cos que s’intenta refredar.

Cal dir que de la mateixa manera que el cicle anterior, en aquest cicle també s’ha considerat
que hi pot haver pèrdues en els dos intercanviadors. Per aquest motiu cal introduir totes
les pressions.

També s’ha de dir que de la mateixa manera que en el cicle de Brayton, en aquest cicle
no es farà la hipòtesi de treballar amb gas ideal, sinó que es calcularan totes les seves
propietats termodinàmiques mitjançant el CoolProp.

Les equacions de cada un dels processos són les característiques dels dispositius. El
coeficient de funcionament del procés és:

Q̇L h6 − h5
COPM F = = (2.27)
ẆN (h2 − h1 ) − (h − 5 − h4 )

A la Figura 2.10 es té un exemple del diagrama T-s d’un cicle ideal de refrigeració per
gas.

Figura 2.10: Diagrama T-s d’un cicle ideal de refrigeració per gas

S’ha consultat [1] i [2] com a fonts bibliogràfiques per la redacció d’aquest capítol.
Totes les figures i equacions utilitzades d’aquest capítol han sigut extretes de [2].
Capítol 3 29

Capítol 3

Modelització mitjançant CoolProp

El CoolProp és l’eina que hi ha darrere del càlcul de totes les propietats termodinàmiques
que realitza l’aplicació i de la representació de tots els diagrames termodinàmics. A
la Figura 3.1 es pot veure el logotip de la llibreria. Exactament, el CoolProp és una
llibreria escrita en C++ que conté les equacions d’estat de més de 100 substàncies pures o
pseudopures, d’aquesta manera es poden modelar molts fluids com l’aire, l’aigua o diversos
refrigerants. Aquesta versatilitat i el fet que malgrat ser una llibreria de C++ permetés
treballar amb Python de manera molt còmode van ser els factors que van determinar que
era l’eina adequada per l’aplicació que s’ha desenvolupat. Altres alternatives que s’havien
considerat van ser: l’XSteam, escrit a codi MATLAB, però es va descartar perquè només
servia per a l’aigua i el REFPROP, que és un software molt conegut, però és de pagament,
mentre que el CoolProp és una llicència oberta que es pot fer servir tant per projectes
acadèmics com comercials.

Figura 3.1: Logotip de la llibreria CoolProp

D’aquesta manera es podria dir que el CoolProp és l’equivalent a les taules termodinàmi-
ques que s’utilitzen per calcular les propietats d’un estat. Això és així perquè la llibreria
també incorpora un interpolador que agilitza els càlculs.

Per altra banda, el CoolProp també permet la construcció de diagrames termodinàmics,


ajudant-se de la llibreria Matplotlib de Python. Aquesta funcionalitat és molt menys
coneguda i requereix un coneixement més en profunditat de les classes i funcions que
ofereix la llibreria i que s’explicaran en els següents apartats.
30 Capítol 3

A mode resum, es podria dir que s’utilitzaran les següents funcions i/o classes de la
llibreria CoolProp:

• Funció PropsSI : Permet el càlcul de les propietats termodinàmiques d’un fluid.

• Classe PropertyPlot: Permet l’elaboració inicial del diagrama sense el cicle, és a


dir, la representació de les isotermes, isòbares i de què el diagrama sigui exacte i no
només qualitatiu.

• Classe StateContainer : Tal com el seu nom indica, és un contenidor dels estats
termodinàmics. Emmagatzema cadascun dels estats amb les seves corresponents
propietats.

• Funció draw_process: És la funció que s’encarrega de representar el cicle termo-


dinàmic sobre el diagrama T-s o P-h.

A continuació, es descriurà el funcionament de cadascuna d’elles.

3.1 Funció PropsSI


La funció PropsSI és pràcticament la part més utilitzada de la biblioteca CoolProp. Amb
ella es poden obtenir les propietats termodinàmiques de les substàncies pures amb les
quals treballarà l’aplicació que s’ha dissenyat. A la Figura 3.2 es pot veure una mostra
de codi Python on s’exemplifica el funcionament de la funció PropsSI.

Figura 3.2: Exemple de Python d’utilització de la funció PropsSI

A la funció doncs, cal introduir-li els següents paràmetres en el mateix ordre:

• 1) name_output: és la propietat termodinàmica de la qual se’n vol conèixer el


valor, que serà el retornar per la funció. S’introdueix a la funció com un string
representatiu de la propietat.

• 2) name_1 : és una de les propietats termodinàmiques que es dóna com a dada


per conèixer el valor de la propietat desconeguda. També s’introdueix a la funció
com un string representatiu de la propietat.
Capítol 3 31

• 3) prop_1 : és el valor numèric de la propietat termodinàmica anterior. S’introdu-


eix a la funció com un float, és a dir, com un nombre real.

• 4) name_2 : és l’altra propietat termodinàmica que cal introduir a la funció per


conèixer el valor de la propietat desconeguda. S’introdueix a la funció com un string
representatiu de la propietat.

• 5) prop_2 : és el valor numèric de la propietat termodinàmica anterior. S’introdu-


eix a la funció com un float, és a dir, com un nombre real.

• 6) name_fluid : és el nom del fluid de treball, pot ser: aigua, aire, diòxid de
carboni, butà, ... S’introdueix a la funció com un string corresponent al nom del
fluid.

Cal dir que la funció PropsSI reconeix com a propietat termodinàmica la qualitat del
vapor, que és el títol, sempre que aquest valor òbviament estigui comprès entre el 0 i
l’1. Si es volgués calcular el títol per un líquid subrefredat o d’un vapor sobreescalfat, la
funció retornaria un -1 ja que no té sentit físic.

Figura 3.3: Taula de string inputs de la funció PropsSI que utilitzarà l’aplicació

Per altra banda, la funció pot calcular altres coses més enllà de les propietats termodinà-
miques classes, per exemple els coeficients del virial, la conductivitat tèrmica, el nombre
de Prandtl, però en l’aplicació que s’està dissenyant es limita exclusivament a les següents
propietats: pressió, temperatura, entalpia específica, entropia específica i el títol. A la
Figura 3.3 es poden veure aquests inputs, el seu string associat i les unitats amb les quals
treballa la funció PropsSI.

Finalment, respecte als fluids de treball, la llista és molt llarga. Per simplificar-ho, s’ha
decidit incloure a l’aplicació només aquells fluids que es trobin a les taules termodinà-
miques utilitzades a l’assignatura de Termodinàmica. Tanmateix, resulta molt simple
ampliar aquesta llista i incloure més fluids. A la Figura 3.4 es pot veure una llista dels
fluids disponibles al CoolProp inclosos a l’aplicació. El nom del fluid en aquesta llista
correspon al string que cal introduir a la funció.
32 Capítol 3

Figura 3.4: Taula de fluids de treball de la llibreria CoolProp que utilitzarà l’aplicació

3.2 Classe PropertyPlot


La classe PropertyPlot permet la construcció del diagrama T-s o P-h i la representació
de les línies isòbares, isotermes i de títol constant utilitzant el mètode calc_isolines. A la
Figura 3.5 es veu un exemple de la utilització d’aquesta classe i a la Figura 3.6 el diagrama
que elabora.

Figura 3.5: Exemple de Python d’utilització de la classe PropertyPlot

En aquesta classe cal introduir-li tres paràmetres en el següent ordre:

• 1) fluid_name: Ha de ser un string corresponent a algun dels fluids inclosos al


CoolProp, com s’ha dit, en aquesta aplicació s’utilitzaran els vistos a la figura 3.4.

• 2) graph_type: Ha de ser un string que indiqui quin tipus de diagrama volem


representar. Si un diagrama T-s (aleshores caldrà introduir ‘TS’) o un diagrama
P-h (aleshores caldrà introduir ‘PH’).

• 3) unit_system: Cal introduir-li un string que indiqui el sistema d’unitats del


gràfic. Hi ha disponibles diferents sistemes. Per l’aplicació s’ha escollit el sistema
europeu (‘EUR’) que són bar, kJ i C.
Capítol 3 33

Figura 3.6: Diagrama P-h del R-134a obtingut utilitzant la classe Property Plot

3.3 Classe StateContainer


La classe StateContainer és una classe que permet emmagatzemar els estats termodinà-
mics del cicle i el valor de les propietats termodinàmiques associades a cada un d’aquests.
D’aquesta manera la classe conté tota la informació essencial per posteriorment poder
representar el cicle sobre el diagrama T-s o P-h. Es pot veure un exemple de la utilització
d’aquesta classe a la Figura 3.7.

Figura 3.7: Exemple de Python d’utilització de la classe PropertyPlot


34 Capítol 3

El funcionament d’aquesta classe és semblant a la d’un diccionari. De manera que cal


introduir-li tres paràmetres. Dos dintre dels claudàtors, separats per una coma, que són:

• 1) state_number : Un integer, és a dir, un nombre enter, que representi l’estat


dins del cicle.

• 2) name_prop: Un string associat al nom de la propietat termodinàmica que es


vol introduir al contenidor. Aquest string ha de ser algun dels que es troben a la
taula de la Figura 3.4.

I un tercer que s’ha d’introduir igualant la classe amb el paràmetre (vegis l’exemple de la
Figura 3.7), que és:

• 3) prop: Un float, que representi el valor de la propietat termodinàmica name_prop


de l’estat state_number que es vol introduir al contenidor.

En conclusió, es podria dir que aquesta classe actua com a pont entre l’eina de càlcul
de l’aplicació (que inclou la funció PropsSI ) i l’eina de representació de diagrames de
l’aplicació (que es basa en la classe PropertyPlot).

3.4 Funció draw_process


La funció draw_process és un mètode de la classe PropertyPlot que s’encarrega de repre-
sentar el cicle termodinàmic a partir d’un objecte de la classe StateContainer. D’aquesta
manera el que fa aquesta funció és dibuixar sobre el diagrama P-h o T-s les línies que
representen l’evolució dels estats termodinàmics del cicle.

Aquesta funció té dos paràmetres:

• 1) property_plot:: És el diagrama P-h o T-s sobre el qual s’ha de representar el


cicle. Per tant és un objecte de la classe PropertyPlot.

• 2) cicle: És un objecte de la classe BaseCycle o qualsevol classe hereva, com pot


ser la classe SimpleCompressionCycle. No s’ha explicat aquesta classe amb detall
perquè només és un pas intermedi. S’aboca el contenidor de la classe StateContainer
amb tota la informació dels estats del cicle sobre aquesta classe. Posteriorment es
poden realitzar canvis estètics. Malgrat que el nom de la classe és SimpleCompres-
sionCycle, no té efectes pràctics en la representació perquè se l’hi introdueixen totes
les dades dels estats. Per tant amb aquesta classe es poden representar els tres tipus
de cicles amb els quals treballa l’aplicació: Rankine, Brayton i refrigeració.

Al codi següent es pot veure un exemple que engloba totes les funcions i classes descrites
per representar un cicle de Rankine amb reescalfament intermedi.
1 import CoolProp as cp
2 from CoolProp.Plots import PropertyPlot
3 from CoolProp.Plots import SimpleCompressionCycle
4 from CoolProp.Plots.SimpleCycles import BaseCycle
Capítol 3 35

5 from CoolProp.Plots.SimpleCycles import StateContainer


6 import matplotlib.pyplot as plt
7 """
8 1) Creacio del diagrama T-s amb la classe PropertyPlot
9 """
10 pp = PropertyPlot(’Water’, ’TS’, unit_system=’EUR’, tp_limits=’ORC’)
11 pp.calc_isolines(cp.iP)
12 pp.calc_isolines(cp.iQ)
13 """
14 2) Introduccio de les dades del cicle termodinamic, obtingudes amb la
funcio PropsSI a la classe StateContainer
15 """
16 sc = StateContainer()
17 NomsPropietats = [0, ’P’, ’T’, ’H’, ’S’, ’Q’]
18 d1={’P’: 10000.0, ’Q’: 0.0, ’T’: 318.9563289239378, ’H’:
191805.94455942558, ’S’: 649.1956051958255}
19 d2={’P’: 15000000.0, ’H’: 206902.15713774692, ’T’: 319.45450957451936, ’S’:
649.1956051958833, ’Q’: -1.0}
20 d3={’T’: 873.15, ’P’: 15000000.0, ’H’: 3583131.7614938673, ’S’:
6679.589593773721, ’Q’: -1.0}
21 d4={’P’: 4000000.0, ’H’: 3153874.542974065, ’T’: 647.8069933521077, ’S’:
6679.589593773719, ’Q’: -1.0}
22 d5={’T’: 873.15, ’P’: 4000000.0, ’H’: 3674867.5226219436, ’S’:
7370.528366729162, ’Q’: -1.0}
23 d6={’P’: 10000.0, ’Q’: 0.896, ’T’: 318.9563289239378, ’H’:
2335085.1881594392, ’S’: 7368.859236987472}
24 dic = [{}, d1, d2, d3, d4, d5, d6]
25 for estat in range(7):
26 if estat != 0:
27 for prop in NomsPropietats[1:5]:
28 sc[estat-1,prop]=dic[estat][prop]
29 """
30 3) S’aboca la classe StateContainer a la classe SimpleCompressionCycle per
poder fer la representacio
31 """
32 rankine = SimpleCompressionCycle(’Water’, ’TS’, unit_system=’EUR’,
tp_limits=’ORC’)
33 rankine._cycle_states=sc
34 rankine.STATECOUNT=6
35 rankine.STATECHANGE = [
36 lambda inp: BaseCycle.state_change(inp, ’S’, ’P’, 0, ty1=’log’, ty2=’
log’),
37 lambda inp: BaseCycle.state_change(inp, ’H’, ’P’, 1, ty1=’log’, ty2=’
log’),
38 lambda inp: BaseCycle.state_change(inp, ’S’, ’P’, 2, ty1=’log’, ty2=’
log’),
39 lambda inp: BaseCycle.state_change(inp, ’H’, ’P’, 3, ty1=’log’, ty2=’
log’),
40 lambda inp: BaseCycle.state_change(inp, ’S’, ’P’, 4, ty1=’log’, ty2=’
log’),
41 lambda inp: BaseCycle.state_change(inp, ’H’, ’P’, 5, ty1=’log’, ty2=’
log’)
42 ]
43
44 rankine.steps = 100
36 Capítol 3

45 cicle = rankine.get_state_changes()
46 """
47 4) Representem el cicle amb la funcio draw_process
48 """
49 plt.close(rankine.figure)
50 pp.draw_process(cicle)
51 pp.title(’Diagrama T-s’)
52 pp.show()
Llistat 3.1: Codi Python necessari per representar un diagrama T-s

I el resultat obtingut és el següent diagrama T-s:

Figura 3.8: Diagrama T-s d’un cicle de Rankine amb reescalfament intermedi

S’ha consultat [3] com a font bibliogràfica per la redacció d’aquest capítol. To-
tes les figures i equacions utilitzades en aquest capítol són d’elaboració pròpia,
excepte la 3.1 que s’ha extret de [3].
Capítol 4 37

Capítol 4

Implementació de l’aplicació amb


Python

L’aplicació s’ha dissenyat a partir del paquet Tkinter de Python. Tkinter concretament
és una adaptació del llenguatge Tk que permet dissenyar interfícies d’usuari gràfiques
(GUI) per Python. A l’hora d’escollir entre aquest paquet davant d’altres, com podrien
ser PyQt o PySide, es va tenir en compte que es volia dissenyar una aplicació que tingués
una interfície senzilla i molt funcional, sense grans virgueries. Va ser determinant que el
paquet Tkinter en general és simple d’entendre, no hi ha molta documentació, però la
que hi ha és bastant clara, això va que permetre familiaritzar-se amb el paquet amb poc
temps.

Entrant en detalls, el disseny de l’aplicació s’ha organitzat de manera que s’ha programat
cada cicle termodinàmic per separat, a partir d’un primer cicle, que va ser el cicle bàsic
de Rankine, que va servir com a plantilla. A partir d’aquest cicle fent modificacions es
van obtenir la resta de cicles. En alguns casos van caldre més canvis i en alguns altres
menys però l’esquema és el mateix. Això s’ha fet d’aquesta manera perquè, per una
banda és molt més ràpid i per altra banda perquè seguint sempre la mateixa plantilla es
minimitzen els errors de programació. Per aquest motiu, tots els extractes de codi Python
que s’aniran mostrant en aquest capítol seran de la plantilla que es va seguir, és a dir, del
cicle bàsic de Rankine. Malgrat això, és cert que entre els cicles hi ha diferències clares,
per exemple: els dos cicles de Rankine incorporen l’opció de fer una anàlisi exergètica, que
no està disponible en la resta; al cicle de Brayton no apareix el títol del sistema perquè
treballa amb gas. Finalment, un cop dissenyats tots els cicles, es va dissenyar un índex
que servis com a primera pàgina de l’aplicació i que donés la opció d’accedir al cicle amb
el qual es vol treballar.

Més en profunditat, el disseny de la plantilla s’ha dividit en tres etapes de disseny que es
podria dir que són independents però que estan interconnectades i són seqüencials. Totes
tres conviuen dins el mateix arxiu de codi Python, a diferència dels cicles, que cadascun
és un arxiu diferent. Aquestes etapes, per ordre, són:
38 Capítol 4

• Disseny general de l’aplicació utilitzant el Tkinter. Es va començar disse-


nyant com havia de ser la interfície gràfica amb la qual l’usuari havia de treballar,
en aquesta etapa es programa com ha de ser la introducció i selecció de dades, la
seva visualització, es programen tots els botons que ha de presentar l’aplicació, totes
les finestres de l’aplicació i com s’interrelacionen. Es podria dir que és la carcassa
de l’aplicació, és a dir, si ens quedéssim només amb aquesta etapa, el resultat seria
la mateixa aplicació però no seria funcional, es podrien introduir les dades però no
s’obtindria cap resultat.

• Disseny específic del solucionador. Perquè l’aplicació sigui funcional cal que
totes les dades introduïdes s’operin i es converteixin en resultats i aquests resultats
s’han de poder visualitzar en el disseny general. Per complir aquesta missió, s’ha
dissenyat una única funció, estructurada en diferents etapes, que és el solucionador.

• Disseny específic dels diagrames termodinàmics. Com s’ha vist en el capítol


anterior, a partir de la llibreria CoolProp es poden obtenir diagrames termodinà-
mics. En aquesta etapa final es recullen els resultats obtinguts pel solucionador i es
representen en els digrames P-h o T-s, segons indiqui l’usuari.

A continuació s’explicarà cadascuna d’aquestes etapes per separat, amb més detalls i
mostrant extractes del codi Python que s’ha utilitzat.

4.1 Disseny general de l’aplicació amb Tkinter


La idea fonamental que s’ha seguit per programar el software és que cada finestra de
l’aplicació és una classe de Python. D’aquesta manera el primer que es va començar a
programar és una primera finestra inicial a partir de la qual es pogués accedir a tota la
resta de finestres. A la Figura 4.1 es pot veure una captura d’aquesta finestra corresponent
al cicle bàsic de Rankine, que com s’ha dit, és el que s’ha utilitzat com a plantilla.

En aquesta classe, en primer lloc es van definir totes les variables que havia d’utilitzar
l’aplicació per treballar, aquestes variables no poden ser variables normals sinó que són
característiques del paquet Tkinter, ja que permeten que l’usuari els doni un valor a través
de la interfície gràfica.

S’ha treballat amb dues classes de variables:

• Variables d’entrada: Que poden ser del tipus DoubleVar(), quan emmagatzemen
un valor numèric real (és a dir, un float), aleshores serviran per emmagatzemar els
valors de totes les propietats termodinàmiques, del flux màssic, etc. O poden ser del
tipus StringVar(), quan emmagatzemen cadenes de caràcters (és a dir, un string).
Aquest darrer tipus de variable només s’ha utilitzat per escollir el valor del fluid de
treball, que és el seu nom.

• Variable de selecció: Que són del tipus IntVar(). Aquestes variables emmagat-
zemen un valor numèric enter (és a dir, un integer). En l’aplicació en particular
Capítol 4 39

aquest valor enter serà binari (0 o 1). Cada una d’aquestes variables de selecció està
lligada a una variable d’entrada i serveixen per poder-li indicar al programa quines
són les dades que se li estan introduint, és a dir, amb quines variables ha d’operar
inicialment. D’aquesta manera, si pren valor 0 significa que no està seleccionada i
si pren valor 1 significa que està seleccionada.

Treballar amb variables de selecció implica pràcticament duplicar el nombre de variables


del programa però alhora és un gran avantatge perquè permet que una vegada fet el càlcul
d’un cicle termodinàmic es pugui veure com canvis en altres variables afecten el cicle.

A la interfície gràfica, es podria dir que, totes les variables d’entrada es representen amb
caixes on es pot introduir el valor numèric de la variable i totes les variables de selecció
es representen amb quadrats que es poden seleccionar o desseleccionar. Se’n pot veure un
exemple a la Figura 4.1.

Totes les variables de treball és declaren com atributs d’aquesta classe inicial que és
CicleRankine(). Per operar millor amb elles s’han organitzat amb llistes, i amb llistes de
llistes per poder fer les iteracions necessàries. Es pot veure un retall del codi Python amb
el que s’ha programat això al Llistat 4.1.

1 class CicleRankine():
2

3 def __init__(self, master):


4
5 self.master=master
6
7 #Declararem totes les variables a la classe principal, al cicle, i
despres les enviarem a les subclasses (subfinestres) a les que pertanyin
.
8
9 """
10 VARIABLES DEL SISTEMA
11 """
12

13 #Variables d’entrada
14 self.Qcald = tk.DoubleVar()
15 self.Qcond = tk.DoubleVar()
16 self.Wturb = tk.DoubleVar()
17 self.Wbomb = tk.DoubleVar()
18 self.eta_turb = tk.DoubleVar()
19 self.eta_bomb = tk.DoubleVar()
20
21 self.eta_termic = tk.DoubleVar()
22
23 self.lVars = [self.Qcald, self.Qcond, self.Wturb, self.Wbomb, self.
eta_turb, self.eta_bomb]
24 self.lVars_noms = ["Qcald", "Qcond", "Wturb", "Wbomb", "eta_turb",
"eta_bomb"]
25
26 self.flux = tk.DoubleVar()
27 self.Wneta = tk.DoubleVar()
28 self.fluid = tk.StringVar()
40 Capítol 4

29
30 self.Tentorn = tk.DoubleVar()
31 self.Treactor = tk.DoubleVar()
32 self.Pentorn = tk.DoubleVar()
33

34
35 #Seleccio de variables d’entrada
36 self.Qcald_chk = tk.IntVar()
37 self.Qcond_chk = tk.IntVar()
38 self.Wturb_chk = tk.IntVar()
39 self.Wbomb_chk = tk.IntVar()
40 self.eta_turb_chk = tk.IntVar()
41 self.eta_bomb_chk = tk.IntVar()
42
43 self.lVars_chk = [self.Qcald_chk, self.Qcond_chk, self.Wturb_chk,
self.Wbomb_chk, self.eta_turb_chk, self.eta_bomb_chk]
44

45 self.flux_chk = tk.IntVar()
46 self.Wneta_chk = tk.IntVar()
47
48
49 #Propietats termodinamiques
50 self.P_1, self.P_2, self.P_3, self.P_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
51 self.T_1, self.T_2, self.T_3, self.T_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
52 self.h_1, self.h_2, self.h_3, self.h_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
53 self.s_1, self.s_2, self.s_3, self.s_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
54 self.x_1, self.x_2, self.x_3, self.x_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
55
56 self.lProps = [[],
57 [0,self.P_1, self.T_1, self.h_1, self.s_1, self.x_1],
58 [0,self.P_2, self.T_2, self.h_2, self.s_2, self.x_2],
59 [0,self.P_3, self.T_3, self.h_3, self.s_3, self.x_3],
60 [0,self.P_4, self.T_4, self.h_4, self.s_4, self.x_4]]
61
62 self.lProps_noms = [[],
63 [0,"P_1", "T_1", "H_1", "S_1", "Q_1"],
64 [0,"P_2", "T_2", "H_2", "S_2", "Q_2"],
65 [0,"P_3", "T_3", "H_3", "S_3", "Q_3"],
66 [0,"P_4", "T_4", "H_4", "S_4", "Q_4"]]
67
68 #Seleccio de propietats termodinamiques
69 self.P_1_chk, self.P_2_chk, self.P_3_chk, self.P_4_chk = tk.IntVar
(), tk.IntVar(), tk.IntVar(), tk.IntVar()
70 self.T_1_chk, self.T_2_chk, self.T_3_chk, self.T_4_chk = tk.IntVar
(), tk.IntVar(), tk.IntVar(), tk.IntVar()
71 self.h_1_chk, self.h_2_chk, self.h_3_chk, self.h_4_chk = tk.IntVar
(), tk.IntVar(), tk.IntVar(), tk.IntVar()
72 self.s_1_chk, self.s_2_chk, self.s_3_chk, self.s_4_chk = tk.IntVar
(), tk.IntVar(), tk.IntVar(), tk.IntVar()
73 self.x_1_chk, self.x_2_chk, self.x_3_chk, self.x_4_chk = tk.IntVar
Capítol 4 41

(), tk.IntVar(), tk.IntVar(), tk.IntVar()


74
75 self.lProps_chk = [[],
76 [0,self.P_1_chk, self.T_1_chk, self.h_1_chk, self.s_1_chk
, self.x_1_chk],
77 [0,self.P_2_chk, self.T_2_chk, self.h_2_chk, self.s_2_chk
, self.x_2_chk],
78 [0,self.P_3_chk, self.T_3_chk, self.h_3_chk, self.s_3_chk
, self.x_3_chk],
79 [0,self.P_4_chk, self.T_4_chk, self.h_4_chk, self.s_4_chk
, self.x_4_chk]]
80
81
82 #Variables pel diagrama
83 self.sc = StateContainer()
84
85

86 self.master.title(’Cicle basic de Rankine’)


87 self.master.geometry(’750x850’)
88 self.master.minsize(750, 850)
89 self.master.maxsize(750, 850)
90
91 self.Esquema(master)
92 self.Menu(master)
93 self.Visualitzador(master)
Llistat 4.1: Inicialització de totes les variables de l’aplicació amb Python.

Aquesta mateixa classe la completen tots els mètodes que s’han implementat i que a
continuació s’explicaran. Els mètodes són les funcions de les variables que anteriorment
hem definit, i es defineixen dins la classe com una funció de Python típica.

En l’aplicació, podríem separar els mètodes en dos grups:

• Mètodes associats a la representació gràfica. Són funcions que s’activen di-


rectament en iniciar l’aplicació i que s’han utilitzat per dissenyar el menú, l’esquema
gràfic del cicle termodinàmic i el visualitzador de resultats, totes tres es poden veure
inicialitzats al Llistat 4.1 i representats a la Figura 4.1. S’ha decidit fer d’aquesta
manera perquè s’ha considerat que estructurant-ho per separat quedava més ordenat
i permetia localitzar els errors de programació de manera més fàcil.

• Mètodes associats a un botó. Són aquelles funcions que s’activen quan l’usuari
prem el botó de la interfície gràfica. Dintre d’aquests mètodes trobem un mètode
que és l’eina de càlcul, que s’explicarà amb detall a l’apartat següent, i que s’activa
quan l’usuari clica el botó Calcular. També hi ha els mètodes associats a les repre-
sentacions dels diagrames termodinàmics. Però, a banda d’aquests, dintre d’aquest
tipus hi ha els mètodes que serveixen de pont entre la classe principal, CicleRanki-
ne, i les subclasses associades, que no són res més que altres finestres, que s’obriran
quan es cliqui el botó que tenen associat.

A continuació, s’explicarà el funcionament dels mètodes de cadascun un d’aquests grups.


42 Capítol 4

Figura 4.1: Pantalla principal del cicle bàsic de Rankine

En primer lloc es té el disseny gràfic de l’esquema del cicle termodinàmic. Per la realització
de les línies, els cercles, els rectangles, etc. s’ha utilitzat l’eina Canvas que és específic pel
disseny de diagrames, dibuixos, gràfics ...

En el Llistat 4.2 es pot veure el codi Python d’aquest gràfic. No té molta complexitat,
Capítol 4 43

bàsicament s’han dibuixat tots els elements, s’ha ficat el títol, etc. Però cal destacar
que cada dispositiu que apareix al cicle termodinàmic (caldera, bomba, condensador o
turbina) són també botons, amb el seu corresponent mètode associat.

1 def Esquema(self, master):


2 """
3 Esquema grafic del cicle basic de Rankine
4 """
5 def circle(canvas,x,y,r,color, click):
6 return canvas.create_oval(x-r,y-r,x+r,y+r, fill=color, tags=
click)
7
8 self.canvas1 = tk.Canvas(self.master, width=750, height=850)
9 self.canvas1.configure(background="white")
10 self.canvas1.place(x=0, y=0)
11
12 Titol = tk.Label(self.master, text="Cicle basic de Rankine", font="
Helvetica 26 bold", bg="white", fg="red").place(relx=0.5, rely=0.02,
anchor=’n’)
13

14 self.canvas1.create_rectangle(25, 75, 725, 425)


15 a, b = -20, 50
16
17 #Caldera
18 playbuttonCald = self.canvas1.create_rectangle(300+a,50+b,450+a
,130+b, fill="light grey", tags="playbuttonCald")
19 playtextCald = self.canvas1.create_text((375+a, 90+b), text="
Caldera", font="Helvetica 11 bold", tags="playbuttonCald")
20 self.canvas1.tag_bind("playbuttonCald", "<Button-1>", self.
clickedCald)
21
22 #Condensador
23 playbuttonCond = self.canvas1.create_rectangle(300+a,250+b,450+a
,330+b, fill="light grey", tags="playbuttonCond")
24 playtextCond = self.canvas1.create_text((375+a, 290+b), text="
Condensador", font="Helvetica 11 bold", tags="playbuttonCond")
25 self.canvas1.tag_bind("playbuttonCond", "<Button-1>", self.
clickedCond)
26
27 #Bomba
28 playbuttonBomb = self.canvas1.create_rectangle(190+a, 150+b, 210+a,
200+b, fill="light grey", tags="playbuttonBomb")
29 playbuttonBomb2 = circle(self.canvas1, 180+a, 200+b, 35, "light
grey", "playbuttonBomb")
30 playtextBomb = self.canvas1.create_text((180+a, 200+b), text="Bomba
", font="Helvetica 11 bold", tags="playbuttonBomb")
31 self.canvas1.tag_bind("playbuttonBomb", "<Button-1>", self.
clickedBomb)
32
33 #Turbina
34 points = [510+a, 150+b, 650+a, 120+b, 650+a, 240+b, 510+a, 210+b]
35 playbuttonTurb = self.canvas1.create_polygon(points, fill="light
grey", outline="black", tags="playbuttonTurb")
36 playtextTurb = self.canvas1.create_text((580+a, 180+b), text="
44 Capítol 4

Turbina", font="Helvetica 11 bold", tags="playbuttonTurb")


37 self.canvas1.tag_bind("playbuttonTurb", "<Button-1>", self.
clickedTurb)
38
39 #Condensador->Bomba
40 self.canvas1.create_line(300+a, 290+b, 180+a, 290+b, fill="blue",
arrow="none", width=5)
41 self.canvas1.create_line(180+a, 293+b, 180+a, 250+b, fill="blue",
arrow="last", width=5)
42 self.canvas1.create_line(180+a, 260+b, 180+a, 235+b, fill="blue",
arrow="none", width=5)
43 #Bomba->Caldera
44 self.canvas1.create_line(200+a, 150+b, 200+a, 90+b, fill="blue",
arrow="none", width=5)
45 self.canvas1.create_line(198+a, 90+b, 240+a, 90+b, fill="blue",
arrow="last", width=5)
46 self.canvas1.create_line(235+a, 90+b, 300+a, 90+b, fill="blue",
arrow="none", width=5)
47 #Caldera->Turbina
48 self.canvas1.create_line(450+a, 90+b, 540+a, 90+b, fill="blue",
arrow="none", width=5)
49 self.canvas1.create_line(540+a, 88+b, 540+a, 120+b, fill="blue",
arrow="last", width=5)
50 self.canvas1.create_line(540+a, 115+b, 540+a, 145+b, fill="blue",
arrow="none", width=5)
51 #Turbina->Condensador
52 self.canvas1.create_line(615+a, 232+b, 615+a, 290+b, fill="blue",
arrow="none", width=5)
53 self.canvas1.create_line(618+a, 290+b, 500+a, 290+b, fill="blue",
arrow="last", width=5)
54 self.canvas1.create_line(520+a, 290+b, 450+a, 290+b, fill="blue",
arrow="none", width=5)
55
56 #Ennumeracio dels estats
57 Estat1 = self.canvas1.create_text((160+a, 260+b), text="1", font="
Helvetica 14 bold")
58 Estat2 = self.canvas1.create_text((230+a, 70+b), text="2", font="
Helvetica 14 bold")
59 Estat3 = self.canvas1.create_text((560+a, 110+b), text="3", font="
Helvetica 14 bold")
60 Estat4 = self.canvas1.create_text((510+a, 310+b), text="4", font="
Helvetica 14 bold")
61
62
63 self.canvas1.pack()
Llistat 4.2: Programació de l’esquema gràfic d’un cicle termodinàmic.
Capítol 4 45

Per tant al clicar sobre cada representació gràfica d’un dispositiu s’obrirà una finestra on
es podran introduir les dades relatives al dispositiu escollit. Això significa que a l’aplicació,
cada element de flux estacionari té una classe Python pròpia, i s’hi pot accedir clicant
sobre el seu dibuix (que és un botó) i aleshores el mètode associat al botó ens conduirà
a la nova finestra. Al Llistat 4.3 es pot veure el codi Python dels mètodes associats a
aquests botons.

1 def clickedCald(self, *args):


2 self.submasterCald = tk.Toplevel(self.master)
3 self.FinestraCald=Caldera(self.submasterCald, self.Qcald, self.
Qcald_chk, self.dVisualVars) #Enviem les variables que utilitzara la
caldera.
4
5 #Implementem un codi per tal que no s’obrin mes finestres Caldera
un cop obrim la finestra Caldera.
6 self.submasterCald.transient(self.master)
7 self.submasterCald.grab_set()
8 self.master.wait_window(self.submasterCald)
9
10
11 def clickedCond(self, *args):
12 self.submasterCond= tk.Toplevel(self.master)
13 self.FinestraCond=Condensador(self.submasterCond, self.Qcond, self.
Qcond_chk, self.dVisualVars)
14
15 self.submasterCond.transient(self.master)
16 self.submasterCond.grab_set()
17 self.master.wait_window(self.submasterCond)
18
19
20 def clickedBomb(self, *args):
21 self.submasterBomb = tk.Toplevel(self.master)
22 self.FinestraBomb=Bomba(self.submasterBomb, self.Wbomb, self.
Wbomb_chk, self.eta_bomb, self.eta_bomb_chk, self.dVisualVars)
23
24 self.submasterBomb.transient(self.master)
25 self.submasterBomb.grab_set()
26 self.master.wait_window(self.submasterBomb)
27
28 def clickedTurb(self, *args):
29 self.submasterTurb = tk.Toplevel(self.master)
30 self.FinestraTurb=Turbina(self.submasterTurb, self.Wturb, self.
Wturb_chk, self.eta_turb, self.eta_turb_chk, self.dVisualVars)
31

32 self.submasterTurb.transient(self.master)
33 self.submasterTurb.grab_set()
34 self.master.wait_window(self.submasterTurb)
Llistat 4.3: Mètodes associats als botons de l’esquema gràfic del cicle termodinàmic

Sobre els mètodes del Llistat 4.3 noti’s que en obrir la nova finestra, també hi envien les
variables inicialitzades a la classe principal (classe CicleRankine) perquè es converteixin
en variables de la nova classe. Per exemple, a la turbina se l’hi enviaran les variables
46 Capítol 4

corresponents al treball que produeix i al seu rendiment, i també les variables de selecció
associades a les anteriors. D’aquesta manera si s’introdueix un valor a aquestes variables
dins la classe Turbina, també s’estarà introduint a la classe CicleRankine i posteriorment
serà molt còmode fer tots els càlculs des d’aquesta classe principal amb totes les dades
introduïdes.

Per altra banda, en obrir una nova finestra sempre es congelarà la finestra principal, per
evitar col·lapsar l’aplicació. Caldrà tancar la finestra oberta per descongelar-la.

En segon lloc es té el disseny gràfic del visualitzador. Tal com el seu nom indica, en el
visualitzador es pot veure en tot moment el valor que pren cadascuna de les variables de
l’aplicació. A més a més, el visualitzador també li permet saber a l’usuari quines són les
variables seleccionades perquè quan ho estiguin, la seva casella es marcarà de color groc.

A la Figura 4.2 es veu un exemple del visualitzador després d’introduir-li les dades d’un
cicle bàsic de Rankine típic.

Figura 4.2: Visualitzador del cicle bàsic de Rankine

Al Llistat 4.4 es té el codi que s’ha utilitzat per programar el visualitzador. D’aquest
codi cal fixar-se en dos aspectes, per una banda que tal com s’ha dit abans és un mètode
que s’inicia amb l’aplicació i que treballa amb les variables de l’aplicació, de manera
que va actualitzant-se amb els canvis de valor que aquestes tinguin. Per altra banda,
el funcionament de les variables tipus diccionari dVisualProp i dVisualVars, que són les
responsables del canvi de color de les caselles si s’han seleccionat o desseleccionat.

1 def Visualitzador(self, master):


2 """
3 Visualitzador del valor de totes les variables del sistema
4 """
5 self.canvas1.create_rectangle(25, 445, 725, 700)
6 self.dVisualProp = {}
7 self.dVisualVars = {}
8
9 #Visualitzador de propietats
10 height = 5 #i
11 width = 6 #j
Capítol 4 47

12 Indexs = ["P [MPa]", "T [ C ]", "h [kJ/kg]", "s [kJ/( K k g )]", "x"]
13 for i in range(height):
14 for j in range(width):
15 if i !=0 and j !=0:
16 VisualProp = tk.Label(self.canvas1, textvariable=self.
lProps[i][j], width=9, font="Helvetica 10", borderwidth=1, relief=’solid
’, bg="white")
17 VisualProp.place(x=80+75*i,y=460+19*j)
18
19 self.dVisualProp[(i,j)]=VisualProp
20

21 elif i == 0 and j !=0: #j = P, T, h, ...


22 VisualIndex = tk.Label(self.canvas1, text=Indexs[j-1],
width=11, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
23 VisualIndex.place(x=70+75*i,y=460+19*j)
24
25 elif i != 0 and j == 0: #i = Estat 1, Estat 2, ...
26 VisualEstat = tk.Label(self.canvas1, text="Estat "+str(
i), width=9, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
27 VisualEstat.place(x=80+75*i,y=460+19*j)
28
29 else:
30 pass
31
32 #Visualitzador d’altres variables
33 IndexsVars = ["Qcald [kW]", "Qcond [kW]", "Wturb [kW]", "Wbomb [kW]
", " _turb [%]", " _bomb [%]"]
34 for i in range(2): #Rows
35 for j in range(6): #Columns
36 if i == 0:
37 NomsVars = tk.Label(self.canvas1, text=IndexsVars[j],
width=11, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
38 NomsVars.place(x=500+90*i,y=460+19*j)
39 elif i == 1:
40 VisualVars = tk.Label(self.canvas1, textvariable=self.
lVars[j], width=10, font="Helvetica 10", borderwidth=1, relief=’solid’,
bg="white")
41 VisualVars.place(x=500+90*i,y=460+19*j)
42
43 self.dVisualVars[j]=VisualVars
44 else:
45 pass
46
47 #Visualitzador del fluid d’estudi, del flux massic, de la potencia
neta i del rendiment termic
48 NomFluid = tk.Label(self.canvas1, text="Fluid d’estudi", width=24,
font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
49 NomFluid.place(x=110,y=600)
50 VisualFluid = tk.Label(self.canvas1, textvariable=self.fluid, width
=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
51 VisualFluid.place(x=300,y=600)
52

53 NomFlux = tk.Label(self.canvas1, text="Flux massic [kg/s]", width


=24, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
54 NomFlux.place(x=110,y=619)
48 Capítol 4

55 VisualFlux = tk.Label(self.canvas1, textvariable=self.flux, width


=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
56 VisualFlux.place(x=300,y=619)
57 self.dVisualProp[(1,0)]=VisualFlux
58

59 NomWneta = tk.Label(self.canvas1, text="Wneta [kW]", width=24, font


="Helvetica 10 bold", borderwidth=1, relief=’solid’)
60 NomWneta.place(x=110,y=638)
61 VisualWneta = tk.Label(self.canvas1, textvariable=self.Wneta, width
=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
62 VisualWneta.place(x=300,y=638)
63 self.dVisualProp[(0,1)]=VisualWneta
64
65 NomRtermic = tk.Label(self.canvas1, text="Rendiment termic [%]",
width=24, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
66 NomRtermic.place(x=110,y=657)
67 VisualRtermic = tk.Label(self.canvas1, textvariable=self.eta_termic
, width=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="
white")
68 VisualRtermic.place(x=300,y=657)
69
70 #Botons del visualitzador
71 BotoCalcular = tk.Button(self.canvas1, text="Calcular", font="
Helvetica 12", relief=’raised’, command=self.clickedCalcular)
72 BotoCalcular.place(x=550, y=600)
73
74 BotoNetejar = tk.Button(self.canvas1, text="Netejar", font="
Helvetica 12", relief=’raised’, command=self.clickedNetejar)
75 BotoNetejar.place(x=553, y=645)
Llistat 4.4: Codi Python del visualitzador del cicle bàsic de Rankine

En tercer lloc, sobre el menú, que és la barra d’eines que es troba a la part superior de la
pantalla principal (corresponent a la Figura 4.1). Al Llistat 4.5 es pot veure el codi amb
què s’ha programat.

1 def Visualitzador(self, master):


2 """
3 Visualitzador del valor de totes les variables del sistema
4 """
5 self.canvas1.create_rectangle(25, 445, 725, 700)
6 self.dVisualProp = {}
7 self.dVisualVars = {}
8
9 #Visualitzador de propietats
10 height = 5 #i
11 width = 6 #j
12 Indexs = ["P [MPa]", "T [ C ]", "h [kJ/kg]", "s [kJ/( K k g )]", "x"]
13 for i in range(height):
14 for j in range(width):
15 if i !=0 and j !=0:
16 VisualProp = tk.Label(self.canvas1, textvariable=self.
lProps[i][j], width=9, font="Helvetica 10", borderwidth=1, relief=’solid
’, bg="white")
Capítol 4 49

17 VisualProp.place(x=80+75*i,y=460+19*j)
18
19 self.dVisualProp[(i,j)]=VisualProp
20
21 elif i == 0 and j !=0: #j = P, T, h, ...
22 VisualIndex = tk.Label(self.canvas1, text=Indexs[j-1],
width=11, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
23 VisualIndex.place(x=70+75*i,y=460+19*j)
24
25 elif i != 0 and j == 0: #i = Estat 1, Estat 2, ...
26 VisualEstat = tk.Label(self.canvas1, text="Estat "+str(
i), width=9, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
27 VisualEstat.place(x=80+75*i,y=460+19*j)
28
29 else:
30 pass
31

32 #Visualitzador d’altres variables


33 IndexsVars = ["Qcald [kW]", "Qcond [kW]", "Wturb [kW]", "Wbomb [kW]
", " _turb [%]", " _bomb [%]"]
34 for i in range(2): #Rows
35 for j in range(6): #Columns
36 if i == 0:
37 NomsVars = tk.Label(self.canvas1, text=IndexsVars[j],
width=11, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
38 NomsVars.place(x=500+90*i,y=460+19*j)
39 elif i == 1:
40 VisualVars = tk.Label(self.canvas1, textvariable=self.
lVars[j], width=10, font="Helvetica 10", borderwidth=1, relief=’solid’,
bg="white")
41 VisualVars.place(x=500+90*i,y=460+19*j)
42
43 self.dVisualVars[j]=VisualVars
44 else:
45 pass
46
47 #Visualitzador del fluid d’estudi, del flux massic, de la potencia
neta i del rendiment termic
48 NomFluid = tk.Label(self.canvas1, text="Fluid d’estudi", width=24,
font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
49 NomFluid.place(x=110,y=600)
50 VisualFluid = tk.Label(self.canvas1, textvariable=self.fluid, width
=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
51 VisualFluid.place(x=300,y=600)
52
53 NomFlux = tk.Label(self.canvas1, text="Flux massic [kg/s]", width
=24, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
54 NomFlux.place(x=110,y=619)
55 VisualFlux = tk.Label(self.canvas1, textvariable=self.flux, width
=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
56 VisualFlux.place(x=300,y=619)
57 self.dVisualProp[(1,0)]=VisualFlux
58
59 NomWneta = tk.Label(self.canvas1, text="Wneta [kW]", width=24, font
="Helvetica 10 bold", borderwidth=1, relief=’solid’)
50 Capítol 4

60 NomWneta.place(x=110,y=638)
61 VisualWneta = tk.Label(self.canvas1, textvariable=self.Wneta, width
=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="white")
62 VisualWneta.place(x=300,y=638)
63 self.dVisualProp[(0,1)]=VisualWneta
64
65 NomRtermic = tk.Label(self.canvas1, text="Rendiment termic [%]",
width=24, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
66 NomRtermic.place(x=110,y=657)
67 VisualRtermic = tk.Label(self.canvas1, textvariable=self.eta_termic
, width=13, font="Helvetica 10", borderwidth=1, relief=’solid’, bg="
white")
68 VisualRtermic.place(x=300,y=657)
69
70 #Botons del visualitzador
71 BotoCalcular = tk.Button(self.canvas1, text="Calcular", font="
Helvetica 12", relief=’raised’, command=self.clickedCalcular)
72 BotoCalcular.place(x=550, y=600)
73
74 BotoNetejar = tk.Button(self.canvas1, text="Netejar", font="
Helvetica 12", relief=’raised’, command=self.clickedNetejar)
75 BotoNetejar.place(x=553, y=645)
Llistat 4.5: Codi Python del menú

Com es veu, és un codi molt simple però té una gran funcionalitat: permet tancar la
finestra principal, reiniciar-la... Però sobretot és útil perquè permet accedir a la pantalla
de selecció del fluid de treball, la pantalla d’introducció de les propietats termodinàmiques,
permet obtenir els gràfics termodinàmics i fer l’anàlisi exergètica.

A continuació es passarà a explicar cadascuna de les pantalles que poden sortir bé sigui
del menú o del diagrama del cicle termodinàmic.

Selecció del fluid


La pantalla de selecció del fluid correspon a la Figura 4.3, i el seu codi és el del Llistat
4.6. Com ja es va comentar en el capítol anterior, tan sols caldria modificar aquesta classe
per ampliar la llista de fluids disponibles per l’aplicació, per tant seria un canvi mínim.

1 class LlistaFluid():
2
3 def __init__(self, master, fluid):
4

5 self.master=master
6 self.fluid=fluid
7
8 self.master.title("Fluid d’estudi")
9 self.master.geometry(’300x300’)
10 self.master.minsize(300, 300)
11 self.master.maxsize(300,300)
12
13 self.Seleccio(master)
14
Capítol 4 51

15 def Seleccio(self, master):


16 self.canvasSeleccio = tk.Canvas(self.master, width=300, height=300)
17 self.canvasSeleccio.place(x=0, y=0)
18
19 Titol = tk.Label(self.master, text=" S e l e c c i del fluid d’estudi",
font="Helvetica 14 bold", fg="black").place(relx=0.50, rely=0.05, anchor
=’n’)
20 SeleccioFluid = ttk.Combobox(self.canvasSeleccio, textvariable=self
.fluid, state="readonly", width=15)
21 SeleccioFluid.place(relx=0.30, rely=0.2)
22 SeleccioFluid["values"] = ["Air", "Water", "Methane", "Ethane", "n-
Propane", "Ethylene", "Benzene", "R134a", "R404A", "R407C", "R410A", "
R507A", "Ammonia", "CarbonDioxide"]
23
24 BotoAcceptar = tk.Button(self.canvasSeleccio, text="Acceptar", font
="Helvetica 10", command=self.master.destroy)
25 BotoAcceptar.place(relx=0.38, rely=0.5)
Llistat 4.6: Codi Python de la classe LlistaFluid

Figura 4.3: Pantalla de selecció del fluid

Taula de propietats termodinàmiques


Aquesta finestra és segurament la segona més important després de la pantalla principal,
ja que és on s’introdueixen les dades dels estats termodinàmics, el flux màssic o el treball
net del cicle. Per tant, és una nova classe, classe TaulaProps que rebrà les llistes de
les variables inicialitzades a la classe principal i a cada una d’elles els associarà o bé un
requadre d’introducció de dades (per les variables d’entrada) o bé una caixeta de selecció
(per les variables de selecció). D’aquesta manera, l’usuari podrà seleccionar la variable
de la qual coneix el valor, assignar-li el valor i directament aquest valor passarà a la
52 Capítol 4

classe CicleRankine i es podrà veure’l a la seva corresponent casella del visualitzador, la


qual haurà canviat al color groc. Es pot veure aquesta pantalla a la Figura 4.4 i es té un
exemple de com es veuria el visualitzador a la Figura 4.2. El codi amb què s’ha programat
la classe TaulaProps és al Llistat 4.7.

Figura 4.4: Pantalla d’introducció de les propietats termodinàmiques, el flux màssic i la


potència neta

Per últim comentar que perquè l’aplicació funcioni cal introduir-li o bé el flux màssic, o
bé el treball net o bé ambdues, tal com s’indica a les instruccions que es poden veure a la
Figura 4.1. També destacar que aquesta pantalla d’introducció de dades serà sempre la
mateixa amb tots els cicles excepte amb el cicle de Rankine amb reescalfament intermedi,
ja que aquest té 6 estats i per tant s’hauran d’afegir dues columnes tant a la taula com
al visualitzador.

1 class TaulaProps():
2
3 def __init__(self, master, flux, flux_chk, Wneta, Wneta_chk, lProps,
lProps_chk, dVisualProp):
4

5 self.master=master
6
7 self.flux=flux
8 self.flux_chk = flux_chk
9
10 self.Wneta = Wneta
11 self.Wneta_chk = Wneta_chk
12
13
14 self.lProps=lProps
Capítol 4 53

15 self.lProps_chk=lProps_chk
16 self.dVisualProp=dVisualProp
17
18 self.master.title(’Taula de propietats’)
19 self.master.geometry(’700x500’)
20 self.master.minsize(700, 500)
21 self.master.maxsize(700, 500)
22
23 self.Taula(master)
24
25 def show_entry(self, var, widget, index):
26 widget.delete(0, "end")
27 if var.get() == 0:
28 widget.configure(state=’disabled’)
29 self.dVisualProp[index].configure(bg="white")
30 else:
31 widget.configure(state=’normal’)
32 self.dVisualProp[index].configure(bg="yellow")
33
34 def Taula(self, master):
35 """
36 Taula de propietats
37 """
38 self.canvasTaula = tk.Canvas(self.master, width=700, height=500)
39 self.canvasTaula.configure(background="white")
40 self.canvasTaula.place(x=0, y=0)
41
42 Titol = tk.Label(self.master, text="Taula de propietats", font="
Helvetica 18 bold", bg="white", fg="black").place(relx=0.50, rely=0.05,
anchor=’n’)
43
44 height = 5 #i
45 width = 6 #j
46 Props = {}
47 Indexs = ["P [MPa]", "T [ C ]", "h [kJ/kg]", "s [kJ/( K k g )]", "x"]
48 for i in range(height): #Rows
49 for j in range(width): #Columns
50 if i !=0 and j !=0:
51 CajaProp = tk.Entry(self.canvasTaula, textvariable=self
.lProps[i][j], width=10, font="Times 12", bg="Yellow", state=’disabled’)
52 CajaProp.place(x=50+120*i,y=100+30*j)
53 chkProp = tk.Checkbutton(self.canvasTaula, variable=
self.lProps_chk[i][j], bg="white", onvalue=1, offvalue=0, command=lambda
v=self.lProps_chk[i][j], e=CajaProp, index=(i,j): self.show_entry(v,e,
index))
54 chkProp.place(x=50+120*i+85, y=100+30*j)
55 Props[(i,j)]=CajaProp
56
57 elif i == 0 and j !=0: #j = P, T, h, ...
58 EntradaIndex = tk.Label(self.canvasTaula, text=Indexs[j
-1], width=11, font="Times 12", borderwidth=2, relief=’solid’)
59 EntradaIndex.place(x=50+120*i,y=100+30*j)
60
61 elif i != 0 and j == 0: #i = Estat 1, Estat 2, ...
62 EntradaEstat = tk.Label(self.canvasTaula, text="Estat "
54 Capítol 4

+str(i), width=11, font="Times 12", borderwidth=2, relief=’solid’)


63 EntradaEstat.place(x=50+120*i,y=90+25*j)
64
65 else:
66 pass
67
68 EntradaFlux= tk.Label(self.canvasTaula, text="Flux massic (kg/s): "
, font="Times 12", bg="White")
69 EntradaFlux.place(relx=0.15, rely=0.6)
70 CajaFlux = tk.Entry(self.canvasTaula, textvariable=self.flux, width
=10, font="Times 12", bg="Yellow", state=’disabled’)
71 CajaFlux.place(relx=0.325, rely=0.6)
72 chkFlux = tk.Checkbutton(self.canvasTaula, variable=self.flux_chk,
bg="white", onvalue=1, offvalue=0, command=lambda v=self.flux_chk, e=
CajaFlux, index=(1,0): self.show_entry(v,e,index))
73 chkFlux.place(relx=0.45, rely=0.6)
74

75 EntradaWneta= tk.Label(self.canvasTaula, text="Wneta (kW): ", font=


"Times 12", bg="White")
76 EntradaWneta.place(relx=0.53, rely=0.6)
77 CajaWneta = tk.Entry(self.canvasTaula, textvariable=self.Wneta,
width=10, font="Times 12", bg="Yellow", state=’disabled’)
78 CajaWneta.place(relx=0.66, rely=0.6)
79 chkWneta = tk.Checkbutton(self.canvasTaula, variable=self.Wneta_chk
, bg="white", onvalue=1, offvalue=0, command=lambda v=self.Wneta_chk, e=
CajaWneta, index=(0,1): self.show_entry(v,e,index))
80 chkWneta.place(relx=0.775, rely=0.6)
81
82 BotoAcceptar = tk.Button(self.canvasTaula, text="Acceptar", font="
Helvetica 10", command=self.master.destroy)
83 BotoAcceptar.place(relx=0.47, rely=0.7)
Llistat 4.7: Codi Python de la classe TaulaProps

Accedir tant a la pantalla de selecció de fluids com a la pantalla d’introducció de les


propietats termodinàmiques, flux màssic i potència neta és possible perquè la classe Ci-
cleRankine té els respectius mètodes, que permeten que en clicar als botons del menú
s’inicialitzin aquestes classes (és a dir, s’obrin les finestres). El codi Python d’aquests
mètodes es pot veure al Llistat 4.8.

1 def clickedProps(self, *args):


2 self.submasterProps = tk.Toplevel(self.master)
3 self.FinestraProps=TaulaProps(self.submasterProps, self.flux, self.
flux_chk, self.Wneta, self.Wneta_chk, self.lProps, self.lProps_chk, self
.dVisualProp)
4
5 self.submasterProps.transient(self.master)
6 self.submasterProps.grab_set()
7 self.master.wait_window(self.submasterProps)
8

9 def clickedFluid(self, *args):


10 self.submasterFluid = tk.Toplevel(self.master)
11 self.FinestraFluid=LlistaFluid(self.submasterFluid, self.fluid)
12
Capítol 4 55

13 self.submasterFluid.transient(self.master)
14 self.submasterFluid.grab_set()
15 self.master.wait_window(self.submasterFluid)
Llistat 4.8: Mètodes associats a la selecció del fluid i a la taula de propietats

Dispositius de flux estacionari


Quan s’ha descrit l’esquema gràfic del cicle de la pantalla principal s’ha explicat que cada
una de les formes geomètriques que representaven la caldera, la turbina la bomba i el
condensador eren, a més a més, botons que a partir dels mètodes del Llistat 4.3, perme-
tien accedir a les pantalles d’introducció de dades de cada un dels dispositius. Aquestes
pantalles es poden veure a la Figura 4.5.

Figura 4.5: Pantalles d’introducció de dades als dispositius de flux estacionari


56 Capítol 4

Com es pot veure, l’esquema de les pantalles és bastant similar, i per tant també ho és
el codi de cada classe, que es pot veure al Llistat 4.8. De la mateixa manera que amb la
taula de propietats termodinàmiques, les dades que s’introdueixen a cada una d’aquestes
finestres s’actualitzen al visualitzador de la pantalla principal.

1 class Caldera():
2
3 def __init__(self, master, Qcald, Qcald_chk, dVisualVars):
4
5 self.master=master
6 self.Qcald=Qcald
7 self.Qcald_chk=Qcald_chk
8 self.dVisualVars=dVisualVars
9
10 self.master.title(’Caldera’)
11 self.master.geometry(’400x400’)
12 self.master.minsize(400, 400)
13 self.master.maxsize(400, 400)
14
15 self.Esquema(master)
16
17 def show_entry(self, var, widget):
18 widget.delete(0, "end")
19 if var.get() == 0:
20 widget.configure(state=’disabled’)
21 self.dVisualVars[0].configure(bg="white")
22 else:
23 widget.configure(state=’normal’)
24 self.dVisualVars[0].configure(bg="yellow")
25
26 def Esquema(self, master):
27 """
28 Esquema g r f i c de la caldera
29 """
30 self.canvasCald = tk.Canvas(self.master, width=400, height=400)
31 self.canvasCald.configure(background="white")
32 self.canvasCald.place(x=0, y=0)
33
34 Titol = tk.Label(self.master, text="Caldera", font="Helvetica 12
bold", bg="white", fg="black").place(relx=0.50, rely=0.07, anchor=’n’)
35
36 self.canvasCald.create_rectangle(120,70,280,150, fill="light grey")
37 self.canvasCald.create_line(40,110,120,110,fill="blue", arrow="last
", width=5)
38 self.canvasCald.create_line(280,110,360,110,fill="blue", arrow="
last", width=5)
39 self.canvasCald.create_line(200, 120, 200, 200, fill="red", arrow="
first", width=5)
40
41 Estat2 = self.canvasCald.create_text((80, 90), text="2", font="
Helvetica 14 bold")
42 Estat3 = self.canvasCald.create_text((320, 90), text="3", font="
Helvetica 14 bold")
43 Calor = self.canvasCald.create_text((220, 180), text="Q", font="
Capítol 4 57

Times 14 italic")
44
45 #Entrada de dades
46 EntradaCalor = tk.Label(self.canvasCald, text="Q (kW): ", font="
Times 12" ,bg="White")
47 EntradaCalor.place(x=120, y=240)
48 CajaCalor = tk.Entry(self.canvasCald, textvariable=self.Qcald,
width=10, font="Times 12", bg="Yellow", state=’disabled’)
49 CajaCalor.place(x=190, y=240)
50 chkCalor = tk.Checkbutton(self.canvasCald, variable=self.Qcald_chk,
bg="white", onvalue=1, offvalue=0, command=lambda v=self.Qcald_chk, e=
CajaCalor: self.show_entry(v,e))
51 chkCalor.place(x=270, y=240)
52
53 BotoAcceptar = tk.Button(self.canvasCald, text="Acceptar", font="
Helvetica 10", command=self.master.destroy)
54 BotoAcceptar.place(relx=0.45, rely=0.7)
55
56
57 class Turbina():
58
59 def __init__(self, master, Wturb, Wturb_chk, eta_turb, eta_turb_chk,
dVisualVars):
60
61 self.master=master
62 self.Wturb=Wturb
63 self.Wturb_chk=Wturb_chk
64 self.eta_turb=eta_turb
65 self.eta_turb_chk=eta_turb_chk
66 self.dVisualVars=dVisualVars
67
68 self.master.title(’Turbina’)
69 self.master.geometry(’400x500’)
70 self.master.minsize(400, 500)
71 self.master.maxsize(400, 500)
72
73 self.Esquema(master)
74
75 def show_entry(self, var, widget, index):
76 widget.delete(0, "end")
77 if var.get() == 0:
78 widget.configure(state=’disabled’)
79 self.dVisualVars[index].configure(bg="white")
80 else:
81 widget.configure(state=’normal’)
82 self.dVisualVars[index].configure(bg="yellow")
83
84 def Esquema(self, master):
85 """
86 Esquema g r f i c de la turbina
87 """
88 self.canvasTurb = tk.Canvas(self.master, width=400, height=500)
89 self.canvasTurb.configure(background="white")
90 self.canvasTurb.place(x=0, y=0)
91
58 Capítol 4

92 Titol = tk.Label(self.master, text="Turbina", font="Helvetica 12


bold", bg="white", fg="black").place(relx=0.50, rely=0.05, anchor=’n’)
93 b = -20
94
95 points = [130, 150+b, 270, 120+b, 270, 240+b, 130, 210+b]
96 self.canvasTurb.create_polygon(points, fill="light grey", outline="
black")
97 self.canvasTurb.create_line(150,87+b,150,147+b,fill="blue", arrow="
last", width=5)
98 self.canvasTurb.create_line(250,236+b,250,296+b,fill="blue", arrow=
"last", width=5)
99 self.canvasTurb.create_line(240,180+b,320,180+b,fill="red", arrow="
last", width=5)
100
101 Estat3 = self.canvasTurb.create_text((135, 110+b), text="3", font="
Helvetica 14 bold")
102 Estat4 = self.canvasTurb.create_text((235, 262+b), text="4", font="
Helvetica 14 bold")
103 Treball = self.canvasTurb.create_text((295, 165+b), text="W", font=
"Times 14")
104
105 #Entrada de dades
106 EntradaTreball = tk.Label(self.canvasTurb, text="W (kW): ", font="
Times 12" ,bg="White")
107 EntradaTreball.place(x=120, y=320+b)
108 CajaTreball = tk.Entry(self.canvasTurb, textvariable=self.Wturb,
width=10, font="Times 12", bg="Yellow", state=’disabled’)
109 CajaTreball.place(x=190, y=320+b)
110 chkTreball = tk.Checkbutton(self.canvasTurb, variable=self.
Wturb_chk, bg="white", onvalue=1, offvalue=0, command=lambda v=self.
Wturb_chk, e=CajaTreball, index=2: self.show_entry(v,e,index))
111 chkTreball.place(x=270, y=320+b)
112
113 EntradaRendiment = tk.Label(self.canvasTurb, text=" (%): ", font=
"Times 12" ,bg="White")
114 EntradaRendiment.place(x=120, y=355+b)
115 CajaRendiment = tk.Entry(self.canvasTurb, textvariable=self.
eta_turb, width=10, font="Times 12", bg="Yellow", state=’disabled’)
116 CajaRendiment.place(x=190, y=355+b)
117 chkRendiment = tk.Checkbutton(self.canvasTurb, variable=self.
eta_turb_chk, bg="white", onvalue=1, offvalue=0, command=lambda v=self.
eta_turb_chk, e=CajaRendiment, index=4: self.show_entry(v,e,index))
118 chkRendiment.place(x=270, y=355+b)
119
120 BotoAcceptar = tk.Button(self.canvasTurb, text="Acceptar", font="
Helvetica 10", command=self.master.destroy)
121 BotoAcceptar.place(relx=0.45, rely=0.8)
122
123
124 class Bomba():
125
126 def __init__(self, master, Wbomb, Wbomb_chk, eta_bomb, eta_bomb_chk,
dVisualVars):
127
128 self.master=master
Capítol 4 59

129 self.Wbomb=Wbomb
130 self.Wbomb_chk=Wbomb_chk
131 self.eta_bomb=eta_bomb
132 self.eta_bomb_chk=eta_bomb_chk
133 self.dVisualVars=dVisualVars
134
135 self.master.title(’Bomba’)
136 self.master.geometry(’400x500’)
137 self.master.minsize(400, 500)
138 self.master.maxsize(400, 500)
139

140 self.Esquema(master)
141
142 def show_entry(self, var, widget, index):
143 widget.delete(0, "end")
144 if var.get() == 0:
145 widget.configure(state=’disabled’)
146 self.dVisualVars[index].configure(bg="white")
147 else:
148 widget.configure(state=’normal’)
149 self.dVisualVars[index].configure(bg="yellow")
150
151 def Esquema(self, master):
152 """
153 Esquema g r f i c de la bomba
154 """
155 def circle(canvas,x,y,r,color):
156 return canvas.create_oval(x-r,y-r,x+r,y+r, fill=color)
157 self.canvasBomb = tk.Canvas(self.master, width=400, height=500)
158 self.canvasBomb.configure(background="white")
159 self.canvasBomb.place(x=0, y=0)
160
161 Titol = tk.Label(self.master, text="Bomba", font="Helvetica 12 bold
", bg="white", fg="black").place(relx=0.50, rely=0.05, anchor=’n’)
162

163 a, b = 20, -20


164 self.canvasBomb.create_rectangle(190+a, 150+b, 210+a, 200+b, fill="
light grey")
165 circle(self.canvasBomb, 180+a, 200+b, 35, "light grey")
166 self.canvasBomb.create_line(180+a, 295+b, 180+a, 235+b, fill="blue"
, arrow="last", width=5)
167 self.canvasBomb.create_line(200+a, 150+b, 200+a, 90+b, fill="blue",
arrow="last", width=5)
168 self.canvasBomb.create_line(100+a,200+b,170+a,200+b,fill="red",
arrow="last", width=5)
169

170 Estat2 = self.canvasBomb.create_text((200, 100), text="2", font="


Helvetica 14 bold")
171 Estat1 = self.canvasBomb.create_text((180, 245), text="1", font="
Helvetica 14 bold")
172 Treball = self.canvasBomb.create_text((135, 160), text="W", font="
Times 14")
173
174 #Entrada de dades
175 EntradaTreball = tk.Label(self.canvasBomb, text="W (kW): ", font="
60 Capítol 4

Times 12" ,bg="White")


176 EntradaTreball.place(x=120, y=300)
177 CajaTreball = tk.Entry(self.canvasBomb, textvariable=self.Wbomb,
width=10, font="Times 12", bg="Yellow", state=’disabled’)
178 CajaTreball.place(x=190, y=300)
179 chkTreball = tk.Checkbutton(self.canvasBomb, variable=self.
Wbomb_chk, bg="white", onvalue=1, offvalue=0, command=lambda v=self.
Wbomb_chk, e=CajaTreball, index=3: self.show_entry(v,e,index))
180 chkTreball.place(x=270, y=300)
181
182 EntradaRendiment = tk.Label(self.canvasBomb, text=" (%): ", font=
"Times 12" ,bg="White")
183 EntradaRendiment.place(x=120, y=335)
184 CajaRendiment = tk.Entry(self.canvasBomb, textvariable=self.
eta_bomb, width=10, font="Times 12", bg="Yellow", state=’disabled’)
185 CajaRendiment.place(x=190, y=335)
186 chkRendiment = tk.Checkbutton(self.canvasBomb, variable=self.
eta_bomb_chk, bg="white", onvalue=1, offvalue=0, command=lambda v=self.
eta_bomb_chk, e=CajaRendiment, index=5: self.show_entry(v,e,index))
187 chkRendiment.place(x=270, y=335)
188
189 BotoAcceptar = tk.Button(self.canvasBomb, text="Acceptar", font="
Helvetica 10", command=self.master.destroy)
190 BotoAcceptar.place(relx=0.45, rely=0.8)
191
192 class Condensador():
193
194 def __init__(self, master, Qcond, Qcond_chk, dVisualVars):
195

196 self.master=master
197 self.Qcond=Qcond
198 self.Qcond_chk=Qcond_chk
199 self.dVisualVars=dVisualVars
200
201 self.master.title(’Condensador’)
202 self.master.geometry(’400x400’)
203 self.master.minsize(400, 400)
204 self.master.maxsize(400, 400)
205
206 self.Esquema(master)
207

208 def show_entry(self, var, widget):


209 widget.delete(0, "end")
210 if var.get() == 0:
211 widget.configure(state=’disabled’)
212 self.dVisualVars[1].configure(bg="white")
213 else:
214 widget.configure(state=’normal’)
215 self.dVisualVars[1].configure(bg="yellow")
216
217
218 def Esquema(self, master):
219 """
220 Esquema g r f i c del condensador
221 """
Capítol 4 61

222 self.canvasCond = tk.Canvas(self.master, width=400, height=400)


223 self.canvasCond.configure(background="white")
224 self.canvasCond.place(x=0, y=0)
225
226 Titol = tk.Label(self.master, text="Condensador", font="Helvetica
12 bold", bg="white", fg="black").place(relx=0.50, rely=0.07, anchor=’n’
)
227
228 self.canvasCond.create_rectangle(120,70,280,150, fill="light grey")
229 self.canvasCond.create_line(40,110,120,110,fill="blue", arrow="
first", width=5)
230 self.canvasCond.create_line(280,110,360,110,fill="blue", arrow="
first", width=5)
231 self.canvasCond.create_line(200, 120, 200, 200, fill="red", arrow="
last", width=5)
232
233 Estat1 = self.canvasCond.create_text((80, 90), text="1", font="
Helvetica 14 bold")
234 Estat4 = self.canvasCond.create_text((320, 90), text="4", font="
Helvetica 14 bold")
235 Calor = self.canvasCond.create_text((220, 180), text="Q", font="
Times 14 italic")
236

237 #Entrada de dades


238 EntradaCalor = tk.Label(self.canvasCond, text="Q (kW): ", font="
Times 12" ,bg="White")
239 EntradaCalor.place(x=120, y=240)
240 CajaCalor = tk.Entry(self.canvasCond, textvariable=self.Qcond,
width=10, font="Times 12", bg="Yellow", state=’disabled’)
241 CajaCalor.place(x=190, y=240)
242 chkCalor = tk.Checkbutton(self.canvasCond, variable=self.Qcond_chk,
bg="white", onvalue=1, offvalue=0, command=lambda v=self.Qcond_chk, e=
CajaCalor: self.show_entry(v,e))
243 chkCalor.place(x=270, y=240)
244

245 BotoAcceptar = tk.Button(self.canvasCond, text="Acceptar", font="


Helvetica 10", command=self.master.destroy)
246 BotoAcceptar.place(relx=0.45, rely=0.7)
Llistat 4.9: Codi Python de les classes Caldera, Turbina, Bomba i Condensador

4.2 Disseny específic del solucionador


El solucionador de l’aplicació és un mètode de la classe principal, CicleRankine, que
s’encarrega d’operar totes les dades introduïdes a l’aplicació per obtenir els resultats
numèrics que finalment puguin veure’s al visualitzador. Aquest mètode és una funció de
Python que s’ha estructurat en 4 etapes, que a continuació s’explicaran.

Etapa 1: Enviar les dades de la interfície gràfica al solucionador


El codi Python corresponent a aquesta etapa inicial es pot veure al Llistat 4.10. Aquesta
etapa consisteix a passar a variables “clàssiques” totes les dades que l’usuari ha intro-
62 Capítol 4

duït a l’aplicació, en variables Tkinter. Fer aquest pas és necessari perquè les variables
DoubleVar() no permeten operar-les entre elles i per tant seria impossible fer càlculs.

D’aquesta manera el que es fa és treballar amb diccionaris, de manera que a cada estat
termodinàmic li correspon un diccionari. I les claus d’aquest diccionari són els string
inputs de la Figura 3.3, els quals el CoolProp associa a una propietat termodinàmica. I
el valor associat a cada una d’aquestes claus serà el valor numèric de la propietat. Fent-
ho així serà molt més fàcil fer càlculs amb aquests valors, utilitzar la funció PropsSI i
emmagatzemar els resultats.

1 def clickedCalcular(self, *args):


2 """
3 ETAPA 1: Passem les dades introdu des de la interficie grafica a
la interficie matematica. Per fer-ho les fiquem en diccionaris
4 """
5 DadesProps = []
6 DadesCicle = []
7 Estat1, Estat2, Estat3, Estat4, Estat2s, Estat4s = {}, {}, {}, {},
{}, {}
8 Estat = [{}, Estat1, Estat2, Estat3, Estat4] # Llista de
diccionaris d’estats. Volem algo tal com Estat[1][’T’]=T_1 (valor
numeric de T1)
9 EstatIdeal = [Estat2s, Estat4s]
10
11 NomsPropietats = [0, ’P’, ’T’, ’H’, ’S’, ’Q’]
12 NomsPots = ["Qcald", "Qcond", "Wturb", "Wbomb"]
13 NomsRends = ["eta_turb", "eta_bomb"]
14

15 Pots = {} #Diccionari de potencies termiques i rendiments. Pots[’


Qcald’]=Qcald (valor numeric de Qcald)
16
17 FluxPot = {}
18 FluxPot[’eta_termic’]=self.eta_termic.get()
19 if self.flux_chk.get() == 1:
20 FluxPot[’flux’]=self.flux.get()
21
22 if self.Wneta_chk.get() == 1:
23 FluxPot[’Wneta’]=self.Wneta.get()*1000
24
25 fluid = self.fluid.get()
26
27 for i in range(len(self.lProps_chk)):
28 for j in range(len(self.lProps_chk[i])):
29 check = self.lProps_chk[i][j]
30 if j != 0 and i !=0:
31 if check.get() == 1:
32 DadesProps.append((self.lProps_noms[i][j],self.
lProps[i][j].get()))
33
34 for i in range(len(self.lVars_chk)):
35 check = self.lVars_chk[i]
36 if check.get() == 1:
37 DadesCicle.append((self.lVars_noms[i], self.lVars[i].get())
Capítol 4 63

)
38
39 for elem in DadesProps:
40 Prop = elem[0]
41 Valor = elem[1]
42 Estat[int(Prop[2])][Prop[0]]=Valor #A cada diccionari/estat li
assignem com a clau la propietat amb el valor corresponent
43
44 for elem in DadesCicle: #Fem un diccionari de potencies termiques
i rendiments. Pots[’Qcald’]=valor n u m r i c de Qcald
45 Nom = elem[0]
46 Valor = elem[1]
47 Pots[Nom]=Valor
48
49 for i in range(5):
50 if ’P’ in Estat[i]:
51 Estat[i][’P’]=Estat[i][’P’]*1e6 #Passem de MPa a Pa per
treballar amb el CoolProp
52 if ’T’ in Estat[i]:
53 Estat[i][’T’]=Estat[i][’T’]+273.15 #Passem de C a K per
treballar amb el CoolProp
54 if ’H’ in Estat[i]:
55 Estat[i][’H’]=Estat[i][’H’]*1000 #Passem de kJ a J per
treballar amb el CoolProp
56 if ’S’ in Estat[i]:
57 Estat[i][’S’]=Estat[i][’S’]*1000 #Passem de kJ a J per
treballar amb el CoolProp
58
59 for elem in Pots:
60 if elem in NomsPots:
61 Pots[elem] = Pots[elem]*1000 #Passem de kW a W per
treballar amb el CoolProp
62 if elem in NomsRends:
63 Pots[elem] = Pots[elem]/100 #Passem de % a tant per u
64

65 #Hem creat els diccionaris i hem fet les converssions per treballar
amb el CoolProp.
Llistat 4.10: Codi Python de la primera etapa del solucionador

També es pot veure que a part dels diccionaris dels estats termodinàmics reals, es tre-
ballarà amb els estats termodinàmics ideals, perquè són necessaris per treballar amb els
rendiments de la turbina i la bomba.

Per altra banda, també es treballa amb dos altres diccionaris: el diccionari Pots que és
per les variables relatives a la caldera, la turbina, la bomba i el condensador: és a dir
les seves potències i rendiments. I el diccionari FluxPot que és per treballar amb el flux
màssic i la potència neta del cicle. El motiu d’aquesta separació és que a l’aplicació cal
introduir-li o el flux màssic o la potència neta, en canvi no es té perquè introduir cap
potència o rendiment dels dispositius del cicle. Per tant el

Finalment, es fa el canvi d’unitats de totes les variables per treballar amb la funció PropsSI
que treballa en les unitats que es poden veure a la Figura 3.3.
64 Capítol 4

Etapa 2: Definició de les funcions matemàtiques per resoldre el cicle


En aquesta etapa no s’operarà amb les dades. L’únic que es farà en aquesta fase és definir
totes les funcions matemàtiques que s’utilitzaran a la següent etapa per resoldre el cicle de
manera iterativa. D’aquesta manera, el que s’ha fet és agrupar totes les equacions vistes
al Capítol 2 en blocs i convertir aquests blocs en funcions de Python.

El que es farà a continuació és comentar cadascuna d’aquestes funcions per separat.

Funció ResoldreEstat

En aquesta funció el que es fa és consultar si d’algun dels estats termodinàmics se’n


coneixen més de dues propietats. Si és el cas, aleshores l’estat ja està resol i només cal
calcular la resta de propietats amb la funció PropsSI. Altrament, no fa res. Es pot veure
el codi Python de la funció al Llistat 4.11.

1 """
2 ETAPA 2: Definim les funcions que farem servir per resoldre el
problema
3 """
4 def ResoldreEstat(Estat, EstatIdeal, fluid):
5 NomsPropietats = [0, ’P’, ’T’, ’H’, ’S’, ’Q’]
6 for i in range(5):
7 if len(Estat[i]) > 1: #CAS 1: Coneixem m s d’una propietat
per algun dels estats! -> Aleshores l’estat ja esta resolt!
8 for Prop in NomsPropietats[1:]:
9 if Prop not in Estat[i]:
10 PropsConec = list(Estat[i])
11 Estat[i][Prop]=PropsSI(Prop,PropsConec[0],Estat
[i][PropsConec[0]],PropsConec[1],Estat[i][PropsConec[1]],fluid)
12 #L’estat ha quedat resolt
13
14 for i in range(len(EstatIdeal)):
15 if len(EstatIdeal[i]) > 1:
16 for Prop in NomsPropietats[1:]:
17 if Prop not in EstatIdeal[i]:
18 PropsConec = list(EstatIdeal[i])
19 EstatIdeal[i][Prop]=PropsSI(Prop,PropsConec[0],
EstatIdeal[i][PropsConec[0]],PropsConec[1],EstatIdeal[i][PropsConec[1]],
fluid)
20 #L’estat ideal queda resolt
Llistat 4.11: Codi Python de la funció ResoldreEstat

Funció CalcularAltres

Amb aquesta funció s’apliquen les equacions d’igualtat de pressions al condensador i la


caldera i les equacions dels estats ideals per poder treballar amb els rendiments de la
turbina i la bomba. El codi Python d’aquesta funció és al Llistat 4.12.

1 def CalcularAltres(Estat, EstatIdeal):


2 for i in range (5):
Capítol 4 65

3 if ’P’ in Estat[i]:
4 if Estat[i][’P’] != 0: #Fent aixo apliquem les
equacions P_2=P_3, P_4=P_1, P_2 = P_2s, P_4 = P_4s
5 if i==1:
6 Estat[4][’P’] = Estat[1][’P’]
7 elif i==2:
8 Estat[3][’P’] = Estat[2][’P’]
9 EstatIdeal[0][’P’] = Estat[2][’P’] #EstatIdeal
[0] -> Estat2s
10 elif i==3:
11 Estat[2][’P’] = Estat[3][’P’]
12 elif i==4:
13 Estat[1][’P’] = Estat[4][’P’]
14 EstatIdeal[1][’P’] = Estat[4][’P’] #EstatIdeal
[1] -> Estat4s
15
16 if ’S’ in Estat[i]:
17 if Estat[i][’S’] != 0: #Fent aixo apliquem les
equacions s_1 = s_2s i s_3 = s_4s
18 if i==1:
19 EstatIdeal[0][’S’] = Estat[1][’S’]
20 elif i==3:
21 EstatIdeal[1][’S’] = Estat[3][’S’]
Llistat 4.12: Codi Python de la funció CalcularAltres

Funció CalcularFlux

Amb aquesta funció s’aplica la següent equació per calcular el flux màssic:

˙
WN
ṁ =
(h3 − h4 ) − (h2 − h1 )

Es pot veure el codi de la funció al Llistat 4.13.

1 def CalcularFlux(Estat, Pots, FluxPot):


2 if (’Wneta’ in FluxPot) and not (’flux’ in FluxPot):
3 if (’H’ in Estat[1]) and (’H’ in Estat[2]) and (’H’ in
Estat[3]) and (’H’ in Estat[4]):
4 FluxPot[’flux’]=abs((FluxPot[’Wneta’]))/abs((abs(Estat
[3][’H’]-Estat[4][’H’])-abs(Estat[2][’H’]-Estat[1][’H’])))
Llistat 4.13: Codi Python de la funció CalcularFlux

Funció CalcularPotencies

Amb aquesta funció s’apliquen les equacions 2.1, 2.2, 2.3, 2.4, 2.5 i 2.7 per calcular les
potències tèrmiques, mecàniques, la potència neta i el rendiment tèrmic del cicle.

Es pot veure el codi de la funció al Llistat 4.14.


66 Capítol 4

1 def CalcularPotencies(Estat, Pots, flux, FluxPot):


2 if (’H’ in Estat[1]) and (’H’ in Estat[4]):
3 Pots[’Qcond’]=flux*(Estat[1][’H’]-Estat[4][’H’])
4 if (’H’ in Estat[3]) and (’H’ in Estat[2]):
5 Pots[’Qcald’]=flux*(Estat[3][’H’]-Estat[2][’H’])
6 if (’H’ in Estat[4]) and (’H’ in Estat[3]):
7 Pots[’Wturb’]=flux*(Estat[4][’H’]-Estat[3][’H’])
8 if (’H’ in Estat[2]) and (’H’ in Estat[1]):
9 Pots[’Wbomb’]=flux*(Estat[2][’H’]-Estat[1][’H’])
10 if (’H’ in Estat[1]) and (’H’ in Estat[2]) and (’H’ in Estat
[3]) and (’H’ in Estat[4]):
11 FluxPot[’Wneta’]=flux*(Estat[4][’H’]-Estat[3][’H’])+flux*(
Estat[2][’H’]-Estat[1][’H’])
12 if (Estat[2][’H’]!=Estat[3][’H’]):
13 FluxPot[’eta_termic’]=abs((Estat[4][’H’]-Estat[3][’H’])
+(Estat[2][’H’]-Estat[1][’H’]))/abs(Estat[3][’H’]-Estat[2][’H’])
14 if (’Wturb’ in Pots) and (’Wbomb’ in Pots):
15 FluxPot[’Wneta’]=Pots[’Wturb’]+Pots[’Wbomb’]
16 if (’Qcald’ in Pots) and (Pots[’Qcald’]!=0):
17 FluxPot[’eta_termic’]=abs(Pots[’Wturb’]+Pots[’Wbomb’])/
abs(Pots[’Qcald’])
Llistat 4.14: Codi Python de la funció CalcularPotencies

Funció RendimentTurbina

Amb aquesta funció s’aplica l’equació 1.9, corresponent al rendiment d’una turbina. Per
aplicar l’equació completament, com que hi apareixen 4 variables, es verifica que per
calcular-ne una d’elles es coneguin la resta. Aleshores s’aplica l’equació amb la varia-
ble incògnita aïllada. Aquesta estratègia és la que se seguirà sempre que es tingui un
rendiment, sigui del dispositiu que sigui.

Es pot veure el codi de la funció al Llistat 4.15.

1 def RendimentTurbina(Estat, EstatIdeal, Pots): #No accepta


rendiment 0, no t sentit f s i c
2 v1 = ’H’ in Estat[3] #Conec h3
3 v2 = ’H’ in Estat[4] #Conec h4
4 v3 = ’H’ in EstatIdeal[1] #Conec h4s
5 v4 = ’eta_turb’ in Pots #Conec rendiment turbina
6
7 if v1 and v3 and v4 and (not v2):
8 Estat[4][’H’]=Estat[3][’H’]+Pots[’eta_turb’]*(EstatIdeal
[1][’H’]-Estat[3][’H’])
9
10 if v2 and v3 and v4 and (not v1) and (Pots[’eta_turb’]!=1):
11 Estat[3][’H’]=(Pots[’eta_turb’]*EstatIdeal[1][’H’]-Estat
[4][’H’])/(Pots[’eta_turb’]-1)
12

13 if v1 and v2 and v4 and (not v3) and (Pots[’eta_turb’]!=0):


14 EstatIdeal[1][’H’]=(Estat[4][’H’]-Estat[3][’H’]+Estat[3][’H
’]*Pots[’eta_turb’])/(Pots[’eta_turb’])
15
Capítol 4 67

16 if v1 and v2 and v3 and (not v4) and (EstatIdeal[1][’H’]!=Estat


[3][’H’]):
17 Pots[’eta_turb’]=(Estat[4][’H’]-Estat[3][’H’])/(EstatIdeal
[1][’H’]-Estat[3][’H’])
18

19 if v4:
20 if Pots[’eta_turb’]==1 and ’S’ in Estat[3]:
21 Estat[4][’S’]=Estat[3][’S’]
22 if Pots[’eta_turb’]==1 and ’S’ in Estat[4]:
23 Estat[3][’S’]=Estat[4][’S’]
Llistat 4.15: Codi Python de la funció RendimentTurbina

Funció RendimentBomba

Amb aquesta funció s’aplica l’equació 1.11, corresponent al rendiment d’una bomba. S’ha
seguit la mateixa estratègia que amb la funció anterior però cal notar que en aquest cas
es pot donar la circumstància que el treball de la bomba sigui negligible (cosa que en
alguns cicles de Rankine és habitual), aleshores el càlcul del rendiment de la bomba perd
el sentit i tampoc es podrien fer la resulta de càlculs, per tant calia allargar el codi del
Llistat 4.16 per fer aquesta distinció.

1 def RendimentBomba(Estat, EstatIdeal, Pots): #No accepta rendiment


0, no t sentit f s i c
2 v1 = ’H’ in Estat[1] #Conec h1
3 v2 = ’H’ in Estat[2] #Conec h2
4 v3 = ’H’ in EstatIdeal[0] #Conec h2s
5 v4 = ’eta_bomb’ in Pots #Conec rendiment bomba
6
7 if ’Wbomb’ in Pots:
8 if Pots[’Wbomb’] == 0:
9 Pots[’eta_bomb’]=-1
10 else:
11 if v1 and v3 and v4 and (not v2) and (Pots[’eta_bomb’
]!=0):
12 Estat[2][’H’]=(EstatIdeal[0][’H’]-Estat[1][’H’]+
Pots[’eta_bomb’]*Estat[1][’H’])/(Pots[’eta_bomb’])
13
14 if v2 and v3 and v4 and (not v1) and (Pots[’eta_bomb’
]!=1):
15 Estat[1][’H’]=(Pots[’eta_bomb’]*Estat[2][’H’]-
EstatIdeal[0][’H’])/(Pots[’eta_bomb’]-1)
16
17 if v1 and v2 and v4 and (not v3):
18 EstatIdeal[0][’H’]=Estat[1][’H’]+Pots[’eta_bomb’]*(
Estat[2][’H’]-Estat[1][’H’])
19
20 if v1 and v2 and v3 and (not v4) and (Estat[2][’H’]!=
Estat[1][’H’]):
21 Pots[’eta_bomb’]=(EstatIdeal[0][’H’]-Estat[1][’H’])
/(Estat[2][’H’]-Estat[1][’H’])
22 else:
23 if v1 and v3 and v4 and (not v2) and (Pots[’eta_bomb’]!=0):
68 Capítol 4

24 Estat[2][’H’]=(EstatIdeal[0][’H’]-Estat[1][’H’]+Pots[’
eta_bomb’]*Estat[1][’H’])/(Pots[’eta_bomb’])
25
26 if v2 and v3 and v4 and (not v1) and (Pots[’eta_bomb’]!=1):
27 Estat[1][’H’]=(Pots[’eta_bomb’]*Estat[2][’H’]-
EstatIdeal[0][’H’])/(Pots[’eta_bomb’]-1)
28
29 if v1 and v2 and v4 and (not v3):
30 EstatIdeal[0][’H’]=Estat[1][’H’]+Pots[’eta_bomb’]*(
Estat[2][’H’]-Estat[1][’H’])
31

32 if v1 and v2 and v3 and (not v4) and (Estat[2][’H’]!=Estat


[1][’H’]):
33 Pots[’eta_bomb’]=(EstatIdeal[0][’H’]-Estat[1][’H’])/(
Estat[2][’H’]-Estat[1][’H’])
34
35 if v4:
36 if Pots[’eta_bomb’]==1 and ’S’ in Estat[1]:
37 Estat[2][’S’]=Estat[1][’S’]
38 if Pots[’eta_bomb’]==1 and ’S’ in Estat[2]:
39 Estat[2][’S’]=Estat[2][’S’]
Llistat 4.16: Codi Python de la funció RendimentBomba

Funció CalcularEntalpies

Aquesta és la darrera funció i també la més llarga de totes. La funció implementa les
equacions 2.1, 2.2, 2.3 i 2.4; les quals anteriorment s’havia vist que s’implementaven
per calcular les potències. En aquest cas, s’utilitzen per calcular les entalpies, donant
per suposat que es coneix alguna potència. De fet el que s’ha fet és aplicar la mateixa
estratègia que amb els rendiments, treballant amb 4 equacions amb 2 variables cada una
d’elles.

Per tant, el que s’ha fet és en primer lloc estudiar totes les combinacions possibles de po-
tències i per cada combinació, si tenia sentit, plantejar cada equació les dues possibilitats,
aïllant la incògnita. Aquesta és la part més ineficient del codi de tota l’aplicació i seria
un punt a millorar pel treball futur perquè segurament es podria optimitzar i fer-lo més
curt. Tot i això, no suposa un problema per l’aplicació, ja que funciona de manera ràpida
i correcte.

Es pot veure el codi de la funció al Llistat 4.17.

1 def CalcularEntalpies(Estat, Pots, flux):


2 v1 = ’Qcald’ in Pots
3 v2 = ’Qcond’ in Pots
4 v3 = ’Wturb’ in Pots
5 v4 = ’Wbomb’ in Pots
6
7 if (not v1) and (not v2) and (not v3) and (not v4): #No conec
ni Qcald, ni Qcond, ni Wturb, ni Wbomb
8 pass
9 #0
Capítol 4 69

10
11 elif (not v1) and (not v2) and (not v3) and (v4): # N o m s conec
Wbomb
12 if ’H’ in Estat[2]:
13 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
14 if ’H’ in Estat[1]:
15 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
16 #1
17
18 elif (not v1) and (not v2) and (v3) and (not v4): # N o m s conec
Wturb
19 if ’H’ in Estat[4]:
20 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
21 if ’H’ in Estat[3]:
22 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
23 #2
24

25
26 elif (not v1) and (not v2) and (v3) and (v4): #Conec Wbomb i
Wturb
27 if ’H’ in Estat[1]:
28 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
29 if ’H’ in Estat[2]:
30 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
31 if ’H’ in Estat[3]:
32 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
33 if ’H’ in Estat[4]:
34 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
35 #3
36
37 elif (not v1) and (v2) and (not v3) and (not v4): # N o m s conec
Qcond
38 if ’H’ in Estat[1]:
39 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
40 if ’H’ in Estat[4]:
41 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
42 #4
43
44 elif (not v1) and (v2) and (not v3) and (v4): #Conec Qcond i
Wbomb
45 if ’H’ in Estat[1]:
46 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
47 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
48 if ’H’ in Estat[2]:
49 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
50 if ’H’ in Estat[4]:
51 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
52 #5
53
54 elif (not v1) and (v2) and (v3) and (not v4): #Conec Qcond i
Wturb
55 if ’H’ in Estat[1]:
56 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
57 if ’H’ in Estat[3]:
58 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
70 Capítol 4

59 if ’H’ in Estat[4]:
60 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
61 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
62 #6
63

64 elif (not v1) and (v2) and (v3) and (v4): #Conec Qcond, Wturb,
Wbomb
65 if ’H’ in Estat[1]:
66 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
67 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
68 if ’H’ in Estat[2]:
69 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
70 if ’H’ in Estat[3]:
71 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
72 if ’H’ in Estat[4]:
73 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
74 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
75 #7
76
77 elif (v1) and (not v2) and (not v3) and (not v4): # N o m s conec
Qcald
78 if ’H’ in Estat[3]:
79 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
80 if ’H’ in Estat[2]:
81 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
82 #8
83
84 elif (v1) and (not v2) and (not v3) and (v4): #Conec Qcald i
Wbomb
85 if ’H’ in Estat[1]:
86 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
87 if ’H’ in Estat[2]:
88 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
89 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
90 if ’H’ in Estat[3]:
91 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
92 #9
93
94 elif (v1) and (not v2) and (v3) and (not v4): #Conec Qcald i
Wturb
95 if ’H’ in Estat[2]:
96 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
97 if ’H’ in Estat[3]:
98 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
99 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
100 if ’H’ in Estat[4]:
101 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
102 #10
103
104 elif (v1) and (not v2) and (v3) and (v4): #Conec Qcald, Wturb i
Wbomb
105 if ’H’ in Estat[1]:
106 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
107 if ’H’ in Estat[2]:
108 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
Capítol 4 71

109 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
110 if ’H’ in Estat[3]:
111 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
112 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
113 if ’H’ in Estat[4]:
114 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
115 #11
116
117 elif (v1) and (v2) and (not v3) and (not v4): #Conec Qcald i
Qcond
118 if ’H’ in Estat[1]:
119 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
120 if ’H’ in Estat[2]:
121 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
122 if ’H’ in Estat[3]:
123 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
124 if ’H’ in Estat[4]:
125 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
126 #12
127
128 elif (v1) and (v2) and (not v3) and (v4): #Conec Qcald, Qcond i
Wbomb
129 if ’H’ in Estat[1]:
130 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
131 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
132 if ’H’ in Estat[2]:
133 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
134 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
135 if ’H’ in Estat[3]:
136 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
137 if ’H’ in Estat[4]:
138 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
139 #13
140
141 elif (v1) and (v2) and (v3) and (not v4): #Conec Qcald, Qcond i
Wturb
142 if ’H’ in Estat[1]:
143 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
144 if ’H’ in Estat[2]:
145 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
146 if ’H’ in Estat[3]:
147 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
148 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
149 if ’H’ in Estat[4]:
150 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
151 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
152 #14
153
154 elif (v1) and (v2) and (v3) and (v4): #Conec tot: Qcald, Qcond,
Wturb, Wbomb
155 if ’H’ in Estat[1]:
156 Estat[4][’H’]=Estat[1][’H’]-Pots[’Qcond’]/flux
157 Estat[2][’H’]=Estat[1][’H’]+Pots[’Wbomb’]/flux
158 if ’H’ in Estat[2]:
159 Estat[3][’H’]=Estat[2][’H’]+Pots[’Qcald’]/flux
72 Capítol 4

160 Estat[1][’H’]=Estat[2][’H’]-Pots[’Wbomb’]/flux
161 if ’H’ in Estat[3]:
162 Estat[2][’H’]=Estat[3][’H’]-Pots[’Qcald’]/flux
163 Estat[4][’H’]=Estat[3][’H’]+Pots[’Wturb’]/flux
164 if ’H’ in Estat[4]:
165 Estat[1][’H’]=Estat[4][’H’]+Pots[’Qcond’]/flux
166 Estat[3][’H’]=Estat[4][’H’]-Pots[’Wturb’]/flux
167 #15
168 else:
169 pass
Llistat 4.17: Codi Python de la funció CalcularEntalpies

Etapa 3: Resolució iterativa del cicle


En aquesta etapa és on s’aplicaran totes les funcions definides anteriorment, de manera
iterativa. Així s’aplicaran totes les equacions diverses vegades i els resultats obtinguts
s’emmagatzemaran als diccionaris definits a l’Etapa 1.

També en aquesta etapa, un cop resolt el cicle, s’aprofita i s’emmagatzemen totes les
dades dels diccionaris d’estats a un objecte de la classe StateContainer, per després poder
fer la representació gràfica del cicle de manera fàcil.

Finalment també és en aquesta etapa on s’ha decidit ficar les alertes d’error, que serveixen
per avisar l’usuari en cas que no hagi introduït suficients dades i no s’hagi pogut dur a
terme la resolució del cicle.

Es pot veure el codi Python d’aquesta etapa al Llistat 4.18.

1 """
2 ETAPA 3: Bucle actualitzant els valors dels diccionaris i les
aprofitem per si volem fer el g r f i c
3 """
4 j=0
5 #long=0
6 while (j<10):
7 ResoldreEstat(Estat, EstatIdeal, fluid)
8 CalcularAltres(Estat, EstatIdeal)
9 CalcularFlux(Estat, Pots, FluxPot)
10 RendimentTurbina(Estat, EstatIdeal, Pots)
11 RendimentBomba(Estat, EstatIdeal, Pots)
12 if ’flux’ in FluxPot:
13 flux=FluxPot[’flux’]
14 CalcularPotencies(Estat, Pots, flux, FluxPot)
15 CalcularEntalpies(Estat, Pots, flux)
16
17 #long=len(Estat[1])+len(Estat[2])+len(Estat[3])+len(Estat[4])
18 j=j+1
19

20 #Guardem les dades per poder fer la representaci gr fica


21 while True:
22 try:
23 for i in range(5):
Capítol 4 73

24 if i != 0:
25 for prop in NomsPropietats[1:5]:
26 self.sc[i-1,prop]=Estat[i][prop]
27 break
28 except KeyError:
29 messagebox.showerror("Error", "No has i n t r o d u t suficients
dades")
30 break
31 except ValueError:
32 messagebox.showerror("Error", "No has i n t r o d u t suficients
dades")
33 break
Llistat 4.18: Codi Python de la tercera etapa del solucionador

Etapa 4: Tornar les dades del solucionador a la interfície gràfica


Aquesta darrera etapa es podria dir que és la inversa de la primera. S’envia tota la
informació continguda en els diccionaris del solucionador a la interfície gràfica perquè
d’aquesta manera es puguin visualitzar els resultats a la pantalla principal. Per fer-ho
simplement cal fer el canvi d’unitats i actualitzar el valor de les variables inicialitzades al
principi de la classe CicleRankine.

També en aquesta etapa s’han col·locat avisos, per exemple, al no obtindre el títol d’un
estat l’aplicació ens informa que és degut al fet que aquell estat es troba fora de la campana
de saturació.

Es pot veure el codi d’aquesta etapa al Llistat 4.19.

1 """
2 ETAPA 4: Actualitzem els valors finals i els retornem a la
interf cie g r f i c a
3 """
4 for i in range(len(self.lProps)): #i=1 -> Estat 1, i=2 -> Estat
2, i=3 -> Estat 3, i=4 -> Estat 4.
5 for j in range(len(self.lProps[i])): #j=1 -> P / j=2 -> T / j=3
-> H / j=4 -> S / j=5 -> Q /
6 if j != 0 and i != 0:
7 if j==1:
8 Estat[i][NomsPropietats[j]]=round(Estat[i][
NomsPropietats[j]]/1e6,4) #Passem de Pa a MPa
9 if j==2:
10 Estat[i][NomsPropietats[j]]=round(Estat[i][
NomsPropietats[j]]-273.15,2) #Passem de K a C
11 if j==3:
12 Estat[i][NomsPropietats[j]]=round(Estat[i][
NomsPropietats[j]]/1000,4) #Passem de J a kJ
13 if j==4:
14 Estat[i][NomsPropietats[j]]=round(Estat[i][
NomsPropietats[j]]/1000,4) #Passem de J a kJ
15 if j==5:
16 Estat[i][NomsPropietats[j]]=round(Estat[i][
NomsPropietats[j]],4)
74 Capítol 4

17 if Estat[i][NomsPropietats[j]] == -1:
18 Estat[i][NomsPropietats[j]]="-"
19 messagebox.showwarning("Advertencia", "Has
obtingut algun t t o l sense sentit f s i c ja que et trobes fora de la
campana")
20
21 self.lProps[i][j].set(Estat[i][NomsPropietats[j]])
22
23
24 for i in range(len(self.lVars)):
25 if self.lVars_noms[i] in NomsPots:
26 if self.lVars_noms[i] in Pots:
27 self.lVars[i].set(round(Pots[self.lVars_noms[i
]]/1000,0)) #Passem de W a kW
28 else:
29 self.lVars[i].set("-") #ERROR NO S’HA CALCULAT DURANT
LA ITERACIO
30 if self.lVars_noms[i] in NomsRends:
31 if self.lVars_noms[i] in Pots:
32 self.lVars[i].set(round(Pots[self.lVars_noms[i]]*100,4)
) #Passem de tant per u a 100
33 else:
34 self.lVars[i].set("-") #ERROR NO S’HA CALCULAT DURANT
LA ITERACIO
35
36 if ’flux’ in FluxPot:
37 self.flux.set(round(FluxPot[’flux’],2))
38 if ’Wneta’ in FluxPot:
39 self.Wneta.set(round(FluxPot[’Wneta’]/1000,0)) #Passem de W a
kW
40 if ’eta_termic’ in FluxPot:
41 self.eta_termic.set(round(FluxPot[’eta_termic’]*100,2)) #Passem
de tant per u a %
Llistat 4.19: Codi Python de la quarta etapa del solucionador

Anàlisi exergètica
Finalment, malgrat que no forma part estrictament del solucionador, pels cicles de Ranki-
ne (bàsic i amb reescalfament intermedi) l’aplicació permet fer l’anàlisi exergètica. Per
fer-lo s’hi pot accedir a través del menú. És una nova pantalla, que es pot veure a la
Figura 4.6.

Com que és una nova pantalla, és una nova classe, la classe AnalisiExergetic. En aquest
cas, com que l’opció de l’anàlisi exergètica es va afegir al final del treball, té una progra-
mació lleugerament diferent que a continuació es comentarà. Es pot veure el codi Python
de l’anàlisi exergètica al Llistat 4.20.

1 class AnalisiExergetic():
2
3 def __init__(self, master, Tentorn, Treactor, Pentorn, fluid, flux,
Wneta, lProps, lVars):
4
Capítol 4 75

Figura 4.6: Pantalla d’anàlisi exergètica

5 self.master=master
6 self.Tentorn=Tentorn
7 self.Treactor=Treactor
8 self.Pentorn=Pentorn
9 self.Wneta=Wneta
10 self.fluid=fluid
11 self.flux=flux
12 self.lProps=lProps
13 self.lVars=lVars
14

15 self.lProps_noms = [[],
16 [0,"P_1", "T_1", "H_1", "S_1", "E_1"],
17 [0,"P_2", "T_2", "H_2", "S_2", "E_2"],
18 [0,"P_3", "T_3", "H_3", "S_3", "E_3"],
19 [0,"P_4", "T_4", "H_4", "S_4", "E_4"]]
20

21 self.lVars_noms = ["Qcald", "Qcond", "Wturb", "Wbomb"]


22
23 self.ex_1, self.ex_2, self.ex_3, self.ex_4 = tk.DoubleVar(), tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
24 self.Wp_cald, self.Wp_cond, self.Wp_turb, self.Wp_bomb = tk.
DoubleVar(), tk.DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
25 self.etaex_cald, self.etaex_cond, self.etaex_turb, self.etaex_bomb
= tk.DoubleVar(), tk.DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
76 Capítol 4

26 self.etaex_instal=tk.DoubleVar()
27
28 self.lex=[0,self.ex_1, self.ex_2, self.ex_3, self.ex_4]
29 self.lWp=[0,self.Wp_cald, self.Wp_cond, self.Wp_turb, self.Wp_bomb]
30 self.letaex=[0,self.etaex_cald, self.etaex_cond, self.etaex_turb,
self.etaex_bomb]
31
32
33 self.master.title(" A n l i s i e x e r g t i c ")
34 self.master.geometry(’600x500’)
35 self.master.minsize(600, 500)
36 self.master.maxsize(600,500)
37
38 self.canvasEx = tk.Canvas(self.master, width=600, height=500, bg=’
white’)
39 self.canvasEx.place(x=0, y=0)
40

41 Titol = tk.Label(self.master, text=" A n l i s i e x e r g t i c ", font="


Helvetica 18 bold", fg="black", bg=’white’).place(relx=0.50, rely=0.05,
anchor=’n’)
42
43 EntradaTentorn= tk.Label(self.canvasEx, text="Temperatura de l’
entorn, T [ C ] : ", font="Times 12", bg="White")
44 EntradaTentorn.place(x=140, y=80)
45 CajaTentorn = tk.Entry(self.canvasEx, textvariable=self.Tentorn,
width=10, font="Times 12", bg="White")
46 CajaTentorn.place(x=355, y=80)
47
48 EntradaTreactor= tk.Label(self.canvasEx, text="Temperatura de la
font, Tf [ C ] : ", font="Times 12", bg="White")
49 EntradaTreactor.place(x=140, y=110)
50 CajaTreactor = tk.Entry(self.canvasEx, textvariable=self.Treactor,
width=10, font="Times 12", bg="White")
51 CajaTreactor.place(x=355, y=110)
52

53 EntradaPentorn= tk.Label(self.canvasEx, text=" P r e s s i de l’entorn,


P [MPa] : ", font="Times 12", bg="White")
54 EntradaPentorn.place(x=160, y=140)
55 CajaPentorn = tk.Entry(self.canvasEx, textvariable=self.Pentorn,
width=10, font="Times 12", bg="White")
56 CajaPentorn.place(x=355, y=140)
57
58 #Visualitzador de exergies
59 height = 5 #i
60 width = 2 #j
61 Indexs = [" e [kJ/kg]"]
62 for i in range(height): #Rows
63 for j in range(width): #Columns
64 if i !=0 and j !=0:
65 VisualProp = tk.Label(self.canvasEx, textvariable=self.
lex[i], width=9, font="Helvetica 10", borderwidth=1, relief=’solid’, bg=
"white")
66 VisualProp.place(x=110+75*i,y=180+19*j)
67
68 elif i == 0 and j !=0: #j = P, T, h, ...
Capítol 4 77

69 VisualIndex = tk.Label(self.canvasEx, text=Indexs[0],


width=11, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
70 VisualIndex.place(x=100+75*i,y=180+19*j)
71
72 elif i != 0 and j == 0: #i = Estat 1, Estat 2, ...
73 VisualEstat = tk.Label(self.canvasEx, text="Estat "+str
(i), width=9, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
74 VisualEstat.place(x=110+75*i,y=180+19*j)
75
76 else:
77 pass
78
79 Subtitol = tk.Label(self.master, text=" C l c u l de treballs perduts
i rendiments exerg tics", font="Helvetica 11 bold underline", fg="black
", bg=’white’).place(relx=0.50, y=225, anchor=’n’)
80
81 #Visualitzador de treballs perduts
82 height = 5 #i
83 width = 3 #j
84 Indexs = [" W [kW]", " [%]"]
85 Noms = [0, ’Caldera’, ’Condensador’, ’Turbina’, ’Bomba’]
86 for i in range(height): #Rows
87 for j in range(width): #Columns
88 if i !=0 and j !=0:
89 if j == 1:
90 VisualProp = tk.Label(self.canvasEx, textvariable=
self.lWp[i], width=13, font="Helvetica 10", borderwidth=1, relief=’solid
’, bg="white")
91 VisualProp.place(x=50+100*i,y=260+19*j)
92 if j == 2:
93 VisualProp = tk.Label(self.canvasEx, textvariable=
self.letaex[i], width=13, font="Helvetica 10", borderwidth=1, relief=’
solid’, bg="white")
94 VisualProp.place(x=50+100*i,y=260+19*j)
95

96 elif i == 0 and j !=0:


97 VisualIndex = tk.Label(self.canvasEx, text=Indexs[j-1],
width=14, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
98 VisualIndex.place(x=40+100*i,y=260+19*j)
99
100 elif i != 0 and j == 0:
101 VisualEstat = tk.Label(self.canvasEx, text=Noms[i],
width=13, font="Helvetica 10 bold", borderwidth=1, relief=’solid’)
102 VisualEstat.place(x=50+100*i,y=260+19*j)
103
104 else:
105 pass
106
107 #Visualitzador del rendiment de la i n s t a l l a c i
108 EntradaRendiment = tk.Label(self.canvasEx, text="Rendiment
e x e r g t i c de la i n s t a l l a c i , [%]: ", font="Helvetica 10
bold", borderwidth=1, relief=’solid’)
109 EntradaRendiment.place(x=100, y=340)
110 VisualRendiment = tk.Label(self.canvasEx, textvariable=self.
etaex_instal, width=9, font="Helvetica 10", borderwidth=1, relief=’solid
78 Capítol 4

’, bg="white")
111 VisualRendiment.place(x=401,y=340)
112
113 BotoCalcular = tk.Button(self.canvasEx, text="Calcular", font="
Helvetica 12", command=self.CalculExergies)
114 BotoCalcular.place(relx=0.3, rely=0.8)
115
116 BotoAcceptar = tk.Button(self.canvasEx, text="Acceptar", font="
Helvetica 12", command=self.master.destroy)
117 BotoAcceptar.place(relx=0.55, rely=0.8)
118

119
120 def CalculExergies(self):
121 DadesProps = []
122 DadesCicle = []
123 Estat0, Estat1, Estat2, Estat3, Estat4 = {}, {}, {}, {}, {}
124 Estat = [Estat0, Estat1, Estat2, Estat3, Estat4] # Llista de
diccionaris d’estats. Volem algo tal com Estat[1][’T’]=T_1 (valor
n u m r i c de T1)
125
126 NomsPropietats = [0, ’P’, ’T’, ’H’, ’S’, ’E’]
127 NomsPots = ["Qcald", "Qcond", "Wturb", "Wbomb"]
128

129 Pots = {} #Diccionari de p o t n c i e s t r m i q u e s i rendiments. Pots[’


Qcald’]=Qcald (valor n u m r i c de Qcald)
130
131 FluxPot = {}
132 FluxPot[’flux’]=self.flux.get()
133 FluxPot[’Wneta’]=self.Wneta.get()
134 fluid = self.fluid.get()
135
136 for i in range(len(self.lProps)):
137 for j in range(len(self.lProps[i])-1):
138 if i!=0 and j!=0:
139 DadesProps.append((self.lProps_noms[i][j],self.lProps[i
][j].get()))
140
141 for i in range(len(self.lVars_noms)):
142 DadesCicle.append((self.lVars_noms[i], self.lVars[i].get()))
143
144 for elem in DadesProps:
145 Prop = elem[0]
146 Valor = elem[1]
147 Estat[int(Prop[2])][Prop[0]]=Valor #A cada diccionari/estat li
assignem com a clau la propietat amb el valor corresponent
148

149 for elem in DadesCicle: #Fem un diccionari de p o t n c i e s


t r m i q u e s . Pots[’Qcald’]=valor n u m r i c de Qcald
150 Nom = elem[0]
151 Valor = elem[1]
152 Pots[Nom]=Valor
153

154 Estat[0][’T’]=self.Tentorn.get()+273.15
155 Estat[0][’P’]=(self.Pentorn.get())*1e6
156 Estat[0][’H’]=PropsSI(’H’, ’T’, Estat[0][’T’], ’P’, Estat[0][’P’],
Capítol 4 79

fluid)
157 Estat[0][’S’]=PropsSI(’S’, ’T’, Estat[0][’T’], ’P’, Estat[0][’P’],
fluid)
158
159 Estat[0][’H’]=Estat[0][’H’]/1000
160 Estat[0][’S’]=Estat[0][’S’]/1000
161 T_f=self.Treactor.get()+273.15
162
163 """
164 C l c u l d’exergies
165 """
166 for i in range(5):
167 if i != 0:
168 Estat[i][’E’]=(Estat[i][’H’]-Estat[0][’H’])-Estat[0][’T’]*(
Estat[i][’S’]-Estat[0][’S’])
169 self.lex[i].set(round(Estat[i][’E’],4))
170

171 Pots[’ExQcald’]=abs(Pots[’Qcald’]*(1-(Estat[0][’T’]/T_f)))
172 Pots[’ExQcond’]=0
173 Pots[’ExWturb’]=abs(Pots[’Wturb’])
174 Pots[’ExWbomb’]=abs(Pots[’Wbomb’])
175
176 """
177 C l c u l de treballs perduts
178 """
179 Pots[’Wpcald’]=Pots[’ExQcald’]+FluxPot[’flux’]*(Estat[2][’E’]-Estat
[3][’E’])
180 Pots[’Wpcond’]=FluxPot[’flux’]*(Estat[4][’E’]-Estat[1][’E’])-Pots[’
ExQcond’]
181 Pots[’Wpturb’]=FluxPot[’flux’]*(Estat[3][’E’]-Estat[4][’E’])-Pots[’
ExWturb’]
182 Pots[’Wpbomb’]=Pots[’ExWbomb’]+FluxPot[’flux’]*(Estat[1][’E’]-Estat
[2][’E’])
183 """
184 C l c u l de rendiments exerg tics
185 """
186 Pots[’eta_ex_cald’]=1-((Pots[’Wpcald’])/(Pots[’ExQcald’]))
187 Pots[’eta_ex_turb’]=Pots[’ExWturb’]/(FluxPot[’flux’]*(Estat[3][’E’
]-Estat[4][’E’]))
188 Pots[’eta_ex_bomb’]=abs(FluxPot[’flux’]*(Estat[1][’E’]-Estat[2][’E’
]))/Pots[’ExWbomb’]
189 Pots[’eta_ex_instal’]=abs(self.Wneta.get())/Pots[’ExQcald’]
190 """
191 Actualitzem els valors
192 """
193 self.lWp[1].set(round(Pots[’Wpcald’],2))
194 self.lWp[2].set(round(Pots[’Wpcond’],2))
195 self.lWp[3].set(round(Pots[’Wpturb’],2))
196 self.lWp[4].set(round(Pots[’Wpbomb’],2))
197
198 self.letaex[1].set(round(Pots[’eta_ex_cald’]*100,2))
199 self.letaex[2].set("-")
200 self.letaex[3].set(round(Pots[’eta_ex_turb’]*100,2))
201 self.letaex[4].set(round(Pots[’eta_ex_bomb’]*100,2))
80 Capítol 4

202 self.etaex_instal.set(round(Pots[’eta_ex_instal’]*100,2))
Llistat 4.20: Codi Python de la classe AnalisiExergetic

La principal diferència respecte a la resta de la resta de codi és que en aquest cas les
variables que s’utilitzaran no s’han declarat a la classe principal. Això no suposa cap
problema perquè aquestes variables no s’han d’enviar a cap altra classe i es visualitzen en
aquesta mateixa finestra, en el visualitzador d’exergies i treballs perduts que es pot veure
a la Figura 4.6.

Respecte al càlcul d’exergies, s’ha implementat com un mètode de la classe AnalisiExer-


getic que segueix l’esquema de l’eina de càlcul descrita en aquest apartat, però de manera
molt més minimalista i simple. Com que per realitzar l’anàlisi exergètica és necessari
haver resolt el cicle prèviament, no cal fer cap mena d’iteració. La funció el que fa és fer
el canvi de variables, realitzar els càlculs aplicant les equacions 1.5, 2.8, 2.10, 2.11, 2.12,
2.13, 2.14, 2.15, 2.16 i 2.17; i desfer el canvi, actualitzant els valors de les variables de
Tkinter, per poder visualitzar els resultats.

4.3 Disseny específic dels diagrames termodinàmics


De la mateixa manera que el solucionador, les funcions per la realització dels diagrames
T-s i P-h també són mètodes de la classe principal, CicleRankine. La creació d’aquests
diagrames s’explica al Capítol 3 i en particular a l’apartat de 3.4 es dóna un exemple on
es poden diferenciar 4 etapes.

El codi per representar els diagrames del cicle bàsic de Rankine també es divideix en
aquestes 4 etapes i es pot veure al Llistat 4.21. En aquest cas però les etapes són més
simples perquè al cicle bàsic de Rankine no cal fer-li modificacions. En canvi, al cicle
de Rankine amb reescalfament intermedi cal introduir-li un codi pràcticament igual al de
l’apartat 3.4.

1 def clickedDiagTS(self, *args):


2 """
3 PART 1: Inicialitzem el diagrama
4 """
5 pp = PropertyPlot(self.fluid.get(), ’TS’, unit_system=’EUR’,
tp_limits=’ORC’)
6 pp.calc_isolines(cp.iP)
7 pp.calc_isolines(cp.iQ)
8 """
9 PART 2: Cal haver fet el c l c u l !
10 """
11 if self.sc.__len__() == 0:
12 return "ERROR"
13 """
14 PART 3: Dibuixem el diagrama amb les dades guardades
15 """
16 cicle = SimpleCompressionCycle(self.fluid.get(), ’TS’, unit_system=
’EUR’, tp_limits=’ORC’)
Capítol 4 81

17 cicle._cycle_states=self.sc
18 cicle.steps = 50
19
20 grafic = cicle.get_state_changes()
21 """
22 PART 4: Mostrem el diagrama
23 """
24 plt.close(cicle.figure)
25 pp.draw_process(grafic)
26 pp.title(’Diagrama T-s’)
27 pp.show()
28
29 def clickedDiagPH(self, *args):
30 """
31 PART 1: Inicialitzem el diagrama
32 """
33 pp = PropertyPlot(self.fluid.get(), ’PH’, unit_system=’EUR’)
34 pp.calc_isolines(cp.iT)
35 pp.calc_isolines(cp.iQ)
36 """
37 PART 2: Cal haver fet el c l c u l !
38 """
39 if self.sc.__len__() == 0:
40 return "ERROR"
41 """
42 PART 3: Dibuixem el diagrama amb les dades guardades
43 """
44 cicle = SimpleCompressionCycle(self.fluid.get(), ’PH’, unit_system=
’EUR’)
45 cicle._cycle_states=self.sc
46 cicle.steps = 50
47
48 grafic = cicle.get_state_changes()
49 """
50 PART 4: Mostrem el diagrama
51 """
52 plt.close(cicle.figure)
53 pp.draw_process(grafic)
54 pp.title(’Diagrama P-h’)
55 pp.show()
Llistat 4.21: Codi Python de la representació dels diagrames T-s i P-h

Al codi es pot veure que entre representar el diagrama T-s i P-h no hi ha quasi cap dife-
rència, tan sols la indicació que cal donar-li a les classe PropertyPlot a l’hora d’inicialitzar
el diagrama i a la classe SimpleCompressionCycle. Pel que fa a la resta és la combinació
ordenada de les funcions i classes de la llibreria CoolProp que es detallen al Capítol 3.

S’ha consultat [4], [5], [6], [7] i [8] com a fonts bibliogràfiques per la redacció
d’aquest capítol.
82 Capítol 5

Capítol 5

Manual d’usuari

Aquest capítol consisteix en el manual d’instruccions que l’usuari ha de seguir per fer
servir l’aplicació. Per aquest motiu, aquest capítol està enfocat des d’una perspectiva
diferent i pretén ser autònom i independent de la resta del projecte, de manera que a
l’usuari tan sols li calgui llegir aquest manual per aprendre a utilitzar l’aplicació.

Una de les prioritats en el disseny de l’aplicació ha estat que aquesta tingués una interfície
gràfica senzilla perquè d’aquesta manera fos fàcil de fer servir, però que alhora mantingués
la funcionalitat desitjada per fer els càlculs. Per tant, aquest manual d’usuari no serà ni
llarg ni enrevessat perquè tampoc ho és l’aplicació.

Figura 5.1: Pantalla inicial de l’aplicació


Capítol 5 83

La pantalla inicial de l’aplicació és la que es pot veure a la Figura 5.1. S’hi pot veure el
títol, l’autor i també els diferents cicles que s’han programat. Aquesta pantalla és també
una pantalla de selecció. Per accedir al cicle amb el qual es volen fer els càlculs, tan sols
cal clicar sobre la casella blanca amb el nom del cicle.

Un cop seleccionat el cicle s’obrirà la finestra corresponent al cicle escollit. Aquestes


finestres es poden veure a les Figures 6.2, 6.9, 6.15, 6.19 i 6.24. En el pròxim capítol es
donaran exemples de resolució de cadascun d’aquests cicles, però el que ha de fer l’usuari
per introduir dades, resoldre el cicle i visualitzar els resultats no depèn del cicle escollit.
Per aquest motiu en aquest manual s’explicarà en genèric.

5.1 Introducció de dades


Un cop escollit el cicle que es vol resoldre cal introduir-li les dades inicials que es disposen.
La introducció d’aquestes dades se separa en tres etapes.

Introducció del fluid de treball


Per escollir el fluid de treball cal anar al menú (la barra d’opcions de sobre de tot), clicar
Editar ⇒ Fluid i escollir el fluid que es vol que circuli pel cicle. A la Figura 5.2 es pot
veure un esquema d’aquest procés.

Figura 5.2: Selecció del fluid de treball


84 Capítol 5

Introducció de les propietats termodinàmiques de cada estat, del flux màssic


i/o de la potència neta.
Per introduir tota aquesta informació cal anar al menú, clicar Editar ⇒ Taula de propietats
i introduir totes les dades a la seva casella corresponent. Inicialment les caselles estaran
desactivades (de color gris) i no s’hi podrà escriure. Per activar-les cal clicar a la casella
del costat i marcar-la amb un check.

En aquesta pantalla, perquè l’aplicació funcioni cal introduir-li o bé el flux màssic, o bé


el treball net o bé ambdues; si no se n’introdueix cap, el cicle no es pot resoldre. Si el
problema està plantejat per unitat de massa i no es coneix el flux màssic, es pot ficar un
flux màssic d’1 kg/s i equival a fer-ho per unitat de massa.

Per altra banda, cal anar amb compte i fixar-se en les unitats indicades a la taula. Pels
cicles de Rankine i refrigeració per compressió de vapor, la temperatura és en graus
Celsius (°C) i la pressió en MPa. En canvi, pels cicles de Brayton i refrigeració per gas,
la temperatura és en Kevlin (K) i la pressió en bar.

Per últim comentar que treure el check d’una casella, queda un espai en blanc en lloc d’un
zero. Això no és un problema, és equivalent. Com que no està seleccionada a l’aplicació
no l’afecta.

A la Figura 5.3 es pot veure un esquema d’aquest procés d’introducció de dades.

Figura 5.3: Introducció de propietats termodinàmiques, flux màssic i potència neta

Introducció dels treballs, calors i rendiments de cada un dels dispositius que


configuren el cicle.
Per introduir aquests paràmetres cal clicar sobre el dibuix del dispositiu que es troba en
la representació gràfica del cicle a la pantalla principal. Aleshores s’obrirà una finestra
on es podran introduir les dades a la casella corresponent. Com en el cas anterior, per
Capítol 5 85

poder introduir la informació primer cal activar la casella amb un check. Es pot veure un
exemple d’aquest procés a la Figura 5.4.

Figura 5.4: Introducció de treballs, calors i rendiments als dispositius

5.2 Resolució del cicle


La resolució del cicle és l’etapa més senzilla. Un cop introduïdes totes les dades, només
cal clicar el botó Calcular de la pantalla principal. Aleshores, si s’han introduït suficients
dades, es resoldrà el cicle. Altrament saltarà l’advertència que es pot veure a la Figura
5.5, que significa que no s’han introduït suficients dades.

Figura 5.5: Error provocat per no haver introduït suficients dades

Un cop feta la resolució del cicle és possible modificar les dades que s’havien introduït,
repetint els passos de l’apartat anterior. D’aquesta manera es pot parametritzar una
dada, com per exemple el rendiment de la turbina, i veure com canvis de valor en aquest
paràmetre afectarien als estats termodinàmics.
86 Capítol 5

5.3 Visualització de resultats i diagrames termodinà-


mics
Un cop resolt el cicle, obtenir els diagrames termodinàmics resulta molt simple. L’únic
que cal fer és clicar el botó Obtenir i escollir el diagrama que es vol visualitzar: el T-s o
el P-h.

Cal tenir en compte que només es pot tenir la finestra d’un diagrama oberta, és a dir,
no es poden visualitzar els dos diagrames alhora, perquè sinó l’aplicació se satura i es
tanca. L’eina de visualització de diagrames és la clàssica de la llibreria Matplotlib i permet
guardar el diagrama com una imatge, fer zoom en alguna zona específica, canviar l’escala
dels eixos...

Per últim lloc, pel cicle bàsic de Rankine i pel cicle de Rankine amb reescalfament inter-
medi, existeix l’opció de fer una anàlisi exergètica, prèviament a aquesta anàlisi cal haver
resolt el cicle. Per fer aquesta anàlisi cal clicar: Obtenir ⇒ Anàlisi exergètica. I se’ns
obrirà la finestra de la Figura 5.6.

Figura 5.6: Pantalla de realització de l’anàlisi exergètica

En aquesta finestra cal introduir-li 3 dades addicionals: la temperatura de l’entorn, la


temperatura de la font i la pressió de l’entorn. I posteriorment clicar Calcular.
Capítol 6 87

Capítol 6

Anàlisi de resultats

En aquest capítol es vol demostrar la solvència de l’aplicació estudiant 5 exemples dife-


rents, un per cada un dels cicles termodinàmics simulats. Per cada un dels cicles es resoldrà
un problema sobre el cicle en qüestió. Primerament es farà a través de l’aplicació, visua-
litzant els resultats, els diagrames P-h i T-s i en particular, pels cicles de Rankine, fent
l’anàlisi exergètica. Posteriorment es farà la verificació mostrant els resultats obtinguts si
s’hagués resolt el cicle mitjançant l’EES.

6.1 Exemple 1: Cicle bàsic de Rankine


Enunciat: Un reactor nuclear dóna calor a una caldera per tal de produir vapor d’aigua
saturat a 8 MPa, que, a la vegada, s’expandeix en una turbina fins a una pressió de
0.075 bar. El vapor que surt de la turbina es refreda en un condensador d’on surt com
a aigua líquida saturada. El condensador cedeix calor a un ambient a 25°C. La potència
de la planta és de 750 MW; els rendiments isentròpics de la turbina i la bomba són,
respectivament: ηT = 0, 9 i ηB = 0, 8. Es demana analitzar termodinàmicament el procés.
Considerar el reactor com una font de calor a 315°C. T0 = 25 °C, P0 = 0, 1 MPa.

Figura 6.1: Esquema del cicle bàsic de Rankine de l’enunciat


88 Capítol 6

Resolució:

Figura 6.2: Introducció de dades del cicle bàsic de Rankine

Figura 6.3: Visualització dels resultats obtinguts un cop realitzat el càlcul


Capítol 6 89

Figura 6.4: Diagrama T-s obtingut per l’aplicació

Figura 6.5: Ampliació del diagrama T-s per diferenciar els Estats 1 i 2
90 Capítol 6

Figura 6.6: Diagrama P-h obtingut per l’aplicació

Figura 6.7: Visualització dels resultats de l’anàlisi exergètica


Capítol 6 91

Verificació de resultats amb l’EES:


A les Taules 6.1 i 6.2 es mostren els resultats obtinguts utilitzant l’EES per resoldre el
problema. Tant el problema com la resolució s’han extret de [9]. Si es comparen els
resultats de les taules amb els de les Figures 6.3 i 6.7 es pot veure com la diferència és
mínima, cosa que dóna per bons els resultats.

Estat 1 Estat 2 Estat 3 Estat 4


P [MPa] 0,0075 8 8 0,0075
T [°C] 40,30 41,02 295,08 40,30
h [kJ/kg] 168,8 178,8 2758 1885
s [kJ/kg·K] 0,5763 0,5827 5,743 6,052
x 0 - 1 0,7137
ex [kJ/kg] 1,495 9,639 1050 85,28

Taula 6.1: Taula de propietats termodinàmiques calculades amb l’EES

Qcald [kW] 2, 243E + 06 W pcald [kW] 200782


Qcond [kW] −1, 493E + 06 W pcond [kW] 72855
Wturb [kW] −758742 W pturb [kW] 80190
Wbomb [kW] 8742 W pbomb [kW] 1661

Taula 6.2: Taula de potències i treballs perduts calculats amb l’EES

6.2 Exemple 2: Cicle de Rankine amb reescalfament


intermedi
Enunciat: Considereu el següent cicle de vapor amb reescalfament. El vapor entra a
la turbina d’alta pressió a 3,5 MPa, 350°C i s’expandeix fins a 0,5 MPa i després es
reescalfa a 350°C. A continuació, el vapor s’expansiona en la turbina de baixa pressió fins
a 7,5 kPa. El líquid saturat que surt del condensador va a la bomba. Cada turbina és
adiabàtica amb un rendiment isentròpic del 85% i la bomba té un rendiment isentròpic
del 80%. Si la potència produïda per les turbines és de 100 MW. Es demana analitzar
termodinàmicament el procés. Considerar una font de calor a 900°C. T0 = 25 °C, P0 = 0, 1
MPa.

Figura 6.8: Esquema del cicle de Rankine amb reescalfament intermedi de l’enunciat
92 Capítol 6

Resolució:
Per la resolució del problema amb l’aplicació es fa la hipòtesi que el treball net és el treball
produït per les dues turbina. Si no es fes això, no es podria resoldre el problema.

També cal anar amb molt de compte en què els estats del problema tenen una numeració
diferent dels de l’aplicació. En aquest exemple es prendrà com a referència l’ordre d’estats
de l’aplicació.

Figura 6.9: Introducció de dades del cicle de Rankine amb reescalfament

Figura 6.10: Visualització dels resultats obtinguts un cop realitzat el càlcul


Capítol 6 93

Figura 6.11: Diagrama T-s obtingut per l’aplicació

Figura 6.12: Diagrama P-h obtingut per l’aplicació


94 Capítol 6

Figura 6.13: Visualització dels resultats de l’anàlisi exergètica

Verificació de resultats amb l’EES:


A les Taules 6.3 i 6.4 es mostren els resultats obtinguts utilitzant l’EES per resoldre el
problema. Tant el problema com la resolució s’han extret dels apunts del professor J.L.
Martin Godoy. Si es comparen els resultats de les taules amb els de les Figures 6.10 i
6.13 es pot veure que hi ha certes diferències, que majoritàriament no son significatives.
Això és degut a la hipòtesi que s’ha hagut de fer per resoldre el cicle. L’aplicació obté un
flux màssic de 97,49 kg/s mentre que en la resolució per EES aquest flux és de 89,9 kg/s.
En particular la diferència es nota en els treballs perduts de les turbines, que és on hi ha
l’error més gran.

Estat 1 Estat 2 Estat 3 Estat 4 Estat 5 Estat 6


P [MPa] 0,0075 3,5 3,5 0,5 0,5 0,0075
T [°C] 40,3 40,6 350 151,8 350 40,3
h [kJ/kg] 168,75 173,2 3104,8 2735,2 3168,1 2420,5
s [kJ/kg·K] 0,5763 0,5792 6,6601 6,7903 7,6346 7,7603
x 0 - - 0,994 - 0,936
ex [kJ/kg] 1,49 5,06 1123,65 715,23 896,40 111,33

Taula 6.3: Taula de propietats termodinàmiques calculades amb l’EES


Capítol 6 95

Qcald [kW] 301153 W pcald [kW] 108220


Qcond [kW] −201551 W pcond [kW] 9920
Wturb1 [kW] −33227 W pturb1 [kW] 3490
Wturb2 [kW] −67209 W pturb2 [kW] 3368
Wbomb [kW] 398, 31 W pbomb [kW] 80

Taula 6.4: Taula de potències i treballs perduts calculats amb l’EES

6.3 Exemple 3: Cicle de Brayton


Enunciat: En el compressor d’una turbina de gas ideal entren 5 m3 /s d’aire a 100 kPa
i 300K. La relació de pressions en el compressor és de 10 i l’aire entra a la turbina a
1400K. Es demana analitzar termodinàmicament el procés. El rendiment isentròpic del
compressor i la turbina és del 80%.

En aquest exemple hi haurà una diferència més significativa entre els resultats de l’aplica-
ció i els obtinguts per l’EES, en comparació amb la resta d’exemples. Això és així perquè
l’aplicació tracta l’aire com un fluid més, amb una equació d’estat donada per la llibreria
CoolProp, mentre que en l’EES els càlculs estan fets considerant l’aire com un gas ideal.

Figura 6.14: Esquema del cicle de Brayton de l’enunciat

Resolució:
Per resoldre el problema però cal treballar amb el flux màssic calculat segons l’equació
dels gasos ideals, que són 5,8 kg/s, perquè fem la comparació per un mateix flux màssic.
96 Capítol 6

Figura 6.15: Introducció de dades del cicle de Brayton

Figura 6.16: Visualització dels resultats obtinguts un cop realitzat el càlcul


Capítol 6 97

Figura 6.17: Diagrama T-s obtingut per l’aplicació

Verificació de resultats amb l’EES:


A les Taules 6.5 i 6.6 es mostren els resultats obtinguts utilitzant l’EES per resoldre el
problema. Tant el problema com la resolució s’han extret de [9]. Si es comparen els
resultats de les taules amb el de la Figura 6.16 es pot veure com tal com s’havia previst
els resultats són diferents. Tanmateix alguns paràmetres divergeixen més, com els treballs
de la turbina i el compressor i d’altres menys com el rendiments tèrmic, que al calcular-lo
amb l’EES s’obté del 25,43% i al calcular-lo amb l’aplicació s’obté del 24,93%.

Estat 1 Estat 2 Estat 3 Estat 4


P [bar] 1 10 10 1
T [K] 300 650.6 1400 858.8

Taula 6.5: Taula de propietats termodinàmiques calculades amb l’EES

Qcamb [kW] 4346


Qinter [kW] −3241
Wturb [kW] −541, 2
Wcomp [kW] 350, 6

Taula 6.6: Taula de potències calculades amb l’EES


98 Capítol 6

6.4 Exemple 4: Cicle de refrigeració per compressió


de vapor
Enunciat: En un refrigerador s’utilitza R-134a com a fluid de treball i opera en un cicle
ideal de refrigeració per compressió de vapor entre 0,14 i 0,8 MPa. Si el cabal màssic és
de 0,05 kg/s, es demana analitzar termodinàmicament el procés. Resolució:

Figura 6.18: Esquema del cicle de refrigeració per compressió de vapor de l’enunciat

Figura 6.19: Introducció de dades al cicle de refrigeració per compressió de vapor


Capítol 6 99

Figura 6.20: Visualització dels resultats obtinguts un cop realitzat el càlcul

Figura 6.21: Diagrama T-s obtingut per l’aplicació


100 Capítol 6

Figura 6.22: Diagrama P-h obtingut per l’aplicació

Verificació de resultats amb l’EES:


A les Taules 6.7 i 6.8 es mostren els resultats obtinguts utilitzant l’EES per resoldre el
problema. Tant el problema com la resolució s’han extret de [9]. Si es comparen els
resultats de les taules amb el de la Figura 6.20 es pot veure que en aquest cas la diferència
és mínima, fins i tot alguns valors coincideixen. El COP calculat amb l’EES és 3,968 i el
COP calculat amb l’aplicació és 3,9676.

Estat 1 Estat 2 Estat 3 Estat 4


P [MPa] 0,14 0,8 0,8 0,14
T [C] -19 39 31 -19
h [kJ/kg] 387,3 423,5 243,6 243,6
s [kJ/kgK] 1,74 1,74 1,15 1,175
x 1 - 0 0,3225

Taula 6.7: Taula de propietats termodinàmiques calculades amb l’EES

Qcond [kW] -8,995


Qevap [kW] 7,185
Wcomp [kW] 1,81

Taula 6.8: Taula de potències calculades amb l’EES


Capítol 6 101

6.5 Exemple 5: Cicle de refrigeració per gas


Enunciat: Es disposa d’una màquina cíclica de gas (aire) que funciona com a bomba de
calor. L’aire entra en el compressor a 1 bar i 150K i es comprimeix adiabàticament fins
a 11 bar. Després passa per un bescanviador en el qual ∆P = −1 bar i on es refreda
fins a 170K. En aquestes condicions entra en una turbina on s’expandeix adiabàticament
fins a 1,5 bar i s’escalfa fins a 150K en un intercanviador en el qual ∆P = −0, 5 bar. El
ηC = 0, 8 i el ηT = 0, 9. Es demana analitzar termodinàmicament el procés.

En aquest exemple passa el mateix que en l’exemple 3 (Cicle de Brayton) ja que també
es treballa amb aire. Per tant és possible que hi hagi certes diferències entre els resultats
obtinguts per l’aplicació i els obtinguts amb l’EES siguin diferents.

Figura 6.23: Esquema del cicle de refrigeració per gas


102 Capítol 6

Resolució:
Per resoldre el problema, com en l’enunciat no es dóna el flux màssic, es treballarà amb
un flux d’1 kg/s per tenir els resultats per unitat de massa d’aire.

Figura 6.24: Introducció de dades del cicle de Brayton

Figura 6.25: Visualització dels resultats obtinguts un cop realitzat el càlcul


Capítol 6 103

Figura 6.26: Diagrama T-s obtingut per l’aplicació

Verificació de resultats amb l’EES:


A les Taules 6.9 i 6.10 es mostren els resultats obtinguts utilitzant l’EES per resoldre
el problema. Tant el problema com la resolució s’han extret de [9]. Si es comparen
els resultats de les taules amb el de la Figura 6.25 es pot veure que malgrat en un cas
considerar l’aire gas ideal i en l’altre no, els resultats són molt semblants. El COP obtingut
per l’aplicació és 1,38 i l’obtingut per l’EES és 1,36.

Estat 1 Estat 2 Estat 3 Estat 4


P [bar] 1 10 10 1
T [K] 300 650.6 1400 858.8

Taula 6.9: Taula de propietats termodinàmiques calculades amb l’EES

QH [kW] −165
QL [kW] 44
Wturb [kW] −64, 19
Wcomp [kW] 185, 4

Taula 6.10: Taula de potències calculades amb l’EES

S’ha consultat [9] i [10] com a fonts bibliogràfiques per la redacció d’aquest capítol.
104

Pressupost

Aquest projecte està destinat a un ús acadèmic i educatiu. El que es vol aconseguir amb
aquesta aplicació és complementar la docència dels cicles termodinàmics i exemplificar
millor el seu funcionament. Tanmateix, també podria haver-se comercialitzat, ja que tot
el programari que s’ha utilitzat és codi lliure i la llibreria CoolProp permet el seu ús tant
per fins educatius com per fins comercials.

El projecte s’ha dut a terme al llarg de vint-i-una setmanes, des de la setmana del 27 de
gener del 2020 fins a la setmana del 22 de juny del 2020. A l’apartat 1.4 (Metodologia)
es detallen quines són les 4 grans fases en les quals s’ha estructurat el projecte, que com
es diu, conviuen entre elles i es realimenten. En el diagrama de Gantt de la Figura 6.27
es pot veure l’ordenació d’aquestes etapes i de les seves subetapes en la cronologia.

Figura 6.27: Diagrama de Gantt del projecte


105

Conegut el repartiment del temps invertit en el projecte, cal determinar-ne els costos. S’ha
estimat que a cada casella del diagrama de Gantt de la Figura 6.27 li corresponen 5 hores
de feina. S’ha fet així perquè s’ha considerat que és una representació més realista, ja
que no totes les setmanes s’ha treballat la mateixa quantitat d’hores. D’aquesta manera
si es conten les caselles, en total n’hi ha 80, que representen 400 hores de feina, que
vindrien a ser aproximadament 12 crèdits ECTS. En paral·lel, cal tenir en compte també
el temps que s’ha dedicat a la redacció d’informes parcials i de la memòria, que serien
aproximadament unes 20 hores.

Un altre factor a tenir en compte és que no totes les tasques tenen el mateix valor. Per
aquest motiu s’han considerat quatre tipus de feines diferents. En primer lloc, unes feines
de tipus creatiu (caselles de color verd), relacionades amb el disseny de tota l’aplicació:
tant de la interfície gràfica com del solucionador, que s’han valorat en 15 A C l’hora. En
segon lloc, les feines de tipus recerca (caselles de color vermell i taronja), que consisteixen
en investigar com transformar aquelles idees en un codi Python i també en solucionar els
errors que poden anar sorgint durant la programació. Aquestes s’han valorat en 15 A C l’ho-
ra. En tercer lloc les feines de tipus programació (caselles de color blau), que consisteixen
en escriure el codi Python, que s’han valorat en 10 A C l’hora. Finalment les feines de tipus
redacció, que consisteixen en la redacció d’informes i de la memòria final, valorades en
8AC l’hora. El valor d’aquests salaris s’ha extret de [11] suposant que s’és un professional
que acaba d’incorporar-se al mercat.

Per altra banda, cal tenir en compte les despeses materials, que en aquest cas corresponen
al cost de l’ordinador amb què s’ha realitzat tot el projecte, que és de 140 A
C.

Seguint en aquesta línia, també es calcularà el cost econòmic de l’electricitat consumida,


no perquè el cost sigui significatiu, sinó per coherència, perquè en el següent apartat
se’n calcularà la petjada ecològica. L’ordinador amb què s’ha treballat té un consum de
200W. A l’haver treballat durant 420h el consum total d’energia són 92,40 kWh. Tenint
en compte el cost de l’energia de 0,145 AC/kWh es té un cost final d’energia elèctrica de
13,4 A
C.

També s’ha de considerar el cost de la llicència acadèmica de l’EES, que s’ha utilitzat en
el capítol 6 per verificar els resultats obtinguts amb l’aplicació. El cost de la llicència de
la qual disposa la UPC correspon al cost de la llicència acadèmica, que és de 1.765 A C. [12]

Finalment també s’hauria de considerar el cost de l’assistència tècnica. Al tractar-se de la


primera versió d’una aplicació, és molt probable que presenti errors que s’hagin d’arreglar.
Tanmateix, aquest cost només tindria sentit si es volgués comercialitzar l’aplicació, i no és
el cas. A banda, la majoria d’empreses aquest tipus de costos els assumeixen, perquè és
més important la satisfacció del client i el normal és que el servei postvenda sigui gratuït.

A la Taula 6.11 es pot veure el còmput final de tot el pressupost, deixant un cost total de
l’aplicació de 7.603,40 A
C.
106

Concepte Dedicació Cost horari ( A


C/h.) Cost total ( A
C)
Tasques creatives 115 h. 15 1.725
Tasques de recerca 190 h. 15 2.850
Tasques de programació 95 h. 10 950
Tasques de redacció 20 h. 8 160
Despeses materials - - 140
Energia consumida - - 13,40
Llicència EES - - 1.765
Total 420h. 7.603,40

Taula 6.11: Taula de costos del projecte


107

Impacte ambiental

En relació amb l’impacte ambiental, el projecte consisteix a desenvolupar un software


per ordinador, de manera que no té un efecte directe sobre el medi ambient. Respecte
els recursos que s’han emprat, quasi tots son virtuals i de l’únic que se’n podria calcu-
lar l’impacte ambiental és del consum elèctric de l’ordinador, durant les hores que s’ha
desenvolupat aquest projecte.

D’acord amb el document [13] el factor d’emissions de kg de CO2 /kWh per l’empresa
ENDESA ENERGIA, S.A de l’any 2019 és de 0,27. En l’apartat anterior s’ha calculat
que el consum total d’energia ha sigut de 92,40 kWh. Per tant, en total s’han emès 25 kg
de CO2 a l’atmosfera aproximadament. Per tenir una referència, per produir un llibre de
100 pàgines s’emeten 21 kg de CO2 . [14]

Per últim, remarcar que l’aplicació és per ús docent, de manera que no té una finalitat
comercial per millorar l’eficiència dels cicles termodinàmics d’una indústria. Si aquest
fos el cas, aleshores sí que hi hauria un impacte ambiental considerable perquè els cicles
termodinàmics estudiats emeten gasos a l’atmosfera i utilitzen combustibles fòssils o nu-
clears per funcionar. Tanmateix, no forma part de l’abast del projecte perquè com s’ha
dit l’ús de l’aplicació es limita a l’àmbit acadèmic.
108

Conclusions i Línies futures

En aquest projecte s’ha descrit el desenvolupament d’una aplicació per ordinador, capaç
de simular cinc cicles termodinàmics diferents. Del programa se’n destaca una interfície
molt senzilla per l’usuari, de manera que, malgrat poder realitzar menys coses que altres
aplicacions existents del mateix estil, ho compensa sent gratuïta i tenint una interfície
intuïtiva, que no requereix cap mena de coneixement previ per fer-la servir.

Pel que fa a l’aspecte informàtic del projecte, es va decidir que s’escriuria el codi de
l’aplicació amb Python per tot el bagatge acumulat durant el grau. Per descomptat
aquests coneixements eren només el tronc indispensable a partir del qual un mateix havia
de ser capaç d’aprendre, de forma autònoma, tots aquells conceptes nous (paquets Tkinter
i CoolProp) que havien de permetre programar aquest software.

Pel que fa al vessant termodinàmic del treball, no s’aprofundeix en nous conceptes més
enllà dels vistos durant el grau, sinó que el que persegueix el projecte és millorar la
docència dels cicles termodinàmics. Per aquest motiu es van escollir cinc dels cicles més
habituals (Rankine bàsic i amb reescalfament; Brayton bàsic; i refrigeració, tant per
compressió de vapor com per gas) perquè en primer lloc, des d’un punt de vista educatiu,
són els més bàsics i els primers a explicar-se als alumnes, de manera que, es va veure
necessari que l’aplicació simulés aquests cicles, per tal que l’estudiantat comprengués
millor el seu funcionament, ja que a partir d’aquests cicles se’n construeixen la resta.
Per altra banda, des d’un punt de vista pràctic, perquè aquest projecte tenia un marge
temporal de quatre mesos i el fet que els 5 cicles tinguessin estructures semblants facilitava
la tasca.

En el treball s’inclou un manual d’usuari i un conjunt d’exemples perquè qui vulgui pugui
aprendre a fer funcionar l’aplicació, la qual cosa resulta molt senzilla, i també verificar-ne
la fiabilitat perquè en els exemples es comparen els resultats obtinguts per l’aplicació amb
els resultats obtinguts fent la resolució amb l’EES, que és l’eina utilitzada actualment a
l’assignatura de Termodinàmica.

Sobre el treball futur d’aquest projecte, en tractar-se de la primera versió d’una aplicació
es dóna per fet que apareixeran petits errors en el codi que s’hauran d’anar corregint.
Per altra banda, s’hauria de millorar l’eficiència del codi de l’eina de càlcul, ja que per
cicles amb més estats termodinàmics s’allarga molt. Des d’un punt de vista funcional
l’aplicació pot millorar-se de dues maneres diferents: la primera és anar programant cicles,
com per exemple el Brayton amb reescalfament o el Rankine amb regeneració, i afegir-los
109

a l’aplicació. Aquesta línia de treball és costosa quant a temps però no difícil, ja que la
metodologia a seguir és la mateixa que la seguida per programar els cicles anteriors. Una
segona línia de millora seria aconseguir que els cicles no s’haguessin de predefinir, sinó
que l’usuari pogués dissenyar-los. Això és un salt a escala de dificultat que requeriria
molt més temps i pot ser només amb les eines informàtiques emprades en el disseny de
l’aplicació no fos suficient.

En últim lloc, destacar que pel disseny de l’aplicació s’ha buscat un compromís entre la
funcionalitat del programa, l’ergonomia que havia de tenir la interfície de cara a l’usuari,
i el límit d’hores de feina que s’han de dedicar a un treball de fi de grau.
110

Bibliografia

[1] Çengel, Y. A., & Boles, M. A, Termodinámica (7a. ed.). McGraw Hill Intera-
mericana Editores, S.A. 2012.

[2] Morancho Llena, J. M., Apunts de termodinámica. CPDA-ETSEIB.

[3] Bell, Ian H. & Wronski, Jorrit & Quoilin, Sylvain & Lemort, Vin-
cent, Pure and Pseudo-pure Fluid Thermophysical Property Evaluation and the
Open-Source Thermophysical Property Library CoolProp. Industrial & Engineering
Chemistry Research, 53(6):2498–2508. 2014.
http://www.coolprop.org
Última consulta feta el 05/06/2020
https://github.com/CoolProp/CoolProp/wiki
Última consulta feta el 23/05/2020

[4] Lundh F., An Introduction to Tkinter. 2005.


https://effbot.org/tkinterbook
Última consulta feta el 01/04/2020

[5] Bodenseo, B. K., Python Tkinter Course. 2020.


https://www.python-course.eu/index.php
Última consulta feta el 07/06/2020

[6] Shipman, J. W., Tkinter 8.5 reference: a GUI for Python. New Mexico Tech.
Computer Center. 2013

[7] Bodnar, J., Tkinter tutorial. 2020.


http://zetcode.com/tkinter
Última consulta feta el 06/04/2020

[8] Giovanni G., Python Programming. 2020.


https://pythonprogramming.altervista.org
Última consulta feta el 11/05/2020

[9] Fernández Francos, X. & Martín Godoy J. L. & Morancho Llena, J. M.


& Ramis Juan, X. & Salla Tarrragó J. M., Termodinàmica. Tests i problemes.
Problemes resolts amb Engineering Equation Solver. CPDA-ETSEIB, 2016.
111

[10] Klein, S.A., Engineering Equation Solver for Microsoft Windows Operating Sys-
tems. F-Chart Software. 2018.

[11] WageIndicator Foundation, Tusalario.es. Función y sueldo. WageIndicator


2020.
https://tusalario.es/carrera/funcion-y-sueldo
Última consulta feta el 09/06/2020

[12] F-Chart Software, EES Academic Site Licenses. F-Chart Software 2020.
http://fchartsoftware.com/ees/academic-site-licenses.php
Última consulta feta el 10/06/2020

[13] Oficina Española del Cambio Climático, Factores de emisión. Registro de


huella de carbono, compensación y proyectos de absorción de dióxido de carbono.
Ministerio para la Transición Ecológica y el Reto Demográfico. 2020.

[14] Solans, T. & Calatayud, D. & Claret, C., 21 kg de CO2. Departament de


Medi Ambient i Habitatge, Generalitat de Catalunya. 2009.

You might also like