-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathDrumBoiler.mo
6336 lines (5883 loc) · 378 KB
/
DrumBoiler.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
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package ModelicaServices "ModelicaServices (OpenModelica implementation) - Models and functions used in the Modelica Standard Library requiring a tool specific implementation"
extends Modelica.Icons.Package;
package Machine "Machine dependent constants"
extends Modelica.Icons.Package;
final constant Real eps = 1e-15 "Biggest number such that 1.0 + eps = 1.0";
final constant Real small = 1e-60 "Smallest number such that small and -small are representable on the machine";
final constant Real inf = 1e60 "Biggest Real number such that inf and -inf are representable on the machine";
final constant Integer Integer_inf = OpenModelica.Internal.Architecture.integerMax() "Biggest Integer number such that Integer_inf and -Integer_inf are representable on the machine";
end Machine;
annotation(version = "4.0.0", versionDate = "2020-06-04", dateModified = "2020-06-04 11:00:00Z");
end ModelicaServices;
package Modelica "Modelica Standard Library - Version 4.0.0"
extends Modelica.Icons.Package;
package Blocks "Library of basic input/output control blocks (continuous, discrete, logical, table blocks)"
extends Modelica.Icons.Package;
import Modelica.Units.SI;
package Continuous "Library of continuous control blocks with internal states"
import Modelica.Blocks.Interfaces;
extends Modelica.Icons.Package;
block PI "Proportional-Integral controller"
import Modelica.Blocks.Types.Init;
parameter Real k(unit = "1") = 1 "Gain";
parameter SI.Time T(start = 1, min = Modelica.Constants.small) "Time Constant (T>0 required)";
parameter Init initType = Init.NoInit "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" annotation(Evaluate = true);
parameter Real x_start = 0 "Initial or guess value of state";
parameter Real y_start = 0 "Initial value of output";
extends Interfaces.SISO;
output Real x(start = x_start) "State of block";
initial equation
if initType == Init.SteadyState then
der(x) = 0;
elseif initType == Init.InitialState then
x = x_start;
elseif initType == Init.InitialOutput then
y = y_start;
end if;
equation
der(x) = u/T;
y = k*(x + u);
end PI;
end Continuous;
package Interfaces "Library of connectors and partial models for input/output blocks"
extends Modelica.Icons.InterfacesPackage;
connector RealInput = input Real "'input Real' as connector";
connector RealOutput = output Real "'output Real' as connector";
partial block SO "Single Output continuous control block"
extends Modelica.Blocks.Icons.Block;
RealOutput y "Connector of Real output signal";
end SO;
partial block SISO "Single Input Single Output continuous control block"
extends Modelica.Blocks.Icons.Block;
RealInput u "Connector of Real input signal";
RealOutput y "Connector of Real output signal";
end SISO;
partial block SignalSource "Base class for continuous signal source"
extends SO;
parameter Real offset = 0 "Offset of output signal y";
parameter SI.Time startTime = 0 "Output y = offset for time < startTime";
end SignalSource;
end Interfaces;
package Math "Library of Real mathematical functions as input/output blocks"
import Modelica.Blocks.Interfaces;
extends Modelica.Icons.Package;
block Gain "Output the product of a gain value with the input signal"
parameter Real k(start = 1, unit = "1") "Gain value multiplied with input signal";
Interfaces.RealInput u "Input signal connector";
Interfaces.RealOutput y "Output signal connector";
equation
y = k*u;
end Gain;
block Feedback "Output difference between commanded and feedback input"
Interfaces.RealInput u1 "Commanded input";
Interfaces.RealInput u2 "Feedback input";
Interfaces.RealOutput y;
equation
y = u1 - u2;
end Feedback;
end Math;
package Nonlinear "Library of discontinuous or non-differentiable algebraic control blocks"
import Modelica.Blocks.Interfaces;
extends Modelica.Icons.Package;
block Limiter "Limit the range of a signal"
parameter Real uMax(start = 1) "Upper limits of input signals";
parameter Real uMin = -uMax "Lower limits of input signals";
parameter Boolean strict = false "= true, if strict limits with noEvent(..)" annotation(Evaluate = true);
parameter Types.LimiterHomotopy homotopyType = Modelica.Blocks.Types.LimiterHomotopy.Linear "Simplified model for homotopy-based initialization" annotation(Evaluate = true);
extends Interfaces.SISO;
protected
Real simplifiedExpr "Simplified expression for homotopy-based initialization";
equation
assert(uMax >= uMin, "Limiter: Limits must be consistent. However, uMax (=" + String(uMax) + ") < uMin (=" + String(uMin) + ")");
simplifiedExpr = (if homotopyType == Types.LimiterHomotopy.Linear then u else if homotopyType == Types.LimiterHomotopy.UpperLimit then uMax else if homotopyType == Types.LimiterHomotopy.LowerLimit then uMin else 0);
if strict then
if homotopyType == Types.LimiterHomotopy.NoHomotopy then
y = smooth(0, noEvent(if u > uMax then uMax else if u < uMin then uMin else u));
else
y = homotopy(actual = smooth(0, noEvent(if u > uMax then uMax else if u < uMin then uMin else u)), simplified = simplifiedExpr);
end if;
else
if homotopyType == Types.LimiterHomotopy.NoHomotopy then
y = smooth(0, if u > uMax then uMax else if u < uMin then uMin else u);
else
y = homotopy(actual = smooth(0, if u > uMax then uMax else if u < uMin then uMin else u), simplified = simplifiedExpr);
end if;
end if;
end Limiter;
end Nonlinear;
package Sources "Library of signal source blocks generating Real, Integer and Boolean signals"
import Modelica.Blocks.Interfaces;
extends Modelica.Icons.SourcesPackage;
block Constant "Generate constant signal of type Real"
parameter Real k(start = 1) "Constant output value";
extends Interfaces.SO;
equation
y = k;
end Constant;
block TimeTable "Generate a (possibly discontinuous) signal by linear interpolation in a table"
parameter Real[:, 2] table = fill(0.0, 0, 2) "Table matrix (time = first column; e.g., table=[0, 0; 1, 1; 2, 4])";
parameter SI.Time timeScale(min = Modelica.Constants.eps) = 1 "Time scale of first table column" annotation(Evaluate = true);
extends Interfaces.SignalSource;
parameter SI.Time shiftTime = startTime "Shift time of first table column";
protected
discrete Real a "Interpolation coefficient a of actual interval (y=a*x+b)";
discrete Real b "Interpolation coefficient b of actual interval (y=a*x+b)";
Integer last(start = 1) "Last used lower grid index";
discrete SI.Time nextEvent(start = 0, fixed = true) "Next event instant";
discrete Real nextEventScaled(start = 0, fixed = true) "Next scaled event instant";
Real timeScaled "Scaled time";
function getInterpolationCoefficients "Determine interpolation coefficients and next time event"
extends Modelica.Icons.Function;
input Real[:, 2] table "Table for interpolation";
input Real offset "y-offset";
input Real startTimeScaled "Scaled time-offset";
input Real timeScaled "Actual scaled time instant";
input Integer last "Last used lower grid index";
input Real TimeEps "Relative epsilon to check for identical time instants";
input Real shiftTimeScaled "Time shift";
output Real a "Interpolation coefficient a (y=a*x + b)";
output Real b "Interpolation coefficient b (y=a*x + b)";
output Real nextEventScaled "Next scaled event instant";
output Integer next "New lower grid index";
protected
Integer columns = 2 "Column to be interpolated";
Integer ncol = 2 "Number of columns to be interpolated";
Integer nrow = size(table, 1) "Number of table rows";
Integer next0;
Real tp;
Real dt;
algorithm
next := last;
nextEventScaled := timeScaled - TimeEps*abs(timeScaled);
tp := timeScaled + TimeEps*abs(timeScaled);
if tp < startTimeScaled then
nextEventScaled := startTimeScaled;
a := 0;
b := offset;
elseif nrow < 2 then
a := 0;
b := offset + table[1, columns];
else
tp := tp - shiftTimeScaled;
while next < nrow and tp >= table[next, 1] loop
next := next + 1;
end while;
if next < nrow then
nextEventScaled := shiftTimeScaled + table[next, 1];
else
end if;
if next == 1 then
next := 2;
else
end if;
next0 := next - 1;
dt := table[next, 1] - table[next0, 1];
if dt <= TimeEps*abs(table[next, 1]) then
a := 0;
b := offset + table[next, columns];
else
a := (table[next, columns] - table[next0, columns])/dt;
b := offset + table[next0, columns] - a*table[next0, 1];
end if;
end if;
b := b - a*shiftTimeScaled;
end getInterpolationCoefficients;
equation
assert(size(table, 1) > 0, "No table values defined.");
timeScaled = time/timeScale;
y = a*timeScaled + b;
algorithm
if noEvent(size(table, 1) > 1) then
assert(not (table[1, 1] > 0.0 or table[1, 1] < 0.0), "The first point in time has to be set to 0, but is table[1,1] = " + String(table[1, 1]));
else
end if;
when {time >= pre(nextEvent), initial()} then
(a, b, nextEventScaled, last) := getInterpolationCoefficients(table, offset, startTime/timeScale, timeScaled, last, 100*Modelica.Constants.eps, shiftTime/timeScale);
nextEvent := nextEventScaled*timeScale;
end when;
end TimeTable;
end Sources;
package Types "Library of constants, external objects and types with choices, especially to build menus"
extends Modelica.Icons.TypesPackage;
type Init = enumeration(NoInit "No initialization (start values are used as guess values with fixed=false)", SteadyState "Steady state initialization (derivatives of states are zero)", InitialState "Initialization with initial states", InitialOutput "Initialization with initial outputs (and steady state of the states if possible)") "Enumeration defining initialization of a block" annotation(Evaluate = true);
type LimiterHomotopy = enumeration(NoHomotopy "Homotopy is not used", Linear "Simplified model without limits", UpperLimit "Simplified model fixed at upper limit", LowerLimit "Simplified model fixed at lower limit") "Enumeration defining use of homotopy in limiter components" annotation(Evaluate = true);
end Types;
package Icons "Icons for Blocks"
extends Modelica.Icons.IconsPackage;
partial block Block "Basic graphical layout of input/output block" end Block;
end Icons;
end Blocks;
package Fluid "Library of 1-dim. thermo-fluid flow models using the Modelica.Media media description"
extends Modelica.Icons.Package;
import Modelica.Units.SI;
import Cv = Modelica.Units.Conversions;
package Examples "Demonstration of the usage of the library"
extends Modelica.Icons.ExamplesPackage;
package DrumBoiler "Drum boiler example, see Franke, Rode, Krüger: On-line Optimization of Drum Boiler Startup, 3rd International Modelica Conference, Linköping, 2003"
extends Modelica.Icons.ExamplesPackage;
model DrumBoiler "Complete drum boiler model, including evaporator and supplementary components"
extends Modelica.Icons.Example;
parameter Boolean use_inputs = false "= true, if external inputs are used, otherwise use test data contained internally";
Modelica.Fluid.Examples.DrumBoiler.BaseClasses.EquilibriumDrumBoiler evaporator(m_D = 300e3, cp_D = 500, V_t = 100, V_l_start = 67, redeclare package Medium = Modelica.Media.Water.StandardWater, energyDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, p_start = 100000);
Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow furnace;
Modelica.Fluid.Sources.FixedBoundary sink(nPorts = 1, p = Cv.from_bar(0.5), redeclare package Medium = Modelica.Media.Water.StandardWaterOnePhase, T = 500);
Modelica.Fluid.Sensors.MassFlowRate massFlowRate(redeclare package Medium = Modelica.Media.Water.StandardWater);
Modelica.Fluid.Sensors.Temperature temperature(redeclare package Medium = Modelica.Media.Water.StandardWater);
Modelica.Fluid.Sensors.Pressure pressure(redeclare package Medium = Modelica.Media.Water.StandardWater);
Modelica.Blocks.Continuous.PI controller(T = 120, k = 10, initType = Modelica.Blocks.Types.Init.InitialState);
Modelica.Fluid.Sources.MassFlowSource_h pump(nPorts = 1, h = 5e5, redeclare package Medium = Modelica.Media.Water.StandardWater, use_m_flow_in = true);
Modelica.Blocks.Math.Feedback feedback;
Modelica.Blocks.Sources.Constant levelSetPoint(k = 67);
Modelica.Blocks.Interfaces.RealOutput T_S(final unit = "degC") "Steam temperature";
Modelica.Blocks.Interfaces.RealOutput p_S(final unit = "bar") "Steam pressure";
Modelica.Blocks.Interfaces.RealOutput qm_S(unit = "kg/s") "Steam flow rate";
Modelica.Blocks.Interfaces.RealOutput V_l(unit = "m3") "Liquid volume inside drum";
Modelica.Blocks.Math.Gain MW2W(k = 1e6);
Modelica.Blocks.Math.Gain Pa2bar(k = 1e-5);
Modelica.Thermal.HeatTransfer.Celsius.FromKelvin K2degC;
Modelica.Blocks.Nonlinear.Limiter limiter(uMin = 0, uMax = 500);
Modelica.Fluid.Valves.ValveLinear SteamValve(redeclare package Medium = Modelica.Media.Water.StandardWater, dp_nominal = 9000000, m_flow_nominal = 180);
inner Modelica.Fluid.System system;
Modelica.Blocks.Sources.TimeTable q_F_Tab(table = [0, 0; 3600, 400; 7210, 400]) if not use_inputs;
Modelica.Blocks.Sources.TimeTable Y_Valve_Tab(table = [0, 0; 900, 1; 7210, 1]) if not use_inputs;
Blocks.Interfaces.RealInput q_F(unit = "MW") if use_inputs "Fuel flow rate";
Blocks.Interfaces.RealInput Y_Valve if use_inputs "Valve opening";
equation
connect(furnace.port, evaporator.heatPort);
connect(controller.u, feedback.y);
connect(massFlowRate.m_flow, qm_S);
connect(evaporator.V, V_l);
connect(MW2W.y, furnace.Q_flow);
connect(pressure.p, Pa2bar.u);
connect(Pa2bar.y, p_S);
connect(K2degC.Celsius, T_S);
connect(controller.y, limiter.u);
connect(limiter.y, pump.m_flow_in);
connect(temperature.T, K2degC.Kelvin);
connect(pressure.port, massFlowRate.port_a);
connect(pump.ports[1], evaporator.port_a);
connect(massFlowRate.port_b, SteamValve.port_a);
connect(SteamValve.port_b, sink.ports[1]);
connect(evaporator.port_b, massFlowRate.port_a);
connect(temperature.port, massFlowRate.port_a);
connect(q_F_Tab.y, MW2W.u);
connect(Y_Valve_Tab.y, SteamValve.opening);
connect(q_F, MW2W.u);
connect(Y_Valve, SteamValve.opening);
connect(evaporator.V, feedback.u2);
connect(levelSetPoint.y, feedback.u1);
annotation(experiment(StopTime = 5400));
end DrumBoiler;
package BaseClasses "Additional components for drum boiler example"
extends Modelica.Icons.BasesPackage;
model EquilibriumDrumBoiler "Simple Evaporator with two states, see Åström, Bell: Drum-boiler dynamics, Automatica 36, 2000, pp.363-378"
extends Modelica.Fluid.Interfaces.PartialTwoPort(final port_a_exposesState = true, final port_b_exposesState = true, redeclare replaceable package Medium = Modelica.Media.Water.StandardWater constrainedby Modelica.Media.Interfaces.PartialTwoPhaseMedium);
import Modelica.Constants;
import Modelica.Fluid.Types;
parameter SI.Mass m_D "Mass of surrounding drum metal";
parameter Medium.SpecificHeatCapacity cp_D "Specific heat capacity of drum metal";
parameter SI.Volume V_t "Total volume inside drum";
parameter Medium.AbsolutePressure p_start = system.p_start "Start value of pressure";
parameter SI.Volume V_l_start = V_t/2 "Start value of liquid volumeStart value of volume";
parameter Boolean allowFlowReversal = system.allowFlowReversal "= true, if flow reversal is enabled, otherwise restrict flow to design direction (port_a -> port_b)" annotation(Evaluate = true);
parameter Types.Dynamics energyDynamics = system.energyDynamics "Formulation of energy balance" annotation(Evaluate = true);
parameter Types.Dynamics massDynamics = system.massDynamics "Formulation of mass balance" annotation(Evaluate = true);
Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort;
Modelica.Blocks.Interfaces.RealOutput V(unit = "m3") "Liquid volume";
Medium.SaturationProperties sat "State vector to compute saturation properties";
Medium.AbsolutePressure p(start = p_start, stateSelect = StateSelect.prefer) "Pressure inside drum boiler";
Medium.Temperature T "Temperature inside drum boiler";
SI.Volume V_v "Volume of vapour phase";
SI.Volume V_l(start = V_l_start, stateSelect = StateSelect.prefer) "Volumes of liquid phase";
Medium.SpecificEnthalpy h_v = Medium.dewEnthalpy(sat) "Specific enthalpy of vapour";
Medium.SpecificEnthalpy h_l = Medium.bubbleEnthalpy(sat) "Specific enthalpy of liquid";
Medium.Density rho_v = Medium.dewDensity(sat) "Density in vapour phase";
Medium.Density rho_l = Medium.bubbleDensity(sat) "Density in liquid phase";
SI.Mass m "Total mass of drum boiler";
SI.Energy U "Internal energy";
Medium.Temperature T_D = heatPort.T "Temperature of drum";
SI.HeatFlowRate q_F = heatPort.Q_flow "Heat flow rate from furnace";
Medium.SpecificEnthalpy h_W = inStream(port_a.h_outflow) "Feed water enthalpy (specific enthalpy close to feedwater port when mass flows in to the boiler)";
Medium.SpecificEnthalpy h_S = inStream(port_b.h_outflow) "Steam enthalpy (specific enthalpy close to steam port when mass flows in to the boiler)";
SI.MassFlowRate qm_W = port_a.m_flow "Feed water mass flow rate";
SI.MassFlowRate qm_S = port_b.m_flow "Steam mass flow rate";
initial equation
if energyDynamics == Types.Dynamics.FixedInitial then
p = p_start;
elseif energyDynamics == Types.Dynamics.SteadyStateInitial then
der(p) = 0;
end if;
if massDynamics == Types.Dynamics.FixedInitial then
V_l = V_l_start;
elseif energyDynamics == Types.Dynamics.SteadyStateInitial then
der(V_l) = 0;
end if;
equation
m = rho_v*V_v + rho_l*V_l + m_D "Total mass";
U = rho_v*V_v*h_v + rho_l*V_l*h_l - p*V_t + m_D*cp_D*T_D "Total energy";
if massDynamics == Types.Dynamics.SteadyState then
0 = qm_W + qm_S "Steady state mass balance";
else
der(m) = qm_W + qm_S "Dynamic mass balance";
end if;
if energyDynamics == Types.Dynamics.SteadyState then
0 = q_F + port_a.m_flow*actualStream(port_a.h_outflow) + port_b.m_flow*actualStream(port_b.h_outflow) "Steady state energy balance";
else
der(U) = q_F + port_a.m_flow*actualStream(port_a.h_outflow) + port_b.m_flow*actualStream(port_b.h_outflow) "Dynamic energy balance";
end if;
V_t = V_l + V_v;
sat.psat = p;
sat.Tsat = T;
sat.Tsat = Medium.saturationTemperature(p);
T_D = T;
port_a.p = p;
port_a.h_outflow = h_l;
port_b.p = p;
port_b.h_outflow = h_v;
V = V_l;
assert(p < Medium.fluidConstants[1].criticalPressure - 10000, "Evaporator model requires subcritical pressure");
end EquilibriumDrumBoiler;
end BaseClasses;
end DrumBoiler;
end Examples;
model System "System properties and default values (ambient, flow direction, initialization)"
parameter SI.AbsolutePressure p_ambient = 101325 "Default ambient pressure";
parameter SI.Temperature T_ambient = 293.15 "Default ambient temperature";
parameter SI.Acceleration g = Modelica.Constants.g_n "Constant gravity acceleration";
parameter Boolean allowFlowReversal = true "= false to restrict to design flow direction (port_a -> port_b)" annotation(Evaluate = true);
parameter Modelica.Fluid.Types.Dynamics energyDynamics = Modelica.Fluid.Types.Dynamics.DynamicFreeInitial "Default formulation of energy balances" annotation(Evaluate = true);
parameter Modelica.Fluid.Types.Dynamics massDynamics = energyDynamics "Default formulation of mass balances" annotation(Evaluate = true);
final parameter Modelica.Fluid.Types.Dynamics substanceDynamics = massDynamics "Default formulation of substance balances" annotation(Evaluate = true);
final parameter Modelica.Fluid.Types.Dynamics traceDynamics = massDynamics "Default formulation of trace substance balances" annotation(Evaluate = true);
parameter Modelica.Fluid.Types.Dynamics momentumDynamics = Modelica.Fluid.Types.Dynamics.SteadyState "Default formulation of momentum balances, if options available" annotation(Evaluate = true);
parameter SI.MassFlowRate m_flow_start = 0 "Default start value for mass flow rates";
parameter SI.AbsolutePressure p_start = p_ambient "Default start value for pressures";
parameter SI.Temperature T_start = T_ambient "Default start value for temperatures";
parameter Boolean use_eps_Re = false "= true to determine turbulent region automatically using Reynolds number" annotation(Evaluate = true);
parameter SI.MassFlowRate m_flow_nominal = if use_eps_Re then 1 else 1e2*m_flow_small "Default nominal mass flow rate";
parameter Real eps_m_flow(min = 0) = 1e-4 "Regularization of zero flow for |m_flow| < eps_m_flow*m_flow_nominal";
parameter SI.AbsolutePressure dp_small(min = 0) = 1 "Default small pressure drop for regularization of laminar and zero flow";
parameter SI.MassFlowRate m_flow_small(min = 0) = 1e-2 "Default small mass flow rate for regularization of laminar and zero flow";
annotation(defaultComponentPrefixes = "inner", missingInnerMessage = "
Your model is using an outer \"system\" component but
an inner \"system\" component is not defined.
For simulation drag Modelica.Fluid.System into your model
to specify system properties.");
end System;
package Valves "Components for the regulation and control of fluid flow"
extends Modelica.Icons.VariantsPackage;
model ValveLinear "Valve for water/steam flows with linear pressure drop"
extends Modelica.Fluid.Interfaces.PartialTwoPortTransport;
parameter SI.AbsolutePressure dp_nominal "Nominal pressure drop at full opening";
parameter Medium.MassFlowRate m_flow_nominal "Nominal mass flowrate at full opening";
final parameter Types.HydraulicConductance k = m_flow_nominal/dp_nominal "Hydraulic conductance at full opening";
Modelica.Blocks.Interfaces.RealInput opening(min = 0, max = 1) "=1: completely open, =0: completely closed";
equation
m_flow = opening*k*dp;
port_a.h_outflow = inStream(port_b.h_outflow);
port_b.h_outflow = inStream(port_a.h_outflow);
end ValveLinear;
end Valves;
package Sources "Define fixed or prescribed boundary conditions"
extends Modelica.Icons.SourcesPackage;
model FixedBoundary "Boundary source component"
import Modelica.Media.Interfaces.Choices.IndependentVariables;
extends Sources.BaseClasses.PartialSource;
parameter Boolean use_p = true "Select p or d" annotation(Evaluate = true);
parameter Medium.AbsolutePressure p = Medium.p_default "Boundary pressure";
parameter Medium.Density d = (if use_T then Medium.density_pTX(Medium.p_default, Medium.T_default, Medium.X_default) else Medium.density_phX(Medium.p_default, Medium.h_default, Medium.X_default)) "Boundary density";
parameter Boolean use_T = true "Select T or h" annotation(Evaluate = true);
parameter Medium.Temperature T = Medium.T_default "Boundary temperature";
parameter Medium.SpecificEnthalpy h = Medium.h_default "Boundary specific enthalpy";
parameter Medium.MassFraction[Medium.nX] X(quantity = Medium.substanceNames) = Medium.X_default "Boundary mass fractions m_i/m";
parameter Medium.ExtraProperty[Medium.nC] C(quantity = Medium.extraPropertiesNames) = Medium.C_default "Boundary trace substances";
protected
Medium.ThermodynamicState state;
equation
Modelica.Fluid.Utilities.checkBoundary(Medium.mediumName, Medium.substanceNames, Medium.singleState, use_p, X, "FixedBoundary");
if use_p or Medium.singleState then
if use_T then
state = Medium.setState_pTX(p, T, X);
else
state = Medium.setState_phX(p, h, X);
end if;
if Medium.ThermoStates == IndependentVariables.dTX then
medium.d = Medium.density(state);
else
medium.p = Medium.pressure(state);
end if;
if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then
medium.h = Medium.specificEnthalpy(state);
else
medium.T = Medium.temperature(state);
end if;
else
if use_T then
state = Medium.setState_dTX(d, T, X);
if Medium.ThermoStates == IndependentVariables.dTX then
medium.d = Medium.density(state);
else
medium.p = Medium.pressure(state);
end if;
if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then
medium.h = Medium.specificEnthalpy(state);
else
medium.T = Medium.temperature(state);
end if;
else
medium.d = d;
medium.h = h;
state = Medium.setState_dTX(d, T, X);
end if;
end if;
medium.Xi = X[1:Medium.nXi];
ports.C_outflow = fill(C, nPorts);
end FixedBoundary;
model MassFlowSource_h "Ideal flow source that produces a prescribed mass flow with prescribed specific enthalpy, mass fraction and trace substances"
import Modelica.Media.Interfaces.Choices.IndependentVariables;
extends Sources.BaseClasses.PartialFlowSource;
parameter Boolean use_m_flow_in = false "Get the mass flow rate from the input connector" annotation(Evaluate = true, HideResult = true);
parameter Boolean use_h_in = false "Get the specific enthalpy from the input connector" annotation(Evaluate = true, HideResult = true);
parameter Boolean use_X_in = false "Get the composition from the input connector" annotation(Evaluate = true, HideResult = true);
parameter Boolean use_C_in = false "Get the trace substances from the input connector" annotation(Evaluate = true, HideResult = true);
parameter Medium.MassFlowRate m_flow = 0 "Fixed mass flow rate going out of the fluid port";
parameter Medium.SpecificEnthalpy h = Medium.h_default "Fixed value of specific enthalpy";
parameter Medium.MassFraction[Medium.nX] X = Medium.X_default "Fixed value of composition";
parameter Medium.ExtraProperty[Medium.nC] C(quantity = Medium.extraPropertiesNames) = Medium.C_default "Fixed values of trace substances";
Modelica.Blocks.Interfaces.RealInput m_flow_in(unit = "kg/s") if use_m_flow_in "Prescribed mass flow rate";
Modelica.Blocks.Interfaces.RealInput h_in(unit = "J/kg") if use_h_in "Prescribed fluid specific enthalpy";
Modelica.Blocks.Interfaces.RealInput[Medium.nX] X_in(each unit = "1") if use_X_in "Prescribed fluid composition";
Modelica.Blocks.Interfaces.RealInput[Medium.nC] C_in if use_C_in "Prescribed boundary trace substances";
protected
Modelica.Blocks.Interfaces.RealInput m_flow_in_internal(unit = "kg/s") "Needed to connect to conditional connector";
Modelica.Blocks.Interfaces.RealInput h_in_internal(unit = "J/kg") "Needed to connect to conditional connector";
Modelica.Blocks.Interfaces.RealInput[Medium.nX] X_in_internal(each unit = "1") "Needed to connect to conditional connector";
Modelica.Blocks.Interfaces.RealInput[Medium.nC] C_in_internal "Needed to connect to conditional connector";
equation
Utilities.checkBoundary(Medium.mediumName, Medium.substanceNames, Medium.singleState, true, X_in_internal, "MassFlowSource_h");
connect(m_flow_in, m_flow_in_internal);
connect(h_in, h_in_internal);
connect(X_in, X_in_internal);
connect(C_in, C_in_internal);
if not use_m_flow_in then
m_flow_in_internal = m_flow;
end if;
if not use_h_in then
h_in_internal = h;
end if;
if not use_X_in then
X_in_internal = X;
end if;
if not use_C_in then
C_in_internal = C;
end if;
if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then
medium.h = h_in_internal;
else
medium.T = Medium.temperature(Medium.setState_phX(medium.p, h_in_internal, X_in_internal));
end if;
sum(ports.m_flow) = -m_flow_in_internal;
medium.Xi = X_in_internal[1:Medium.nXi];
ports.C_outflow = fill(C_in_internal, nPorts);
end MassFlowSource_h;
package BaseClasses "Base classes used in the Sources package (only of interest to build new component models)"
extends Modelica.Icons.BasesPackage;
partial model PartialSource "Partial component source with one fluid connector"
import Modelica.Constants;
parameter Integer nPorts = 0 "Number of ports";
replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model within the source" annotation(choicesAllMatching = true);
Medium.BaseProperties medium "Medium in the source";
Interfaces.FluidPorts_b[nPorts] ports(redeclare each package Medium = Medium, m_flow(each max = if flowDirection == Types.PortFlowDirection.Leaving then 0 else +Constants.inf, each min = if flowDirection == Types.PortFlowDirection.Entering then 0 else -Constants.inf));
protected
parameter Types.PortFlowDirection flowDirection = Types.PortFlowDirection.Bidirectional "Allowed flow direction" annotation(Evaluate = true);
equation
for i in 1:nPorts loop
assert(cardinality(ports[i]) <= 1, "
each ports[i] of boundary shall at most be connected to one component.
If two or more connections are present, ideal mixing takes
place with these connections, which is usually not the intention
of the modeller. Increase nPorts to add an additional port.
");
ports[i].p = medium.p;
ports[i].h_outflow = medium.h;
ports[i].Xi_outflow = medium.Xi;
end for;
end PartialSource;
partial model PartialFlowSource "Partial component source with one fluid connector"
import Modelica.Constants;
parameter Integer nPorts = 0 "Number of ports";
replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model within the source" annotation(choicesAllMatching = true);
Medium.BaseProperties medium "Medium in the source";
Interfaces.FluidPort_b[nPorts] ports(redeclare each package Medium = Medium, m_flow(each max = if flowDirection == Types.PortFlowDirection.Leaving then 0 else +Constants.inf, each min = if flowDirection == Types.PortFlowDirection.Entering then 0 else -Constants.inf));
protected
parameter Types.PortFlowDirection flowDirection = Types.PortFlowDirection.Bidirectional "Allowed flow direction" annotation(Evaluate = true);
equation
assert(abs(sum(abs(ports.m_flow)) - max(abs(ports.m_flow))) <= Modelica.Constants.small, "FlowSource only supports one connection with flow");
assert(nPorts > 0, "At least one port needs to be present (nPorts > 0), otherwise the model is singular");
for i in 1:nPorts loop
assert(cardinality(ports[i]) <= 1, "
each ports[i] of boundary shall at most be connected to one component.
If two or more connections are present, ideal mixing takes
place with these connections, which is usually not the intention
of the modeller. Increase nPorts to add an additional port.
");
ports[i].p = medium.p;
ports[i].h_outflow = medium.h;
ports[i].Xi_outflow = medium.Xi;
end for;
end PartialFlowSource;
end BaseClasses;
end Sources;
package Sensors "Ideal sensor components to extract signals from a fluid connector"
extends Modelica.Icons.SensorsPackage;
model Pressure "Ideal pressure sensor"
extends Sensors.BaseClasses.PartialAbsoluteSensor;
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput p(final quantity = "Pressure", final unit = "Pa", displayUnit = "bar", min = 0) "Pressure at port";
equation
p = port.p;
end Pressure;
model Temperature "Ideal one port temperature sensor"
extends Sensors.BaseClasses.PartialAbsoluteSensor;
Modelica.Blocks.Interfaces.RealOutput T(final quantity = "ThermodynamicTemperature", final unit = "K", displayUnit = "degC", min = 0) "Temperature in port medium";
equation
T = Medium.temperature(Medium.setState_phX(port.p, inStream(port.h_outflow), inStream(port.Xi_outflow)));
end Temperature;
model MassFlowRate "Ideal sensor for mass flow rate"
extends Sensors.BaseClasses.PartialFlowSensor;
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput m_flow(quantity = "MassFlowRate", final unit = "kg/s") "Mass flow rate from port_a to port_b";
equation
m_flow = port_a.m_flow;
end MassFlowRate;
package BaseClasses "Base classes used in the Sensors package (only of interest to build new component models)"
extends Modelica.Icons.BasesPackage;
partial model PartialAbsoluteSensor "Partial component to model a sensor that measures a potential variable"
replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the sensor" annotation(choicesAllMatching = true);
Modelica.Fluid.Interfaces.FluidPort_a port(redeclare package Medium = Medium, m_flow(min = 0));
equation
port.m_flow = 0;
port.h_outflow = Medium.h_default;
port.Xi_outflow = Medium.X_default[1:Medium.nXi];
port.C_outflow = zeros(Medium.nC);
end PartialAbsoluteSensor;
partial model PartialFlowSensor "Partial component to model sensors that measure flow properties"
extends Modelica.Fluid.Interfaces.PartialTwoPort;
parameter Medium.MassFlowRate m_flow_nominal = system.m_flow_nominal "Nominal value of m_flow = port_a.m_flow";
parameter Medium.MassFlowRate m_flow_small(min = 0) = if system.use_eps_Re then system.eps_m_flow*m_flow_nominal else system.m_flow_small "Regularization for bi-directional flow in the region |m_flow| < m_flow_small (m_flow_small > 0 required)";
equation
0 = port_a.m_flow + port_b.m_flow;
port_a.p = port_b.p;
port_a.h_outflow = inStream(port_b.h_outflow);
port_b.h_outflow = inStream(port_a.h_outflow);
port_a.Xi_outflow = inStream(port_b.Xi_outflow);
port_b.Xi_outflow = inStream(port_a.Xi_outflow);
port_a.C_outflow = inStream(port_b.C_outflow);
port_b.C_outflow = inStream(port_a.C_outflow);
end PartialFlowSensor;
end BaseClasses;
end Sensors;
package Interfaces "Interfaces for steady state and unsteady, mixed-phase, multi-substance, incompressible and compressible flow"
extends Modelica.Icons.InterfacesPackage;
connector FluidPort "Interface for quasi one-dimensional fluid flow in a piping network (incompressible or compressible, one or more phases, one or more substances)"
replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model" annotation(choicesAllMatching = true);
flow Medium.MassFlowRate m_flow "Mass flow rate from the connection point into the component";
Medium.AbsolutePressure p "Thermodynamic pressure in the connection point";
stream Medium.SpecificEnthalpy h_outflow "Specific thermodynamic enthalpy close to the connection point if m_flow < 0";
stream Medium.MassFraction[Medium.nXi] Xi_outflow "Independent mixture mass fractions m_i/m close to the connection point if m_flow < 0";
stream Medium.ExtraProperty[Medium.nC] C_outflow "Properties c_i/m close to the connection point if m_flow < 0";
end FluidPort;
connector FluidPort_a "Generic fluid connector at design inlet"
extends FluidPort;
end FluidPort_a;
connector FluidPort_b "Generic fluid connector at design outlet"
extends FluidPort;
end FluidPort_b;
connector FluidPorts_b "Fluid connector with outlined, large icon to be used for vectors of FluidPorts (vector dimensions must be added after dragging)"
extends FluidPort;
end FluidPorts_b;
partial model PartialTwoPort "Partial component with two ports"
import Modelica.Constants;
outer Modelica.Fluid.System system "System wide properties";
replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component" annotation(choicesAllMatching = true);
parameter Boolean allowFlowReversal = system.allowFlowReversal "= true to allow flow reversal, false restricts to design direction (port_a -> port_b)" annotation(Evaluate = true);
Modelica.Fluid.Interfaces.FluidPort_a port_a(redeclare package Medium = Medium, m_flow(min = if allowFlowReversal then -Constants.inf else 0)) "Fluid connector a (positive design flow direction is from port_a to port_b)";
Modelica.Fluid.Interfaces.FluidPort_b port_b(redeclare package Medium = Medium, m_flow(max = if allowFlowReversal then +Constants.inf else 0)) "Fluid connector b (positive design flow direction is from port_a to port_b)";
protected
parameter Boolean port_a_exposesState = false "= true if port_a exposes the state of a fluid volume";
parameter Boolean port_b_exposesState = false "= true if port_b.p exposes the state of a fluid volume";
parameter Boolean showDesignFlowDirection = true "= false to hide the arrow in the model icon";
end PartialTwoPort;
partial model PartialTwoPortTransport "Partial element transporting fluid between two ports without storage of mass or energy"
extends PartialTwoPort(final port_a_exposesState = false, final port_b_exposesState = false);
parameter Medium.AbsolutePressure dp_start(min = -Modelica.Constants.inf) = 0.01*system.p_start "Guess value of dp = port_a.p - port_b.p";
parameter Medium.MassFlowRate m_flow_start = system.m_flow_start "Guess value of m_flow = port_a.m_flow";
parameter Medium.MassFlowRate m_flow_small = if system.use_eps_Re then system.eps_m_flow*system.m_flow_nominal else system.m_flow_small "Small mass flow rate for regularization of zero flow";
parameter Boolean show_T = true "= true, if temperatures at port_a and port_b are computed";
parameter Boolean show_V_flow = true "= true, if volume flow rate at inflowing port is computed";
Medium.MassFlowRate m_flow(min = if allowFlowReversal then -Modelica.Constants.inf else 0, start = m_flow_start) "Mass flow rate in design flow direction";
SI.Pressure dp(start = dp_start) "Pressure difference between port_a and port_b (= port_a.p - port_b.p)";
SI.VolumeFlowRate V_flow = m_flow/Modelica.Fluid.Utilities.regStep(m_flow, Medium.density(state_a), Medium.density(state_b), m_flow_small) if show_V_flow "Volume flow rate at inflowing port (positive when flow from port_a to port_b)";
Medium.Temperature port_a_T = Modelica.Fluid.Utilities.regStep(port_a.m_flow, Medium.temperature(state_a), Medium.temperature(Medium.setState_phX(port_a.p, port_a.h_outflow, port_a.Xi_outflow)), m_flow_small) if show_T "Temperature close to port_a, if show_T = true";
Medium.Temperature port_b_T = Modelica.Fluid.Utilities.regStep(port_b.m_flow, Medium.temperature(state_b), Medium.temperature(Medium.setState_phX(port_b.p, port_b.h_outflow, port_b.Xi_outflow)), m_flow_small) if show_T "Temperature close to port_b, if show_T = true";
protected
Medium.ThermodynamicState state_a "State for medium inflowing through port_a";
Medium.ThermodynamicState state_b "State for medium inflowing through port_b";
equation
state_a = Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), inStream(port_a.Xi_outflow));
state_b = Medium.setState_phX(port_b.p, inStream(port_b.h_outflow), inStream(port_b.Xi_outflow));
dp = port_a.p - port_b.p;
m_flow = port_a.m_flow;
assert(m_flow > -m_flow_small or allowFlowReversal, "Reversing flow occurs even though allowFlowReversal is false");
port_a.m_flow + port_b.m_flow = 0;
port_a.Xi_outflow = inStream(port_b.Xi_outflow);
port_b.Xi_outflow = inStream(port_a.Xi_outflow);
port_a.C_outflow = inStream(port_b.C_outflow);
port_b.C_outflow = inStream(port_a.C_outflow);
end PartialTwoPortTransport;
end Interfaces;
package Types "Common types for fluid models"
extends Modelica.Icons.TypesPackage;
type HydraulicConductance = Modelica.Icons.TypeReal(final quantity = "HydraulicConductance", final unit = "kg/(s.Pa)") "Real type for hydraulic conductance";
type Dynamics = enumeration(DynamicFreeInitial "DynamicFreeInitial -- Dynamic balance, Initial guess value", FixedInitial "FixedInitial -- Dynamic balance, Initial value fixed", SteadyStateInitial "SteadyStateInitial -- Dynamic balance, Steady state initial with guess value", SteadyState "SteadyState -- Steady state balance, Initial guess value") "Enumeration to define definition of balance equations";
type PortFlowDirection = enumeration(Entering "Fluid flow is only entering", Leaving "Fluid flow is only leaving", Bidirectional "No restrictions on fluid flow (flow reversal possible)") "Enumeration to define whether flow reversal is allowed";
end Types;
package Utilities "Utility models to construct fluid components (should not be used directly)"
extends Modelica.Icons.UtilitiesPackage;
function checkBoundary "Check whether boundary definition is correct"
extends Modelica.Icons.Function;
input String mediumName;
input String[:] substanceNames "Names of substances";
input Boolean singleState;
input Boolean define_p;
input Real[:] X_boundary;
input String modelName = "??? boundary ???";
protected
Integer nX = size(X_boundary, 1);
String X_str;
algorithm
assert(not singleState or singleState and define_p, "
Wrong value of parameter define_p (= false) in model \"" + modelName + "\":
The selected medium \"" + mediumName + "\" has Medium.singleState=true.
Therefore, an boundary density cannot be defined and
define_p = true is required.
");
for i in 1:nX loop
assert(X_boundary[i] >= 0.0, "
Wrong boundary mass fractions in medium \"" + mediumName + "\" in model \"" + modelName + "\":
The boundary value X_boundary(" + String(i) + ") = " + String(X_boundary[i]) + "
is negative. It must be positive.
");
end for;
if nX > 0 and abs(sum(X_boundary) - 1.0) > 1e-10 then
X_str := "";
for i in 1:nX loop
X_str := X_str + " X_boundary[" + String(i) + "] = " + String(X_boundary[i]) + " \"" + substanceNames[i] + "\"\n";
end for;
Modelica.Utilities.Streams.error("The boundary mass fractions in medium \"" + mediumName + "\" in model \"" + modelName + "\"\n" + "do not sum up to 1. Instead, sum(X_boundary) = " + String(sum(X_boundary)) + ":\n" + X_str);
else
end if;
end checkBoundary;
function regStep "Approximation of a general step, such that the characteristic is continuous and differentiable"
extends Modelica.Icons.Function;
input Real x "Abscissa value";
input Real y1 "Ordinate value for x > 0";
input Real y2 "Ordinate value for x < 0";
input Real x_small(min = 0) = 1e-5 "Approximation of step for -x_small <= x <= x_small; x_small >= 0 required";
output Real y "Ordinate value to approximate y = if x > 0 then y1 else y2";
algorithm
y := smooth(1, if x > x_small then y1 else if x < -x_small then y2 else if x_small > 0 then (x/x_small)*((x/x_small)^2 - 3)*(y2 - y1)/4 + (y1 + y2)/2 else (y1 + y2)/2);
end regStep;
end Utilities;
end Fluid;
package Media "Library of media property models"
extends Modelica.Icons.Package;
import Modelica.Units.SI;
import Cv = Modelica.Units.Conversions;
package Interfaces "Interfaces for media models"
extends Modelica.Icons.InterfacesPackage;
partial package PartialMedium "Partial medium properties (base package of all media packages)"
extends Modelica.Media.Interfaces.Types;
extends Modelica.Icons.MaterialPropertiesPackage;
constant Modelica.Media.Interfaces.Choices.IndependentVariables ThermoStates "Enumeration type for independent variables";
constant String mediumName = "unusablePartialMedium" "Name of the medium";
constant String[:] substanceNames = {mediumName} "Names of the mixture substances. Set substanceNames={mediumName} if only one substance.";
constant String[:] extraPropertiesNames = fill("", 0) "Names of the additional (extra) transported properties. Set extraPropertiesNames=fill(\"\",0) if unused";
constant Boolean singleState "= true, if u and d are not a function of pressure";
constant Boolean reducedX = true "= true if medium contains the equation sum(X) = 1.0; set reducedX=true if only one substance (see docu for details)";
constant Boolean fixedX = false "= true if medium contains the equation X = reference_X";
constant MassFraction[nX] reference_X = fill(1/nX, nX) "Default mass fractions of medium";
constant AbsolutePressure p_default = 101325 "Default value for pressure of medium (for initialization)";
constant Temperature T_default = Modelica.Units.Conversions.from_degC(20) "Default value for temperature of medium (for initialization)";
constant SpecificEnthalpy h_default = specificEnthalpy_pTX(p_default, T_default, X_default) "Default value for specific enthalpy of medium (for initialization)";
constant MassFraction[nX] X_default = reference_X "Default value for mass fractions of medium (for initialization)";
constant ExtraProperty[nC] C_default = fill(0, nC) "Default value for trace substances of medium (for initialization)";
final constant Integer nS = size(substanceNames, 1) "Number of substances";
constant Integer nX = nS "Number of mass fractions";
constant Integer nXi = if fixedX then 0 else if reducedX then nS - 1 else nS "Number of structurally independent mass fractions (see docu for details)";
final constant Integer nC = size(extraPropertiesNames, 1) "Number of extra (outside of standard mass-balance) transported properties";
replaceable record FluidConstants = Modelica.Media.Interfaces.Types.Basic.FluidConstants "Critical, triple, molecular and other standard data of fluid";
replaceable record ThermodynamicState "Minimal variable set that is available as input argument to every medium function"
extends Modelica.Icons.Record;
end ThermodynamicState;
replaceable partial model BaseProperties "Base properties (p, d, T, h, u, R_s, MM and, if applicable, X and Xi) of a medium"
InputAbsolutePressure p "Absolute pressure of medium";
InputMassFraction[nXi] Xi(start = reference_X[1:nXi]) "Structurally independent mass fractions";
InputSpecificEnthalpy h "Specific enthalpy of medium";
Density d "Density of medium";
Temperature T "Temperature of medium";
MassFraction[nX] X(start = reference_X) "Mass fractions (= (component mass)/total mass m_i/m)";
SpecificInternalEnergy u "Specific internal energy of medium";
SpecificHeatCapacity R_s "Gas constant (of mixture if applicable)";
MolarMass MM "Molar mass (of mixture or single fluid)";
ThermodynamicState state "Thermodynamic state record for optional functions";
parameter Boolean preferredMediumStates = false "= true if StateSelect.prefer shall be used for the independent property variables of the medium" annotation(Evaluate = true);
parameter Boolean standardOrderComponents = true "If true, and reducedX = true, the last element of X will be computed from the other ones";
Modelica.Units.NonSI.Temperature_degC T_degC = Modelica.Units.Conversions.to_degC(T) "Temperature of medium in [degC]";
Modelica.Units.NonSI.Pressure_bar p_bar = Modelica.Units.Conversions.to_bar(p) "Absolute pressure of medium in [bar]";
connector InputAbsolutePressure = input SI.AbsolutePressure "Pressure as input signal connector";
connector InputSpecificEnthalpy = input SI.SpecificEnthalpy "Specific enthalpy as input signal connector";
connector InputMassFraction = input SI.MassFraction "Mass fraction as input signal connector";
equation
if standardOrderComponents then
Xi = X[1:nXi];
if fixedX then
X = reference_X;
end if;
if reducedX and not fixedX then
X[nX] = 1 - sum(Xi);
end if;
for i in 1:nX loop
assert(X[i] >= -1.e-5 and X[i] <= 1 + 1.e-5, "Mass fraction X[" + String(i) + "] = " + String(X[i]) + "of substance " + substanceNames[i] + "\nof medium " + mediumName + " is not in the range 0..1");
end for;
end if;
assert(p >= 0.0, "Pressure (= " + String(p) + " Pa) of medium \"" + mediumName + "\" is negative\n(Temperature = " + String(T) + " K)");
end BaseProperties;
replaceable partial function setState_pTX "Return thermodynamic state as function of p, T and composition X or Xi"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input Temperature T "Temperature";
input MassFraction[:] X = reference_X "Mass fractions";
output ThermodynamicState state "Thermodynamic state record";
end setState_pTX;
replaceable partial function setState_phX "Return thermodynamic state as function of p, h and composition X or Xi"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input SpecificEnthalpy h "Specific enthalpy";
input MassFraction[:] X = reference_X "Mass fractions";
output ThermodynamicState state "Thermodynamic state record";
end setState_phX;
replaceable partial function setState_psX "Return thermodynamic state as function of p, s and composition X or Xi"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input SpecificEntropy s "Specific entropy";
input MassFraction[:] X = reference_X "Mass fractions";
output ThermodynamicState state "Thermodynamic state record";
end setState_psX;
replaceable partial function setState_dTX "Return thermodynamic state as function of d, T and composition X or Xi"
extends Modelica.Icons.Function;
input Density d "Density";
input Temperature T "Temperature";
input MassFraction[:] X = reference_X "Mass fractions";
output ThermodynamicState state "Thermodynamic state record";
end setState_dTX;
replaceable partial function setSmoothState "Return thermodynamic state so that it smoothly approximates: if x > 0 then state_a else state_b"
extends Modelica.Icons.Function;
input Real x "m_flow or dp";
input ThermodynamicState state_a "Thermodynamic state if x > 0";
input ThermodynamicState state_b "Thermodynamic state if x < 0";
input Real x_small(min = 0) "Smooth transition in the region -x_small < x < x_small";
output ThermodynamicState state "Smooth thermodynamic state for all x (continuous and differentiable)";
end setSmoothState;
replaceable partial function dynamicViscosity "Return dynamic viscosity"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output DynamicViscosity eta "Dynamic viscosity";
end dynamicViscosity;
replaceable partial function thermalConductivity "Return thermal conductivity"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output ThermalConductivity lambda "Thermal conductivity";
end thermalConductivity;
replaceable partial function pressure "Return pressure"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output AbsolutePressure p "Pressure";
end pressure;
replaceable partial function temperature "Return temperature"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output Temperature T "Temperature";
end temperature;
replaceable partial function density "Return density"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output Density d "Density";
end density;
replaceable partial function specificEnthalpy "Return specific enthalpy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificEnthalpy h "Specific enthalpy";
end specificEnthalpy;
replaceable partial function specificInternalEnergy "Return specific internal energy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificEnergy u "Specific internal energy";
end specificInternalEnergy;
replaceable partial function specificEntropy "Return specific entropy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificEntropy s "Specific entropy";
end specificEntropy;
replaceable partial function specificGibbsEnergy "Return specific Gibbs energy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificEnergy g "Specific Gibbs energy";
end specificGibbsEnergy;
replaceable partial function specificHelmholtzEnergy "Return specific Helmholtz energy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificEnergy f "Specific Helmholtz energy";
end specificHelmholtzEnergy;
replaceable partial function specificHeatCapacityCp "Return specific heat capacity at constant pressure"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificHeatCapacity cp "Specific heat capacity at constant pressure";
end specificHeatCapacityCp;
replaceable partial function specificHeatCapacityCv "Return specific heat capacity at constant volume"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SpecificHeatCapacity cv "Specific heat capacity at constant volume";
end specificHeatCapacityCv;
replaceable partial function isentropicExponent "Return isentropic exponent"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output IsentropicExponent gamma "Isentropic exponent";
end isentropicExponent;
replaceable partial function isentropicEnthalpy "Return isentropic enthalpy"
extends Modelica.Icons.Function;
input AbsolutePressure p_downstream "Downstream pressure";
input ThermodynamicState refState "Reference state for entropy";
output SpecificEnthalpy h_is "Isentropic enthalpy";
end isentropicEnthalpy;
replaceable partial function velocityOfSound "Return velocity of sound"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output VelocityOfSound a "Velocity of sound";
end velocityOfSound;
replaceable partial function isobaricExpansionCoefficient "Return overall the isobaric expansion coefficient beta"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output IsobaricExpansionCoefficient beta "Isobaric expansion coefficient";
end isobaricExpansionCoefficient;
replaceable partial function isothermalCompressibility "Return overall the isothermal compressibility factor"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output SI.IsothermalCompressibility kappa "Isothermal compressibility";
end isothermalCompressibility;
replaceable partial function density_derp_h "Return density derivative w.r.t. pressure at const specific enthalpy"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output DerDensityByPressure ddph "Density derivative w.r.t. pressure";
end density_derp_h;
replaceable partial function density_derh_p "Return density derivative w.r.t. specific enthalpy at constant pressure"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output DerDensityByEnthalpy ddhp "Density derivative w.r.t. specific enthalpy";
end density_derh_p;
replaceable partial function molarMass "Return the molar mass of the medium"
extends Modelica.Icons.Function;
input ThermodynamicState state "Thermodynamic state record";
output MolarMass MM "Mixture molar mass";
end molarMass;
replaceable function specificEnthalpy_pTX "Return specific enthalpy from p, T, and X or Xi"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input Temperature T "Temperature";
input MassFraction[:] X = reference_X "Mass fractions";
output SpecificEnthalpy h "Specific enthalpy";
algorithm
h := specificEnthalpy(setState_pTX(p, T, X));
annotation(inverse(T = temperature_phX(p, h, X)));
end specificEnthalpy_pTX;
replaceable function specificEntropy_pTX "Return specific enthalpy from p, T, and X or Xi"
extends Modelica.Icons.Function;
input AbsolutePressure p "Pressure";
input Temperature T "Temperature";
input MassFraction[:] X = reference_X "Mass fractions";
output SpecificEntropy s "Specific entropy";
algorithm
s := specificEntropy(setState_pTX(p, T, X));
annotation(inverse(T = temperature_psX(p, s, X)));
end specificEntropy_pTX;
replaceable function density_pTX "Return density from p, T, and X or Xi"