Skip to content

Commit 3752458

Browse files
author
Daniel Pütz
committed
EXAMPLES:
- updated conditions for the human model example to the ones mentioned in the HDIH, also added hummod raw data and a comparison to it in xls format - Added a parallelized optimization script for CCAA to find the best combination of geometry parameters (currently only optimizes lenght and broadness) LIB: - fixed a bug in the human model which occured at lower than 1 bar pressures. - fixed the plant water content to reflect the water content stored in the food information - Adjusted CCAA dimension to the result of the parameter fitting (0.23x0.23 m is also a quite realistic value)
1 parent 8883bd6 commit 3752458

File tree

9 files changed

+316
-28
lines changed

9 files changed

+316
-28
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ main_*.m
2828
.DS_Store
2929

3030
# Ignore Excel temporary files
31-
~*
31+
~*
32+
*.log

lib/+components/+matter/+CCAA/CCAA.m

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@
9898
% Wieland, 1998, page 104, where the CCAA fan power consumption is
9999
% mentioned to be 410 W
100100
fCurrentPowerConsumption = 410; % W
101+
102+
tParameterOverwrite;
101103
end
102104

103105
methods
@@ -168,6 +170,10 @@
168170
this.interpolateEffectiveness = griddedInterpolant(X, Y, Z, aPermutedEffectiveness);
169171
end
170172

173+
function setParameterOverwrite(this, tParameters)
174+
this.tParameterOverwrite = tParameters;
175+
end
176+
171177
function createMatterStructure(this)
172178
createMatterStructure@vsys(this);
173179

@@ -219,13 +225,21 @@ function createMatterStructure(this)
219225
% Some configurating variables
220226
sHX_type = 'plate_fin'; % Heat exchanger type
221227
% broadness of the heat exchange area in m
222-
tGeometry.fBroadness = 1;
228+
if isfield(this.tParameterOverwrite, 'fBroadness')
229+
tGeometry.fBroadness = this.tParameterOverwrite.fBroadness;
230+
else
231+
tGeometry.fBroadness = 0.23;
232+
end
233+
% length of the heat exchanger in m
234+
if isfield(this.tParameterOverwrite, 'fLength')
235+
tGeometry.fLength = this.tParameterOverwrite.fLength;
236+
else
237+
tGeometry.fLength = 0.23;
238+
end
223239
% Height of the channel for fluid 1 in m
224240
tGeometry.fHeight_1 = 0.002;
225241
% Height of the channel for fluid 2 in m
226242
tGeometry.fHeight_2 = 0.002;
227-
% length of the heat exchanger in m
228-
tGeometry.fLength = 1;
229243
% thickness of the plate in m
230244
tGeometry.fThickness = 0.0002;
231245
% number of layers stacked

lib/+components/+matter/+DetailedHuman/+components/RespirationGasExchangeO2.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ function calculateFlowRate(this, afInsideInFlowRate, aarInsideInPartials, afOuts
135135

136136
afCurrentMolsIn = (afPartialFlowsAir_Out./ this.oMT.afMolarMass);
137137
arFractions = afCurrentMolsIn ./ sum(afCurrentMolsIn);
138-
afPP_Out = arFractions .* fGasPressure;
138+
afPP_Out = arFractions .* fGasPressure;
139+
afPP_Out(afPP_Out < 0) = 0;
139140

140141
[fConcentrationBloodO2_Out, fConcentrationBloodCO2_Out] = this.calculateBloodConcentrations(afPP_Out(this.oMT.tiN2I.O2), afPP_Out(this.oMT.tiN2I.CO2));
141142

lib/+components/+matter/+PlantModuleV2/PlantCulture.m

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,17 @@
118118
this.fPlantTimeInit = 0;
119119
end
120120

121-
this.oTimer.setMinStep(1e-20);
122-
123121
this.txPlantParameters = components.matter.PlantModuleV2.plantparameters.importPlantParameters(txInput.sPlantSpecies);
122+
123+
this.iEdibleBiomass = this.oMT.tiN2I.(this.txPlantParameters.sPlantSpecies);
124+
this.iInedibleBiomass = this.oMT.tiN2I.([this.txPlantParameters.sPlantSpecies, 'Inedible']);
125+
126+
trBaseCompositionEdible = this.oMT.ttxMatter.(this.oMT.csI2N{this.iEdibleBiomass}).trBaseComposition;
127+
trBaseCompositionInedible = this.oMT.ttxMatter.(this.oMT.csI2N{this.iInedibleBiomass}).trBaseComposition;
128+
129+
this.txPlantParameters.fFBWF_Edible = trBaseCompositionEdible.H2O * (1 - trBaseCompositionEdible.H2O)^-1;
130+
this.txPlantParameters.fFBWF_Inedible = trBaseCompositionInedible.H2O * (1 - trBaseCompositionInedible.H2O)^-1;
131+
124132
this.txInput = txInput;
125133

126134
% the flowrates set here are all used in the manipulator
@@ -262,10 +270,6 @@ function createMatterStructure(this)
262270
% immediatly after the previous generation!)
263271
this.txInput.mfSowTime = zeros(1,this.txInput.iConsecutiveGenerations);
264272
end
265-
266-
267-
this.iEdibleBiomass = this.oMT.tiN2I.(this.txPlantParameters.sPlantSpecies);
268-
this.iInedibleBiomass = this.oMT.tiN2I.([this.txPlantParameters.sPlantSpecies, 'Inedible']);
269273
end
270274

