RDX_linlsqOptimization.m 25 KB

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