-
Notifications
You must be signed in to change notification settings - Fork 152
/
MoistAirUnsaturated.mo
482 lines (426 loc) · 19.4 KB
/
MoistAirUnsaturated.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
within Buildings.Media.GasesConstantDensity;
package MoistAirUnsaturated
"Package with moist air model that decouples pressure and temperature and that has no liquid water"
extends Modelica.Media.Interfaces.PartialCondensingGases(
mediumName="MoistAirPTDecoupledUnsaturated",
substanceNames={"water", "air"},
final reducedX=true,
final singleState=true,
reference_X={0.01,0.99},
fluidConstants = {Modelica.Media.IdealGases.Common.FluidData.H2O,
Modelica.Media.IdealGases.Common.FluidData.N2});
constant Integer Water=1
"Index of water (in substanceNames, massFractions X, etc.)";
constant Integer Air=2
"Index of air (in substanceNames, massFractions X, etc.)";
constant Real k_mair = steam.MM/dryair.MM "ratio of molar weights";
constant Buildings.Media.PerfectGases.Common.DataRecord dryair=
Buildings.Media.PerfectGases.Common.SingleGasData.Air;
constant Buildings.Media.PerfectGases.Common.DataRecord steam=
Buildings.Media.PerfectGases.Common.SingleGasData.H2O;
import SI = Modelica.SIunits;
constant AbsolutePressure pStp = 101325 "Pressure for which dStp is defined";
constant Density dStp = 1.2 "Fluid density at pressure pStp";
redeclare record extends ThermodynamicState(
p(start=p_default),
T(start=T_default),
X(start=X_default)) "ThermodynamicState record for moist air"
end ThermodynamicState;
redeclare replaceable model extends BaseProperties(
T(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
p(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
Xi(each stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
final standardOrderComponents=true)
/* p, T, X = X[Water] are used as preferred states, since only then all
other quantities can be computed in a recursive sequence.
If other variables are selected as states, static state selection
is no longer possible and non-linear algebraic equations occur.
*/
MassFraction x_water "Mass of total water/mass of dry air";
Real phi "Relative humidity";
protected
constant SI.MolarMass[2] MMX = {steam.MM,dryair.MM}
"Molar masses of components";
// MassFraction X_liquid "Mass fraction of liquid water";
MassFraction X_steam "Mass fraction of steam water";
MassFraction X_air "Mass fraction of air";
MassFraction X_sat
"Steam water mass fraction of saturation boundary in kg_water/kg_moistair";
MassFraction x_sat
"Steam water mass content of saturation boundary in kg_water/kg_dryair";
AbsolutePressure p_steam_sat "Partial saturation pressure of steam";
equation
assert(T >= 200.0 and T <= 423.15, "
Temperature T is not in the allowed range
200.0 K <= (T ="
+ String(T) + " K) <= 423.15 K
required from medium model \"" + mediumName + "\".");
/*
assert(Xi[Water] <= X_sat, "The medium model '" + mediumName + "' must not be saturated.\n"
+ "To model a saturated medium, use 'Buildings.Media.GasesConstantDensity.MoistAir' instead of this medium.\n"
+ " T = " + String(T) + "\n"
+ " X_sat = " + String(X_sat) + "\n"
+ " Xi[Water] = " + String(Xi[Water]) + "\n"
+ " phi = " + String(phi) + "\n"
+ " p = " + String(p));
*/
MM = 1/(Xi[Water]/MMX[Water]+(1.0-Xi[Water])/MMX[Air]);
p_steam_sat = min(saturationPressure(T),0.999*p);
X_sat = min(p_steam_sat * k_mair/max(100*Modelica.Constants.eps, p - p_steam_sat)*(1 - Xi[Water]), 1.0)
"Water content at saturation with respect to actual water content";
// X_liquid = max(Xi[Water] - X_sat, 0.0);
// X_steam = Xi[Water]-X_liquid;
X_steam = Xi[Water]; // There is no liquid in this medium model
X_air = 1-Xi[Water];
h = specificEnthalpy_pTX(p,T,Xi);
R = dryair.R*(1 - Xi[Water]) + steam.R*Xi[Water];
// Equation for ideal gas, from h=u+p*v and R*T=p*v, from which follows that u = h-R*T.
// u = h-R*T;
// However, in this medium, the gas law is d=dStp (=constant), from which follows using h=u+pv that
// u= h-p*v = h-p/d = h-p/dStp
u = h-p/dStp;
d = dStp;// = p/pStp;
/* Note, u and d are computed under the assumption that the volume of the liquid
water is neglible with respect to the volume of air and of steam
*/
state.p = p;
state.T = T;
state.X = X;
// this x_steam is water load / dry air!!!!!!!!!!!
x_sat = k_mair*p_steam_sat/max(100*Modelica.Constants.eps,p - p_steam_sat);
x_water = Xi[Water]/max(X_air,100*Modelica.Constants.eps);
phi = p/p_steam_sat*Xi[Water]/(Xi[Water] + k_mair*X_air);
end BaseProperties;
function Xsaturation = Buildings.Media.PerfectGases.MoistAir.Xsaturation
"Steam water mass fraction of saturation boundary in kg_water/kg_moistair";
redeclare function setState_pTX
"Thermodynamic state as function of p, T and composition X"
extends Buildings.Media.PerfectGases.MoistAir.setState_pTX;
end setState_pTX;
redeclare function setState_phX
"Thermodynamic state as function of p, h and composition X"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input SpecificEnthalpy h "Specific enthalpy";
input MassFraction X[:] "Mass fractions";
output ThermodynamicState state;
algorithm
state := if size(X,1) == nX then
ThermodynamicState(p=p,T=T_phX(p,h,X),X=X) else
ThermodynamicState(p=p,T=T_phX(p,h,cat(1,X,{1-sum(X)})), X=cat(1,X,{1-sum(X)}));
// ThermodynamicState(p=p,T=T_phX(p,h,X), X=cat(1,X,{1-sum(X)}));
annotation (Documentation(info="<html>
Function to set the state for given pressure, enthalpy and species concentration.
This function needed to be reimplemented in order for the medium model to use
the implementation of <code>T_phX</code> provided by this package as opposed to the
implementation provided by its parent package.
</html>"));
end setState_phX;
redeclare function setState_dTX
"Thermodynamic state as function of d, T and composition X"
extends Modelica.Icons.Function;
input Density d "density";
input Temperature T "Temperature";
input MassFraction X[:]=reference_X "Mass fractions";
output ThermodynamicState state "Thermodynamic state";
algorithm
ModelicaError("The function 'setState_dTX' must not be used in GasesConstantDensity as
in this medium model, the pressure cannot be determined from the density.\n");
state :=setState_pTX(pStp, T, X);
end setState_dTX;
redeclare function gasConstant
"Gas constant (computation neglects liquid fraction)"
extends Buildings.Media.PerfectGases.MoistAir.gasConstant;
end gasConstant;
function saturationPressureLiquid =
Buildings.Media.PerfectGases.MoistAir.saturationPressureLiquid
"Saturation curve valid for 273.16 <= T <= 373.16. Outside of these limits a (less accurate) result is returned"
annotation(smoothOrder=5,derivative=saturationPressureLiquid_der);
function saturationPressureLiquid_der =
Buildings.Media.PerfectGases.MoistAir.saturationPressureLiquid_der
"Saturation curve valid for 273.16 <= T <= 373.16. Outside of these limits a (less accurate) result is returned"
annotation (Documentation(info="<html>
This function declares the first derivative of
<a href=\"modelica://Buildings.Media.GasesConstantDensity.MoistAir.saturationPressureLiquid\">
Buildings.Media.GasesConstantDensity.MoistAir.saturationPressureLiquid</a>.
It is required since otherwise, Dymola 7.3 cannot find the derivative of the inherited function
<code>saturationPressureLiquid</code>.
</html>"));
function sublimationPressureIce =
Buildings.Media.PerfectGases.MoistAir.sublimationPressureIce
"Saturation curve valid for 223.16 <= T <= 273.16. Outside of these limits a (less accurate) result is returned";
redeclare function extends saturationPressure
"Saturation curve valid for 223.16 <= T <= 373.16 (and slightly outside with less accuracy)"
algorithm
psat := Buildings.Utilities.Math.Functions.spliceFunction(
saturationPressureLiquid(Tsat),sublimationPressureIce(Tsat),Tsat-273.16,1.0);
annotation(Inline=false,smoothOrder=5);
end saturationPressure;
redeclare function pressure "Gas pressure"
extends Buildings.Media.PerfectGases.MoistAir.pressure;
end pressure;
redeclare function temperature "Gas temperature"
extends Buildings.Media.PerfectGases.MoistAir.temperature;
end temperature;
redeclare function density "Gas density"
extends Modelica.Icons.Function;
input ThermodynamicState state;
output Density d "Density";
algorithm
d := dStp;
end density;
redeclare function specificEntropy
"Specific entropy (liquid part neglected, mixing entropy included)"
extends Buildings.Media.PerfectGases.MoistAir.specificEntropy;
end specificEntropy;
redeclare function extends enthalpyOfVaporization
"Enthalpy of vaporization of water"
algorithm
r0 := 2501014.5;
end enthalpyOfVaporization;
function HeatCapacityOfWater
"Specific heat capacity of water (liquid only) which is constant"
extends Modelica.Icons.Function;
input Temperature T;
output SpecificHeatCapacity cp_fl;
algorithm
cp_fl := 4186;
end HeatCapacityOfWater;
redeclare replaceable function extends enthalpyOfLiquid
"Enthalpy of liquid (per unit mass of liquid) which is linear in the temperature"
algorithm
h := (T - 273.15)*4186;
annotation(smoothOrder=5, derivative=der_enthalpyOfLiquid);
end enthalpyOfLiquid;
replaceable function der_enthalpyOfLiquid
"Temperature derivative of enthalpy of liquid per unit mass of liquid"
extends Modelica.Icons.Function;
input Temperature T "temperature";
input Real der_T "temperature derivative";
output Real der_h "derivative of liquid enthalpy";
algorithm
der_h := 4186*der_T;
end der_enthalpyOfLiquid;
redeclare function enthalpyOfCondensingGas
"Enthalpy of steam per unit mass of steam"
extends Modelica.Icons.Function;
input Temperature T "temperature";
output SpecificEnthalpy h "steam enthalpy";
algorithm
h := (T-273.15) * steam.cp + Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.enthalpyOfVaporization(T);
annotation(smoothOrder=5, derivative=der_enthalpyOfCondensingGas);
end enthalpyOfCondensingGas;
replaceable function der_enthalpyOfCondensingGas
"Derivative of enthalpy of steam per unit mass of steam"
extends Modelica.Icons.Function;
input Temperature T "temperature";
input Real der_T "temperature derivative";
output Real der_h "derivative of steam enthalpy";
algorithm
der_h := steam.cp*der_T;
end der_enthalpyOfCondensingGas;
redeclare replaceable function extends enthalpyOfGas
"Enthalpy of gas mixture per unit mass of gas mixture"
algorithm
h := Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.enthalpyOfCondensingGas(T)*X[Water]
+ Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.enthalpyOfDryAir(T)*(1.0-X[Water]);
end enthalpyOfGas;
replaceable function enthalpyOfDryAir
"Enthalpy of dry air per unit mass of dry air"
extends Modelica.Icons.Function;
input Temperature T "temperature";
output SpecificEnthalpy h "dry air enthalpy";
algorithm
h := (T - 273.15)*dryair.cp;
annotation(smoothOrder=5, derivative=der_enthalpyOfDryAir);
end enthalpyOfDryAir;
replaceable function der_enthalpyOfDryAir
"Derivative of enthalpy of dry air per unit mass of dry air"
extends Modelica.Icons.Function;
input Temperature T "temperature";
input Real der_T "temperature derivative";
output Real der_h "derivative of dry air enthalpy";
algorithm
der_h := dryair.cp*der_T;
end der_enthalpyOfDryAir;
redeclare function specificHeatCapacityCp =
Buildings.Media.PerfectGases.MoistAir.specificHeatCapacityCp
"Specific heat capacity of gas mixture at constant pressure";
redeclare function specificHeatCapacityCv =
Buildings.Media.PerfectGases.MoistAir.specificHeatCapacityCv
"Specific heat capacity of gas mixture at constant volume";
redeclare function extends dynamicViscosity "dynamic viscosity of dry air"
algorithm
eta := 1.85E-5;
end dynamicViscosity;
redeclare function extends thermalConductivity
"Thermal conductivity of dry air as a polynomial in the temperature"
import Modelica.Media.Incompressible.TableBased.Polynomials_Temp;
algorithm
lambda := Polynomials_Temp.evaluate({(-4.8737307422969E-008), 7.67803133753502E-005, 0.0241814385504202},
Modelica.SIunits.Conversions.to_degC(state.T));
end thermalConductivity;
redeclare function extends specificEnthalpy "Specific enthalpy"
algorithm
h := Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.h_pTX(state.p, state.T, state.X);
end specificEnthalpy;
redeclare function extends specificInternalEnergy "Specific internal energy"
extends Modelica.Icons.Function;
algorithm
u := Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.h_pTX(state.p,state.T,state.X) - state.p/dStp;
end specificInternalEnergy;
redeclare function extends specificGibbsEnergy "Specific Gibbs energy"
extends Modelica.Icons.Function;
algorithm
g := Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.h_pTX(state.p,state.T,state.X) - state.T*specificEntropy(state);
end specificGibbsEnergy;
redeclare function extends specificHelmholtzEnergy "Specific Helmholtz energy"
extends Modelica.Icons.Function;
algorithm
f := Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.h_pTX(state.p,state.T,state.X)
- gasConstant(state)*state.T
- state.T*Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.specificEntropy(state);
end specificHelmholtzEnergy;
////////////////////////////////////////////////////////////////////////////
function h_pTX
"Compute specific enthalpy from pressure, temperature and mass fraction"
extends Modelica.Icons.Function;
input SI.Pressure p "Pressure";
input SI.Temperature T "Temperature";
input SI.MassFraction X[nX] "Mass fractions of moist air";
output SI.SpecificEnthalpy h "Specific enthalpy at p, T, X";
protected
SI.AbsolutePressure p_steam_sat "Partial saturation pressure of steam";
SI.MassFraction x_sat "steam water mass fraction of saturation boundary";
SI.SpecificEnthalpy hDryAir "Enthalpy of dry air";
algorithm
p_steam_sat :=saturationPressure(T);
x_sat :=k_mair*p_steam_sat/(p - p_steam_sat);
/*
assert(X[Water] < x_sat/(1 + x_sat), "The medium model '" + mediumName + "' must not be saturated.\n"
+ "To model a saturated medium, use 'Buildings.Media.GasesConstantDensity.MoistAir' instead of this medium.\n"
+ " T = " + String(T) + "\n"
+ " x_sat = " + String(x_sat) + "\n"
+ " X[Water] = " + String(X[Water]) + "\n"
+ " phi = " + String(X[Water]/((x_sat)/(1+x_sat))) + "\n"
+ " p = " + String(p));
*/
h := (T - 273.15)*dryair.cp * (1 - X[Water]) + ((T-273.15) * steam.cp + 2501014.5) * X[Water];
annotation(smoothOrder=5);
end h_pTX;
function T_phX "Compute temperature from specific enthalpy and mass fraction"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input SpecificEnthalpy h "specific enthalpy";
input MassFraction[:] X "mass fractions of composition";
output Temperature T "temperature";
protected
SI.AbsolutePressure p_steam_sat "Partial saturation pressure of steam";
SI.MassFraction x_sat "steam water mass fraction of saturation boundary";
algorithm
T := 273.15 + (h-2501014.5 * X[Water])/(dryair.cp * (1 - X[Water])+steam.cp*X[Water]);
// Check for saturation
p_steam_sat :=saturationPressure(T);
x_sat :=k_mair*p_steam_sat/(p - p_steam_sat);
/*
assert(X[Water] < x_sat/(1 + x_sat), "The medium model '" + mediumName + "' must not be saturated.\n"
+ "To model a saturated medium, use 'Buildings.Media.GasesConstantDensity.MoistAir' instead of this medium.\n"
+ " T = " + String(T) + "\n"
+ " x_sat = " + String(x_sat) + "\n"
+ " X[Water] = " + String(X[Water]) + "\n"
+ " phi = " + String(X[Water]/((x_sat)/(1+x_sat))) + "\n"
+ " p = " + String(p));
*/
annotation(smoothOrder=5);
end T_phX;
redeclare function enthalpyOfNonCondensingGas
"Enthalpy of non-condensing gas per unit mass"
extends Modelica.Icons.Function;
input Temperature T "temperature";
output SpecificEnthalpy h "enthalpy";
algorithm
h := enthalpyOfDryAir(T);
annotation(smoothOrder=5, derivative=der_enthalpyOfNonCondensingGas);
end enthalpyOfNonCondensingGas;
replaceable function der_enthalpyOfNonCondensingGas
"Derivative of enthalpy of non-condensing gas per unit mass"
extends Modelica.Icons.Function;
input Temperature T "temperature";
input Real der_T "temperature derivative";
output Real der_h "derivative of steam enthalpy";
algorithm
der_h := der_enthalpyOfDryAir(T, der_T);
end der_enthalpyOfNonCondensingGas;
annotation (preferredView="info", Documentation(info="<html>
<p>
This is a medium model that is identical to
<a href=\"modelica://Buildings.Media.GasesConstantDensity.MoistAir\">
Buildings.Media.GasesConstantDensity.MoistAir</a>, but
in this model, the air must not be saturated. If the air is saturated,
use the medium model
<a href=\"modelica://Buildings.Media.GasesConstantDensity.MoistAir\">
Buildings.Media.GasesConstantDensity.MoistAir</a> instead of this one.
</p>
<p>
This medium model has been added to allow an explicit computation of
the function
<code>T_phX</code> so that it is once differentiable in <code>h</code>
with a continuous derivative. This allows obtaining an analytic
expression for the Jacobian, and therefore simplifies the computation
of initial conditions that can be numerically challenging for
thermo-fluid systems.
</p>
<p>
This new formulation often leads to smaller systems of nonlinear equations
because it allows to invert the function <code>T_phX</code> analytically.
</p>
</html>", revisions="<html>
<ul>
<li>
March 29, 2013, by Michael Wetter:<br/>
Added <code>final standardOrderComponents=true</code> in the
<code>BaseProperties</code> declaration. This avoids an error
when models are checked in Dymola 2014 in the pedenatic mode.
</li>
<li>
April 12, 2012, by Michael Wetter:<br/>
Added keyword <code>each</code> to <code>Xi(stateSelect=...</code>.
</li>
<li>
April 4, 2012, by Michael Wetter:<br/>
Added redeclaration of <code>ThermodynamicState</code> to avoid a warning
during model check and translation.
</li>
<li>
August 3, 2011, by Michael Wetter:<br/>
Fixed bug in <code>u=h-R*T</code>, which is only valid for ideal gases.
For this medium, the function is <code>u=h-p/dStp</code>.
</li>
<li>
August 2, 2011, by Michael Wetter:<br/>
Fixed error in the function <code>density</code> which returned a non-constant density,
and added a call to <code>ModelicaError(...)</code> in <code>setState_dTX</code> since this
function cannot assign the medium pressure based on the density (as density is a constant
in this model).
</li>
<li>
January 27, 2010, by Michael Wetter:<br/>
Fixed bug in <code>else</code> branch of function <code>setState_phX</code>
that lead to a run-time error when the constructor of this function was called.
</li>
<li>
January 22, 2010, by Michael Wetter:<br/>
Added implementation of function
<a href=\"modelica://Buildings.Media.GasesConstantDensity.MoistAirUnsaturated.enthalpyOfNonCondensingGas\">
enthalpyOfNonCondensingGas</a> and its derivative.
<li>
January 13, 2010, by Michael Wetter:<br/>
Fixed implementation of derivative functions.
</li>
<li>
August 28, 2008, by Michael Wetter:<br/>
First implementation.
</li>
</ul>
</html>"));
end MoistAirUnsaturated;