comparison m-toolbox/m/gui/gltpda/ltpdasim.m @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 function ltpdasim(block)
2
3 % This is the automatic function wrapper
4 % ========================================================================
5 % ==================== level-2 M file S-function =========================
6 % ========================================================================
7 % This wrapper is the automatic function, called by every function block
8 % in Simulink, able to execute m-file functions retrieving from Simulink:
9 % (1) the pointer to the object(s) to be analyzed, coming in as input of
10 % the corresponding block (ie, the DATA),
11 % (2) the name of the function to be applied on those data, from the tag
12 % of the currently executed block (ie, the true FUNCTION),
13 % (3) the parameters for that particular block, retrieved from the global
14 % shared workspace by the handle of the block (ie, the PARAMETERS).
15 %
16 % The output is then generated as:
17 % OUTPUT = FUNCTION(DATA,PARAMETERS)
18 %
19 % This output in the end is saved into the global array containing all
20 % the AOs (ie, all the DATA go together with other data): thus this output
21 % will be freely accessible by all the other functions.
22 %
23 % The only real output to Simulink will be just the ordinal number of the
24 % so-generated AO into the global array of AOs.
25 %
26 % $Id: ltpdasim.m,v 1.37 2008/12/01 20:03:51 nicola Exp $
27
28
29 setup(block);
30
31
32 %% Setup
33
34 function setup(block)
35
36 % Register dialog parameter: none, because they're retrieved directly
37 % from the memory. This will prevent the user to modify the parameters
38 % outside the proper parameters panel:
39 block.NumDialogPrms = 0;
40
41 % Register number of input and output ports
42 block.NumInputPorts = 1;
43 block.NumOutputPorts = 1;
44
45 % Setup functional port properties to dynamically inherited.
46 block.SetPreCompInpPortInfoToDynamic;
47
48 block.InputPort(1).DirectFeedthrough = true;
49 block.InputPort(1).DatatypeID = 0;
50 block.InputPort(1).Complexity = 0;
51 block.OutputPort(1).DatatypeID = 0;
52 block.OutputPort(1).Complexity = 0;
53 block.SampleTimes = [0 0];
54 block.SetAccelRunOnTLC(false);
55
56 block.OutputPort(1).Dimensions = 1;
57
58 % Register methods
59 block.RegBlockMethod('SetInputPortSamplingMode',@SetInpPortFrameData);
60 block.RegBlockMethod('SetInputPortDimensions', @SetInpPortDims);
61 block.RegBlockMethod('Outputs', @Outputs);
62
63 function SetInpPortFrameData(block, idx, fd) %#ok<INUSL>
64 block.InputPort(1).SamplingMode = fd;
65 block.OutputPort(1).SamplingMode = fd;
66
67 function SetInpPortDims(block, idx, di)
68 block.InputPort(idx).Dimensions = di;
69
70
71 %% Outputs
72 function Outputs(block)
73 global LTPDAinvar gl loop execModels %#ok<NUSED>
74
75 % disp(['The name of the currently executed block is ',get_param(get_param(gcbh,'Parent'),'Name')])
76 % disp(['The current size of LTPDAinvar is ',num2str(size(LTPDAinvar,1))])
77
78 currparent = get_param(gcbh,'Parent');
79 subsyshandle = get_param(currparent, 'handle');
80
81 % Check if it's a partial execution:
82 if ismember('executionList',evalin('base','who'))
83 executionList = evalin('base','executionList');
84 blockIndex = -1;
85 for i=1:size(executionList,1), if executionList(i)==gcbh, blockIndex = i; break; end; end
86 if blockIndex == -1 % this block is not included in the list of those to be executed.
87 disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will be skipped.'])
88 block.OutputPort(1).Data = -1;
89 return
90 else
91 % Check if the block must receive data from Simulink or retrieve them from a previous calculation:
92 if block.InputPort(1).Data(1)==-1
93 annotation = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
94 execHistory = get_param(annotation,'UserData');
95 blockIndex = -1;
96 for i=1:size(execHistory,1), if execHistory{i,2}==subsyshandle, blockIndex = i; end; end
97 if blockIndex == -1 % something's wrong: the selected block is not included in the previous execution history
98 warning(['*** Partial execution: impossible to retrieve inputs. The block ',get_param(subsyshandle,'Name'),' seems to have never been executed.'])
99 block.OutputPort(1).Data = -1;
100 return
101 end
102 j=3;
103 while ~isempty(execHistory{blockIndex,j})
104 block.InputPort(1).Data(j-2) = execHistory{blockIndex,j};
105 j = j+1;
106 end
107 disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will now be executed with data retrieved from previous calculation.'])
108 else
109 disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will now be executed.'])
110 end
111 end
112 end
113
114 % Check if the user wants to stop the execution:
115 lastChar = get(findobj('Name','LTPDA Progress Bar'),'CurrentCharacter');
116 if ~isempty(lastChar) && strcmp(lastChar,'x')
117 set_param(bdroot, 'SimulationCommand', 'stop')
118 return
119 end
120
121 % Update the progress bar window:
122 progressBar = findobj('Tag','progressaxes');
123 if ~isempty(progressBar)
124 blocksTotal = get(findobj('Tag','blockstotal'),'UserData');
125 blocksDone = get(findobj('Tag','done'),'UserData');
126 blocksToGo = get(findobj('Tag','togo'),'UserData');
127 set(findobj('Tag','done'),'UserData',blocksDone+1);
128 set(findobj('Tag','togo'),'UserData',blocksToGo-1);
129 set(findobj('Tag','done'),'String',['Done: ',num2str(blocksDone+1)]);
130 set(findobj('Tag','togo'),'String',['To go: ',num2str(blocksToGo-1)]);
131 currblockname = get_param(get_param(gcbh,'Parent'),'Name');
132 for i=1:numel(currblockname), if double(currblockname(i))==10, currblockname(i)=' '; end; end
133 set(findobj('Tag','currentexec'),'String',['Current block: ',currblockname]);
134 progressSize = get(progressBar,'UserData');
135 progressPosition = get(progressBar,'Position');
136 progressPosition(3) = 1+(progressSize(3)/blocksTotal)*(blocksDone+1);
137 set(progressBar,'Position',progressPosition)
138 progressBarLoop = findobj('Tag','progressaxes2');
139 if ~isempty(progressBarLoop)
140 set(findobj('Tag','done'),'String',['Blocks done: ',num2str(blocksDone+1)]);
141 loopStatus = getappdata(0,'loopStatus');
142 progressSize = get(progressBarLoop,'UserData');
143 progressLoopPosition = get(progressBarLoop,'Position');
144 progressLoopPosition(3) = 1+(progressSize(3)/loopStatus(1))*loopStatus(2);
145 set(progressBarLoop,'Position',progressLoopPosition)
146 end
147 drawnow
148 end
149
150 % Retrieve the function name from the block tag:
151 blocktag = get_param(gcb, 'tag');
152 if strcmp(blocktag,'generic')
153 paramcommand = get_param(get_param(get_param(gcbh,'Parent'), 'handle'),'Description');
154 eval(paramcommand)
155 blocktag = functionName;
156 end
157
158 % ========================================================================
159 %% For arithmetic blocks
160 % ========================================================================
161
162 if strcmp(blocktag,'arithmetic')
163 eq = get_param(currparent,'MaskDescription');
164 if strcmp(eq,'Arithmetic block: the user can type in the desidered equation.')
165 disp(sprintf('Error: in the ''%s'' arithmetic block no equation has been typed in.',get_param(currparent,'Name')))
166 close(findobj('Name','LTPDA Progress Bar'))
167 error(['Error: in the ',get_param(currparent,'Name'),' arithmetic block no equation has been typed in.'])
168 end
169 equation = eq;
170 equation(strfind(equation,char(10))) = [];
171
172 paramcommand = get_param(subsyshandle,'Description');
173 if isempty(paramcommand), paramcommand = 'params=plist(''alpha'',''-->'',''beta'',''-->'');'; end
174 eval(paramcommand)
175
176 for i=1:nparams(params)
177 inputBlock = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','Name',lower(params.params(i).key));
178 if numel(inputBlock)>1, inputBlock(1)=[]; end % this can happen only when the currparent has the same name: then, ignore the 1st, ie. the currparent itself
179 inputNumb = get_param(inputBlock{1},'Tag');
180 equation = strrep(equation,lower(params.params(i).key),['LTPDAinvar{block.InputPort(1).Data(',inputNumb,'),1}']);
181 end
182
183 try
184 outdata = eval(equation);
185
186 % Setting the output signal name:
187 ifsetName = get_param(currparent,'UserData');
188 if ~isempty(ifsetName) && isa(ifsetName,'char')
189 % ifsetName = ifsetName;
190 else
191 ifsetName = get_param(currparent,'Name');
192 end
193 if numel(outdata)==1
194 outdata = setName(outdata,ifsetName);
195 else
196 for i=1:size(outdata,1)
197 for j=1:size(outdata,2)
198 outdata(i,j) = setName(outdata(i,j),[ifsetName,'_',num2str(i),'-',num2str(j)]);
199 end
200 end
201 end
202
203 x = size(LTPDAinvar,1);
204 blockName = currparent;
205 for i=1:numel(blockName), if double(blockName(i))==10, blockName(i)=' '; end; end
206 LTPDAinvar = [LTPDAinvar;{outdata,0,blockName}];
207 block.OutputPort(1).Data = x+1;
208
209 probe = get_param(currparent,'MaskHelp');
210 if ~isempty(probe) && isa(probe,'char') && strcmp(probe,'probe')
211 LTPDAinvar{size(LTPDAinvar,1),2} = 2;
212 end
213
214 return
215
216 catch err
217 disp(sprintf('Something''s wrong in the calculation of the results of the ''%s'' arithmetic block.',get_param(currparent,'Name')))
218 disp('The inputs were:')
219 for i=1:nparams(params)
220 disp(['Input ',num2str(i),':'])
221 LTPDAinvar{block.InputPort(1).Data(i),1} %#ok<NOPRT>
222 end
223 disp('The equation was:')
224 disp(eq)
225 close(findobj('Name','LTPDA Progress Bar'))
226
227 rethrow(err)
228
229 end
230 end % for the arithmetic block
231
232 % ========================================================================
233 %% =================== RETRIEVE THE PARAMETERS LIST ======================
234 % ========================================================================
235
236 % The command line to produce the proper parameters list for the block
237 % currently executed is retrieved from the description of this parent subsystem
238 paramcommand = get_param(subsyshandle,'Description');
239 loopExecution = 0;
240
241 if ~isempty(paramcommand)
242 eval(paramcommand);
243
244 % Verify if a global variable or nested loop variable has been used:
245 for i=1:nparams(params)
246 currVal = params.params(i).val;
247 if ischar(currVal) && numel(currVal)>5 && strcmp(currVal(1:5),'loop.')
248 loopExecution = 1;
249 try %#ok<ALIGN>
250 setVal(params.params(i),eval(params.params(i).val));
251 catch, end
252 end
253 if ischar(currVal) && numel(currVal)>3 && strcmp(currVal(1:3),'gl.')
254 try %#ok<ALIGN>
255 setVal(params.params(i),eval(params.params(i).val));
256 catch, end
257 end
258 end
259
260 if ~exist('paramEnabled','var'), paramEnabled = ones(1,nparams(params)); end
261 currparam = plist();
262 for jj=1:nparams(params)
263 if paramEnabled(jj)==1
264 if numel(params.params(jj).key)>6 && strcmpi(params.params(jj).key(1:7),'addPar_'), params.params(jj).setKey(params.params(jj).key(8:end)); end
265 currparam = append(currparam,params.params(jj));
266 end
267 end
268 else
269 currparam = plist();
270 end
271
272 % Retrieve parameters from Simulink:
273 paramFromSimulink = 0;
274 for i=1:nparams(currparam)
275 paramKey2 = currparam.params(i).key;
276 if numel(paramKey2)>7 && strcmpi(paramKey2(1:7),'addPar_'), paramKey = paramKey2(8:end); else paramKey = paramKey2; end
277 paramVal = currparam.params(i).val;
278 if (isa(paramVal,'char') && strcmp(paramVal,'-->')) || (isa(paramVal,'cell') && numel(paramVal)==1 && isa(paramVal{1},'char') && strcmp(paramVal{1},'-->'))
279 paramFromSimulink = paramFromSimulink + 1;
280 mux = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','BlockType','Mux');
281 muxWidths = get_param(mux{1},'CompiledPortWidths');
282 muxWidths = muxWidths.Inport;
283 inputBlock = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','Name',lower(paramKey));
284 if numel(inputBlock)>1, inputBlock(1)=[]; end % this can happen only when the currparent has the same name: then, ignore the 1st, ie. the currparent itself
285 inputNumb = str2double(get_param(inputBlock{1},'Tag'));
286 x = 0;
287 for j=1:inputNumb-1
288 x=x+muxWidths(j);
289 end
290 paramVal = [LTPDAinvar{block.InputPort(1).Data(x+1)}];
291 if muxWidths(inputNumb)>1
292 for j=x+2:x+muxWidths(inputNumb)
293 paramVal = [paramVal,LTPDAinvar{block.InputPort(1).Data(j)}];
294 end
295 end
296 currparam = pset(currparam,paramKey2,paramVal);
297 end
298 end
299
300 % ========================================================================
301 %% ======================== CALCULATE THE RESULTS =========================
302 % ========================================================================
303
304 y = length(block.InputPort(1).Data) - paramFromSimulink;
305
306 % Check to verify the type of function:
307 specFunc = {'display'};
308 [meth,clas] = strtok(get(get_param(currparent,'Handle'),'Tag'));
309 category = '';
310 if strcmp(meth,'method')
311 clas = strtrim(clas);
312 infoObj = eval([clas,'.getInfo(''',blocktag,''')']);
313 category = infoObj.mcategory;
314 end
315
316 if ismember(blocktag,specFunc)
317 % Input, but no parameters and no output
318 type = 4;
319 % ======================================
320 elseif strcmp(category,'Output')
321 % Input and parameters, but no output
322 type = 3;
323 % ===================================
324 elseif ~isempty(utils.prog.find_in_models(currparent,'SearchDepth',1,'LookUnderMasks','all','BlockType','Ground')) || block.InputPort(1).Data(1)==0
325 % No input, but parameters and output
326 type = 2;
327 % ===================================
328 elseif nparams(currparam)==0
329 % Input and output, but no parameters
330 type = 1;
331 % ============================
332 else
333 % Input, parameters and output
334 type = 0;
335 % ============================
336 end
337
338
339 switch type
340 % ====================================================================
341 case 4 % Input, but no parameters and no output
342 command = 'feval(blocktag,';
343 for i=1:y
344 dVal = block.InputPort(1).Data(i);
345 if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
346 end
347 command = [command(1:end-1) ');'];
348 try
349 eval(command)
350 catch err
351 disp('4')
352 disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
353 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
354 disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
355 close(findobj('Name','LTPDA Progress Bar'))
356 rethrow(err)
357 end
358 block.OutputPort(1).Data = 1; % this will be ignored: no output expected.
359 return;
360
361 % ====================================================================
362 case 3 % Input and parameters, but no output
363 try
364 command = 'feval(blocktag,';
365 for i=1:y
366 dVal = block.InputPort(1).Data(i);
367 if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
368 end
369 command = [command 'currparam);'];
370 if strcmpi(blocktag,'plot'), figure; end;
371 eval(command);
372 catch err
373 disp('3')
374 disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
375 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
376 disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
377 disp('The plist passed was:')
378 currparam %#ok<NOPRT>
379 close(findobj('Name','LTPDA Progress Bar'))
380 rethrow(err)
381 end
382 block.OutputPort(1).Data = 1; % this will be ignored: no output expected.
383 return;
384
385 % ====================================================================
386 case 2 % No input, but parameters and output
387 try
388 outdata = feval(blocktag,currparam);
389 catch err
390 disp('2')
391 disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
392 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
393 disp('The plist passed was:')
394 currparam %#ok<NOPRT>
395 close(findobj('Name','LTPDA Progress Bar'))
396 rethrow(err)
397 end
398
399 % ====================================================================
400 case 1 % Input and output, but no parameters
401 try
402 command = 'outdata = feval(blocktag,';
403 for i=1:y
404 dVal = block.InputPort(1).Data(i);
405 if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
406 end
407 command = [command(1:end-1) ');'];
408 eval(command);
409 catch err
410 disp('1')
411 disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
412 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
413 disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
414 disp('The plist passed was:')
415 currparam %#ok<NOPRT>
416 close(findobj('Name','LTPDA Progress Bar'))
417 rethrow(err)
418 end
419 % ====================================================================
420 case 0 % Input, parameters and output
421 try
422 command = 'outdata = feval(blocktag,';
423 for i=1:y
424 dVal = block.InputPort(1).Data(i);
425 if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
426 end
427 command = [command 'currparam);'];
428 eval(command);
429 catch err
430 disp('1')
431 disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
432 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
433 disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
434 disp('The plist passed was:')
435 currparam %#ok<NOPRT>
436 close(findobj('Name','LTPDA Progress Bar'))
437 rethrow(err)
438 end
439
440 end
441
442 %==========================================================================
443 %% Cycle to remove objects from memory as soon as they're no longer necessary:
444 %==========================================================================
445
446 if ~getappdata(0,'maintainresults');
447 portConnectivity = get(subsyshandle,'PortConnectivity');
448 for i = 1:numel(portConnectivity)
449 notUsedAnymore = 1;
450 if isempty(portConnectivity(i).DstBlock)
451 parentBlock = portConnectivity(i).SrcBlock;
452 childrenBlocks = utils.prog.findchildren(parentBlock);
453 for j = 1:numel(childrenBlocks)
454 if isempty(get_param(childrenBlocks(j),'MaskVariables')), notUsedAnymore = 0; end
455 end
456 % disp(sprintf('*** (Currently executing block %s)',get_param(currparent,'Name')))
457 if notUsedAnymore && LTPDAinvar{block.InputPort(1).Data(i),2}==0
458 % disp(['*** Removed object n°.',num2str(block.InputPort(1).Data(i)),', which was a ',class(LTPDAinvar{block.InputPort(1).Data(i),1}),'.'])
459 disp(sprintf('*** (Currently executing block %s)',get_param(currparent,'Name')))
460 LTPDAinvar(block.InputPort(1).Data(i),:) = [];
461
462 % Update the execution history:
463 ltpda_annotation = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
464 execHistory = get_param(ltpda_annotation,'UserData');
465 for xx=1:size(execHistory,1)
466 for yy=3:size(execHistory,2),
467 if execHistory{xx,yy} == block.InputPort(1).Data(i), execHistory{xx,yy} = -1; end
468 if execHistory{xx,yy} > block.InputPort(1).Data(i), execHistory{xx,yy} = execHistory{xx,yy}-1; end
469 end
470 if execHistory{xx,1} == block.InputPort(1).Data(i), execHistory{xx,1} = -1; end
471 if execHistory{xx,1} > block.InputPort(1).Data(i), execHistory{xx,1} = execHistory{xx,1}-1; end
472 end
473 set_param(ltpda_annotation,'UserData',execHistory);
474
475 disp('*** LTPDA object cleared from memory, being now unused')
476 end
477 end
478 end
479 end
480 % ========================================================================
481 %% =============================== OUTPUTS ================================
482 % ========================================================================
483
484 % Set this block status to 'executed':
485 set(subsyshandle,'MaskVariables','1');
486 set(gcbh,'MaskVariables','1');
487
488 signalName = get_param(currparent,'Name');
489 ifsetName = get_param(currparent,'UserData');
490 if ~isempty(ifsetName) && isa(ifsetName,'double') && ifsetName==1
491 if numel(outdata)==1
492 outdata = setName(outdata,signalName);
493 else
494 for i=1:size(outdata,1)
495 for j=1:size(outdata,2)
496 outdata(i,j) = set(outdata(i,j),'name',[signalName,'_',num2str(i),'-',num2str(j)]);
497 end
498 end
499 end
500 end
501
502 try
503 % To prevent the result calculated here from being canceled at the
504 % end of the calculation:
505 probe = get_param(currparent,'MaskHelp');
506 if ~isempty(probe) && isa(probe,'char') && strcmp(probe,'probe'), keepThis = 1;
507 else keepThis = 0;
508 end
509
510 % Appending the results:
511 ltpda_annotation = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
512 execHistory = get_param(ltpda_annotation,'UserData');
513 % If there's a previous execution history, check if the current block belongs to it:
514 if ~isempty(execHistory)
515 member = 0;
516 for i=1:size(execHistory,1)
517 if execHistory{i,2}==subsyshandle, member = 1; index = i; break; end
518 end
519 end
520 if ~isempty(execHistory) && member && ~loopExecution
521 x = execHistory{index,1};
522 if x==-1, x = size(LTPDAinvar,1)+1; end
523 for i=1:numel(currparent), if double(currparent(i))==10, currparent(i)=' '; end; end
524 LTPDAinvar(x,:) = {outdata,keepThis,currparent};
525 block.OutputPort(1).Data = x;
526 execHistory{index,1} = x ;
527 execHistory{index,2} = subsyshandle ;
528 for i=1:y , execHistory{index,2+i} = block.InputPort(1).Data(i); end
529 else
530 if isempty(execHistory), execHistory = {}; end
531 x = size(LTPDAinvar,1);
532 LTPDAinvar = [LTPDAinvar;{outdata,keepThis,currparent}];
533 block.OutputPort(1).Data = x+1;
534 execHistory{end+1,1} = x+1 ;
535 execHistory{end,2} = subsyshandle ;
536 for i=1:y , execHistory{end,2+i} = block.InputPort(1).Data(i); end
537 end
538
539 % Update the execution history:
540 % Are stored the the output obj pointer, the handle of the current subsytem and the input obj pointers:
541 set_param(ltpda_annotation,'UserData',execHistory);
542
543
544 catch
545 disp('------------------============================================------------------')
546 disp('------------------============================================------------------')
547 disp('------------------============================================------------------')
548 disp(sprintf('Something''s wrong in appending to the global variable LTPDAinvar the results of the %s function.',blocktag))
549 disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
550 disp('The data to be saved, calculated into this block, are:')
551 outdata %#ok<NOPRT>
552 disp('------------------============================================------------------')
553 disp('------------------============================================------------------')
554 disp('------------------============================================------------------')
555 end
556
557 %==========================================================================
558
559 % The output to Simulink is just the ordinal number (ONE AND ONLY ONE
560 % NUMBER) of the object appended to the global array LTPDAinvar.
561
562 % endfunction