linlsqOptimization.m 24 KB

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