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