271275
function createSolverStructure(this)
@@ -436,7 +440,7 @@ function update(this)
436440
fWaterConsumptionInedible = fTotalPlantBiomassWaterConsumption - fWaterConsumptionEdible;
437441

438442
if fWaterConsumptionInedible < 0
439-
error('In the plant module too much water is used for edible plant biomass production')
443+
error('In the plant module too much water is used for inedible plant biomass production')
440444
end
441445
if fWaterConsumptionInedible - afPartialFlows(1, this.iInedibleBiomass) > 1e-6
442446
error('In the plant module more water is consumed than biomass is created! This might be due to a mismatch between the defined water content for the edible plant biomass and the assumed edible biomass water content in the MEC model')

user/+examples/+CCAA/+systems/Example.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
end
2222

2323
methods
24-
function this = Example(oParent, sName)
24+
function this = Example(oParent, sName, tParameters)
2525
% Call parent constructor. Third parameter defined how often
2626
% the .exec() method of this subsystem is called. This can be
2727
% used to change the system state, e.g. close valves or switch
@@ -113,6 +113,8 @@
113113
tFixValues.fVolumetricAirFlowRate = tProtoTestSetpoints(iProtoflightTest).fVolumetricAirFlow;
114114
tFixValues.fCoolantFlowRate = tProtoTestSetpoints(iProtoflightTest).fCoolantFlow;
115115
oCCAA.setFixValues(tFixValues);
116+
117+
oCCAA.setParameterOverwrite(tParameters);
116118
end
117119

