linlsqOptimization.asv 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. function [weights, dose] = linlsqOptimization(optSettings)
  2. % Input: optSettings structure, defined as follows:
  3. % optSettings
  4. % .prescInfo
  5. % .savePresc = 'yes' or 'no' (defaults to 'no')
  6. % .presc (prescription structure, which has the form:
  7. % .presc.siz = [M N Q] size of each beamlet
  8. % .presc.tissue = Structure array of tissue-specific
  9. % prescription. savePrescription subfunction has details.
  10. % Defaults to an empty matrix.
  11. % .prescFile (defaults to 'prescription.txt')
  12. %
  13. % .beamletInfo
  14. % .saveBeamlets = 'yes' or 'no' (defaults to 'no')
  15. % .saveBeamletHeader = 'yes' or 'no' (defaults to 'no')
  16. % .beamletFolder (defaults to 'beamlets')
  17. % .beamlets (cell array of Matlab-style sparse beamlets stored as:
  18. % .beamlets{n}.Dij{m}, where Dij contains the sparse data)
  19. % Defaults to an empty matrix.
  20. % .beamletBatchBaseName (defaults to 'beamletbatch')
  21. % .beamletBatchExtension (defaults to 'bin')
  22. % .beamletHeaderFileName (defaults to 'beamlet_header.txt')
  23. % .Nbeamlets (defaults to 1)
  24. % .Nbeamletbatches (defaults to 1)
  25. % .beamletDim (defaults to [1 1 1])
  26. %
  27. % .optInfo
  28. % .saveOptInfo = 'yes' or 'no' (defaults to 'no')
  29. % .optRun = 'yes' or 'no' (defaults to 'no')
  30. % .waitForResults = 'yes' or 'no' (defaults to 'no')
  31. % .optFolder (Folder containing optimizer's Visual C++ code)
  32. % Defaults to 'C:\rtflynn\library\matlabAdditions\optimization\linlsq\linlsqOpt'
  33. % .inputFolder (defaults to 'input')
  34. % .outputFolder (defaults to 'output')
  35. % .inputFile (defaults to optInput.txt)
  36. % .Niterations (Defaults to 100)
  37. % .Nperbatch (Defaults to 50)
  38. % .doseBatchBaseName (defaults to 'dosebatch')
  39. % .doseBatchExtension (defaults to 'img')
  40. % .weightBatchBaseName (defaults to 'weightbatch')
  41. % .weightBatchExtension (defaults to 'img')
  42. % .optExeName (defaults to 'linlsqOpt.exe')
  43. % .objFuncFileName (defaults to 'objFunc.bin')
  44. %
  45. % .initialGuessInfo
  46. % .saveInitialGuess = 'yes' or 'no' (defaults to 'no')
  47. % .w0 (initial guess for all beamlet weights, defaults to empty matrix)
  48. % .initBeamWeightFile (defaults to 'init_beam_weight_file')
  49. %
  50. % Outputs:
  51. % w optimized beamlet weights
  52. % dose optimized dose distribution
  53. %
  54. % Outputs are empty matrices unless the 'waitForResults' flag is set to
  55. % 'yes'. Otherwise, load results at a later time.
  56. % Check input structure for inconistencies, set defaults for fields that
  57. % were optional and not included.
  58. optSettings = parseInput(optSettings);
  59. % From this point on, it is assumed that all fields that are required for
  60. % any functions for the rest of this script are present.
  61. origDir = pwd; % copy the directory path from which this script was called
  62. warning off;
  63. % Change to the directory containing the linlsqOpt source code.
  64. cd(optSettings.optInfo.optFolder);
  65. % save prescription info
  66. savePrescription(optSettings);
  67. % save beamlet info
  68. saveBeamlets(optSettings);
  69. % save initial guess info
  70. saveInitialGuess(optSettings);
  71. % save optimization info
  72. saveOptInfo(optSettings);
  73. % run optimizer
  74. [weights, dose] = runOptimizer(optSettings);
  75. cd(origDir); % go back to the original directory
  76. function optSettings = parseInput(optSettings)
  77. % Checks the optSettings input structure for inconsistencies and applies
  78. % defaults to optional fields that were not included in the input
  79. % structure.
  80. % set optimizer defaults
  81. optSettingsDefault.prescInfo.savePresc = 'no';
  82. optSettingsDefault.prescInfo.presc = [];
  83. optSettingsDefault.prescInfo.prescFileName = 'prescription.txt';
  84. optSettingsDefault.beamletInfo.saveBeamlets = 'no';
  85. optSettingsDefault.beamletInfo.saveBeamletHeader = 'no';
  86. optSettingsDefault.beamletInfo.beamletFolder = 'beamlets';
  87. optSettingsDefault.beamletInfo.beamlets = [];
  88. optSettingsDefault.beamletInfo.beamletBatchBaseName = 'beamletbatch';
  89. optSettingsDefault.beamletInfo.beamletBatchExtension = 'bin';
  90. optSettingsDefault.beamletInfo.beamletHeaderFileName = 'beamlet_header.txt';
  91. optSettingsDefault.beamletInfo.Nbeamlets = 1;
  92. optSettingsDefault.beamletInfo.Nbeamletbatches = 1;
  93. optSettingsDefault.beamletInfo.beamletDim = [1 1 1];
  94. optSettingsDefault.optInfo.saveOptInfo = 'no';
  95. optSettingsDefault.optInfo.optRun = 'no';
  96. optSettingsDefault.optInfo.waitForResults = 'no';
  97. optSettingsDefault.optInfo.optFolder = ...
  98. 'C:\rtflynn\library\matlabAdditions\optimization\linlsq\linlsqOpt';
  99. optSettingsDefault.optInfo.inputFolder = 'input';
  100. optSettingsDefault.optInfo.outputFolder = 'output';
  101. optSettingsDefault.optInfo.inputFile = 'optInput.txt';
  102. optSettingsDefault.optInfo.Niterations = 100;
  103. optSettingsDefault.optInfo.Nperbatch = 50;
  104. optSettingsDefault.optInfo.doseBatchBaseName = 'dosebatch';
  105. optSettingsDefault.optInfo.doseBatchExtension = 'img';
  106. optSettingsDefault.optInfo.weightBatchBaseName = 'weightbatch';
  107. optSettingsDefault.optInfo.weightBatchExtension = 'img';
  108. optSettingsDefault.optInfo.optExeName = 'linlsqOpt.exe';
  109. optSettingsDefault.optInfo.objFuncFileName = 'objFunc.bin';
  110. optSettingsDefault.initialGuessInfo.saveInitialGuess = 'no';
  111. optSettingsDefault.initialGuessInfo.w0 = [];
  112. optSettingsDefault.initialGuessInfo.initBeamWeightFile = 'init_beam_weight_file.img';
  113. % Traverse the optSettings structure tree, and, whenever a leaf is missing
  114. % in the tree, fill it in with its default value.
  115. nodeVec = [1 1]; % vector corresponding to nodes in the structure tree
  116. while length(nodeVec) > 1 % traverse as long as we're not back at the top of the tree
  117. % Create a field access string, which is used to access the node
  118. % referred to by nodeVec.
  119. fldAccStr = 'optSettingsDefault';
  120. for k=2:length(nodeVec)
  121. eval(['fieldNames = fieldnames(' fldAccStr ');']);
  122. fldAccStr = [fldAccStr '.' fieldNames{nodeVec(k)}];
  123. end
  124. % Check if the field is a number or a string (leaf).
  125. eval(['numFlag = isnumeric(' fldAccStr ');']);
  126. eval(['charFlag = ischar(' fldAccStr ');']);
  127. if numFlag == 1 | charFlag == 1 % found a leaf
  128. % Traverse input structure, searching for leaves that exist in the
  129. % default structure but do not exist in the input structure. This
  130. % is done by working down the input tree until either a node or a
  131. % leaf is missing from the input
  132. fldAccStrInp = 'optSettings';
  133. fldAccStrDummy = 'optSettingsDefault';
  134. for k=2:length(nodeVec)
  135. eval(['fieldNamesDummy = fieldnames(' fldAccStrDummy ');']);
  136. eval(['fieldFlag = isfield(' fldAccStrInp ',''' fieldNamesDummy{nodeVec(k)} ''');']);
  137. if fieldFlag == 1
  138. fldAccStrInp = [fldAccStrInp '.' fieldNamesDummy{nodeVec(k)}];
  139. fldAccStrDummy = [fldAccStrDummy '.' fieldNamesDummy{nodeVec(k)}];
  140. else % Leaf cannot exist, so set it to its default.
  141. [tok,rem] = strtok(fldAccStr,'.');
  142. fldAccStrInp = ['optSettings' rem];
  143. eval([fldAccStrInp ' = ' fldAccStr ';']);
  144. break;
  145. end
  146. end
  147. % go to the next node vector
  148. while length(nodeVec) > 1
  149. fldAccStr = 'optSettingsDefault';
  150. for k=2:length(nodeVec)
  151. eval(['fieldNames = fieldnames(' fldAccStr ');']);
  152. fldAccStr = [fldAccStr '.' fieldNames{nodeVec(k)}];
  153. end
  154. % Go to next leaf or nodes on the same level, if one exists.
  155. if nodeVec(end) + 1 <= length(fieldNames)
  156. nodeVec(end) = nodeVec(end) + 1;
  157. break;
  158. else % No more leaves or nodes on this level, so go up a level.
  159. nodeVec(:,end) = [];
  160. end
  161. end
  162. else % found another field that is not a leaf
  163. nodeVec = [nodeVec 1]; % go down a level in the tree
  164. end
  165. end
  166. % If beamlets were included with optSettings, ensure that the
  167. % Nbeamletbatches, Nbeamlets, and beamletDim fields are consistent
  168. if ~isempty(optSettings.beamletInfo.beamlets)
  169. % get the beamlet dimensions
  170. [Xcount,Ycount,Zcount] = size(full(optSettings.beamletInfo.beamlets{1}.Dij{1}));
  171. optSettings.beamletInfo.beamletDim = [Xcount Ycount Zcount];
  172. optSettings.beamletInfo.Nbeamletbatches = numel(optSettings.beamletInfo.beamlets);
  173. optSettings.beamletInfo.Nbeamlets = 0;
  174. for k=1:optSettings.beamletInfo.Nbeamletbatches
  175. optSettings.beamletInfo.Nbeamlets = optSettings.beamletInfo.Nbeamlets ...
  176. + numel(optSettings.beamletInfo.beamlets{k}.Dij);
  177. end
  178. else
  179. % If optSettings.prescInfo.presc.siz was defined, then that field
  180. % should contain beamlet dimensions.
  181. if isfield(optSettings.prescInfo.presc,'siz')
  182. optSettings.beamletInfo.beamletDim = optSettings.prescInfo.presc.siz;
  183. end
  184. end
  185. function savePrescription(optSettings)
  186. % If the savePresc field is 'yes', saves a prescription structure to a file
  187. % format that is useable by the linear least squares optimizer.
  188. %% .prescInfo
  189. %% .savePresc = 'yes' or 'no'
  190. % .presc (prescription structure, which has the form:
  191. % .presc.siz = [M N Q] size of each beamlet
  192. % .presc.tissue = structure array of tissue-specific
  193. % prescription. savePrescription subfunction has details.
  194. % .prescFile (defaults to 'prescription.txt')
  195. infValue = 10e10; % value of infinity
  196. % Check the savePresc flag. If 'no', do not save prescription
  197. if strcmp(lower(optSettings.prescInfo.savePresc),'no')
  198. return;
  199. end
  200. % create input and output folders
  201. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.inputFolder]);
  202. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.outputFolder]);
  203. % Check the prescription structure to ensure all proper fields are present.
  204. prescTissueFields = {'name' 'alpha' 'betaVPlus' 'dVPlus' 'vPlus' 'betaVMinus' ...
  205. 'dVMinus' 'vMinus' 'betaPlus' 'betaMinus' 'ind' 'dPlus' 'dMinus'};
  206. if isempty(optSettings.prescInfo.presc)
  207. error('optSettings.prescInfo.presc not defined -- define it or set optSettings.prescInfo.savePresc to ''no''');
  208. end
  209. if ~isfield(optSettings.prescInfo.presc,'siz')
  210. error('Missing ''siz'' field from optSettings.prescInfo.presc structure');
  211. end
  212. % siz is present, so make sure it is a 3 element vector
  213. if numel(optSettings.prescInfo.presc.siz) ~= 3
  214. error('The ''siz'' field from optSettings.prescInfo.presc structure must be a 3-element vector');
  215. end
  216. if ~isfield(optSettings.prescInfo.presc,'tissue')
  217. error('Missing ''tissue'' field from prescInfo.presc structure');
  218. end
  219. % search for subfields of tissue field
  220. for k=1:numel(prescTissueFields)
  221. if ~isfield(optSettings.prescInfo.presc.tissue,prescTissueFields{k})
  222. error(['Missing ''' prescTissueFields{k} ''' field from optSettings.prescInfo.presc.tissue structure']);
  223. end
  224. end
  225. % write the prescription file
  226. fid = fopen([optSettings.optInfo.optFolder '\' optSettings.optInfo.inputFolder '\' optSettings.prescInfo.prescFileName],'w');
  227. if fid == -1
  228. error('Failed to open prescription file.');
  229. end
  230. fprintf(fid,'Ntissue\n');
  231. fprintf(fid,[num2str(numel(optSettings.prescInfo.presc.tissue)) '\n\n']);
  232. for k=1:numel(optSettings.prescInfo.presc.tissue) % write prescription parameters for each tissue type
  233. % write the current tissue number
  234. fprintf(fid,'tissueNum\n');
  235. fprintf(fid,[num2str(k-1) '\n']);
  236. fprintf(fid,'name\n');
  237. fprintf(fid,[optSettings.prescInfo.presc.tissue(k).name '\n']);
  238. % throughout writing process, always check for infinities before printing values
  239. % write the tissue importance
  240. fprintf(fid,'alpha\n');
  241. if isinf(optSettings.prescInfo.presc.tissue(k).alpha)
  242. fprintf(fid,[num2str(infValue) '\n']);
  243. else
  244. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).alpha) '\n']);
  245. end
  246. fprintf(fid,'betaVPlus\n');
  247. if isinf(optSettings.prescInfo.presc.tissue(k).betaVPlus)
  248. fprintf(fid,[num2str(infValue) '\n']);
  249. else
  250. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).betaVPlus) '\n']);
  251. end
  252. fprintf(fid,'dVPlus\n');
  253. if isinf(optSettings.prescInfo.presc.tissue(k).dVPlus)
  254. fprintf(fid,[num2str(infValue) '\n']);
  255. else
  256. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).dVPlus) '\n']);
  257. end
  258. fprintf(fid,'vPlus\n');
  259. if isinf(optSettings.prescInfo.presc.tissue(k).vPlus)
  260. fprintf(fid,[num2str(infValue) '\n']);
  261. else
  262. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).vPlus) '\n']);
  263. end
  264. fprintf(fid,'betaVMinus\n');
  265. if isinf(optSettings.prescInfo.presc.tissue(k).betaVMinus)
  266. fprintf(fid,[num2str(infValue) '\n']);
  267. else
  268. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).betaVMinus) '\n']);
  269. end
  270. fprintf(fid,'dVMinus\n');
  271. if isinf(optSettings.prescInfo.presc.tissue(k).dVMinus)
  272. fprintf(fid,[num2str(infValue) '\n']);
  273. else
  274. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).dVMinus) '\n']);
  275. end
  276. fprintf(fid,'vMinus\n');
  277. if isinf(optSettings.prescInfo.presc.tissue(k).vMinus)
  278. fprintf(fid,[num2str(infValue) '\n']);
  279. else
  280. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).vMinus) '\n']);
  281. end
  282. fprintf(fid,'betaPlus\n');
  283. if isinf(optSettings.prescInfo.presc.tissue(k).betaPlus)
  284. fprintf(fid,[num2str(infValue) '\n']);
  285. else
  286. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).betaPlus) '\n']);
  287. end
  288. % create a name for the underdose penalty prescription file
  289. dose_presc_filename = [regexprep(optSettings.prescInfo.presc.tissue(k).name,{' ','\','/'},'') 'DosePlus.bin'];
  290. fprintf(fid,'dosePlusFilename\n');
  291. fprintf(fid,[optSettings.optInfo.inputFolder '/' dose_presc_filename '\n']);
  292. % save the mask as a sparse matrix
  293. fid2 = fopen([optSettings.optInfo.optFolder '\' optSettings.optInfo.inputFolder '\' dose_presc_filename],'wb');
  294. fwrite(fid2,optSettings.prescInfo.presc.siz,'int');
  295. fwrite(fid2,length(optSettings.prescInfo.presc.tissue(k).ind),'int');
  296. fwrite(fid2,optSettings.prescInfo.presc.tissue(k).ind-1,'int');
  297. fwrite(fid2,optSettings.prescInfo.presc.tissue(k).dPlus,'single');
  298. fclose(fid2);
  299. fprintf(fid,'betaMinus\n');
  300. if isinf(optSettings.prescInfo.presc.tissue(k).betaMinus)
  301. fprintf(fid,[num2str(infValue) '\n']);
  302. else
  303. fprintf(fid,[num2str(optSettings.prescInfo.presc.tissue(k).betaMinus) '\n']);
  304. end
  305. % create a name for the overdose penalty prescription file
  306. dose_presc_filename = [regexprep(optSettings.prescInfo.presc.tissue(k).name,{' ','\','/'},'') 'DoseMinus.bin'];
  307. fprintf(fid,'doseMinusFilename\n');
  308. fprintf(fid,[optSettings.optInfo.inputFolder '/' dose_presc_filename '\n']);
  309. % save the mask as a sparse matrix
  310. fid2 = fopen([optSettings.optInfo.optFolder '\' optSettings.optInfo.inputFolder '\' dose_presc_filename],'wb');
  311. fwrite(fid2,optSettings.prescInfo.presc.siz,'int');
  312. fwrite(fid2,length(optSettings.prescInfo.presc.tissue(k).ind),'int');
  313. fwrite(fid2,optSettings.prescInfo.presc.tissue(k).ind-1,'int');
  314. fwrite(fid2,optSettings.prescInfo.presc.tissue(k).dMinus,'single');
  315. fclose(fid2);
  316. fprintf(fid,'\n');
  317. end
  318. fclose(fid);
  319. function saveBeamlets(optSettings)
  320. % Saves a set of beamlets to filename. Dij is a cell array of beamlets,
  321. % each of which must be sparse 2D Matlab matrices.
  322. %
  323. %% .beamletInfo
  324. %% .saveBeamlets = 'yes' or 'no' (if 'yes', beamlets saved)
  325. %% .beamletFolder (required, folder containing beamlets their header)
  326. % .beamlets (cell array of Matlab-style sparse beamlets stored as:
  327. % .beamlets{n}.Dij{m}, where Dij contains the sparse data)
  328. % .beamletBatchBaseName (defaults to 'beamletbatch')
  329. % .beamletBatchExtension (defaults to 'bin')
  330. % .beamletHeaderFileName (defaults to 'beamlet_header.txt')
  331. % .Nbeamlets
  332. % .Nbeamletbatches
  333. % .beamletDim
  334. % Check if the beamlet header will need to be saved
  335. if strcmp(lower(optSettings.beamletInfo.saveBeamletHeader),'no') ...
  336. && strcmp(lower(optSettings.beamletInfo.saveBeamlets),'no')
  337. return;
  338. end
  339. if strcmp(lower(optSettings.beamletInfo.saveBeamlets),'yes')
  340. % delete all of the old beamlets and header info
  341. success = rmdir([optSettings.optInfo.optFolder '/' optSettings.beamletInfo.beamletFolder],'s');
  342. mkdir([optSettings.optInfo.optFolder '/' optSettings.beamletInfo.beamletFolder]);
  343. end
  344. % create a folder for the beamlets if it doesn't already exist
  345. mkdir(optSettings.beamletInfo.beamletFolder);
  346. % create the beamlet header file
  347. fid = fopen([optSettings.beamletInfo.beamletFolder '/' optSettings.beamletInfo.beamletHeaderFileName],'w');
  348. if fid == -1
  349. error('Failed to open beamlet header file.');
  350. end
  351. fprintf(fid,'beamletDim\n');
  352. fprintf(fid,'%g %g %g\n',optSettings.beamletInfo.beamletDim);
  353. fprintf(fid,'Nbeamlets\n');
  354. fprintf(fid,'%g\n',optSettings.beamletInfo.Nbeamlets);
  355. fprintf(fid,'Nbeamletbatches\n');
  356. fprintf(fid,'%g\n',optSettings.beamletInfo.Nbeamletbatches);
  357. fprintf(fid,'beamletFolder\n');
  358. fprintf(fid,[optSettings.beamletInfo.beamletFolder '\n']);
  359. fprintf(fid,'beamletBatchBaseName\n');
  360. fprintf(fid,[optSettings.beamletInfo.beamletBatchBaseName '\n']);
  361. fprintf(fid,'beamletBatchExtension\n');
  362. fprintf(fid,[optSettings.beamletInfo.beamletBatchExtension '\n']);
  363. fclose(fid);
  364. % Check if the beamlets need to be saved
  365. if strcmp(lower(optSettings.beamletInfo.saveBeamlets),'no')
  366. return;
  367. end
  368. if isempty(optSettings.beamletInfo.beamlets)
  369. error('No beamlets to save -- supply beamlets or set saveBeamlets to ''no''');
  370. end
  371. % Save the beamlets
  372. beamletNum = 0;
  373. for k=1:optSettings.beamletInfo.Nbeamletbatches
  374. beamletFileName = [optSettings.beamletInfo.beamletFolder '/' ...
  375. optSettings.beamletInfo.beamletBatchBaseName num2str(k-1) '.' ...
  376. optSettings.beamletInfo.beamletBatchExtension];
  377. fid = fopen(beamletFileName,'wb');
  378. if fid == -1
  379. error(['Failed to open ' beamletFileName]);
  380. end
  381. Nbib = numel(optSettings.beamletInfo.beamlets{k}.Dij); % number of beamlet in batch
  382. fwrite(fid,Nbib,'int');
  383. for m=1:Nbib
  384. % dimensions of current beamlet
  385. [Nx,Ny,Nz] = size(full(optSettings.beamletInfo.beamlets{k}.Dij{m}));
  386. if Nx - optSettings.beamletInfo.beamletDim(1) ~= 0 ...
  387. || Ny - optSettings.beamletInfo.beamletDim(2) ~= 0 ...
  388. || Nz - optSettings.beamletInfo.beamletDim(3) ~= 0
  389. error(['Dimensions of optSettings.beamletInfo.beamlets{' ...
  390. num2str(k) '}.Dij{' num2str(m) '} are not (' ...
  391. num2str(optSettings.beamletInfo.beamletDim(1)) ',' ...
  392. num2str(optSettings.beamletInfo.beamletDim(2)) ',' ...
  393. num2str(optSettings.beamletInfo.beamletDim(3)) ')']);
  394. end
  395. % save the beamlet
  396. fwrite(fid,beamletNum,'int'); % beamlet number
  397. fwrite(fid,optSettings.beamletInfo.beamletDim,'int'); % beamlet dimensions
  398. ind = find(optSettings.beamletInfo.beamlets{k}.Dij{m}); % beamlet indices
  399. ind = ind - 1; % convert Matlab indices to C indices
  400. fwrite(fid,numel(ind),'int'); % number of non-zero indices
  401. fwrite(fid,ind,'int'); % non-zero indices
  402. fwrite(fid,nonzeros(optSettings.beamletInfo.beamlets{k}.Dij{m}),'float');
  403. beamletNum = beamletNum + 1;
  404. end
  405. fclose(fid); % close the current beamlet batch
  406. end
  407. function saveInitialGuess(optSettings)
  408. % save the initial guess
  409. % exit if the initial guess is not to be saved
  410. if strcmp(lower(optSettings.initialGuessInfo.saveInitialGuess),'no')
  411. return;
  412. end
  413. % create input and output folders
  414. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.inputFolder]);
  415. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.outputFolder]);
  416. initBeamWeightFile = [optSettings.optInfo.optFolder '\' optSettings.optInfo.inputFolder ...
  417. '\' optSettings.initialGuessInfo.initBeamWeightFile];
  418. fid = fopen(initBeamWeightFile,'wb');
  419. if fid == -1
  420. error(['Failed to open initial beam weight file: ' initBeamWeightFile]);
  421. end
  422. fwrite(fid,optSettings.initialGuessInfo.w0,'float');
  423. fclose(fid);
  424. function saveOptInfo(optSettings)
  425. % Exit if optimization info is not supposed to be saved
  426. if strcmp(lower(optSettings.optInfo.saveOptInfo),'no')
  427. return;
  428. end
  429. % create input and output folders
  430. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.inputFolder]);
  431. success = mkdir([optSettings.optInfo.optFolder '/' optSettings.optInfo.outputFolder]);
  432. % create the optimization input file
  433. optInfoFileName = [optSettings.optInfo.optFolder '/' optSettings.optInfo.inputFile];
  434. fid = fopen(optInfoFileName,'w');
  435. if fid == -1
  436. error(['Failed to open optimization input file: ' optInfoFileName]);
  437. end
  438. fprintf(fid,'Niterations\n');
  439. fprintf(fid,'%d\n',optSettings.optInfo.Niterations);
  440. fprintf(fid,'Nperbatch\n');
  441. fprintf(fid,'%d\n',optSettings.optInfo.Nperbatch);
  442. fprintf(fid,'prescFile\n');
  443. fprintf(fid,[optSettings.optInfo.inputFolder '/' optSettings.prescInfo.prescFileName '\n']);
  444. fprintf(fid,'initBeamWeightFile\n');
  445. fprintf(fid,[optSettings.optInfo.inputFolder '/' optSettings.initialGuessInfo.initBeamWeightFile '\n']);
  446. fprintf(fid,'beamletHeaderFileName\n');
  447. fprintf(fid,[optSettings.beamletInfo.beamletFolder '/' optSettings.beamletInfo.beamletHeaderFileName '\n']);
  448. fprintf(fid,'doseBatchBaseName\n');
  449. fprintf(fid,[optSettings.optInfo.outputFolder '/' optSettings.optInfo.doseBatchBaseName '\n']);
  450. fprintf(fid,'doseBatchExtension\n');
  451. fprintf(fid,[optSettings.optInfo.doseBatchExtension '\n']);
  452. fprintf(fid,'weightBatchBaseName\n');
  453. fprintf(fid,[optSettings.optInfo.outputFolder '/' optSettings.optInfo.weightBatchBaseName '\n']);
  454. fprintf(fid,'weightBatchExtension\n');
  455. fprintf(fid,[optSettings.optInfo.weightBatchExtension '\n']);
  456. fprintf(fid,'objFuncFileName\n');
  457. fprintf(fid,[optSettings.optInfo.outputFolder '/' optSettings.optInfo.objFuncFileName '\n']);
  458. fclose(fid);
  459. % copy the most recent build of optimizer to the optFolder
  460. exeDebugFile = [optSettings.optInfo.optFolder '\Debug\' optSettings.optInfo.optExeName];
  461. exeFile = [optSettings.optInfo.optFolder '\' optSettings.optInfo.optExeName];
  462. success = copyfile(exeDebugFile,exeFile);
  463. if success == 0
  464. error(['Error copying ' exeDebugFile ' to ' exeFile]);
  465. end
  466. function [weights, dose] = runOptimizer(optSettings)
  467. dose = [];
  468. weights = [];
  469. % Exit function of optimizer is not to be run.
  470. if strcmp(lower(optSettings.optInfo.optRun),'no')
  471. return;
  472. end
  473. currDir = pwd; % save a copy of the current directory path
  474. % do the optimization
  475. delete('optDone.txt'); % delete the old dose file if it exists
  476. fprintf('Running optimizer...\n');
  477. % run the optimization from the console
  478. eval(['! ' optSettings.optInfo.optExeName ' ' optSettings.optInfo.inputFile ' &']);
  479. % Exit if the user did not wish to wait for results
  480. if strcmp(lower(optSettings.optInfo.waitForResults),'no')
  481. return;
  482. end
  483. % Check for the flag indicating that the optimization is finished
  484. while ~exist('optDone.txt','file')
  485. pause(1); % pause before resuming
  486. end
  487. % Optimization finished, so load the beamlet weights
  488. fid = fopen([optSettings.optInfo.outputFolder '/' ...
  489. optSettings.optInfo.weightBatchBaseName num2str(optSettings.optInfo.Niterations) '.' ...
  490. optSettings.optInfo.weightBatchExtension],'rb');
  491. if fid == -1
  492. error('Unable to open optimized weight results.');
  493. end
  494. weights = fread(fid,'float');
  495. fclose(fid);
  496. % load the dose distribution
  497. fid = fopen([optSettings.optInfo.outputFolder '/' ...
  498. optSettings.optInfo.doseBatchBaseName num2str(optSettings.optInfo.Niterations) '.' ...
  499. optSettings.optInfo.doseBatchExtension],'rb');
  500. if fid == -1
  501. error('Unable to open optimized dose results.');
  502. end
  503. dose = fread(fid,'float');
  504. try
  505. dose = reshape(dose,optSettings.beamletInfo.beamletDim);
  506. catch
  507. error(['Unable to reshape optimized dose distribution. Ensure ' ...
  508. 'that optSettings.beamletInfo.beamletDim field matches dose grid dimensions']);
  509. end