118120
this.tProtoTestSetpoints = tProtoTestSetpoints;
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
function Optimization()
2+
mfLength = 0.2:0.01:0.4;
3+
mfBroadness = 0.2:0.01:0.4;
4+
5+
mfAirOutletDiff = zeros(length(mfLength), length(mfBroadness));
6+
mfCoolantOutletDiff = zeros(length(mfLength), length(mfBroadness));
7+
mfWaterProduced = zeros(length(mfLength), length(mfBroadness));
8+
9+
Data = load('user\+examples\+CCAA\+TestData\ProtoflightData.mat');
10+
11+
iSimTicks = 4;
12+
13+
% Creating a matter table object. Due to the file system access that is
14+
% done during the matter table instantiation, this cannot be done within
15+
% the parallel loop.
16+
oMT = matter.table();
17+
18+
% Creating a timer object. This is necessary, because we want to
19+
% use a multiWaitbar to show the progress of the individual
20+
% simulations. Since the multiWaitbar function is not designed to
21+
% be called from multiple workers simultaneously, the timer object
22+
% acts as the queue manager for the calls to update the wait bar.
23+
% For that we actually need to explicitly set the BusyMode property
24+
% of the timer to 'queue'.
25+
oWaitBarTimer = timer;
26+
oWaitBarTimer.BusyMode = 'queue';
27+
28+
% Now we set the timer function to the nested updateWaitbar()
29+
% function that is defined at the end of this function. It needs to
30+
% be generic regarding its input because it needs to handle both
31+
% the 'update' calls as well as the 'close' calls when a simulation
32+
% is completed.
33+
oWaitBarTimer.TimerFcn = @(xInput) updateWaitBar(xInput);
34+
35+
% It may be the case that a user wants to abort all simulations
36+
% while they are still running. Since they are running on parallel
37+
% workers, creating the 'STOP' file in the base directory won't
38+
% work. So we provide a nice, big, red STOP button here that calls
39+
% the other nested function called stopAllSims(). This callback
40+
% changes dynamically based on the number of simulations that are
41+
% currently running. So the actual assignment of that callback is
42+
% done later on. Here we just create the figure and the button.
43+
oFigure = figure('Name','Control', ...
44+
'MenuBar','none');
45+
oFigure.Position(3:4) = [200 150];
46+
47+
oButton = uicontrol(oFigure, ...
48+
'Style', 'Pushbutton', ...
49+
'Units', 'normalized', ...
50+
'String', 'STOP', ...
51+
'ForegroundColor', 'red', ...
52+
'FontSize', 20, ...
53+
'FontWeight', 'bold');
54+
55+
oButton.Units = 'normalized';
56+
oButton.Position = [0.25 0.25 0.5 0.5];
57+
58+
% The button callback will set this boolean variable to true so we
59+
% can properly abort the for and while loops below.
60+
bCancelled = false;
61+
62+
% In order to steer the while loop within the for loop below, we
63+
% need these variables to keep track of which simulations are
64+
% currently running.
65+
abActiveSimulations = false(length(mfBroadness),1);
66+
iActiveSimulations = 0;
67+
68+
for iLength = 1:length(mfLength)
69+
fLength = mfLength(iLength);
70+
disp(['Currently calculating Length: ', num2str(fLength)])
71+
72+
% The parallel pool memory usage continues to pile up if it is not
73+
% restarted
74+
% create a parallel pool
75+
oPool = gcp();
76+
77+
% Creating an empty array of pollable data queues so we can get
78+
% information from the workers and their simulations while they are
79+
% running.
80+
aoDataQueues = parallel.pool.DataQueue.empty(length(mfBroadness),0);
81+
aoResultObjects = parallel.FevalFuture.empty(length(mfBroadness),0);
82+
83+
for iBroadness = 1:length(mfBroadness)
84+
fBroadness = mfBroadness(iBroadness);
85+
% Now we create a wait bar for each simulation. We do this here and
86+
% not within the for loop below so the user can see all simulations
87+
% at once and not just the ones that are currently running.
88+
tools.multiWaitbar(['Broadness: ', num2str(mfBroadness(iBroadness))], 0);
89+
90+
aoDataQueues(iBroadness) = parallel.pool.DataQueue;
91+
92+
% The afterEach() function will execute the timer function
93+
% after each transmission from the worker. There the send()
94+
% method is called with a payload of data which is passed
95+
% directly to the timer function by afterEach(). Here this
96+
% is used to update the waitbar for the individual
97+
% simulation.
98+
afterEach(aoDataQueues(iBroadness), oWaitBarTimer.TimerFcn);
99+
100+
aoResults(iBroadness) = parfeval(oPool, @runCCAASim, 1, fBroadness, fLength, iSimTicks, Data, oMT, aoDataQueues(iBroadness), iBroadness);
101+
102+
% Now that the FevalFuture object hast been added to the
103+
% aoResultObjects array, we can update the callback of the
104+
% stop button to include the current version of this array.
105+
% This ensures that all simulations that are currently
106+
% running are properly aborted when the button is pressed.
107+
oButton.Callback = { @stopAllSims, aoResults };
108+
109+
% In order to control the addition of new simulations to
110+
% the parallel pool, we need to keep track of how many
111+
% simulations are currently running, so we set the
112+
% following variables accordingly.
113+
iActiveSimulations = iActiveSimulations + 1;
114+
abActiveSimulations(iBroadness) = true;
115+
end
116+
117+
118+
Results = cell(1, length(mfBroadness));
119+
for idx = 1:length(mfBroadness)
120+
% fetchNext blocks until more results are available, and
121+
% returns the index into f that is now complete, as well
122+
% as the value computed by f.
123+
if ~bCancelled
124+
[completedIdx, value] = fetchNext(aoResults);
125+
Results{completedIdx} = value;
126+
disp(['got Results for Broadness: ', num2str(mfBroadness(completedIdx))])
127+
128+
mfAirOutletDiff(iLength, completedIdx) = Results{completedIdx}.fAirOutletDiff;
129+
mfCoolantOutletDiff(iLength, completedIdx) = Results{completedIdx}.fCoolantOutletDiff;
130+
mfWaterProduced(iLength, completedIdx) = Results{completedIdx}.fWaterProduced;
131+
delete(aoResults(completedIdx));
132+
end
133+
% No matter the reason, this simulation is done, so
134+
% we can delete the wait bar for it.
135+
oWaitBarTimer.TimerFcn(completedIdx);
136+
end
137+
try
138+
oPool.delete
139+
catch
140+
% well if we cannot delete it, it is probably already deleted
141+
end
142+
end
143+
144+
% gets the screen size
145+
scrsz = get(groot,'ScreenSize');
146+
147+
X = ones(1, length(mfBroadness)) .* mfLength';
148+
Y = ones(length(mfLength), 1) .* mfBroadness;
149+
figure1 = figure('name', 'Air Outlet Temperature Difference');
150+
figure1.Position = [scrsz(3)/12, scrsz(4)/12 + scrsz(4)/2, scrsz(3)/3 scrsz(4)/3];
151+
mesh(X, Y, mfAirOutletDiff);
152+
xlabel('Length / m')
153+
ylabel('Broadness / m')
154+
zlabel('Air Outlet Temperature Difference / K')
155+
156+
figure2 = figure('name', 'Coolant Outlet Temperature Difference');
157+
figure2.Position = [scrsz(3)/12 + scrsz(3)/2, scrsz(4)/12 + scrsz(4)/2, scrsz(3)/3 scrsz(4)/3];
158+
mesh(X, Y, mfCoolantOutletDiff);
159+
xlabel('Length / m')
160+
ylabel('Broadness / m')
161+
zlabel('Coolant Outlet Temperature Difference / K')
162+
163+
figure3 = figure('name', 'Condensate Flow Difference');
164+
figure3.Position = [scrsz(3)/12, scrsz(4)/12, scrsz(3)/3 scrsz(4)/3];
165+
mesh(X, Y, mfWaterProduced);
166+
xlabel('Length / m')
167+
ylabel('Broadness / m')
168+
zlabel('Condensate Flow Difference / kg/h')
169+
170+
% Get minimum Index for Broadness
171+
% [~, minIndexBroadnessAir] = min(min(abs(mfAirOutletDiff), [], 1));
172+
% [~, minIndexLengthAir] = min(min(abs(mfAirOutletDiff), [], 2));
173+
%
174+
% [~, minIndexBroadnessCoolant] = min(min(abs(mfCoolantOutletDiff), [], 1));
175+
% [~, minIndexLengthCoolant] = min(min(abs(mfCoolantOutletDiff), [], 2));
176+
%
177+
% [~, minIndexBroadnessCondensate] = min(min(abs(mfWaterProduced), [], 1));
178+
% [~, minIndexLengthCondensate] = min(min(abs(mfWaterProduced), [], 2));
179+
%% Nested functions
180+
181+
function updateWaitBar(xInput)
182+
% This function updates the wait bar for an individual simulation
183+
% or deletes it. Both functions call the tools.multiWaitbar()
184+
% function with different sets of input arguments.
185+
% For the 'update' case, this function is called by the afterEach()
186+
% method of a parallel data queue. The input parameters are
187+
% provided as a 2x1 double array containing the simulation's index
188+
% and it's progress as a percentage. The index is converted to a
189+
% string containing the name of the simulation, which acts as the
190+
% identifier within the wait bar. For the 'close' case, this
191+
% function is called with just the index of the simulation.
192+
193+
% To discern between these two callers, we enclose the 'update'
194+
% call in a try catch block. If the xInput argument contains a
195+
% second element (xInput(2)) then we are being called from the data
196+
% queue to update the wait bar. If xInput only has one element,
197+
% this call will fail, so within the catch part, we handle the
198+
% closing of the waitbar.
199+
try
200+
tools.multiWaitbar(['Broadness: ', num2str(mfBroadness(xInput(1)))], xInput(2));
201+
catch %#ok<CTCH>
202+
tools.multiWaitbar(['Broadness: ', num2str(mfBroadness(xInput(1)))], 'Close');
203+
end
204+
end
205+
206+
function stopAllSims(~, ~, aoResultObjects)
207+
% This function is the callback for the STOP button. When it is
208+
% pressed, this function loops through all parallel.FevalFuture
209+
% objects in the aoResultsObjects input argument and cancels the
210+
% worker, unless it is already finished.
211+
for iObject = 1:length(aoResultObjects)
212+
if ~strcmp(aoResultObjects(iObject).State, 'finished')
213+
cancel(aoResultObjects(iObject));
214+
end
215+
end
216+
217+
% In order to prevent further simulations from being added after
218+
% the button is pressed, we set this boolean variable to true.
219+
bCancelled = true;
220+
end
221+
end
222+
223+
function tCCAA = runCCAASim(fBroadness, fLength, iSimTicks, Data, oMT, oDataQueue, iSim)
224+
oLastSimObj = vhab.sim('examples.CCAA.setup', containers.Map({'ParallelExecution', 'tParameters'}, {{oMT, oDataQueue, iSim}, struct('fBroadness', fBroadness, 'fLength', fLength, 'iSimTicks', iSimTicks)}), []);
225+
226+
% Actually running the simulation
227+
oLastSimObj.run();
228+
229+
oLogger = oLastSimObj.toMonitors.oLogger;
230+
231+
mfAirOutletTemperature = zeros(oLogger.iLogIndex, 6);
232+
mfMixedAirOutletTemperature = zeros(oLogger.iLogIndex, 6);
233+
mfCoolantOutletTemperature = zeros(oLogger.iLogIndex, 6);
234+
mfCondensateFlow = zeros(oLogger.iLogIndex, 6);
235+
for iLog = 1:length(oLogger.tLogValues)
236+
for iProtoflightTest = 1:6
237+
if strcmp(oLogger.tLogValues(iLog).sLabel, ['CCAA_', num2str(iProtoflightTest), ' Air Outlet Temperature'])
238+
mfAirOutletTemperature(:, iProtoflightTest) = oLogger.mfLog(1:oLogger.iLogIndex, oLogger.tLogValues(iLog).iIndex);
239+
elseif strcmp(oLogger.tLogValues(iLog).sLabel, ['CCAA_', num2str(iProtoflightTest), ' Mixed Air Outlet Temperature'])
240+
mfMixedAirOutletTemperature(:, iProtoflightTest) = oLogger.mfLog(1:oLogger.iLogIndex, oLogger.tLogValues(iLog).iIndex);
241+
elseif strcmp(oLogger.tLogValues(iLog).sLabel, ['CCAA_', num2str(iProtoflightTest), ' Coolant Outlet Temperature'])
242+
mfCoolantOutletTemperature(:, iProtoflightTest) = oLogger.mfLog(1:oLogger.iLogIndex, oLogger.tLogValues(iLog).iIndex);
243+
elseif strcmp(oLogger.tLogValues(iLog).sLabel, ['CCAA_', num2str(iProtoflightTest), ' Condensate Flow Rate'])
244+
mfCondensateFlow(:, iProtoflightTest) = oLogger.mfLog(1:oLogger.iLogIndex, oLogger.tLogValues(iLog).iIndex);
245+
end
246+
end
247+
end
248+
249+
mfAirTemperatureDifference = mfMixedAirOutletTemperature(4,:) - Data.ProtoflightTestData.AirOutletTemperature';
250+
mfCoolantTemperatureDifference = mfCoolantOutletTemperature(4,:) - Data.ProtoflightTestData.CoolantOutletTemperature';
251+
mfCondensateDifference = mfCondensateFlow(4,:) * 3600 - Data.ProtoflightTestData.CondensateMassFlow';
252+
253+
tCCAA.fAirOutletDiff = mean(mfAirTemperatureDifference);
254+
tCCAA.fCoolantOutletDiff = mean(mfCoolantTemperatureDifference);
255+
tCCAA.fWaterProduced = mean(mfCondensateDifference);
256+
end

0 commit comments

Comments
 (0)