diff --git a/examples/matRad_example10_4DphotonRobust.m b/examples/matRad_example10_4DphotonRobust.m index aa088f4b5..ce697be9c 100644 --- a/examples/matRad_example10_4DphotonRobust.m +++ b/examples/matRad_example10_4DphotonRobust.m @@ -13,19 +13,19 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will +%% In this example we will % (i) create a small artifical phantom % (ii) add a movement to the phantom to create a 4D CT % (iii) create a photon treatment plan with two beams % (iv) perform dose calculation on each 4D CT % (iv) perform first a fluence optimization on the first CT scenario and then secondly -% another fluence optimization using the composite worst case paradigm +% another fluence optimization using the composite worst case paradigm % considering all 4D CTs -% (v) visualise all individual dose scenarios +% (v) visualise all individual dose scenarios % (vi) sample discrete scenarios from Gaussian uncertainty assumptions %% set matRad runtime configuration -matRad_rc +matRad_rc; %% Create an artifiical CT image series xDim = 150; @@ -37,82 +37,81 @@ ct.resolution.y = 2; % mm ct.resolution.z = 3; % mm ct.numOfCtScen = 1; - + % create an ct image series with zeros - it will be filled later ct.cubeHU{1} = ones(ct.cubeDim) * -1024; %% Create the VOI data for the phantom -% Now we define two structures for the phantom +% Now we define two structures for the phantom ixOAR = 1; ixPTV = 2; % define general VOI properties -cst{ixOAR,1} = 0; -cst{ixOAR,2} = 'contour'; -cst{ixOAR,3} = 'OAR'; -cst{ixPTV,1} = 1; -cst{ixPTV,2} = 'target'; -cst{ixPTV,3} = 'TARGET'; - -% define optimization parameter for both VOIs -cst{ixOAR,5}.TissueClass = 1; -cst{ixOAR,5}.alphaX = 0.1000; -cst{ixOAR,5}.betaX = 0.0500; -cst{ixOAR,5}.Priority = 2; % overlap priority for optimization - a higher number corresponds to a lower priority -cst{ixOAR,5}.Visible = 1; +cst{ixOAR, 1} = 0; +cst{ixOAR, 2} = 'contour'; +cst{ixOAR, 3} = 'OAR'; +cst{ixPTV, 1} = 1; +cst{ixPTV, 2} = 'target'; +cst{ixPTV, 3} = 'TARGET'; -cst{ixOAR,6}{1} = struct(DoseObjectives.matRad_SquaredOverdosing(10,30)); +% define optimization parameter for both VOIs +cst{ixOAR, 5}.TissueClass = 1; +cst{ixOAR, 5}.alphaX = 0.1000; +cst{ixOAR, 5}.betaX = 0.0500; +cst{ixOAR, 5}.Priority = 2; % overlap priority for optimization - a higher number corresponds to a lower priority +cst{ixOAR, 5}.Visible = 1; -cst{ixPTV,5}.TissueClass = 1; -cst{ixPTV,5}.alphaX = 0.1000; -cst{ixPTV,5}.betaX = 0.0500; -cst{ixPTV,5}.Priority = 1; % overlap priority for optimization - a lower number corresponds to a higher priority -cst{ixPTV,5}.Visible = 1; +cst{ixOAR, 6}{1} = struct(DoseObjectives.matRad_SquaredOverdosing(10, 30)); -cst{ixPTV,6}{1} = struct(DoseObjectives.matRad_SquaredDeviation(50,60)); +cst{ixPTV, 5}.TissueClass = 1; +cst{ixPTV, 5}.alphaX = 0.1000; +cst{ixPTV, 5}.betaX = 0.0500; +cst{ixPTV, 5}.Priority = 1; % overlap priority for optimization - a lower number corresponds to a higher priority +cst{ixPTV, 5}.Visible = 1; +cst{ixPTV, 6}{1} = struct(DoseObjectives.matRad_SquaredDeviation(50, 60)); %% Lets create a cubic phantom % first define the dimensions of the OAR cubeHelper = zeros(ct.cubeDim); -xLowOAR = round(xDim/2 - xDim/6); -xHighOAR = round(xDim/2 + xDim/6); -yLowOAR = round(yDim/2 - yDim/6); -yHighOAR = round(yDim/2 + yDim/6); -zLowOAR = round(zDim/2 - zDim/4); -zHighOAR = round(zDim/2 + zDim/4); +xLowOAR = round(xDim / 2 - xDim / 6); +xHighOAR = round(xDim / 2 + xDim / 6); +yLowOAR = round(yDim / 2 - yDim / 6); +yHighOAR = round(yDim / 2 + yDim / 6); +zLowOAR = round(zDim / 2 - zDim / 4); +zHighOAR = round(zDim / 2 + zDim / 4); for x = xLowOAR:1:xHighOAR - for y = yLowOAR:1:yHighOAR - for z = zLowOAR:1:zHighOAR - cubeHelper(x,y,z) = 1; - end - end + for y = yLowOAR:1:yHighOAR + for z = zLowOAR:1:zHighOAR + cubeHelper(x, y, z) = 1; + end + end end - + % extract the linear voxel indices and save it in the cst -cst{ixOAR,4}{1} = find(cubeHelper); +cst{ixOAR, 4}{1} = find(cubeHelper); % second the PTV cubeHelper = zeros(ct.cubeDim); -radiusPTV = xDim/14; +radiusPTV = xDim / 14; for x = 1:xDim - for y = 1:yDim - for z = 1:zDim - currPost = [x y z] - round([ct.cubeDim./2]); - if sqrt(sum(currPost.^2)) < radiusPTV - cubeHelper(x,y,z) = 1; - end - end - end + for y = 1:yDim + for z = 1:zDim + currPost = [x y z] - round([ct.cubeDim ./ 2]); + if sqrt(sum(currPost.^2)) < radiusPTV + cubeHelper(x, y, z) = 1; + end + end + end end % extract the linear voxel indices and save it in the cst -cst{ixPTV,4}{1} = find(cubeHelper); +cst{ixPTV, 4}{1} = find(cubeHelper); % assign relative electron densities -vIxOAR = cst{ixOAR,4}{1}; -vIxPTV = cst{ixPTV,4}{1}; +vIxOAR = cst{ixOAR, 4}{1}; +vIxPTV = cst{ixPTV, 4}{1}; ct.cubeHU{1}(vIxOAR) = 300; % assign HU of soft tissue ct.cubeHU{1}(vIxPTV) = 0; % assign HU of water @@ -120,64 +119,63 @@ %% add motion to the phantom and artificially create a 4D CT with vector fields amplitude = [5 0 0]; % [voxels] numOfCtScen = 5; -motionPeriod = 2.5; % [s] +motionPeriod = 2.5; % [s] -[ct,cst] = matRad_addMovement(ct, cst,motionPeriod, numOfCtScen, amplitude,'visBool',true); +[ct, cst] = matRad_addMovement(ct, cst, motionPeriod, numOfCtScen, amplitude, 'visBool', true); % show the deformation vector field slice = 25; % select a specific slice and to plot the vector field -[a,xDim,yDim,zDim] = size(ct.dvf{1}); - -[mX,mY] = meshgrid(1:xDim,1:yDim); - -figure, -for ctPhase = 1:ct.numOfCtScen - clf; - xVectorField = squeeze(ct.dvf{ctPhase}(1,:,:,slice)); % retrieve the deformation vector field in x-direction of slice 25 - yVectorField = squeeze(ct.dvf{ctPhase}(2,:,:,slice)); % retrieve the deformation vector field in y-direction of slice 25 - quiver(mX,mY,yVectorField,xVectorField); title(['deformation vector field of phase ' num2str(ctPhase)]), - set(gca,'XLim',[70 80]);set(gca,'YLim',[70 80]); - % flip y axis to be consistent with the previous plot - ax = gca; set(ax,'YDir','reverse'); - pause(0.5); +[a, xDim, yDim, zDim] = size(ct.dvf{1}); + +[mX, mY] = meshgrid(1:xDim, 1:yDim); + +figure; +for ctPhase = 1:ct.numOfCtScen + clf; + xVectorField = squeeze(ct.dvf{ctPhase}(1, :, :, slice)); % retrieve the deformation vector field in x-direction of slice 25 + yVectorField = squeeze(ct.dvf{ctPhase}(2, :, :, slice)); % retrieve the deformation vector field in y-direction of slice 25 + quiver(mX, mY, yVectorField, xVectorField); + title(['deformation vector field of phase ' num2str(ctPhase)]); + set(gca, 'XLim', [70 80]); + set(gca, 'YLim', [70 80]); + % flip y axis to be consistent with the previous plot + ax = gca; + set(ax, 'YDir', 'reverse'); + pause(0.5); end % magnitude of the vector field should change over ct scenarios % vector field refers to the first (initial) ct scenario % investigate in the difference of 4D dose accumulatino - % clear helper variables to get clean workspace -clear x y z xDim yDim zDim xHighOAR xLowOAR xHighOAR yHighOAR yLowOAR zHighOAR zLowOAR vIxOAR vIxPTV cubeHelper currPost radiusPTV +clear x y z xDim yDim zDim xHighOAR xLowOAR xHighOAR yHighOAR yLowOAR zHighOAR zLowOAR vIxOAR vIxPTV cubeHelper currPost radiusPTV; %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This +% The next step is to define your treatment plan labeled as 'pln'. This % structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this % example we would like to use protons for robust treatment planning. Next, we -% need to define a treatment machine to correctly load the corresponding +% need to define a treatment machine to correctly load the corresponding % base data. matRad features generic base data in the file % 'carbon_Generic.mat'; consequently the machine has to be set accordingly -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; pln.bioModel = 'none'; % retrieve 9 worst case scenarios for dose calculation and optimziation -pln.multScen = matRad_multScen(ct,'wcScen'); +pln.multScen = matRad_multScen(ct, 'wcScen'); pln.multScen.listAllScenarios(); - %% % The remaining plan parameters are set like in the previous example files pln.numOfFractions = 20; pln.propStf.gantryAngles = [0 90]; pln.propStf.couchAngles = [0 0]; pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] @@ -188,113 +186,125 @@ pln.propOpt.quantityOpt = 'physicalDose'; %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMPT based on RBE-weighted dose -% The goal of the fluence optimization is to find a set of bixel/spot +% The goal of the fluence optimization is to find a set of bixel/spot % weights which yield the best possible dose distribution according to the % clinical objectives and constraints underlying the radiation treatment. -% +% -%Activate 4D Optimization -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +% Activate 4D Optimization +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Trigger robust optimization % Make the objective to a composite worst case objective -cst{ixPTV,6}{1}.robustness = 'COWC'; -cst{ixOAR,6}{1}.robustness = 'COWC'; +cst{ixPTV, 6}{1}.robustness = 'COWC'; +cst{ixOAR, 6}{1}.robustness = 'COWC'; -%Activate 4D Optimization +% Activate 4D Optimization pln.propOpt.scen4D = 'all'; % voxel wise worst case 'VWWC' does not work for 4D robust optimization % parameters for stochastic optimization -%cst{ixPTV,6}.robustness = 'STOCH'; -%cst{ixOAR,6}.robustness = 'STOCH'; -%pln.multScen.scenProb = (1/ct.numOfCtScen) * ones(ct.numOfCtScen,1); % assign probabilities to 4D scenarios +% cst{ixPTV,6}.robustness = 'STOCH'; +% cst{ixOAR,6}.robustness = 'STOCH'; +% pln.multScen.scenProb = (1/ct.numOfCtScen) * ones(ct.numOfCtScen,1); % assign probabilities to 4D scenarios % % parameters for objective wise worst case -%cst{ixPTV,6}.robustness = 'OWC'; -%cst{ixOAR,6}.robustness = 'OWC'; +% cst{ixPTV,6}.robustness = 'OWC'; +% cst{ixOAR,6}.robustness = 'OWC'; -resultGUIrobust = matRad_fluenceOptimization(dij,cst,pln); +resultGUIrobust = matRad_fluenceOptimization(dij, cst, pln); % add resultGUIrobust dose cubes to the existing resultGUI structure to allow the visualization in the GUI -resultGUI = matRad_appendResultGUI(resultGUI,resultGUIrobust,0,'robust'); +resultGUI = matRad_appendResultGUI(resultGUI, resultGUIrobust, 0, 'robust'); %% calc 4D dose -totalPhaseMatrix = ones(dij.totalNumOfBixels,ct.numOfCtScen)/ct.numOfCtScen; % the total phase matrix determines a mapping what fluence will be delivered in the which phase -totalPhaseMatrix = bsxfun(@times,totalPhaseMatrix,resultGUIrobust.w); % equally distribute the fluence over all fluences +totalPhaseMatrix = ones(dij.totalNumOfBixels, ct.numOfCtScen) / ct.numOfCtScen; % the total phase matrix determines a mapping what fluence will be delivered in the which phase +totalPhaseMatrix = bsxfun(@times, totalPhaseMatrix, resultGUIrobust.w); % equally distribute the fluence over all fluences -[resultGUIrobust4D, timeSequence] = matRad_calc4dDose(ct, pln, dij, stf, cst, resultGUIrobust,totalPhaseMatrix); +resultGUIrobust4D = matRad_calc4dDose(dij, pln, stf, resultGUIrobust, totalPhaseMatrix); +resultGUIrobust4D = matRad_acc4dDose(dij, pln, ct, cst, resultGUIrobust4D, 'DDM'); %% Visualize results plane = 3; -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -maxDose = max([max(resultGUI.([pln.propOpt.quantityOpt])(:,:,slice)) max(resultGUIrobust.([pln.propOpt.quantityOpt])(:,:,slice))])+1e-4; -doseIsoLevels = linspace(0.1 * maxDose,maxDose,10); -figure, -subplot(121),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('conventional plan - beam1') -subplot(122),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.([pln.propOpt.quantityOpt]) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('conventional plan') -%subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan - beam1') -%subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt]) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan') - -figure -subplot(121),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('robust plan - beam1') -subplot(122),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt]) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('robust plan') -%subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan - beam1') -%subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan') - - -figure -subplot(131),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust4D.([pln.propOpt.quantityOpt]) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('robust plan') -subplot(132),matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust4D.('accPhysicalDose') ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels);title('robust plan dose accumulation') -%subplot(131),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan') -%subplot(132),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.('accPhysicalDose'),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan dose accumulation') +maxDose = max([max(resultGUI.([pln.propOpt.quantityOpt])(:, :, slice)) max(resultGUIrobust.([pln.propOpt.quantityOpt])(:, :, slice))]) + 1e-4; +doseIsoLevels = linspace(0.1 * maxDose, maxDose, 10); +figure; +subplot(121); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('conventional plan - beam1'); +subplot(122); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.([pln.propOpt.quantityOpt]), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('conventional plan'); +% subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan - beam1') +% subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt]) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan') + +figure; +subplot(121); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('robust plan - beam1'); +subplot(122); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt]), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('robust plan'); +% subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan - beam1') +% subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan') + +figure; +subplot(131); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust4D.([pln.propOpt.quantityOpt]), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('robust plan'); +subplot(132); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust4D.('accPhysicalDose'), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +title('robust plan dose accumulation'); +% subplot(131),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan') +% subplot(132),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.('accPhysicalDose'),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan dose accumulation') % create an interactive plot to slide through individual scnearios -f = figure; title('individual scenarios'); +f = figure; +title('individual scenarios'); numScen = 1; -maxDose = max(max(resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))])(:,:,slice)))+0.2; -doseIsoLevels = linspace(0.1 * maxDose,maxDose,10); -matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels); - -[env,envver] = matRad_getEnvironment(); -if strcmp(env,'MATLAB') || str2double(envver(1)) >= 5 - b = uicontrol('Parent',f,'Style','slider','Position',[50,5,419,23],... - 'value',numScen, 'min',1, 'max',pln.multScen.totNumScen,'SliderStep', [1/(pln.multScen.totNumScen-1) , 1/(pln.multScen.totNumScen-1)]); - set(b,'Callback',@(es,ed) matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', round(get(es,'Value')), 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(get(es,'Value')))]) ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels)); - %matRad_plotSliceWrapper(gca,ct,cst,round(get(es,'Value')),resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(get(es,'Value')))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels)); +maxDose = max(max(resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))])(:, :, slice))) + 0.2; +doseIsoLevels = linspace(0.1 * maxDose, maxDose, 10); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels); + +[env, envver] = matRad_getEnvironment(); +if strcmp(env, 'MATLAB') || str2double(envver(1)) >= 5 + b = uicontrol('Parent', f, 'Style', 'slider', 'Position', [50, 5, 419, 23], ... + 'value', numScen, 'min', 1, 'max', pln.multScen.totNumScen, 'SliderStep', [1 / (pln.multScen.totNumScen - 1), 1 / (pln.multScen.totNumScen - 1)]); + set(b, 'Callback', @(es, ed) matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', round(get(es, 'Value')), 'dose', resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(get(es, 'Value')))]), 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 maxDose], 'doseIsoLevels', doseIsoLevels)); + % matRad_plotSliceWrapper(gca,ct,cst,round(get(es,'Value')),resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(get(es,'Value')))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels)); end %% Indicator calculation and show DVH and QI -resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln); +resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); %% Perform sampling % select structures to include in sampling; leave empty to sample dose for all structures % sampling does not know on which scenario sampling should be performed structSel = {}; % structSel = {'PTV','OAR1'}; -[caSamp, mSampDose, plnSamp, resultGUInomScen] = matRad_sampling(ct,stf,cst,pln,resultGUI.w,structSel); -[cstStat, resultGUISamp, meta] = matRad_samplingAnalysis(ct,cst,plnSamp,caSamp, mSampDose, resultGUInomScen); - -[caSampRob, mSampDoseRob, plnSampRob, resultGUInomScen] = matRad_sampling(ct,stf,cst,pln,resultGUIrobust.w,structSel); -[cstStatRob, resultGUISampRob, metaRob] = matRad_samplingAnalysis(ct,cst,plnSampRob,caSampRob, mSampDoseRob, resultGUInomScen); - -figure,title('std dose cube based on sampling - conventional') -matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISamp.stdCube ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISamp.stdCube(:))]); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISamp.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISamp.stdCube(:))]); - -figure,title('std dose cube based on sampling - robust') -matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISampRob.stdCube ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISampRob.stdCube(:))]); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISampRob.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISampRob.stdCube(:))]); +[caSamp, mSampDose, plnSamp, resultGUInomScen] = matRad_sampling(ct, stf, cst, pln, resultGUI.w, structSel); +[cstStat, resultGUISamp, meta] = matRad_samplingAnalysis(ct, cst, plnSamp, caSamp, mSampDose, resultGUInomScen); +[caSampRob, mSampDoseRob, plnSampRob, resultGUInomScen] = matRad_sampling(ct, stf, cst, pln, resultGUIrobust.w, structSel); +[cstStatRob, resultGUISampRob, metaRob] = matRad_samplingAnalysis(ct, cst, plnSampRob, caSampRob, mSampDoseRob, resultGUInomScen); +figure; +title('std dose cube based on sampling - conventional'); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISamp.stdCube, 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISamp.stdCube(:))]); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISamp.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISamp.stdCube(:))]); +figure; +title('std dose cube based on sampling - robust'); +matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISampRob.stdCube, 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISampRob.stdCube(:))]); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISampRob.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISampRob.stdCube(:))]); diff --git a/examples/matRad_example11_helium.m b/examples/matRad_example11_helium.m index 98c17011d..a9cb52030 100644 --- a/examples/matRad_example11_helium.m +++ b/examples/matRad_example11_helium.m @@ -13,49 +13,48 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to load patient data into matRad -% (ii) how to setup a helium dose calculation -% (iii) how to inversely optimize the pencil beam intensities directly from command window in MATLAB. +% (ii) how to setup a helium dose calculation +% (iii) how to inversely optimize the pencil beam intensities directly from command window in MATLAB. %% set matRad runtime configuration -matRad_rc +matRad_rc; %% Patient Data Import load('BOXPHANTOM.mat'); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This -% structure requires input from the treatment planner and defines the most +% The next step is to define your treatment plan labeled as 'pln'. This +% structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this % example we would like to use protons for treatment planning. Next, we -% need to define a treatment machine to correctly load the corresponding +% need to define a treatment machine to correctly load the corresponding % base data. matRad features generic base data in the file % 'proton_Generic.mat'; consequently the machine has to be set accordingly -pln.radiationMode = 'helium'; +pln.radiationMode = 'helium'; pln.machine = 'Generic'; pln.multScen = 'nomScen'; % Define the flavor of biological optimization for treatment planning along % with the quantity that should be used for optimization. As we use helium, -% we follow a data-driven RBE parametrization to obtbain the variable -% relative biological effectiveness. +% we follow a data-driven RBE parametrization to obtbain the variable +% relative biological effectiveness. pln.bioModel = 'HEL'; - %% % Now we have to set the remaining plan parameters. pln.numOfFractions = 30; pln.propStf.gantryAngles = [0]; pln.propStf.couchAngles = [0]; pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); +pln.propStf.isoCenter = ones(pln.propStf.numOfBeams, 1) * matRad_getIsoCenter(cst, ct, 0); +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] @@ -73,27 +72,32 @@ pln.propOpt.quantityOpt = 'effect'; %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -% Lets generate dosimetric information by pre-computing dose influence -% matrices for unit beamlet intensities. Having dose influences available -% allows for subsequent inverse optimization. -dij = matRad_calcParticleDose(ct,stf,pln,cst); +% Lets generate dosimetric information by pre-computing dose influence +% matrices for unit beamlet intensities. Having dose influences available +% allows for subsequent inverse optimization. +dij = matRad_calcParticleDose(ct, stf, pln, cst); %% Inverse Optimization for IMPT -% The goal of the fluence optimization is to find a set of bixel/spot -% weights which yield the best possible dose distribution according to the +% The goal of the fluence optimization is to find a set of bixel/spot +% weights which yield the best possible dose distribution according to the % clinical objectives and constraints underlying the radiation treatment -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Plot the Resulting Dose Slice % Let's plot the transversal iso-center dose slice -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure -subplot(121),imagesc(resultGUI.physicalDose(:,:,slice)),colorbar,colormap(jet),title('physical dose') -subplot(122),imagesc(resultGUI.RBExDose(:,:,slice)),colorbar,colormap(jet),title('RBExDose') - - - +figure; +subplot(121); +imagesc(resultGUI.physicalDose(:, :, slice)); +colorbar; +colormap(jet); +title('physical dose'); +subplot(122); +imagesc(resultGUI.RBExDose(:, :, slice)); +colorbar; +colormap(jet); +title('RBExDose'); diff --git a/examples/matRad_example13_fitAnalyticalParticleBaseData.m b/examples/matRad_example13_fitAnalyticalParticleBaseData.m index ad4ef6fec..b425ff967 100644 --- a/examples/matRad_example13_fitAnalyticalParticleBaseData.m +++ b/examples/matRad_example13_fitAnalyticalParticleBaseData.m @@ -151,8 +151,6 @@ pln.propOpt.optimizer = 'IPOPT'; pln.propOpt.bioOptimization = 'none'; % none: physical optimization; const_RBExDose; constant RBE of 1.1; % LEMIV_effect: effect-based optimization; LEMIV_RBExDose: optimization of RBE-weighted dose -pln.propOpt.runDAO = false; % 1/true: run DAO, 0/false: don't / will be ignored for particles -pln.propOpt.runSequencing = false; % 1/true: run sequencing, 0/false: don't / will be ignored for particles and also triggered by runDAO below % retrieve scenarios for dose calculation and optimziation pln.multScen = matRad_multScen(ct,'nomScen'); diff --git a/examples/matRad_example14_spotRemoval.m b/examples/matRad_example14_spotRemoval.m index 08e835617..bdd3a09b9 100644 --- a/examples/matRad_example14_spotRemoval.m +++ b/examples/matRad_example14_spotRemoval.m @@ -15,7 +15,7 @@ clear; %% set matRad runtime configuration -matRad_rc; %If this throws an error, run it from the parent directory first to set the paths +matRad_rc; % If this throws an error, run it from the parent directory first to set the paths %% Patient Data Import % Let's begin with a clear Matlab environment and import the prostate @@ -24,18 +24,18 @@ load('PROSTATE.mat'); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This -% structure requires input from the treatment planner and defines the most +% The next step is to define your treatment plan labeled as 'pln'. This +% structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this % example we would like to use protons for treatment planning. Next, we -% need to define a treatment machine to correctly load the corresponding +% need to define a treatment machine to correctly load the corresponding % base data. matRad features generic base data in the file % 'proton_Generic.mat'; consequently the machine has to be set accordingly -pln.radiationMode = 'protons'; +pln.radiationMode = 'protons'; pln.machine = 'Generic'; pln.bioModel = 'constRBE'; pln.multScen = 'nomScen'; @@ -45,16 +45,14 @@ % alongside the physical dose. Therefore you need to activate the % corresponding option during dose calculcation pln.propDoseCalc.calcLET = 0; - + %% % Now we have to set the remaining plan parameters. pln.numOfFractions = 30; pln.propStf.gantryAngles = [90 270]; pln.propStf.couchAngles = [0 0]; pln.propStf.bixelWidth = 3; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] @@ -62,28 +60,28 @@ pln.propDoseCalc.doseGrid.resolution.z = 5; % [mm] %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -% Lets generate dosimetric information by pre-computing dose influence -% matrices for unit beamlet intensities. Having dose influences available -% allows for subsequent inverse optimization. -dij = matRad_calcParticleDose(ct,stf,pln,cst); +% Lets generate dosimetric information by pre-computing dose influence +% matrices for unit beamlet intensities. Having dose influences available +% allows for subsequent inverse optimization. +dij = matRad_calcParticleDose(ct, stf, pln, cst); %% Inverse Optimization for IMPT -% The goal of the fluence optimization is to find a set of bixel/spot -% weights which yield the best possible dose distribution according to the +% The goal of the fluence optimization is to find a set of bixel/spot +% weights which yield the best possible dose distribution according to the % clinical objectives and constraints underlying the radiation treatment pln.propOpt.quantityOpt = quantityOpt; -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Spot removal % instantiate spot removal class -spotRemover = matRad_SpotRemovalDij(dij,resultGUI.w); +spotRemover = matRad_SpotRemovalDij(dij, resultGUI.w); spotRemover.removalMode = 'relative'; spotRemover.propSpotRemoval.relativeThreshold = 0.05; -resultGUI2 = spotRemover.reoptimize(cst,pln); +resultGUI2 = spotRemover.reoptimize(cst, pln); % numOfRemovedSpots = sr_cfg.numOfRemovedSpots; @@ -93,6 +91,4 @@ % weightLogical = sr_cfg.getLogical; %% Plot difference of the doses -matRad_compareDose(resultGUI.RBExDose,resultGUI2.RBExDose,ct,cst); - - +matRad_compareDose(resultGUI.RBExDose, resultGUI2.RBExDose, ct, cst); diff --git a/examples/matRad_example16_photonMC_MLC.m b/examples/matRad_example16_photonMC_MLC.m index 6def7eec4..bdb22ea9f 100644 --- a/examples/matRad_example16_photonMC_MLC.m +++ b/examples/matRad_example16_photonMC_MLC.m @@ -13,43 +13,40 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to load patient data into matRad -% (ii) how to setup a photon dose calculation based on the VMC++ Monte Carlo algorithm -% (iii) how to inversely optimize the beamlet intensities directly from command window in MATLAB. +% (ii) how to setup a photon dose calculation based on the VMC++ Monte Carlo algorithm +% (iii) how to inversely optimize the beamlet intensities directly from command window in MATLAB. % (iv) how to visualize the result %% set matRad runtime configuration -matRad_rc %If this throws an error, run it from the parent directory first to set the paths +matRad_rc; % If this throws an error, run it from the parent directory first to set the paths %% Patient Data Import % Let's begin with a clear Matlab environment and import the boxphantom -% into your workspace. +% into your workspace. load('BOXPHANTOM.mat'); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This +% The next step is to define your treatment plan labeled as 'pln'. This % structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; pln.numOfFractions = 30; pln.propStf.gantryAngles = [0:72:359]; pln.propStf.couchAngles = [0 0 0 0 0]; -%pln.propStf.gantryAngles = [0]; -%pln.propStf.couchAngles = [0]; +% pln.propStf.gantryAngles = [0]; +% pln.propStf.couchAngles = [0]; pln.propStf.bixelWidth = 10; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -% Enable sequencing and direct aperture optimization (DAO). -pln.propOpt.runSequencing = 1; -pln.propOpt.runDAO = 1; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); -quantityOpt = 'physicalDose'; -modelName = 'none'; +quantityOpt = 'physicalDose'; +modelName = 'none'; % retrieve bio model parameters -pln.bioModel = matRad_bioModel(pln.radiationMode,quantityOpt, modelName); +pln.bioModel = matRad_bioModel(pln.radiationMode, quantityOpt, modelName); % retrieve scenarios for dose calculation and optimziation pln.multScen = matRad_NominalScenario(ct); @@ -59,40 +56,43 @@ pln.propDoseCalc.doseGrid.resolution.z = 3; % [mm] %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMRT pln.propOpt.quantityOpt = quantityOpt; -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Sequencing -% This is a multileaf collimator leaf sequencing algorithm that is used in -% order to modulate the intensity of the beams with multiple static -% segments, so that translates each intensity map into a set of deliverable +% This is a multileaf collimator leaf sequencing algorithm that is used in +% order to modulate the intensity of the beams with multiple static +% segments, so that translates each intensity map into a set of deliverable % aperture shapes. -resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,5,1); -[pln,stf] = matRad_aperture2collimation(pln,stf,resultGUI.sequencing,resultGUI.apertureInfo); +resultGUI = matRad_sequencing(resultGUI, stf, pln, dij); +[pln, stf] = matRad_aperture2collimation(pln, stf, resultGUI.sequencing, resultGUI.sequencing.apertureInfo); + %% Aperture visualization % Use a matrad function to visualize the resulting aperture shapes -matRad_visApertureInfo(resultGUI.apertureInfo) +matRad_visApertureInfo(resultGUI.sequencing.apertureInfo); %% Plot the Resulting Dose Slice % Just let's plot the transversal iso-center dose slice -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure, -imagesc(resultGUI.physicalDose(:,:,slice)),colorbar, colormap(jet) +figure; +imagesc(resultGUI.physicalDose(:, :, slice)); +colorbar; +colormap(jet); %% Dose Calculation -%resultGUI_MC = matRad_calcDoseInfluence(ct,cst,stf,pln); +% resultGUI_MC = matRad_calcDoseInfluence(ct,cst,stf,pln); pln.propDoseCalc.engine = 'TOPAS'; pln.propDoseCalc.beamProfile = 'phasespace'; pln.propDoseCalc.externalCalculation = 'write'; -resultGUI_MC = matRad_calcDoseForward(ct,cst,stf,pln,resultGUI.w); +resultGUI_MC = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); %% readout -waitforbuttonpress; %We will wait since we do need to do the external calculation first +waitforbuttonpress; % We will wait since we do need to do the external calculation first pln.propDoseCalc.externalCalculation = resultGUI_MC.meta.TOPASworkingDir; -resultGUI_MC = matRad_calcDoseForward(ct,cst,stf,pln,resultGUI.w); +resultGUI_MC = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); diff --git a/examples/matRad_example17_biologicalModels.m b/examples/matRad_example17_biologicalModels.m index f87b4375a..0854f6f42 100644 --- a/examples/matRad_example17_biologicalModels.m +++ b/examples/matRad_example17_biologicalModels.m @@ -12,9 +12,7 @@ pln.propStf.gantryAngles = 0; pln.propStf.couchAngles = 0; pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propSeq.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 8; @@ -22,11 +20,11 @@ pln.propDoseCalc.doseGrid.resolution.z = 8; pln.propDoseCalc.engine = 'HongPB'; -%Optimization Settings +% Optimization Settings pln.propOpt.quantityOpt = 'RBExDose'; %% stf -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose calc @@ -38,19 +36,19 @@ % We will compare the MCN model to constRBE. % We will plan with the the MCN model. -pln.bioModel = matRad_MCNamara(); -%alternative: pln.bioModel = matRad_bioModel(pln.radiationMode,'MCN'); -%altnerative: pln.bioModel = 'MCN'; -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +pln.bioModel = matRad_MCNamara(); +% alternative: pln.bioModel = matRad_bioModel(pln.radiationMode,'MCN'); +% altnerative: pln.bioModel = 'MCN'; +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Fluence optimization -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Now let's recalculate with the constRBE model pln.bioModel = matRad_ConstantRBE(); -pln.bioModel.RBE = 1.1; %1.1 is standard, this is for illustration +pln.bioModel.RBE = 1.1; % 1.1 is standard, this is for illustration -resultGUI_recalc = matRad_calcDoseForward(ct,cst,stf,pln,resultGUI.w); +resultGUI_recalc = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); %% Compare Dose distributions -matRad_compareDose(resultGUI.RBExDose,resultGUI_recalc.RBExDose,ct,cst); \ No newline at end of file +matRad_compareDose(resultGUI.RBExDose, resultGUI_recalc.RBExDose, ct, cst); diff --git a/examples/matRad_example18_FREDMC.m b/examples/matRad_example18_FREDMC.m index 30d95d3c7..e43a81b00 100644 --- a/examples/matRad_example18_FREDMC.m +++ b/examples/matRad_example18_FREDMC.m @@ -13,7 +13,7 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to compute a simple plan using the FRED MC engine % (ii) how to compute LETd distributions and apply a biological model @@ -29,12 +29,10 @@ pln.propDoseCalc.calcLET = 0; pln.numOfFractions = 30; -pln.propStf.gantryAngles = [30,330]; +pln.propStf.gantryAngles = [30, 330]; pln.propStf.couchAngles = zeros(size(pln.propStf.gantryAngles)); pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propSeq.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] @@ -42,20 +40,20 @@ pln.propDoseCalc.doseGrid.resolution.z = 5; % [mm] % Start with a simple physical dose model -pln.bioParam = matRad_bioModel(pln.radiationMode,'none'); +pln.bioParam = matRad_bioModel(pln.radiationMode, 'none'); pln.multScen = matRad_multScen(ct, 'nomScen'); -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Let's start with the analytical dose calculation algorithm pln.propDoseCalc.engine = 'HongPB'; % Compute the dose influence matrix -dij_PB = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij_PB = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Fluence optimization -resultGUI_PB = matRad_fluenceOptimization(dij_PB,cst,pln); +resultGUI_PB = matRad_fluenceOptimization(dij_PB, cst, pln); wOptimized = resultGUI_PB.w; %% Let's now re-compute the dose distribution with FRED MC @@ -63,29 +61,29 @@ pln.propDoseCalc.useGPU = false; % For illustraton, let's compute a dose influence matrix. -dij_FRED = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij_FRED = matRad_calcDoseInfluence(ct, cst, stf, pln); % And compute the dose cube from the dij: -resultGUI_FRED = matRad_calcCubes(wOptimized,dij_FRED,1); +resultGUI_FRED = matRad_calcCubes(wOptimized, dij_FRED, 1); % Alternative is to perform a direct dose calculation: % resultGUI_FRED = matRad_calcDoseForward(ct,cst,stf,pln,wOptimized) %% Compare doses -matRad_compareDose(resultGUI_PB.physicalDose, resultGUI_FRED.physicalDose,ct,cst,[],'on'); +matRad_compareDose(resultGUI_PB.physicalDose, resultGUI_FRED.physicalDose, ct, cst, [], 'on'); %% Tune the MC calculation parameters % This example illustrates some of the parameters that can be tuned for the % Engine. For more information run: % help DoseEngines.matRad_ParticleFREDEngine -pln.bioParam = matRad_bioModel(pln.radiationMode,'MCN'); +pln.bioParam = matRad_bioModel(pln.radiationMode, 'MCN'); pln.propDoseCalc.useGPU = true; -pln.propDoseCalc.sourceModel = 'gaussian'; %alternatives: {'gaussian', 'emittance', 'sigmaSqrModel'} +pln.propDoseCalc.sourceModel = 'gaussian'; % alternatives: {'gaussian', 'emittance', 'sigmaSqrModel'} pln.propDoseCalc.HUtable = 'internal'; % default: matRad_default_FredMaterialConverter pln.propDoseCalc.scorers = {'Dose', 'LETd'}; -resultGUI_recalc = matRad_calcDoseForward(ct,cst,stf,pln, wOptimized); +resultGUI_recalc = matRad_calcDoseForward(ct, cst, stf, pln, wOptimized); %% Compare physical dose and RBExD distributions matRad_compareDose(resultGUI_recalc.physicalDose, resultGUI_recalc.RBExDose,ct,cst,[],'on'); diff --git a/examples/matRad_example19_CT_sCT_DVH_difference_photons.m b/examples/matRad_example19_CT_sCT_DVH_difference_photons.m index 4082459af..c8f10b0d0 100644 --- a/examples/matRad_example19_CT_sCT_DVH_difference_photons.m +++ b/examples/matRad_example19_CT_sCT_DVH_difference_photons.m @@ -13,9 +13,8 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %% In this example, we will show: -% (0) how to export .mat data into DICOM format to illustrate a synthetic CT data generation, +% (0) how to export .mat data into DICOM format to illustrate a synthetic CT data generation, % as it is usually stored in DICOM format; % (i) how to load DICOM patient data into matRad and modify dosimetric % objectives and constraints; @@ -24,43 +23,42 @@ % (iv) how to calculate the DVH differences between the plans. %% Preliminary step: DICOM data preparation and export from .mat format to .dcm for further use -% Start with a clean MATLAB environment. We will export Liver phantom data -% from .mat format to .dcm, consisting of 3D body volume slices as well as a structure file and use it as a real CT. -% Additionally, we will slightly modify the intensities of the phantom to generate +% Start with a clean MATLAB environment. We will export Liver phantom data +% from .mat format to .dcm, consisting of 3D body volume slices as well as a structure file and use it as a real CT. +% Additionally, we will slightly modify the intensities of the phantom to generate % synthetic CT images, illustrating dose difference. -matRad_cfg = matRad_rc; %If this throws an error, run it from the parent directory first to set the paths +matRad_cfg = matRad_rc; % If this throws an error, run it from the parent directory first to set the paths -% Create directories to store DICOM real CT and synthetic (fake) CT data +% Create directories to store DICOM real CT and synthetic (fake) CT data patDir = [matRad_cfg.primaryUserFolder filesep 'syntheticCT' filesep]; % If you want to export your data, use "userdata" folder as it is ignored by git name = 'LIVER'; patDirRealCT = [name '_realCT']; patDirFakeCT = [name '_fakeCT']; -if ~mkdir(patDir,patDirRealCT) || ~mkdir(patDir,patDirFakeCT) - matRad_cfg.dispError('Error creating directories %s and %s in %s',patDirRealCT,patDirFakeCT,patDir); +if ~mkdir(patDir, patDirRealCT) || ~mkdir(patDir, patDirFakeCT) + matRad_cfg.dispError('Error creating directories %s and %s in %s', patDirRealCT, patDirFakeCT, patDir); end -patDirRealCT = fullfile(patDir,patDirRealCT); -patDirFakeCT = fullfile(patDir,patDirFakeCT); +patDirRealCT = fullfile(patDir, patDirRealCT); +patDirFakeCT = fullfile(patDir, patDirFakeCT); % Now, as the directories are created, let us create the DICOM data. Here, the DICOM % files will be created from the .mat format. load('LIVER.mat'); -indicators = {'mean','std','max', 'min', 'D_2','D_5', 'D_95', 'D_98'}; +indicators = {'mean', 'std', 'max', 'min', 'D_2', 'D_5', 'D_95', 'D_98'}; realCTct = ct; realCTcst = cst; -%review real CT volume +% review real CT volume matRadGUI; -%saving as DICOMs +% saving as DICOMs dcmExpRealCT = matRad_DicomExporter; % create instance of matRad_DicomExporter dcmExpRealCT.dicomDir = patDirRealCT; % set the output path for the Dicom export dcmExpRealCT.cst = cst; % set the structure set for the Dicom export dcmExpRealCT.ct = realCTct; % set the image volume for the Dicom export -dcmExpRealCT.matRad_exportDicom(); - +dcmExpRealCT.matRad_exportDicom(); -% In order to create a fake or synthetic CT volume, we will use real CT data and re-use its HU values +% In order to create a fake or synthetic CT volume, we will use real CT data and re-use its HU values % for illustrative purposes. First, we extract the original 3D HU image data. fakeCTct = ct; fakeCTcubeHU = fakeCTct.cubeHU{1}; @@ -68,38 +66,39 @@ % Then, we define the range values to be coerced to create the fake CT volume. For this, we will % coerce the soft tissue range and scale values in the range [0,100] to % [100,300] and export them as .dcm files. -oldMin = 0; oldMax = 100; % Original range -newMin = 100; newMax = 300; % Target range -%scaling +oldMin = 0; +oldMax = 100; % Original range +newMin = 100; +newMax = 300; % Target range +% scaling mask = (fakeCTcubeHU >= oldMin) & (fakeCTcubeHU <= oldMax); fakeCTcubeHU(mask) = newMin + (fakeCTcubeHU(mask) - oldMin) * (newMax - newMin) / (oldMax - oldMin); fakeCTct.cubeHU{1} = fakeCTcubeHU; -%review fake CT volume +% review fake CT volume ct = fakeCTct; hGUI = matRadGUI; -%saving as DICOMs -dcmExpFakeCT= matRad_DicomExporter; % create instance of matRad_DicomExporter +% saving as DICOMs +dcmExpFakeCT = matRad_DicomExporter; % create instance of matRad_DicomExporter dcmExpFakeCT.dicomDir = patDirFakeCT; % set the output path for the Dicom export dcmExpFakeCT.cst = []; % set the structure set for the Dicom export [we will keep it empty and use further the real CT cst] dcmExpFakeCT.ct = fakeCTct; % set the image volume for the Dicom export -dcmExpFakeCT.matRad_exportDicom(); +dcmExpFakeCT.matRad_exportDicom(); -%clear all except of paths, close windows to start from clean space +% clear all except of paths, close windows to start from clean space delete(hGUI); - -%% Patient Data Import from DICOM +%% Patient Data Import from DICOM % Here we are. Let us import prepared DICOM real CT data. If you % have your data you can try to replace it with your data in the folder, -% mentioned above. -% Functions for importing DICOM files will search for .dcm files in the directory -% and automatically recognize 3D volumes and structure files. -% The DICOM files will then be read into the workspace as 'ct' and 'cst', -% representing the CT images and the structure set, respectively. -% Ensure that the matRad root directory, along with all its subdirectories, -% is added to the MATLAB search path. +% mentioned above. +% Functions for importing DICOM files will search for .dcm files in the directory +% and automatically recognize 3D volumes and structure files. +% The DICOM files will then be read into the workspace as 'ct' and 'cst', +% representing the CT images and the structure set, respectively. +% Ensure that the matRad root directory, along with all its subdirectories, +% is added to the MATLAB search path. impRealCT = matRad_DicomImporter(patDirRealCT); matRad_importDicom(impRealCT); @@ -109,41 +108,41 @@ cst = realCTcst; end -% Review the exported file and automatically identified -% optimization objectives and constraints. +% Review the exported file and automatically identified +% optimization objectives and constraints. matRadGUI; -%% Modifying Plan Optimization Objectives -% The constraints for all defined volumes of interest (VOIs) are stored in the cst cell. -% When importing DICOM structure files, matRad uses matRad_createCst() to generate this cell. -% Default regular expressions are used to define target objectives. However, -% additional constraints for organs at risk (OARs) need to be added for the plan. +%% Modifying Plan Optimization Objectives +% The constraints for all defined volumes of interest (VOIs) are stored in the cst cell. +% When importing DICOM structure files, matRad uses matRad_createCst() to generate this cell. +% Default regular expressions are used to define target objectives. However, +% additional constraints for organs at risk (OARs) need to be added for the plan. % One of the methods to do this is by directly modifying the cst cell. -% If different volumes with varying optimization objectives need to be loaded, -% refer to the documentation for matRad_createCst() and choose suitable options -% to create a custom implementation. In this case, the plan will be modified directly here. +% If different volumes with varying optimization objectives need to be loaded, +% refer to the documentation for matRad_createCst() and choose suitable options +% to create a custom implementation. In this case, the plan will be modified directly here. for i = 1:size(cst, 1) % Accessing each optimization objective and constain - % Read more about how to set it in matRad_DoseOptimizationFunction - Superclass for objectives and constraints. - % This is the superclass for all objectives and constraints to enable easy + % Read more about how to set it in matRad_DoseOptimizationFunction - Superclass for objectives and constraints. + % This is the superclass for all objectives and constraints to enable easy % identification. - if strcmp(cst{i,3},'OAR') - if ~isempty(regexpi(cst{i,2},'spinal', 'once')) - objective = DoseObjectives.matRad_MaxDVH; + if strcmp(cst{i, 3}, 'OAR') + if ~isempty(regexpi(cst{i, 2}, 'spinal', 'once')) + objective = DoseObjectives.matRad_MaxDVH; objective.penalty = 1; - objective.parameters = {12,1}; %dose, to volume - cst{i,6} = {}; - cst{i,6}{1} = struct(objective); + objective.parameters = {12, 1}; % dose, to volume + cst{i, 6} = {}; + cst{i, 6}{1} = struct(objective); - elseif ~isempty(regexpi(cst{i,2},'stomach','once')) || ... - ~isempty(regexpi(cst{i,2},'duodenum', 'once')) + elseif ~isempty(regexpi(cst{i, 2}, 'stomach', 'once')) || ... + ~isempty(regexpi(cst{i, 2}, 'duodenum', 'once')) objective = DoseObjectives.matRad_MaxDVH; objective.penalty = 1; - objective.parameters = {30,1}; %dose, to volume - cst{i,6} = {}; - cst{i,6}{1} = struct(objective); + objective.parameters = {30, 1}; % dose, to volume + cst{i, 6} = {}; + cst{i, 6}{1} = struct(objective); end end @@ -152,26 +151,26 @@ % Verify that the new objectives have been added and are visible in the % user interface. We save it for further synthetic CT calculations matRadGUI; -realCTcst=cst; +realCTcst = cst; %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This -% matlab structure requires input from the treatment planner and defines +% The next step is to define your treatment plan labeled as 'pln'. This +% matlab structure requires input from the treatment planner and defines % the most important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this case -% we want to use photons. Then, we need to define a treatment machine to +% we want to use photons. Then, we need to define a treatment machine to % correctly load the corresponding base data. matRad includes base data for -% generic photon linear accelerator called 'Generic'. By this means matRad -% will look for 'photons_Generic.mat' in our root directory and will use +% generic photon linear accelerator called 'Generic'. By this means matRad +% will look for 'photons_Generic.mat' in our root directory and will use % the data provided in there for dose calculation. -% The number of fractions is set to 30. Internally, matRad considers the -% fraction dose for optimization, however, objetives and constraints are +% The number of fractions is set to 30. Internally, matRad considers the +% fraction dose for optimization, however, objetives and constraints are % defined for the entire treatment. -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; pln.numOfFractions = 1; @@ -187,26 +186,26 @@ % As we are using photons, we simply set the parameter to 'none' pln.bioModel = 'none'; -%% +%% % It is possible to request multiple error scenarios for robustness % analysis and optimization. Here, we just use the "nominal scenario" % (nomScen) pln.multScen = 'nomScen'; %% -% Now we have to set some beam parameters. We can define multiple beam -% angles for the treatment which might be used in generic fashion for quick dose calculation. All corresponding couch angles are set to 0 at this -% point. Moreover, we set the bixelWidth to 4, which results in a beamlet -% size of 4 x 4 mm in the isocenter plane. +% Now we have to set some beam parameters. We can define multiple beam +% angles for the treatment which might be used in generic fashion for quick dose calculation. All corresponding couch angles are set to 0 at this +% point. Moreover, we set the bixelWidth to 4, which results in a beamlet +% size of 4 x 4 mm in the isocenter plane. pln.propStf.gantryAngles = [0 50 100 150 200 250 300]; -pln.propStf.couchAngles = zeros(1,numel(pln.propStf.gantryAngles)); +pln.propStf.couchAngles = zeros(1, numel(pln.propStf.gantryAngles)); pln.propStf.bixelWidth = 5; -% Obtain the number of beams and voxels from the existing variables and -% calculate the iso-center which is per default the center of gravity of +% Obtain the number of beams and voxels from the existing variables and +% calculate the iso-center which is per default the center of gravity of % all target voxels. -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); %% Dose calculation settings % set resolution of dose calculation and optimization @@ -214,87 +213,80 @@ pln.propDoseCalc.doseGrid.resolution.y = 7; % [mm] pln.propDoseCalc.doseGrid.resolution.z = 7; % [mm] -%% -% Enable sequencing and disable direct aperture optimization (DAO) for now. -% A DAO optimization is shown in a seperate example. -pln.propSeq.runSequencing = 1; -pln.propOpt.runDAO = 0; - %% Generate Beam Geometry STF -% The steering file struct comprises the complete beam geometry along with +% The steering file struct comprises the complete beam geometry along with % ray position, pencil beam positions and energies, source to axis distance (SAD) etc. -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation for real CT -% Let's generate dosimetric information by pre-computing dose influence -% matrices for unit beamlet intensities for real CT of a patient. Having dose influences available +% Let's generate dosimetric information by pre-computing dose influence +% matrices for unit beamlet intensities for real CT of a patient. Having dose influences available % allows subsequent inverse optimization. -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMRT for real CT -% The goal of the fluence optimization is to find a set of beamlet/pencil +% The goal of the fluence optimization is to find a set of beamlet/pencil % beam weights which yield the best possible dose distribution according to -% the clinical objectives and constraints underlying the radiation -% treatment. Once the optimization has finished, trigger once the GUI to +% the clinical objectives and constraints underlying the radiation +% treatment. Once the optimization has finished, trigger once the GUI to % visualize the optimized dose cubes. -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); -% Get the weights of the optimized plan to apply them to the synthetic CT image of the patient later. -% Call the matRad_planAnalysis function with the prepared arguments to extract DVH -% parameters calculated for the real CT image. -weights=resultGUI.w; -resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln); +% Get the weights of the optimized plan to apply them to the synthetic CT image of the patient later. +% Call the matRad_planAnalysis function with the prepared arguments to extract DVH +% parameters calculated for the real CT image. +weights = resultGUI.w; +resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); -%Get the plan parameters +% Get the plan parameters dvh = resultGUI.dvh; qi = resultGUI.qi; -% Save DVH parameters calculated on the real CT to the table -% for further comparison with plans calculated on the synthetic CT. -% Include the patient number and indicate the CT type as "real" +% Save DVH parameters calculated on the real CT to the table +% for further comparison with plans calculated on the synthetic CT. +% Include the patient number and indicate the CT type as "real" % to facilitate delta calculations between the plans later. -if matRad_cfg.isMatlab %tables not supported by Octave - dvhTableReal=struct2table(qi); +if matRad_cfg.isMatlab % tables not supported by Octave + dvhTableReal = struct2table(qi); % Select only DVH parameters from QI table you are interested in comparison - dvhTableReal=dvhTableReal(:,horzcat({'name'},indicators)); - dvhTableReal.patient= repmat(char(name),length(qi),1); - dvhTableReal.ct_type = repmat('real',length(qi),1); + dvhTableReal = dvhTableReal(:, horzcat({'name'}, indicators)); + dvhTableReal.patient = repmat(char(name), length(qi), 1); + dvhTableReal.ct_type = repmat('real', length(qi), 1); % Check DVH table for real CT disp(dvhTableReal); end -%% Now clear the data from the real CT image, except for the plan parameters, -% and load the synthetic (fake) CT image of the same patient. -% It is important that your image sets are compatible (i.e., same number of CT slices, -% same isocenter position, etc.). We will re-use the structure file from real CT with adjusted objectives +%% Now clear the data from the real CT image, except for the plan parameters, +% and load the synthetic (fake) CT image of the same patient. +% It is important that your image sets are compatible (i.e., same number of CT slices, +% same isocenter position, etc.). We will re-use the structure file from real CT with adjusted objectives % for dose calculations. delete(matRadGUI); clear resultGUI ct cst idx qi* dvh dij; - impFakeCT = matRad_DicomImporter(patDirFakeCT); matRad_importDicom(impFakeCT); -cst=realCTcst; +cst = realCTcst; % Review the exported file and previously identified -% optimization objectives and constraints. +% optimization objectives and constraints. matRadGUI; -%% Perform the dose calculation for synthetic CT by using the weights of plan, calculated on real CT -% (i.e., obtain the dij variable) for the synthetic CT image. -resultGUI = matRad_calcDoseDirect(ct,stf,pln,cst, weights); -resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln); +%% Perform the dose calculation for synthetic CT by using the weights of plan, calculated on real CT +% (i.e., obtain the dij variable) for the synthetic CT image. +resultGUI = matRad_calcDoseDirect(ct, stf, pln, cst, weights); +resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); matRadGUI; -%Get the plan parameters calculated on synthetic CT +% Get the plan parameters calculated on synthetic CT dvh = resultGUI.dvh; qi = resultGUI.qi; -% In the similar fashion, save DVH parameters calculated on the synthetic CT to the table -if matRad_cfg.isMatlab %tables not supported by Octave - dvhTableFake=struct2table(qi); +% In the similar fashion, save DVH parameters calculated on the synthetic CT to the table +if matRad_cfg.isMatlab % tables not supported by Octave + dvhTableFake = struct2table(qi); % Select the same DVH parameters for further comparison - dvhTableFake=dvhTableFake(:,horzcat({'name'},indicators)); - dvhTableFake.patient= repmat(char(name),length(qi),1); - dvhTableFake.ct_type = repmat('fake',length(qi),1); + dvhTableFake = dvhTableFake(:, horzcat({'name'}, indicators)); + dvhTableFake.patient = repmat(char(name), length(qi), 1); + dvhTableFake.ct_type = repmat('fake', length(qi), 1); end %% Calculate the difference in between of DVH calculated on real CT (dvh_table_real) and synthetic CT (dvh_table_fake) @@ -302,15 +294,15 @@ dvhTableDiff = dvhTableReal; for i = 1:height(dvhTableReal) for j = indicators - if cell2mat(dvhTableReal{i,'name'}) == cell2mat(dvhTableFake{i,'name'}) - dvhTableDiff{i,j} = (dvhTableReal{i,j} - dvhTableFake{i,j}) / dvhTableReal{i,j}*100; + if cell2mat(dvhTableReal{i, 'name'}) == cell2mat(dvhTableFake{i, 'name'}) + dvhTableDiff{i, j} = (dvhTableReal{i, j} - dvhTableFake{i, j}) / dvhTableReal{i, j} * 100; else - dvhTableDiff{i,j} = 'error'; + dvhTableDiff{i, j} = 'error'; end end end - disp(dvhTableDiff) + disp(dvhTableDiff); %%% Save the results to CSV file %file_path_dvh_diff = "YOUR PATH" diff --git a/examples/matRad_example1_phantom.m b/examples/matRad_example1_phantom.m index 0f90fccef..eac7e2a9c 100644 --- a/examples/matRad_example1_phantom.m +++ b/examples/matRad_example1_phantom.m @@ -19,78 +19,76 @@ % (iii) generate a treatment plan for this phantom %% set matRad runtime configuration -%clear all; %somewhat needed for the phantom builder -matRad_rc; %If this throws an error, run it from the parent directory first to set the paths -matRad_cfg = MatRad_Config.instance(); %This creates a matRad-Config object holding global configuration parameters +% clear all; %somewhat needed for the phantom builder +matRad_rc; % If this throws an error, run it from the parent directory first to set the paths +matRad_cfg = MatRad_Config.instance(); % This creates a matRad-Config object holding global configuration parameters %% Create a CT image series -ctDim = [200,200,100]; % x,y,z dimensions -ctResolution = [2,2,3]; % x,y,z the same here! +ctDim = [200, 200, 100]; % x,y,z dimensions +ctResolution = [2, 2, 3]; % x,y,z the same here! -%This uses the phantombuilder class, which helps to easily implement a -%water phantom containing geometrical 3D shapes as targets and organs -builder = matRad_PhantomBuilder(ctDim,ctResolution,1); +% This uses the phantombuilder class, which helps to easily implement a +% water phantom containing geometrical 3D shapes as targets and organs +builder = matRad_PhantomBuilder(ctDim, ctResolution, 1); %% Create the VOI data for the phantom -%To add a VOI there are (so far) two different options -%either a Box or a spherical Volume (either OAR or Target) -%NOTE: The order in which the objectives are initialized matters! +% To add a VOI there are (so far) two different options +% either a Box or a spherical Volume (either OAR or Target) +% NOTE: The order in which the objectives are initialized matters! % In case of overlaps in the objectives, the firstly created objectives have % a higher priority! This means that if two VOI have an overlap with % different HU, then the value of the firstly initialized objective will be % set in the overlap region +% define objectives for the VOI -%define objectives for the VOI +objective1 = struct(DoseObjectives.matRad_SquaredDeviation(800, 45)); +objective2 = struct(DoseObjectives.matRad_SquaredOverdosing(400, 0)); +objective3 = struct(DoseObjectives.matRad_SquaredOverdosing(10, 0)); -objective1 = struct(DoseObjectives.matRad_SquaredDeviation(800,45)); -objective2 = struct(DoseObjectives.matRad_SquaredOverdosing(400,0)); -objective3 = struct(DoseObjectives.matRad_SquaredOverdosing(10,0)); - -builder.addSphericalTarget('Volume1',20,'objectives',objective1,'HU',0); -builder.addBoxOAR('Volume2',[60,30,60],'offset',[0 -15 0],'objectives',objective2); -builder.addBoxOAR('Volume3',[60,30,60],'offset',[0 15 0],'objectives',objective3); +builder.addSphericalTarget('Volume1', 20, 'objectives', objective1, 'HU', 0); +builder.addBoxOAR('Volume2', [60, 30, 60], 'offset', [0 -15 0], 'objectives', objective2); +builder.addBoxOAR('Volume3', [60, 30, 60], 'offset', [0 15 0], 'objectives', objective3); % This adds two Box OAR and one Spherical Target in the middle % For Box Volumes a name (here Volume2 and Volume3) has to be specified, % as well as the dimension of the Volume. % For spherical Volumes a name has to be specified, as well as the radius % of the sphere -%(Optional) To move the VOI from the center of the ct an offset can be set. -%(Optional) The objectives can already be set here, however this can also -%be done later on -%(Optional) The HU of the VOI can be set (normal value: 0 (water)) - +% (Optional) To move the VOI from the center of the ct an offset can be set. +% (Optional) The objectives can already be set here, however this can also +% be done later on +% (Optional) The HU of the VOI can be set (normal value: 0 (water)) %% Get the ct and cst (stored as properties of the phantomBuilder class) -[ct,cst] = builder.getctcst(); +[ct, cst] = builder.getctcst(); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This +% The next step is to define your treatment plan labeled as 'pln'. This % structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this % example we would like to use photons for treatment planning. Next, we -% need to define a treatment machine to correctly load the corresponding +% need to define a treatment machine to correctly load the corresponding % base data. matRad features generic base data in the file % 'photons_Generic.mat'; consequently the machine has to be set to 'Generic' -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; % Define the biological optimization model for treatment planning. % Examples for possible models are: % 'none': no biological model (physical dose only); -% 'constRBE': constant RBE of 1.1; -% 'MCN': McNamara-variable RBE model for protons; +% 'constRBE': constant RBE of 1.1; +% 'MCN': McNamara-variable RBE model for protons; % 'WED': Wedenberg-variable RBE model for protons % 'LEM': Local Effect Model (based on precomputed kernels) pln.bioModel = 'none'; -% matRad allows for an uncertainty scenario model to be respected in planning, +% matRad allows for an uncertainty scenario model to be respected in planning, % which compute a number of error scenarios which can be used in optimization. % Examples for possible scenario models % 'nomScen': Only Nominal Scenario is considered @@ -107,11 +105,7 @@ pln.propStf.gantryAngles = [0:70:355]; pln.propStf.couchAngles = zeros(size(pln.propStf.gantryAngles)); pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); - -% Settings for Optimization -pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 3; % [mm] @@ -122,36 +116,36 @@ matRadGUI; %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for intensity-modulated photon therapy -% The goal of the fluence optimization is to find a set of bixel/spot +% The goal of the fluence optimization is to find a set of bixel/spot % weights which yield the best possible dose distribution according to the % clinical objectives and constraints underlying the radiation treatment. -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Visualization -matRadGUI +matRadGUI; %% Plot the resulting dose slice plane = 3; -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); doseWindow = [0 max([resultGUI.physicalDose(:)])]; -figure,title('phantom plan') +figure; +title('phantom plan'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.physicalDose, 'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', doseWindow); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.physicalDose,plane,slice,[],[],colorcube,[],doseWindow,[]); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.physicalDose,plane,slice,[],[],colorcube,[],doseWindow,[]); -%% -% We export the the created phantom & dose as dicom. This is handled by the +%% +% We export the the created phantom & dose as dicom. This is handled by the % class matRad_DicomExporter. When no arguments are given, the exporter searches % the workspace itself for matRad-structures. The output directory can be set by -% the property dicomDir. While the different DICOM datasets (ct, RTStruct, etc) +% the property dicomDir. While the different DICOM datasets (ct, RTStruct, etc) % can be exported individually, we call the wrapper to do all possible exports. dcmExport = matRad_DicomExporter(); dcmExport.dicomDir = [matRad_cfg.primaryUserFolder filesep 'dicomExport']; dcmExport.matRad_exportDicom(); - diff --git a/examples/matRad_example2_photons.m b/examples/matRad_example2_photons.m index d2b2e27cb..9779f639c 100644 --- a/examples/matRad_example2_photons.m +++ b/examples/matRad_example2_photons.m @@ -13,79 +13,79 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to load patient data into matRad -% (ii) how to setup a photon dose calculation and +% (ii) how to setup a photon dose calculation and % (iii) how to inversely optimize beamlet intensities % (iv) how to visually and quantitatively evaluate the result %% Patient Data Import % Let's begin with a clear Matlab environment. Then, import the TG119 % phantom into your workspace. The phantom is comprised of a 'ct' and 'cst' -% structure defining the CT images and the structure set. Make sure the -% matRad root directory with all its subdirectories is added to the Matlab +% structure defining the CT images and the structure set. Make sure the +% matRad root directory with all its subdirectories is added to the Matlab % search path. -matRad_cfg = matRad_rc; %If this throws an error, run it from the parent directory first to set the paths +matRad_cfg = matRad_rc; % If this throws an error, run it from the parent directory first to set the paths load('TG119.mat'); %% -% The file TG119.mat contains two Matlab variables. Let's check what we +% The file TG119.mat contains two Matlab variables. Let's check what we % have just imported. First, the 'ct' variable comprises the ct cube along -%with some meta information describing properties of the ct cube (cube -% dimensions, resolution, number of CT scenarios). Please note that -%multiple ct cubes (e.g. 4D CT) can be stored in the cell array ct.cube{} +% with some meta information describing properties of the ct cube (cube +% dimensions, resolution, number of CT scenarios). Please note that +% multiple ct cubes (e.g. 4D CT) can be stored in the cell array ct.cube{} disp(ct); %% -% The 'cst' cell array defines volumes of interests along with information -% required for optimization. Each row belongs to one certain volume of -% interest (VOI), whereas each column defines different properties. -% Specifically, the second and third column show the name and the type of -% the structure. The type can be set to OAR, TARGET or IGNORED. The fourth -% column contains a linear index vector that lists all voxels belonging to +% The 'cst' cell array defines volumes of interests along with information +% required for optimization. Each row belongs to one certain volume of +% interest (VOI), whereas each column defines different properties. +% Specifically, the second and third column show the name and the type of +% the structure. The type can be set to OAR, TARGET or IGNORED. The fourth +% column contains a linear index vector that lists all voxels belonging to % a certain VOI. disp(cst); %% % The fifth column represents meta parameters for optimization. The overlap -% priority is used to resolve ambiguities of overlapping structures (voxels +% priority is used to resolve ambiguities of overlapping structures (voxels % belonging to multiple structures will only be assigned to the VOI(s) with -% the highest overlap priority, i.e.. the lowest value). The parameters -% alphaX and betaX correspond to the tissue's photon-radiosensitivity +% the highest overlap priority, i.e.. the lowest value). The parameters +% alphaX and betaX correspond to the tissue's photon-radiosensitivity % parameter of the linear quadratic model. These parameter are required for -% biological treatment planning using a variable RBE. Let's output the meta +% biological treatment planning using a variable RBE. Let's output the meta % optimization parameter of the target, which is stored in the thrid row: ixTarget = 3; -disp(cst{ixTarget,5}); +disp(cst{ixTarget, 5}); %% % The sixth column contains optimization information such as objectives and -% constraints which are required to calculate the objective function value. +% constraints which are required to calculate the objective function value. % Please note, that multiple objectives/constraints can be defined for -% individual structures. Here, we have defined a squared deviation -% objective making it 'expensive/costly' for the optimizer to over- and -% underdose the target structure (both are equally important). +% individual structures. Here, we have defined a squared deviation +% objective making it 'expensive/costly' for the optimizer to over- and +% underdose the target structure (both are equally important). -disp(cst{ixTarget,6}); +disp(cst{ixTarget, 6}); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This -% matlab structure requires input from the treatment planner and defines +% The next step is to define your treatment plan labeled as 'pln'. This +% matlab structure requires input from the treatment planner and defines % the most important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this case -% we want to use photons. Then, we need to define a treatment machine to +% we want to use photons. Then, we need to define a treatment machine to % correctly load the corresponding base data. matRad includes base data for -% generic photon linear accelerator called 'Generic'. By this means matRad -% will look for 'photons_Generic.mat' in our root directory and will use +% generic photon linear accelerator called 'Generic'. By this means matRad +% will look for 'photons_Generic.mat' in our root directory and will use % the data provided in there for dose calculation. -% The number of fractions is set to 30. Internally, matRad considers the -% fraction dose for optimization, however, objetives and constraints are +% The number of fractions is set to 30. Internally, matRad considers the +% fraction dose for optimization, however, objetives and constraints are % defined for the entire treatment. -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; pln.numOfFractions = 30; @@ -101,29 +101,29 @@ % As we are using photons, we simply set the parameter to 'none' pln.bioModel = 'none'; -%% +%% % It is possible to request multiple error scenarios for robustness % analysis and optimization. Here, we just use the "nominal scenario" % (nomScen) pln.multScen = 'nomScen'; %% -% Now we have to set some beam parameters. We can define multiple beam -% angles for the treatment and pass these to the plan as a vector. matRad +% Now we have to set some beam parameters. We can define multiple beam +% angles for the treatment and pass these to the plan as a vector. matRad % will then interpret the vector as multiple beams. In this case, we define -% linear spaced beams from 0 degree to 359 degree in 40 degree steps. This -% results in 9 beams. All corresponding couch angles are set to 0 at this -% point. Moreover, we set the bixelWidth to 5, which results in a beamlet -% size of 5 x 5 mm in the isocenter plane. +% linear spaced beams from 0 degree to 359 degree in 40 degree steps. This +% results in 9 beams. All corresponding couch angles are set to 0 at this +% point. Moreover, we set the bixelWidth to 5, which results in a beamlet +% size of 5 x 5 mm in the isocenter plane. pln.propStf.gantryAngles = [0:40:359]; -pln.propStf.couchAngles = zeros(1,numel(pln.propStf.gantryAngles)); +pln.propStf.couchAngles = zeros(1, numel(pln.propStf.gantryAngles)); pln.propStf.bixelWidth = 5; -% Obtain the number of beams and voxels from the existing variables and -% calculate the iso-center which is per default the center of gravity of +% Obtain the number of beams and voxels from the existing variables and +% calculate the iso-center which is per default the center of gravity of % all target voxels. -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); %% dose calculation settings % set resolution of dose calculation and optimization @@ -131,65 +131,59 @@ pln.propDoseCalc.doseGrid.resolution.y = 3; % [mm] pln.propDoseCalc.doseGrid.resolution.z = 3; % [mm] -%% -% Enable sequencing and disable direct aperture optimization (DAO) for now. -% A DAO optimization is shown in a seperate example. -pln.propSeq.runSequencing = 1; -pln.propOpt.runDAO = 0; - - %% % and et voila our treatment plan structure is ready. Lets have a look: disp(pln); - %% Generate Beam Geometry STF -% The steering file struct comprises the complete beam geometry along with +% The steering file struct comprises the complete beam geometry along with % ray position, pencil beam positions and energies, source to axis distance (SAD) etc. -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% % Let's display the beam geometry information of the 6th beam disp(stf(6)); %% Dose Calculation -% Let's generate dosimetric information by pre-computing dose influence -% matrices for unit beamlet intensities. Having dose influences available +% Let's generate dosimetric information by pre-computing dose influence +% matrices for unit beamlet intensities. Having dose influences available % allows subsequent inverse optimization. -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMRT -% The goal of the fluence optimization is to find a set of beamlet/pencil +% The goal of the fluence optimization is to find a set of beamlet/pencil % beam weights which yield the best possible dose distribution according to -% the clinical objectives and constraints underlying the radiation -% treatment. Once the optimization has finished, trigger once the GUI to +% the clinical objectives and constraints underlying the radiation +% treatment. Once the optimization has finished, trigger once the GUI to % visualize the optimized dose cubes. -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); matRadGUI; %% Plot the Resulting Dose Slice % Let's plot the transversal iso-center dose slice -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure -imagesc(resultGUI.physicalDose(:,:,slice)),colorbar, colormap(jet); +figure; +imagesc(resultGUI.physicalDose(:, :, slice)); +colorbar; +colormap(jet); %% Now let's create another treatment plan but this time use a coarser beam spacing. % Instead of 40 degree spacing use a 50 degree geantry beam spacing pln.propStf.gantryAngles = [0:50:359]; -pln.propStf.couchAngles = zeros(1,numel(pln.propStf.gantryAngles)); +pln.propStf.couchAngles = zeros(1, numel(pln.propStf.gantryAngles)); -%Let's rerun the dose calculation and optimization -stf = matRad_generateStf(ct,cst,pln); +% Let's rerun the dose calculation and optimization +stf = matRad_generateStf(ct, cst, pln); pln.propStf.isoCenter = vertcat(stf.isoCenter); -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); -resultGUI_coarse = matRad_fluenceOptimization(dij,cst,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); +resultGUI_coarse = matRad_fluenceOptimization(dij, cst, pln); -%We append the new result to the resultGUI variable (recognized by the GUI) -%using the identifier coarse -resultGUI = matRad_appendResultGUI(resultGUI,resultGUI_coarse,false,'coarse'); -%A GUI update ensures the current values are updated +% We append the new result to the resultGUI variable (recognized by the GUI) +% using the identifier coarse +resultGUI = matRad_appendResultGUI(resultGUI, resultGUI_coarse, false, 'coarse'); +% A GUI update ensures the current values are updated matRadGUI; %% Visual Comparison of results @@ -199,42 +193,44 @@ plane = 3; doseWindow = [0 max([resultGUI.physicalDose(:); resultGUI_coarse.physicalDose(:)])]; -figure,title('original plan - fine beam spacing') +figure; +title('original plan - fine beam spacing'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.physicalDose, 'plane', plane, 'slice', slice, 'alpha', 0.75, 'contourColorMap', colorcube, 'doseWindow', doseWindow); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.physicalDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); -figure,title('modified plan - coarse beam spacing') +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.physicalDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); +figure; +title('modified plan - coarse beam spacing'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI_coarse.physicalDose, 'plane', plane, 'slice', slice, 'alpha', 0.75, 'contourColorMap', colorcube, 'doseWindow', doseWindow); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_coarse.physicalDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_coarse.physicalDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); -%% -% At this point we would like to see the absolute difference of the first -% optimization (finer beam spacing) and the second optimization (coarser +%% +% At this point we would like to see the absolute difference of the first +% optimization (finer beam spacing) and the second optimization (coarser % beam spacing) -absDiffCube = resultGUI.physicalDose-resultGUI_coarse.physicalDose; -figure,title( 'fine beam spacing plan - coarse beam spacing plan') +absDiffCube = resultGUI.physicalDose - resultGUI_coarse.physicalDose; +figure; +title('fine beam spacing plan - coarse beam spacing plan'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', absDiffCube, 'plane', plane, 'slice', slice, 'contourColorMap', colorcube); -%matRad_plotSliceWrapper(gca,ct,cst,1,absDiffCube,plane,slice,[],[],colorcube); +% matRad_plotSliceWrapper(gca,ct,cst,1,absDiffCube,plane,slice,[],[],colorcube); %% Obtain dose statistics % Two more columns will be added to the cst structure depicting the DVH and % standard dose statistics such as D95,D98, mean dose, max dose etc. -resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln); -resultGUI_coarse = matRad_planAnalysis(resultGUI_coarse,ct,cst,stf,pln); +resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); +resultGUI_coarse = matRad_planAnalysis(resultGUI_coarse, ct, cst, stf, pln); %% % The treatment plan using more beams should in principle result in a -% better OAR sparing. Therefore lets have a look at the D95 of the OAR of +% better OAR sparing. Therefore lets have a look at the D95 of the OAR of % both plans ixOAR = 2; disp(resultGUI.qi(ixOAR).D_95); disp(resultGUI_coarse.qi(ixOAR).D_95); - -%% -% Export the dose in binary format. matRad can write multiple formats, here we -% choose to export in nrrd format using the matRad_writeCube function, which +%% +% Export the dose in binary format. matRad can write multiple formats, here we +% choose to export in nrrd format using the matRad_writeCube function, which % chooses the appropriate subroutine from the extension. The metadata struct can % also store more optional parameters, but requires only the resolution to be s % set. -metadata = struct('resolution',[ct.resolution.x ct.resolution.y ct.resolution.z]); -matRad_writeCube(fullfile(matRad_cfg.primaryUserFolder,'photonDose_example2.nrrd'),resultGUI.physicalDose,'double',metadata); +metadata = struct('resolution', [ct.resolution.x ct.resolution.y ct.resolution.z]); +matRad_writeCube(fullfile(matRad_cfg.primaryUserFolder, 'photonDose_example2.nrrd'), resultGUI.physicalDose, 'double', metadata); diff --git a/examples/matRad_example3_photonsDAO.m b/examples/matRad_example3_photonsDAO.m index 9ac82ff1a..043cb18bf 100644 --- a/examples/matRad_example3_photonsDAO.m +++ b/examples/matRad_example3_photonsDAO.m @@ -46,9 +46,9 @@ pln.multScen = 'nomScen'; % dose calculation settings -pln.propDoseCalc.doseGrid.resolution.x = 3; % [mm] -pln.propDoseCalc.doseGrid.resolution.y = 3; % [mm] -pln.propDoseCalc.doseGrid.resolution.z = 3; % [mm] +pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] +pln.propDoseCalc.doseGrid.resolution.y = 5; % [mm] +pln.propDoseCalc.doseGrid.resolution.z = 5; % [mm] % We can also use other solver for optimization than IPOPT. matRad % currently supports fmincon from the MATLAB Optimization Toolbox. First we @@ -62,11 +62,6 @@ end pln.propOpt.quantityOpt = 'physicalDose'; -%% -% Enable sequencing and direct aperture optimization (DAO). -pln.propSeq.runSequencing = true; -pln.propOpt.runDAO = true; - %% Generate Beam Geometry STF stf = matRad_generateStf(ct, cst, pln); @@ -90,16 +85,26 @@ % order to modulate the intensity of the beams with multiple static % segments, so that translates each intensity map into a set of deliverable % aperture shapes. -resultGUI = matRad_sequencing(resultGUI, stf, dij, pln); + +%% some testing of sequencing +pln.propSeq.sequencer = 'siochi'; +pln.propSeq.sequencingLevel = 10; +resultGUI_SIOCHI = matRad_sequencing(resultGUI, stf, pln, dij); + +pln.propSeq.sequencer = 'xia'; +resultGUI_XIA = matRad_sequencing(resultGUI, stf, pln, dij); + +pln.propSeq.sequencer = 'engel'; +resultGUI_ENGEL = matRad_sequencing(resultGUI, stf, pln, dij); %% DAO - Direct Aperture Optimization % The Direct Aperture Optimization is an optimization approach where we % directly optimize aperture shapes and weights. -resultGUI = matRad_directApertureOptimization(dij, cst, resultGUI.apertureInfo, resultGUI, pln); +resultGUI_SIOCHI_DAO = matRad_directApertureOptimization(dij, cst, resultGUI_SIOCHI.sequencing.apertureInfo, pln); %% Aperture visualization % Use a matrad function to visualize the resulting aperture shapes -matRad_visApertureInfo(resultGUI.apertureInfo); +matRad_visApertureInfo(resultGUI_SIOCHI_DAO.sequencing.apertureInfo); %% Indicator Calculation and display of DVH and QI resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); diff --git a/examples/matRad_example4_photonsMC.m b/examples/matRad_example4_photonsMC.m index 6246e086b..395e2462f 100644 --- a/examples/matRad_example4_photonsMC.m +++ b/examples/matRad_example4_photonsMC.m @@ -13,37 +13,35 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to load patient data into matRad -% (ii) how to setup a photon dose calculation based on the VMC++ Monte Carlo algorithm -% (iii) how to inversely optimize the beamlet intensities directly from command window in MATLAB. +% (ii) how to setup a photon dose calculation based on the VMC++ Monte Carlo algorithm +% (iii) how to inversely optimize the beamlet intensities directly from command window in MATLAB. % (iv) how to visualize the result %% set matRad runtime configuration -matRad_rc %If this throws an error, run it from the parent directory first to set the paths +matRad_rc; % If this throws an error, run it from the parent directory first to set the paths %% Patient Data Import % Let's begin with a clear Matlab environment and import the boxphantom -% into your workspace. +% into your workspace. load('BOXPHANTOM.mat'); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This +% The next step is to define your treatment plan labeled as 'pln'. This % structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. -pln.radiationMode = 'photons'; +pln.radiationMode = 'photons'; pln.machine = 'Generic'; pln.numOfFractions = 30; -pln.bioModel = 'none'; +pln.bioModel = 'none'; pln.multScen = 'nomScen'; pln.propStf.gantryAngles = [0]; pln.propStf.couchAngles = [0]; pln.propStf.bixelWidth = 10; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propSeq.runSequencing = 0; -pln.propOpt.runDAO = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings % We can choose a different dose calculation engine, here "ompMC", by @@ -57,32 +55,36 @@ pln.propDoseCalc.doseGrid.resolution.z = 3; % [mm] %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -% Calculate dose influence matrix for unit pencil beam intensities using +% Calculate dose influence matrix for unit pencil beam intensities using % a Monte Carlo algorithm -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMRT -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Plot the Resulting Dose Slice % Just let's plot the transversal iso-center dose slice -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure, -imagesc(resultGUI.physicalDose(:,:,slice)),colorbar, colormap(jet) +figure; +imagesc(resultGUI.physicalDose(:, :, slice)); +colorbar; +colormap(jet); %% % Exemplary, we show how to obtain the dose in the target and plot the histogram -ixTarget = cst{2,4}{1}; +ixTarget = cst{2, 4}{1}; doseInTarget = resultGUI.physicalDose(ixTarget); -figure +figure; hist(doseInTarget); - % use hist for compatibility with GNU Octave -title('dose in target'),xlabel('[Gy]'),ylabel('#'); +% use hist for compatibility with GNU Octave +title('dose in target'); +xlabel('[Gy]'); +ylabel('#'); %% compute integral energy -matRad_calcIntEnergy(resultGUI.physicalDose,ct,pln); +matRad_calcIntEnergy(resultGUI.physicalDose, ct, pln); diff --git a/examples/matRad_example5_protons.m b/examples/matRad_example5_protons.m index 9efccaa17..0af07cb92 100644 --- a/examples/matRad_example5_protons.m +++ b/examples/matRad_example5_protons.m @@ -13,16 +13,16 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) how to load patient data into matRad -% (ii) how to setup a proton dose calculation -% (iii) how to inversely optimize the pencil beam intensities directly from command window in MATLAB. -% (iv) how to simulate a lateral patient displacement by shifting the iso-center +% (ii) how to setup a proton dose calculation +% (iii) how to inversely optimize the pencil beam intensities directly from command window in MATLAB. +% (iv) how to simulate a lateral patient displacement by shifting the iso-center % (v) how to recalculated the dose considering the shifted geometry and the previously optimized pencil beam intensities % (vi) how to compare the two results %% set matRad runtime configuration -matRad_rc; %If this throws an error, run it from the parent directory first to set the paths +matRad_rc; % If this throws an error, run it from the parent directory first to set the paths %% Patient Data Import % Let's begin with a clear Matlab environment and import the prostate @@ -31,18 +31,18 @@ load('PROSTATE.mat'); %% Treatment Plan -% The next step is to define your treatment plan labeled as 'pln'. This -% structure requires input from the treatment planner and defines the most +% The next step is to define your treatment plan labeled as 'pln'. This +% structure requires input from the treatment planner and defines the most % important cornerstones of your treatment plan. %% % First of all, we need to define what kind of radiation modality we would % like to use. Possible values are photons, protons or carbon. In this % example we would like to use protons for treatment planning. Next, we -% need to define a treatment machine to correctly load the corresponding +% need to define a treatment machine to correctly load the corresponding % base data. matRad features generic base data in the file % 'proton_Generic.mat'; consequently the machine has to be set accordingly -pln.radiationMode = 'protons'; +pln.radiationMode = 'protons'; pln.machine = 'Generic'; pln.bioModel = 'constRBE'; pln.multScen = 'nomScen'; @@ -54,16 +54,14 @@ % use the Hong Pencil Beam Algorithm pln.propDoseCalc.calcLET = 0; pln.propDoseCalc.engine = 'HongPB'; - + %% % Now we have to set the remaining plan parameters. pln.numOfFractions = 30; pln.propStf.gantryAngles = [90 270]; pln.propStf.couchAngles = [0 0]; pln.propStf.bixelWidth = 5; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runDAO = 0; -pln.propSeq.runSequencing = 0; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 3; % [mm] @@ -74,96 +72,116 @@ pln.propOpt.quantityOpt = 'RBExDose'; %% Generate Beam Geometry STF -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); %% Dose Calculation -% Lets generate dosimetric information by pre-computing dose influence -% matrices for unit beamlet intensities. Having dose influences available -% allows for subsequent inverse optimization. -dij = matRad_calcDoseInfluence(ct,cst,stf,pln); +% Lets generate dosimetric information by pre-computing dose influence +% matrices for unit beamlet intensities. Having dose influences available +% allows for subsequent inverse optimization. +dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% Inverse Optimization for IMPT -% The goal of the fluence optimization is to find a set of bixel/spot +% The goal of the fluence optimization is to find a set of bixel/spot % weights which yield the best possible dose distribution according to the % clinical objectives and constraints underlying the radiation treatment -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% Plot the Resulting Dose Slice % Let's plot the transversal iso-center dose slice -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure -imagesc(resultGUI.RBExDose(:,:,slice)),colorbar,colormap(jet) +figure; +imagesc(resultGUI.RBExDose(:, :, slice)); +colorbar; +colormap(jet); %% Plot the Resulting Beam Dose Slice % Let's plot the transversal iso-center dose slice of beam 1 and beam 2 -% separately -figure -subplot(121),imagesc(resultGUI.RBExDose_beam1(:,:,slice)),colorbar,colormap(jet),title('dose of beam 1') -subplot(122),imagesc(resultGUI.RBExDose_beam2(:,:,slice)),colorbar,colormap(jet),title('dose of beam 2') +% separately +figure; +subplot(121); +imagesc(resultGUI.RBExDose_beam1(:, :, slice)); +colorbar; +colormap(jet); +title('dose of beam 1'); +subplot(122); +imagesc(resultGUI.RBExDose_beam2(:, :, slice)); +colorbar; +colormap(jet); +title('dose of beam 2'); %% and the corresponding LET distribution % Transversal iso-center slice if pln.propDoseCalc.calcLET - figure - imagesc(resultGUI.LET(:,:,slice)),colormap(jet),colorbar,title('LET [keV/�m]') + figure; + imagesc(resultGUI.LET(:, :, slice)); + colormap(jet); + colorbar; + title('LET [keV/�m]'); end %% % Now let's simulate a patient shift in y direction for both beams stf(1).isoCenter(2) = stf(1).isoCenter(2) - 4; stf(2).isoCenter(2) = stf(2).isoCenter(2) - 4; -pln.propStf.isoCenter = reshape([stf.isoCenter],[3 numel(stf)])'; +pln.propStf.isoCenter = reshape([stf.isoCenter], [3 numel(stf)])'; %% Recalculate Plan % Let's use the existing optimized pencil beam weights and recalculate the RBE weighted dose -resultGUI_isoShift = matRad_calcDoseForward(ct,cst,stf,pln,resultGUI.w); +resultGUI_isoShift = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); %% Visual Comparison of results % Let's compare the new recalculation against the optimization result. plane = 3; doseWindow = [0 max([resultGUI.RBExDose(:); resultGUI_isoShift.RBExDose(:)])]; -figure,title('original plan') +figure; +title('original plan'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI.RBExDose, 'plane', plane, 'slice', slice, 'alpha', 0.75, 'contourColorMap', colorcube, 'doseWindow', doseWindow); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.RBExDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); -figure,title('shifted plan') +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.RBExDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); +figure; +title('shifted plan'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUI_isoShift.RBExDose, 'plane', plane, 'slice', slice, 'alpha', 0.75, 'contourColorMap', colorcube, 'doseWindow', doseWindow); -%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_isoShift.RBExDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); +% matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_isoShift.RBExDose,plane,slice,[],0.75,colorcube,[],doseWindow,[]); -absDiffCube = resultGUI.RBExDose-resultGUI_isoShift.RBExDose; -figure,title('absolute difference') +absDiffCube = resultGUI.RBExDose - resultGUI_isoShift.RBExDose; +figure; +title('absolute difference'); matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', absDiffCube, 'plane', plane, 'slice', slice, 'contourColorMap', colorcube); -%matRad_plotSliceWrapper(gca,ct,cst,1,absDiffCube,plane,slice,[],[],colorcube); +% matRad_plotSliceWrapper(gca,ct,cst,1,absDiffCube,plane,slice,[],[],colorcube); % Let's plot single profiles that are perpendicular to the beam direction -ixProfileY = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +ixProfileY = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); ixProfileY = ixProfileY(2); -profileOrginal = resultGUI.RBExDose(:,ixProfileY,slice); -profileShifted = resultGUI_isoShift.RBExDose(:,ixProfileY,slice); - -figure,plot(profileOrginal,'LineWidth',2),grid on,hold on, -plot(profileShifted,'LineWidth',2),legend({'original profile','shifted profile'}), -xlabel('mm'),ylabel('Gy(RBE)'),title('profile plot') +profileOrginal = resultGUI.RBExDose(:, ixProfileY, slice); +profileShifted = resultGUI_isoShift.RBExDose(:, ixProfileY, slice); + +figure; +plot(profileOrginal, 'LineWidth', 2); +grid on; +hold on; +plot(profileShifted, 'LineWidth', 2); +legend({'original profile', 'shifted profile'}); +xlabel('mm'); +ylabel('Gy(RBE)'); +title('profile plot'); %% Quantitative Comparison of results % Compare the two dose cubes using a gamma-index analysis. The gamma index -% is a composite quality distribution equally taking into account a dose +% is a composite quality distribution equally taking into account a dose % difference and a distance to agreement criterion in order to quantify % differences between two dose cubes. A gamma-index value of smaller than 1 -% indicates a successful test and a value greater than 1 illustrates a -% failed test. An alternative analysis could be performed with the +% indicates a successful test and a value greater than 1 illustrates a +% failed test. An alternative analysis could be performed with the % matRad_compareDose function, which includes the gamma test doseDifference = 2; distToAgreement = 2; n = 1; -[gammaCube,gammaPassRateCell] = matRad_gammaIndex(... - resultGUI_isoShift.RBExDose,resultGUI.RBExDose,... - [ct.resolution.x, ct.resolution.y, ct.resolution.z],... - [doseDifference distToAgreement],slice,n,'global',cst); +[gammaCube, gammaPassRateCell] = matRad_gammaIndex( ... + resultGUI_isoShift.RBExDose, resultGUI.RBExDose, ... + [ct.resolution.x, ct.resolution.y, ct.resolution.z], ... + [doseDifference distToAgreement], slice, n, 'global', cst); % Let's plot the gamma index histogram -figure -hist(gammaCube(gammaCube>0),100) -title('gamma index histogram') - - +figure; +hist(gammaCube(gammaCube > 0), 100); +title('gamma index histogram'); diff --git a/examples/matRad_example9_4DDoseCalcMinimal.m b/examples/matRad_example9_4DDoseCalcMinimal.m index 712a3fd6c..4d5252976 100644 --- a/examples/matRad_example9_4DDoseCalcMinimal.m +++ b/examples/matRad_example9_4DDoseCalcMinimal.m @@ -12,22 +12,22 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% In this example we will show +%% In this example we will show % (i) the structure of 4D data within matRad % (ii) how to perform standard treatment planning -% (iii) how to run a dose recalculation considering interplay effects - +% (iii) how to run a dose recalculation considering interplay effects + %% set matRad runtime configuration -matRad_rc +matRad_rc; %% Load data, add generic 4D information, and display 'moving' geometry -load BOXPHANTOM.mat +load BOXPHANTOM.mat; amplitude = [0 3 0]; % [voxels] numOfCtScen = 5; -motionPeriod = 2.5; % [s] +motionPeriod = 2.5; % [s] -[ct,cst] = matRad_addMovement(ct, cst,motionPeriod, numOfCtScen, amplitude,'dvfType','pull'); +[ct, cst] = matRad_addMovement(ct, cst, motionPeriod, numOfCtScen, amplitude, 'dvfType', 'pull'); % Set up a plan, compute dose influence on all phases, conventional optimization % meta information for treatment plan @@ -44,53 +44,58 @@ % beam geometry settings pln.propStf.bixelWidth = 5; % [mm] / also corresponds to lateral spot spacing for particles pln.propStf.longitudinalSpotSpacing = 5; % only relevant for HIT machine, not generic -pln.propStf.gantryAngles = [90]; -pln.propStf.couchAngles = [0]; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); - +pln.propStf.gantryAngles = [90]; +pln.propStf.couchAngles = [0]; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); %% % generate steering file -stf = matRad_generateStf(ct,cst,pln); +stf = matRad_generateStf(ct, cst, pln); -%% +%% % dose calculation -dij = matRad_calcParticleDose(ct,stf,pln,cst); +dij = matRad_calcParticleDose(ct, stf, pln, cst); -%% +%% % inverse planning for imrt on a static CT -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); -%% +%% % post processing % This step is necessary to remove beam spots with too few particles that % cannot not be delivered, dose is recalculated accordingly -resultGUI = matRad_postprocessing(resultGUI, dij, pln, cst, stf) ; +resultGUI = matRad_postprocessing(resultGUI, dij, pln, cst, stf); %% % calc 4D dose % make sure that the correct pln, dij and stf are loeaded in the workspace -[resultGUI, timeSequence] = matRad_calc4dDose(ct, pln, dij, stf, cst, resultGUI); +resultGUI = matRad_calc4dDose(dij, pln, stf, resultGUI); +resultGUI = matRad_acc4dDose(dij, pln, ct, cst, resultGUI, 'DDM'); % plot the result in comparison to the static dose -slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1,:),ct); +slice = matRad_world2cubeIndex(pln.propStf.isoCenter(1, :), ct); slice = slice(3); -figure - -subplot(2,2,1) -imagesc(resultGUI.RBExDose(:,:,slice)),colorbar, colormap(jet); -title('static dose distribution [Gy (RBE)]') -axis equal - -subplot(2,2,3) -imagesc(resultGUI.accRBExDose(:,:,slice)),colorbar, colormap(jet); -title('accumulated (4D) dose distribution [Gy (RBE)]') -axis equal - -subplot(2,2,2) -imagesc(resultGUI.RBExDose(:,:,slice) - resultGUI.accRBExDose(:,:,slice)) ,colorbar, colormap(jet); -title('static dose distribution - accumulated (4D) dose distribution [Gy (RBE)]') - -axis equal - +figure; + +subplot(2, 2, 1); +imagesc(resultGUI.RBExDose(:, :, slice)); +colorbar; +colormap(jet); +title('static dose distribution [Gy (RBE)]'); +axis equal; + +subplot(2, 2, 3); +imagesc(resultGUI.accRBExDose(:, :, slice)); +colorbar; +colormap(jet); +title('accumulated (4D) dose distribution [Gy (RBE)]'); +axis equal; + +subplot(2, 2, 2); +imagesc(resultGUI.RBExDose(:, :, slice) - resultGUI.accRBExDose(:, :, slice)); +colorbar; +colormap(jet); +title('static dose distribution - accumulated (4D) dose distribution [Gy (RBE)]'); + +axis equal; diff --git a/matRad.m b/matRad.m index a369d6215..947bfc2ce 100644 --- a/matRad.m +++ b/matRad.m @@ -14,31 +14,31 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set matRad runtime configuration -matRad_rc +matRad_rc; %% load patient data, i.e. ct, voi, cst -load TG119.mat -%load HEAD_AND_NECK -%load PROSTATE.mat -%load LIVER.mat -%load BOXPHANTOM.mat +load TG119.mat; +% load HEAD_AND_NECK +% load PROSTATE.mat +% load LIVER.mat +% load BOXPHANTOM.mat % meta information for treatment plan pln.numOfFractions = 30; pln.radiationMode = 'photons'; % either photons / protons / helium / carbon / brachy / VHEE pln.machine = 'Generic'; % generic for RT / LDR or HDR for BT / Generic or Focused for VHEE -pln.bioModel = 'none'; % none: for all % constRBE: constant RBE for photons and protons - % MCN: McNamara-variable RBE model for protons % WED: Wedenberg-variable RBE model for protons - % LEM: Local Effect Model for carbon ions % HEL: data-driven RBE parametrization for helium +pln.bioModel = 'none'; % none: for all % constRBE: constant RBE for photons and protons +% MCN: McNamara-variable RBE model for protons % WED: Wedenberg-variable RBE model for protons +% LEM: Local Effect Model for carbon ions % HEL: data-driven RBE parametrization for helium -pln.multScen = 'nomScen'; % scenario creation type 'nomScen' 'wcScen' 'impScen' 'rndScen' +pln.multScen = 'nomScen'; % scenario creation type 'nomScen' 'wcScen' 'impScen' 'rndScen' % beam geometry settings pln.propStf.bixelWidth = 5; % [mm] / also corresponds to lateral spot spacing for particles pln.propStf.gantryAngles = [0:72:359]; % [°] ; -pln.propStf.couchAngles = [0 0 0 0 0]; % [°] ; -pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); +pln.propStf.couchAngles = [0 0 0 0 0]; % [°] ; +pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm] @@ -51,32 +51,29 @@ pln.propOpt.runDAO = false; % 1/true: run DAO, 0/false: don't / will be ignored for particles pln.propSeq.runSequencing = true; % true: run sequencing, false: don't / will be ignored for particles and also triggered by runDAO below - %% initial visualization and change objective function settings if desired -matRadGUI +matRadGUI; -%% generate steering file -stf = matRad_generateStf(ct,cst,pln); +%% generate steering file +stf = matRad_generateStf(ct, cst, pln); %% dose calculation dij = matRad_calcDoseInfluence(ct, cst, stf, pln); %% inverse planning for imrt -resultGUI = matRad_fluenceOptimization(dij,cst,pln); +resultGUI = matRad_fluenceOptimization(dij, cst, pln); %% sequencing -resultGUI = matRad_sequencing(resultGUI,stf,dij,pln); - +resultGUI = matRad_sequencing(resultGUI, stf, pln, dij); %% DAO -if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO - resultGUI = matRad_directApertureOptimization(dij,cst,resultGUI.apertureInfo,resultGUI,pln); - matRad_visApertureInfo(resultGUI.apertureInfo); +if strcmp(pln.radiationMode, 'photons') && pln.propOpt.runDAO + resultGUI = matRad_directApertureOptimization(dij, cst, resultGUI.apertureInfo, resultGUI, pln); + matRad_visApertureInfo(resultGUI.apertureInfo); end %% start gui for visualization of result -matRadGUI +matRadGUI; %% indicator calculation and show DVH and QI -resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln); - +resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln); diff --git a/matRad/4D/matRad_acc4dDose.m b/matRad/4D/matRad_acc4dDose.m new file mode 100644 index 000000000..1a9276e83 --- /dev/null +++ b/matRad/4D/matRad_acc4dDose.m @@ -0,0 +1,76 @@ +function resultGUI = matRad_acc4dDose( dij, pln, ct, cst,resultGUI, accType) +% wrapper for the whole 4D dose calculation pipeline and calculated dose +% accumulation +% +% call +% ct = matRad_calc4dDose(ct, pln, dij, stf, cst, resultGUI) +% +% input +% ct : ct cube +% pln: matRad plan meta information struct +% dij: matRad dij struct +% stf: matRad steering information struct +% cst: matRad cst struct +% resultGUI: struct containing optimized fluence vector +% totalPhaseMatrix optional intput for totalPhaseMatrix +% accType: witch algorithim for dose accumulation +% output +% resultGUI: structure containing phase dose, RBE weighted dose, etc +% timeSequence: timing information about the irradiation +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2018 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSE.md. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +matRad_cfg = MatRad_Config.instance(); + +if ~isa(pln.bioModel,'matRad_BiologicalModel') + pln.bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode); +end + +% accumulation +resultGUI.accPhysicalDose = matRad_doseAcc(ct,resultGUI.phaseDose, cst, accType); +if isa(pln.bioModel,'matRad_ConstantRBE') + + resultGUI.accRBExDose = matRad_doseAcc(ct,resultGUI.phaseRBExDose, cst, accType); + +elseif isa(pln.bioModel,'matRad_LQBasedModel') + + resultGUI.accAlphaDose = matRad_doseAcc(ct,resultGUI.phaseAlphaDose, cst,accType); + resultGUI.accSqrtBetaDose = matRad_doseAcc(ct,resultGUI.phaseSqrtBetaDose, cst, accType); + + % only compute where we have biologically defined tissue + ix = (ax{1} ~= 0); + + resultGUI.accEffect = resultGUI.accAlphaDose + resultGUI.accSqrtBetaDose.^2; + + resultGUI.accRBExDose = zeros(ct.cubeDim); + resultGUI.accRBExDose(ix) = ((sqrt(ax{1}(ix).^2 + 4 .* bx{1}(ix) .* resultGUI.accEffect(ix)) - ax{1}(ix))./(2.*bx{1}(ix))); +end + +for beamIx = 1:dij.numOfBeams + resultGUI.(['accPhysicalDose_beam', num2str(beamIx)])= matRad_doseAcc(ct,resultGUI.(['phaseDose_beam', num2str(beamIx)]), cst, accType); + if isa(pln.bioModel,'matRad_ConstantRBE') + resultGUI.(['accRBExDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]), cst, accType); + elseif isa(pln.bioModel,'matRad_LQBasedModel') + resultGUI.(['accAlphaDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]), cst, accType); + resultGUI.(['accSqrtBetaDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]), cst, accType); + resultGUI.(['accEffect_beam', num2str(beamIx)]) = resultGUI.(['accAlphaDose_beam', num2str(beamIx)]) + resultGUI.(['accSqrtBetaDose_beam', num2str(beamIx)]).^2; + resultGUI.(['accRBExDose_beam', num2str(beamIx)]){i} = zeros(ct.cubeDim); + resultGUI.(['accRBExDose_beam', num2str(beamIx)]){i} = ((sqrt(ax{i}(ix).^2 + 4 .* bx{i}(ix) .* resultGUI.(['accEffect_beam', num2str(beamIx)]){i}(ix)) - ax{i}(ix))./(2.*bx{i}(ix))); + end +end + + +end \ No newline at end of file diff --git a/matRad/4D/matRad_calc4dDose.m b/matRad/4D/matRad_calc4dDose.m index a074778e7..df363e61c 100644 --- a/matRad/4D/matRad_calc4dDose.m +++ b/matRad/4D/matRad_calc4dDose.m @@ -1,22 +1,16 @@ -function [resultGUI, timeSequence] = matRad_calc4dDose(ct, pln, dij, stf, cst, resultGUI, totalPhaseMatrix,accType) +function resultGUI = matRad_calc4dDose(dij, pln, stf, resultGUI, totalPhaseMatrix) % wrapper for the whole 4D dose calculation pipeline and calculated dose % accumulation % % call: % ct = matRad_calc4dDose(ct, pln, dij, stf, cst, resultGUI) % -% input: -% ct : ct cube -% pln: matRad plan meta information struct +% input % dij: matRad dij struct -% stf: matRad steering information struct -% cst: matRad cst struct % resultGUI: struct containing optimized fluence vector % totalPhaseMatrix optional intput for totalPhaseMatrix -% accType: witch algorithim for dose accumulation -% output: +% output % resultGUI: structure containing phase dose, RBE weighted dose, etc -% timeSequence: timing information about the irradiation % % References % - @@ -34,125 +28,60 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% matRad_cfg = MatRad_Config.instance(); -if ~exist('accType','var') - accType = 'DDM'; -end - -if ~exist('totalPhaseMatrix','var') - % make a time sequence for when each bixel is irradiated, the sequence - % follows the backforth spot scanning - timeSequence = matRad_makeBixelTimeSeq(stf, resultGUI); - - % prepare a phase matrix - motion = 'linear'; % the assumed motion type - timeSequence = matRad_makePhaseMatrix(timeSequence, ct.numOfCtScen, ct.motionPeriod, motion); - - resultGUI.bioModel = pln.bioModel; - - % the total phase matrix determines what beamlet will be administered in what ct phase - totalPhaseMatrix = vertcat(timeSequence.phaseMatrix); -else - timeSequence = []; +if ~exist('totalPhaseMatrix', 'var') + sequencer = matRad_SequencerBase.getSequencerFromPln(pln); + sequence = sequencer.sequence(resultGUI.w, stf); + sequence = sequencer.makePhaseMatrix(sequence, pln.multScen.numOfCtScen, pln.multScen.motionPeriod); + resultGUI.sequencing = sequence; + totalPhaseMatrix = vertcat(sequence.phaseMatrix); end -% Get Biological Model -if ~isfield(pln,'bioModel') - pln.bioModel = 'none'; -end -if ~isa(pln.bioModel,'matRad_BiologicalModel') - pln.bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode); -end - -if isa(pln.bioModel,'matRad_LQBasedModel') - [ax,bx] = matRad_getPhotonLQMParameters(cst,numel(resultGUI.physicalDose)); +if ~isa(pln.bioModel, 'matRad_BiologicalModel') + pln.bioModel = matRad_BiologicalModel.validate(pln.bioModel, pln.radiationMode); end % compute all phases -for i = 1:ct.numOfCtScen +for i = 1:size(totalPhaseMatrix, 2) - tmpResultGUI = matRad_calcCubes(totalPhaseMatrix(:,i),dij,i); + tmpResultGUI = matRad_calcCubes(totalPhaseMatrix(:, i), dij, i); % compute physical dose for physical opt resultGUI.phaseDose{i} = tmpResultGUI.physicalDose; - if ~isa(pln.bioModel,'matRad_EmptyBiologicalModel') + if ~isa(pln.bioModel, 'matRad_EmptyBiologicalModel') % compute RBExDose - if isa(pln.bioModel,'matRad_ConstantRBE') + if isa(pln.bioModel, 'matRad_ConstantRBE') resultGUI.phaseRBExDose{i} = tmpResultGUI.RBExDose; - elseif isa(pln.bioModel,'matRad_LQBasedModel') + elseif isa(pln.bioModel, 'matRad_LQBasedModel') resultGUI.phaseAlphaDose{i} = tmpResultGUI.alpha .* tmpResultGUI.physicalDose; resultGUI.phaseSqrtBetaDose{i} = sqrt(tmpResultGUI.beta) .* tmpResultGUI.physicalDose; - ix = ax{i} ~=0; + ix = dij.ax{i} ~= 0; resultGUI.phaseEffect{i} = resultGUI.phaseAlphaDose{i} + resultGUI.phaseSqrtBetaDose{i}.^2; resultGUI.phaseRBExDose{i} = zeros(ct.cubeDim); - resultGUI.phaseRBExDose{i}(ix) = ((sqrt(ax{i}(ix).^2 + 4 .* bx{i}(ix) .* resultGUI.phaseEffect{i}(ix)) - ax{i}(ix))./(2.*bx{i}(ix))); + resultGUI.phaseRBExDose{i}(ix) = ((sqrt(dij.ax{i, 1}(ix).^2 + 4 .* dij.bx{i, 1}(ix) .* resultGUI.phaseEffect{i}(ix)) - dij.ax{i, 1}(ix)) ./ (2 .* dij.bx{i, 1}(ix))); else - matRad_cfg.dispError('Unsupported biological model %s!',pln.bioModel.model); + matRad_cfg.dispError('Unsupported biological model %s!', pln.bioModel.model); end end - for beamIx = 1:dij.numOfBeams + for beamIx = 1:dij.numOfBeams resultGUI.(['phaseDose_beam', num2str(beamIx)]){i} = tmpResultGUI.(['physicalDose_beam', num2str(beamIx)]); - if ~isa(pln.bioModel,'matRad_EmptyBiologicalModel') - % compute RBExD - if isa(pln.bioModel,'matRad_ConstantRBE') + if ~isa(pln.bioModel, 'matRad_EmptyBiologicalModel') + % compute RBExD + if isa(pln.bioModel, 'matRad_ConstantRBE') resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]){i} = tmpResultGUI.(['RBExDose_beam', num2str(beamIx)]); - elseif isa(pln.bioModel,'matRad_LQBasedModel') - resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]){i} = tmpResultGUI.(['alpha_beam', num2str(beamIx)]).*tmpResultGUI.(['physicalDose_beam', num2str(beamIx)]); - resultGUI.(['phaseSqrtBetaDose_beam', num2str(beamIx)]){i} = sqrt(tmpResultGUI.(['beta_beam', num2str(beamIx)])).*tmpResultGUI.(['physicalDose_beam', num2str(beamIx)]); - ix = ax{i} ~=0; - resultGUI.(['phaseEffect_beam', num2str(beamIx)]){i} = resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]){i} + resultGUI.(['phaseSqrtBetaDose_beam', num2str(beamIx)]){i}.^2; - resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]){i} = zeros(ct.cubeDim); - resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]){i} = ((sqrt(ax{i}(ix).^2 + 4 .* bx{i}(ix) .* resultGUI.(['phaseEffect_beam', num2str(beamIx)]){i}(ix)) - ax{i}(ix))./(2.*bx{i}(ix))); + elseif isa(pln.bioModel, 'matRad_LQBasedModel') + resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]){i} = tmpResultGUI.(['alpha_beam', num2str(beamIx)]) .* tmpResultGUI.(['physicalDose_beam', num2str(beamIx)]); + resultGUI.(['phaseSqrtBetaDose_beam', num2str(beamIx)]){i} = sqrt(tmpResultGUI.(['beta_beam', num2str(beamIx)])) .* tmpResultGUI.(['physicalDose_beam', num2str(beamIx)]); + ix = dij.ax{i} ~= 0; + resultGUI.(['phaseEffect_beam', num2str(beamIx)]){i} = resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]){i} + resultGUI.(['phaseSqrtBetaDose_beam', num2str(beamIx)]){i}.^2; + resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]){i} = zeros(ct.cubeDim); + resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]){i} = ((sqrt(dij.ax{i, 1}(ix).^2 + 4 .* dij.bx{i, 1}(ix) .* resultGUI.(['phaseEffect_beam', num2str(beamIx)]){i}(ix)) - dij.ax{i, 1}(ix)) ./ (2 .* dij.bx{i, 1}(ix))); else - matRad_cfg.dispError('Unsupported biological model %s!',pln.bioModel.model); + matRad_cfg.dispError('Unsupported biological model %s!', pln.bioModel.model); end end end end -% accumulation -resultGUI.accPhysicalDose = matRad_doseAcc(ct,resultGUI.phaseDose, cst, accType); - -if ~isa(pln.bioModel,'matRad_EmptyBiologicalModel') - if isa(pln.bioModel,'matRad_ConstantRBE') - - resultGUI.accRBExDose = matRad_doseAcc(ct,resultGUI.phaseRBExDose, cst, accType); - - elseif isa(pln.bioModel,'matRad_LQBasedModel') - - resultGUI.accAlphaDose = matRad_doseAcc(ct,resultGUI.phaseAlphaDose, cst,accType); - resultGUI.accSqrtBetaDose = matRad_doseAcc(ct,resultGUI.phaseSqrtBetaDose, cst, accType); - - % only compute where we have biologically defined tissue - ix = (ax{1} ~= 0); - - resultGUI.accEffect = resultGUI.accAlphaDose + resultGUI.accSqrtBetaDose.^2; - - resultGUI.accRBExDose = zeros(ct.cubeDim); - resultGUI.accRBExDose(ix) = ((sqrt(ax{1}(ix).^2 + 4 .* bx{1}(ix) .* resultGUI.accEffect(ix)) - ax{1}(ix))./(2.*bx{1}(ix))); - else - matRad_cfg.dispError('Unsupported biological model %s!',pln.bioModel.model); - end -end - -for beamIx = 1:dij.numOfBeams - resultGUI.(['accPhysicalDose_beam', num2str(beamIx)])= matRad_doseAcc(ct,resultGUI.(['phaseDose_beam', num2str(beamIx)]), cst, accType); - if ~isa(pln.bioModel,'matRad_EmptyBiologicalModel') - if isa(pln.bioModel,'matRad_ConstantRBE') - resultGUI.(['accRBExDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseRBExDose_beam', num2str(beamIx)]), cst, accType); - elseif isa(pln.bioModel,'matRad_LQBasedModel') - resultGUI.(['accAlphaDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]), cst, accType); - resultGUI.(['accSqrtBetaDose_beam', num2str(beamIx)]) = matRad_doseAcc(ct,resultGUI.(['phaseAlphaDose_beam', num2str(beamIx)]), cst, accType); - resultGUI.(['accEffect_beam', num2str(beamIx)]) = resultGUI.(['accAlphaDose_beam', num2str(beamIx)]) + resultGUI.(['accSqrtBetaDose_beam', num2str(beamIx)]).^2; - resultGUI.(['accRBExDose_beam', num2str(beamIx)]){i} = zeros(ct.cubeDim); - resultGUI.(['accRBExDose_beam', num2str(beamIx)]){i} = ((sqrt(ax{i}(ix).^2 + 4 .* bx{i}(ix) .* resultGUI.(['accEffect_beam', num2str(beamIx)]){i}(ix)) - ax{i}(ix))./(2.*bx{i}(ix))); - else - matRad_cfg.dispError('Unsupported biological model %s!',pln.bioModel.model); - end - end end - - -end - diff --git a/matRad/4D/matRad_makeBixelTimeSeq.m b/matRad/4D/matRad_makeBixelTimeSeq.m index 3b3a52f11..4e8ad54f6 100644 --- a/matRad/4D/matRad_makeBixelTimeSeq.m +++ b/matRad/4D/matRad_makeBixelTimeSeq.m @@ -32,176 +32,11 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% defining the constant parameters -% -% time required for synchrotron to change energy - -es_time = 3 * 10^6; % [\mu s] -% time required for synchrotron to recharge it' spill -spill_recharge_time = 2 * 10^6; % [\mu s] -% number of particles generated in each spill -spill_size = 4 * 10 ^ 10; -% speed of synchrotron's lateral scanning in an IES -scan_speed = 10; % m/s -% number of particles per second -spill_intensity = 4 * 10 ^ 8; - +matRad_cfg = MatRad_Config.instance(); +matRad_cfg.dispWarning('This function is Outdated use new SequencingClass'); -steerTime = [stf.bixelWidth] * (10 ^ 3)/ scan_speed; % [\mu s] - -timeSequence = struct; - -% first loop loops over all bixels to store their position and ray number -% in each IES -wOffset = 0; -for i = 1:length(stf) % looping over all beams - - usedEnergies = unique([stf(i).ray(:).energy]); - usedEnergiesSorted = sort(usedEnergies, 'descend'); - - timeSequence(i).orderToSTF = zeros(stf(i).totalNumOfBixels, 1); - timeSequence(i).orderToSS = zeros(stf(i).totalNumOfBixels, 1); - timeSequence(i).time = zeros(stf(i).totalNumOfBixels, 1); - timeSequence(i).e = zeros(stf(i).totalNumOfBixels, 1); - - - for e = 1:length(usedEnergies) % looping over IES's - - s = 1; - - for j = 1:stf(i).numOfRays % looping over all rays - - % find the rays which are active in current IES - if(any(stf(i).ray(j).energy == usedEnergiesSorted(e))) - - x = stf(i).ray(j).rayPos_bev(1); - y = stf(i).ray(j).rayPos_bev(3); - % - timeSequence(i).IES(e).x(s) = x; % store x position - timeSequence(i).IES(e).y(s) = y; % store y position - timeSequence(i).IES(e).w_index(s) = wOffset + ... - sum(stf(i).numOfBixelsPerRay(1:(j-1))) + ... - find(stf(i).ray(j).energy == usedEnergiesSorted(e)); % store index - - s = s + 1; - - end - end - end - - wOffset = wOffset + sum(stf(i).numOfBixelsPerRay); - -end +sequencer = matRad_ParticleSequencer(); -% after storing all the required information, -% same loop over all bixels will put each bixel in it's order - -spill_usage = 0; -offset = 0; - -for i = 1:length(stf) - - usedEnergies = unique([stf(i).ray(:).energy]); - - t = 0; - order_count = 1; - - for e = 1: length(usedEnergies) - - % sort the y positions from high to low (backforth is up do down) - y_sorted = sort(unique(timeSequence(i).IES(e).y), 'descend'); - x_sorted = sort(timeSequence(i).IES(e).x, 'ascend'); - - for k = 1:length(y_sorted) - - y = y_sorted(k); - % find indexes corresponding to current y position - % in other words, number of bixels in the current row - ind_y = find(timeSequence(i).IES(e).y == y); - - % since backforth fasion is zig zag like, flip the order every - % second row - if ~rem(k,2) - ind_y = fliplr(ind_y); - end - - % loop over all the bixels in the row - for is = 1:length(ind_y) - - s = ind_y(is); - - x = x_sorted(s); - - w_index = timeSequence(i).IES(e).w_index(s); - - % in case there were holes inside the plan "multi" - % multiplies the steertime to take it into account: - if(k == 1 && is == 1) - x_prev = x; - y_prev = y; - end - % x direction - multi = abs(x_prev - x)/stf(i).bixelWidth; - % y direction - multi = multi + abs(y_prev - y)/stf(i).bixelWidth; - % - x_prev = x; - y_prev = y; - - % calculating the time: - - % required spot fluence - numOfParticles = resultGUI.w(w_index)* 10^6; - % time spent to spill the required spot fluence - spillTime = numOfParticles * 10^6 / spill_intensity; - - % spotTime:time spent to steer scan along IES per bixel - t = t + multi * steerTime(i) + spillTime; - - % taking account of the time to recharge the spill in case - % the required fluence was more than spill size - if(spill_usage + numOfParticles > spill_size) - t = t + spill_recharge_time; - spill_usage = 0; - end - - % used amount of fluence from current spill - spill_usage = spill_usage + numOfParticles; - - % storing the time and the order of bixels - - % make the both counter and index 'per beam' - help index - w_ind = w_index - offset; - - % timeline according to the spot scanning order - timeSequence(i).time(order_count) = t; - % IES of bixels according to the spot scanning order - timeSequence(i).e(order_count) = e; - % according to spot scanning order, sorts w index of all - % bixels, use this order to transfer STF order to Spot - % Scanning order - timeSequence(i).orderToSS(order_count) = w_ind; - - % according to STF order, gives us order of irradiation of - % each bixel, use this order to transfer Spot Scanning - % order to STF order - % orderToSTF(orderToSS) = orderToSS(orderToSTF) = 1:#bixels - timeSequence(i).orderToSTF(w_ind) = order_count; - - order_count = order_count + 1; - - end - end - - t = t + es_time; - - end - - % storing the fluence per beam - timeSequence(i).w = resultGUI.w(offset + 1: offset + stf(i).totalNumOfBixels); - - offset = offset + stf(i).totalNumOfBixels; - -end +timeSequence = sequencer.sequence(stf, resultGUI.w); end diff --git a/matRad/4D/matRad_makePhaseMatrix.m b/matRad/4D/matRad_makePhaseMatrix.m index 8830bcca5..717fc4a68 100644 --- a/matRad/4D/matRad_makePhaseMatrix.m +++ b/matRad/4D/matRad_makePhaseMatrix.m @@ -1,7 +1,7 @@ function timeSequence = matRad_makePhaseMatrix(timeSequence, numOfPhases, motionPeriod, motion) % using the time sequence and the ordering of the bixel iradiation, and % number of scenarios, makes a phase matrix of size number of bixels * -% number of scenarios +% number of scenarios % % % call: @@ -12,7 +12,7 @@ % time sequence of the spot scanning % numOfCtScen: number of the desired phases % motionPeriod: the extent of a whole breathing cycle (in seconds) -% motion: motion scenario: 'linear'(default), 'sampled_period' +% motion: motion scenario: 'linear'(default), 'sampled_period' % % output: % timeSequence: phase matrix field added @@ -33,45 +33,10 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% time of each phase [/mu s] -phaseTime = motionPeriod * 10 ^ 6/numOfPhases; +matRad_cfg = MatRad_Config.instance(); +matRad_cfg.dispWarning('This function is Outdated use new SequencingClass'); -for i = 1:length(timeSequence) - - realTime = phaseTime; - timeSequence(i).phaseMatrix = zeros(length(timeSequence(i).time),numOfPhases); - - iPhase = 1; - iTime = 1; +sequencer = matRad_ParticleSequencer(); +timeSequence = sequencer.makePhaseMatrix(timeSequence, numOfPhases, motionPeriod); - while (iTime <= length(timeSequence(i).time)) - if(timeSequence(i).time(iTime) < realTime) - - while(iTime <= length(timeSequence(i).time) && timeSequence(i).time(iTime) < realTime) - timeSequence(i).phaseMatrix(iTime, iPhase) = 1; - iTime = iTime + 1; - end - - else - - - iPhase = iPhase + 1; - - % back to 1 after going over all phases - if(iPhase > numOfPhases) - iPhase = 1; - end - - realTime = realTime + phaseTime; - - end - end - - % permuatation of phaseMatrix from SS order to STF order - timeSequence(i).phaseMatrix = timeSequence(i).phaseMatrix(timeSequence(i).orderToSTF,:); - [timeSequence(i).phaseNum,~] = find(timeSequence(i).phaseMatrix'); - % inserting the fluence in phaseMatrix - timeSequence(i).phaseMatrix = timeSequence(i).phaseMatrix .* timeSequence(i).w; - end - diff --git a/matRad/MatRad_Config.m b/matRad/MatRad_Config.m index e4b2e66db..cdfa3153a 100644 --- a/matRad/MatRad_Config.m +++ b/matRad/MatRad_Config.m @@ -241,8 +241,8 @@ function setDefaultProperties(obj) obj.defaults.propOpt.enableGPU = false; %Sequencing Options - obj.defaults.propSeq.sequencer = 'siochi'; - + obj.defaults.propSeq.sequencer = {'siochi', 'IMPT'}; + obj.disableGUI = false; diff --git a/matRad/gui/widgets/matRad_PlanWidget.m b/matRad/gui/widgets/matRad_PlanWidget.m index b684d8a40..7503172de 100644 --- a/matRad/gui/widgets/matRad_PlanWidget.m +++ b/matRad/gui/widgets/matRad_PlanWidget.m @@ -25,741 +25,742 @@ end properties (Access = private) - hTissueWindow; + hTissueWindow - currentMachine; - plotPlan = false; + currentMachine + plotPlan = false end properties (Constant) - modalities = {'photons','protons','carbon', 'helium','brachy', 'VHEE'}; - availableProjections = { 'physicalDose'; 'RBExDose'; 'effect'; 'BED'; } + modalities = {'photons', 'protons', 'carbon', 'helium', 'brachy', 'VHEE'} + availableProjections = { 'physicalDose'; 'RBExDose'; 'effect'; 'BED'} end methods + function this = matRad_PlanWidget(handleParent) matRad_cfg = MatRad_Config.instance(); if nargin < 1 - handleParent = figure(... - 'Units','characters',... - 'Position',[100 50 125 15],... - 'Visible','on',... - 'Color',matRad_cfg.gui.backgroundColor,... - 'IntegerHandle','off',... - 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... - 'MenuBar','none',... - 'Name','MatRad Plan',... - 'NumberTitle','off',... - 'HandleVisibility','callback',... - 'Tag','figure1'); + handleParent = figure( ... + 'Units', 'characters', ... + 'Position', [100 50 125 15], ... + 'Visible', 'on', ... + 'Color', matRad_cfg.gui.backgroundColor, ... + 'IntegerHandle', 'off', ... + 'Colormap', [0 0 0.5625; 0 0 0.625; 0 0 0.6875; 0 0 0.75; 0 0 0.8125; 0 0 0.875; 0 0 0.9375; 0 0 1; 0 0.0625 1; 0 0.125 1; 0 0.1875 1; 0 0.25 1; 0 0.3125 1; 0 0.375 1; 0 0.4375 1; 0 0.5 1; 0 0.5625 1; 0 0.625 1; 0 0.6875 1; 0 0.75 1; 0 0.8125 1; 0 0.875 1; 0 0.9375 1; 0 1 1; 0.0625 1 1; 0.125 1 0.9375; 0.1875 1 0.875; 0.25 1 0.8125; 0.3125 1 0.75; 0.375 1 0.6875; 0.4375 1 0.625; 0.5 1 0.5625; 0.5625 1 0.5; 0.625 1 0.4375; 0.6875 1 0.375; 0.75 1 0.3125; 0.8125 1 0.25; 0.875 1 0.1875; 0.9375 1 0.125; 1 1 0.0625; 1 1 0; 1 0.9375 0; 1 0.875 0; 1 0.8125 0; 1 0.75 0; 1 0.6875 0; 1 0.625 0; 1 0.5625 0; 1 0.5 0; 1 0.4375 0; 1 0.375 0; 1 0.3125 0; 1 0.25 0; 1 0.1875 0; 1 0.125 0; 1 0.0625 0; 1 0 0; 0.9375 0 0; 0.875 0 0; 0.8125 0 0; 0.75 0 0; 0.6875 0 0; 0.625 0 0; 0.5625 0 0], ... + 'MenuBar', 'none', ... + 'Name', 'MatRad Plan', ... + 'NumberTitle', 'off', ... + 'HandleVisibility', 'callback', ... + 'Tag', 'figure1'); if matRad_cfg.isMatlab handleParent.AutoResizeChildren = 'off'; end end this = this@matRad_Widget(handleParent); - handles=this.handles; + handles = this.handles; if matRad_cfg.eduMode - %Visisbility in Educational Mode - eduHideHandles = {handles.radiobutton3Dconf,... - handles.btnRunDAO}; - eduDisableHandles = {handles.editCouchAngle,handles.popUpMachine}; - cellfun(@(h) set(h,'Visible','Off'),eduHideHandles); - cellfun(@(h) set(h,'Enable','Off'),eduDisableHandles); + % Visisbility in Educational Mode + eduHideHandles = {handles.radiobutton3Dconf, ... + handles.btnRunDAO}; + eduDisableHandles = {handles.editCouchAngle, handles.popUpMachine}; + cellfun(@(h) set(h, 'Visible', 'Off'), eduHideHandles); + cellfun(@(h) set(h, 'Enable', 'Off'), eduDisableHandles); end - this.handles=handles; + this.handles = handles; end end - methods(Access = protected) + methods (Access = protected) + function this = createLayout(this) h12 = this.widgetHandle; matRad_cfg = MatRad_Config.instance(); gridSize = [5 8]; - [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); - gridPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize),i,j,'UniformOutput',false); + [i, j] = ndgrid(1:gridSize(1), 1:gridSize(2)); + gridPos = arrayfun(@(i, j) computeGridPos(this, [i j], gridSize), i, j, 'UniformOutput', false); txt = sprintf('Photons: Choose width (and height) of quadratic photon bixel (i.e. discrete fluence elements)\nParticles: Choose lateral spot distance'); - %Text bixel width - h13 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','bixel width in [mm]',... - 'TooltipString',txt,... - 'Style','text',... - 'Position',gridPos{1,1},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Tag','txtBixelWidth',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Edit Bixel Width + % Text bixel width + h13 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'bixel width in [mm]', ... + 'TooltipString', txt, ... + 'Style', 'text', ... + 'Position', gridPos{1, 1}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Tag', 'txtBixelWidth', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Edit Bixel Width txt = sprintf('Photons: Choose width (and height) of quadratic photon bixel (i.e. discrete fluence elements)\nParticles: Choose lateral spot distance'); - h14 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','5',... - 'TooltipString',txt,... - 'Style','edit',... - 'Position',gridPos{2,1},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editBixelWidth',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Text Gantry Angle + h14 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '5', ... + 'TooltipString', txt, ... + 'Style', 'edit', ... + 'Position', gridPos{2, 1}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editBixelWidth', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Text Gantry Angle txt = sprintf('Define gantry angles according to the matRad coordinate system\nEvery gantry angle defines a beam and needs a couch angle\nSeparate individual angles by blanks'); - h15 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Gantry Angle in °',... - 'TooltipString',txt,... - 'Style','text',... - 'Position',gridPos{1,2},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Tag','txtGantryAngle',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Edit Gantry Angle + h15 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Gantry Angle in °', ... + 'TooltipString', txt, ... + 'Style', 'text', ... + 'Position', gridPos{1, 2}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Tag', 'txtGantryAngle', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Edit Gantry Angle txt = sprintf('Define gantry angles according to the matRad coordinate system\nEvery gantry angle defines a beam and needs a couch angle\nSeparate individual angles by blanks'); - h16 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','0',... - 'TooltipString',txt,... - 'Style','edit',... - 'Position',gridPos{2,2},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata)updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editGantryAngle',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Text Couch Angle + h16 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '0', ... + 'TooltipString', txt, ... + 'Style', 'edit', ... + 'Position', gridPos{2, 2}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata)updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editGantryAngle', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Text Couch Angle txt = sprintf('Define couch angles according to the matRad coordinate system\nEvery couch angle defines a beam and needs a gantry angle\nSeparate individual angles by blanks'); - h17 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Couch Angle in °',... - 'TooltipString',txt,... - 'Style','text',... - 'Position',gridPos{1,3},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Tag','txtCouchAngle',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Edit Couch Angle + h17 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Couch Angle in °', ... + 'TooltipString', txt, ... + 'Style', 'text', ... + 'Position', gridPos{1, 3}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Tag', 'txtCouchAngle', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Edit Couch Angle txt = sprintf('Define couch angles according to the matRad coordinate system\nEvery couch angle defines a beam and needs a gantry angle\nSeparate individual angles by blanks'); - h18 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','0',... - 'TooltipString',txt,... - 'Style','edit',... - 'Position',gridPos{2,3},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata)updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editCouchAngle',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %PopUp Menu RadMode - h19 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',this.modalities,...,... - 'TooltipString','Choose a radiation modality (photons, protons, carbon, helium or brachy)',... - 'Style','popupmenu',... - 'Value',1,... - 'Position',gridPos{2,4},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata)popupRadMode_Callback(this,hObject,eventdata),... - 'Tag','popupRadMode',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Text RadMode - h20 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Radiation Mode',... - 'TooltipString','Choose a radiation modality (photons, protons, carbon, helium or brachy)',... - 'Style','text',... - 'Position',gridPos{1,4},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtRadMode'); - - %Text # Fractions - h21 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','# Fractions',... - 'TooltipString','Define the number of fractions',... - 'Style','text',... - 'Position',gridPos{1,7},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtNumOfFractions'); - - %Edit # Fraction - h22 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'TooltipString','Define the number of fractions',... - 'String','30',... - 'Style','edit',... - 'Position',gridPos{2,7},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editFraction',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Text Iso Center + h18 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '0', ... + 'TooltipString', txt, ... + 'Style', 'edit', ... + 'Position', gridPos{2, 3}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata)updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editCouchAngle', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % PopUp Menu RadMode + h19 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', this.modalities, ...,... + 'TooltipString', 'Choose a radiation modality (photons, protons, carbon, helium or brachy)', ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', gridPos{2, 4}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata)popupRadMode_Callback(this, hObject, eventdata), ... + 'Tag', 'popupRadMode', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Text RadMode + h20 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Radiation Mode', ... + 'TooltipString', 'Choose a radiation modality (photons, protons, carbon, helium or brachy)', ... + 'Style', 'text', ... + 'Position', gridPos{1, 4}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtRadMode'); + + % Text # Fractions + h21 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '# Fractions', ... + 'TooltipString', 'Define the number of fractions', ... + 'Style', 'text', ... + 'Position', gridPos{1, 7}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtNumOfFractions'); + + % Edit # Fraction + h22 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'TooltipString', 'Define the number of fractions', ... + 'String', '30', ... + 'Style', 'edit', ... + 'Position', gridPos{2, 7}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editFraction', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Text Iso Center txt = sprintf('Choose the isocenter of the treatment plan in voxel coordinates within the ct.cube\nIf Auto. is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell'); - h23 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','IsoCenter in [mm]',... - 'TooltipString',txt,... - 'Style','text',... - 'Position',gridPos{1,6},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Tag','txtIsoCenter',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Edit Iso centr + h23 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'IsoCenter in [mm]', ... + 'TooltipString', txt, ... + 'Style', 'text', ... + 'Position', gridPos{1, 6}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Tag', 'txtIsoCenter', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Edit Iso centr txt = sprintf('Choose the isocenter of the treatment plan in voxel coordinates within the ct.cube\nIf Auto. is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell'); - h24 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','0 0 0',... - 'TooltipString', txt,... - 'Style','edit',... - 'Position',gridPos{2,6},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) editIsocenter_Callback(this,hObject,eventdata),... - 'Enable','off',... - 'Tag','editIsoCenter',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Auto Iso Center Checkbox - h25 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Auto.',... - 'TooltipString','If this is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell',... - 'Style','checkbox',... - 'Value',1,... - 'Position',gridPos{3,6},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','checkIsoCenter'); - - %Popup menu for Machine file + h24 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '0 0 0', ... + 'TooltipString', txt, ... + 'Style', 'edit', ... + 'Position', gridPos{2, 6}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) editIsocenter_Callback(this, hObject, eventdata), ... + 'Enable', 'off', ... + 'Tag', 'editIsoCenter', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Auto Iso Center Checkbox + h25 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Auto.', ... + 'TooltipString', 'If this is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell', ... + 'Style', 'checkbox', ... + 'Value', 1, ... + 'Position', gridPos{3, 6}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'checkIsoCenter'); + + % Popup menu for Machine file txt = sprintf('Choose a base data set\nIf Generic is selected for a photon treatment plan, the already available photons_Generic.mat file is loaded'); - h30 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',{'Generic','generic_MCsquare'},... - 'TooltipString',txt,... - 'Style','popupmenu',... - 'Value',1,... - 'Position',gridPos{2,5},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) popUpMachine_Callback(this,hObject,eventdata),... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','popUpMachine'); - - %Text Machine - h31 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Machine',... - 'TooltipString','Choose a base data set',... - 'Style','text',... - 'Position',gridPos{1,5},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtMachine' ); - - %Set tissue button + h30 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', {'Generic', 'generic_MCsquare'}, ... + 'TooltipString', txt, ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', gridPos{2, 5}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) popUpMachine_Callback(this, hObject, eventdata), ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'popUpMachine'); + + % Text Machine + h31 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Machine', ... + 'TooltipString', 'Choose a base data set', ... + 'Style', 'text', ... + 'Position', gridPos{1, 5}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtMachine'); + + % Set tissue button txt = sprintf('Set the tissue parameters of the VOIs\nThe base data file contains depth-dependent alpha and beta values, which are different depending on the tissue class'); - h32 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Set Tissue α/β',... - 'TooltipString',txt,... - 'Position',gridPos{3,8},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) btnSetTissue_Callback(this,hObject,eventdata),... - 'Enable','off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','btnSetTissue'); - - %Popup menu for Biological model and optimized quantity + h32 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Set Tissue α/β', ... + 'TooltipString', txt, ... + 'Position', gridPos{3, 8}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) btnSetTissue_Callback(this, hObject, eventdata), ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'btnSetTissue'); + + % Popup menu for Biological model and optimized quantity txt = sprintf('Choose a quantity to optimize \nPhysical Dose: physical dose is optimized\nRBExDose: RBE-weighted dose is optimized\neffect: effect calculated according to LQ model is optimized'); - h33 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String', this.availableProjections,... - 'TooltipString',txt,... - 'Style','popupmenu',... - 'Value',1,... - 'Position',gridPos{2,8},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) popMenuQuantityOpt_Callback(this,hObject,eventdata),... - 'Tag','popMenuQuantityOpt',... - 'Enable', 'off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Text for Biological model and optimized quantity + h33 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', this.availableProjections, ... + 'TooltipString', txt, ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', gridPos{2, 8}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) popMenuQuantityOpt_Callback(this, hObject, eventdata), ... + 'Tag', 'popMenuQuantityOpt', ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Text for Biological model and optimized quantity txt = sprintf('Choose a quantity to optimize \nPhysical Dose: physical dose is optimized\nRBExDose: RBE-weighted dose is optimized\neffect: effect calculated according to LQ model is optimized'); - h34 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Optimized quantity',... - 'TooltipString',txt,... - 'Style','text',... - 'Position',gridPos{1,8},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Interruptible','off',... - 'Tag','txtQuantityOpt',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + h34 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Optimized quantity', ... + 'TooltipString', txt, ... + 'Style', 'text', ... + 'Position', gridPos{1, 8}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Interruptible', 'off', ... + 'Tag', 'txtQuantityOpt', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); % Radiobutton 3d Conformal - pos = gridPos{4,1}; - pos(3) = pos(3)*2; - - h36 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','3D conformal/SFUD',... - 'TooltipString','Check this if you want to execute 3D conformal or Single Field Uniform Dose (SFUD) planning',... - 'Style','radiobutton',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Enable','off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','radiobutton3Dconf' ); - - %Run Sequencing radiobutton - pos = gridPos{5,1}; - pos(3) = pos(3)*2; + pos = gridPos{4, 1}; + pos(3) = pos(3) * 2; + + h36 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '3D conformal/SFUD', ... + 'TooltipString', 'Check this if you want to execute 3D conformal or Single Field Uniform Dose (SFUD) planning', ... + 'Style', 'radiobutton', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'radiobutton3Dconf'); + + % Run Sequencing radiobutton + pos = gridPos{5, 1}; + pos(3) = pos(3) * 2; txt = sprintf('Check this if you want to run a MLC sequencing\nThe number of stratification levels can be adjusted below'); - h26 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Run Sequencing',... - 'TooltipString',txt,... - 'Style','radiobutton',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Enable','off',... - 'Tag','btnRunSequencing'); - - %Text Sequencing - pos = gridPos{4,2}; + h26 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Run Sequencing', ... + 'TooltipString', txt, ... + 'Style', 'radiobutton', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Enable', 'off', ... + 'Tag', 'btnRunSequencing'); + + % Text Sequencing + pos = gridPos{4, 2}; pos(3) = pos(3) * 1.5; - h28 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Stratification Levels:',... - 'TooltipString','Choose the number of stratification levels in case you run a MLC sequencing',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtSequencing' ); - - %Sequencing Level Edit - pos = gridPos{5,2}; + h28 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Stratification Levels:', ... + 'TooltipString', 'Choose the number of stratification levels in case you run a MLC sequencing', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtSequencing'); + + % Sequencing Level Edit + pos = gridPos{5, 2}; pos(3) = pos(3) / 2; - h29 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','7',... - 'TooltipString','Choose the number of stratification levels in case you run a MLC sequencing',... - 'Style','edit',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata)updatePlnInWorkspace(this,hObject,eventdata),... - 'Enable','off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','editSequencingLevel'); - - %Text Sequencer - h40 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Sequencer : ',... - 'TooltipString','Set the sequencing algorithm',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',gridPos{4,3},... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Interruptible','off',... - 'Tag','txtSequencer',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - %Popup menu selectin sequencing algorithm# + h29 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '7', ... + 'TooltipString', 'Choose the number of stratification levels in case you run a MLC sequencing', ... + 'Style', 'edit', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata)updatePlnInWorkspace(this, hObject, eventdata), ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'editSequencingLevel'); + + % Text Sequencer + h40 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Sequencer : ', ... + 'TooltipString', 'Set the sequencing algorithm', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', gridPos{4, 3}, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Interruptible', 'off', ... + 'Tag', 'txtSequencer', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + % Popup menu selectin sequencing algorithm# txt = sprintf('Choose a sequencing algorithm (siochi, xia or engel)'); - h41 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',{ 'siochi','xia','engel' },... - 'TooltipString',txt,... - 'Style','popupmenu',... - 'Value',1,... - 'Position',gridPos{5,3},... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) popUpMenuSequencer_Callback(this,hObject,eventdata),... - 'Tag','popUpMenuSequencer',... - 'Enable', 'off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + h41 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', { 'siochi', 'xia', 'engel' }, ... + 'TooltipString', txt, ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', gridPos{5, 3}, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) popUpMenuSequencer_Callback(this, hObject, eventdata), ... + 'Tag', 'popUpMenuSequencer', ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); % Direct Aperture Optimization radiobutton - pos = gridPos{4,4}; - pos(3) = pos(3)*2; - - h27 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Run Direct Aperture Optimization',... - 'TooltipString','Check this if you want to run an additional direct aperture optimization',... - 'Style','radiobutton',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata)updatePlnInWorkspace(this,hObject,eventdata),... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Enable','off',... - 'Tag','btnRunDAO' ); - - %Biological Model - pos = gridPos{4,5}; + pos = gridPos{4, 4}; + pos(3) = pos(3) * 2; + + h27 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Run Direct Aperture Optimization', ... + 'TooltipString', 'Check this if you want to run an additional direct aperture optimization', ... + 'Style', 'radiobutton', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata)updatePlnInWorkspace(this, hObject, eventdata), ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Enable', 'off', ... + 'Tag', 'btnRunDAO'); + + % Biological Model + pos = gridPos{4, 5}; pos(3) = pos(3) * 1.5; - h45 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Biological model:',... - 'TooltipString','Choose the biological model',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtBioModel'); - - pos = gridPos{5,5}; + h45 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Biological model:', ... + 'TooltipString', 'Choose the biological model', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtBioModel'); + + pos = gridPos{5, 5}; pos(3) = pos(3) / 2; - h55 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',{'none'},... - 'TooltipString','Choose a biological model to be applied',... - 'Style','popupmenu',... - 'Value',1,... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) popMenuBioModel_Callback(this,hObject,eventdata),... - 'Tag','popMenuBioModel',... - 'Enable', 'off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - pos = gridPos{4,6}; + h55 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', {'none'}, ... + 'TooltipString', 'Choose a biological model to be applied', ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) popMenuBioModel_Callback(this, hObject, eventdata), ... + 'Tag', 'popMenuBioModel', ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + pos = gridPos{4, 6}; pos(3) = pos(3) * 1.5; - h46 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Scenario Selection:',... - 'TooltipString','Choose the scenario sampling method',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - 'Tag','txtMultScen' ); + h46 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Scenario Selection:', ... + 'TooltipString', 'Choose the scenario sampling method', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + 'Tag', 'txtMultScen'); scenarioModels = matRad_ScenarioModel.getAvailableModels(); scenarioModels = {scenarioModels.shortName}; - pos = gridPos{5,6}; + pos = gridPos{5, 6}; pos(3) = pos(3) / 2; - h56 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',scenarioModels,... - 'TooltipString','List of available scenario Models',... - 'Style','popupmenu',... - 'Value',1,... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) popMenuMultScen_Callback(this,hObject,eventdata),... - 'Tag','popMenuMultScen',... - 'Enable', 'off',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - + h56 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', scenarioModels, ... + 'TooltipString', 'List of available scenario Models', ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) popMenuMultScen_Callback(this, hObject, eventdata), ... + 'Tag', 'popMenuMultScen', ... + 'Enable', 'off', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); % Text for dose Grid resolution - pos = gridPos{4,7}; - pos(3) = pos(3)*2; - - h35 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Dose Grid Resolution: ',... - 'TooltipString','Set the size of an individual voxel in the dose cube',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Interruptible','off',... - 'Tag','textDoseGrid',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - pos = gridPos{5,7}; - %pos(3) = pos(3)*2; - - h36 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','use CT grid',... - 'TooltipString', txt,... - 'Style','pushbutton',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) applyCtGrid_callback(this,hObject,eventdata),... - 'Enable','on',... - 'Value',0,... - 'Tag','buttonUseCtGrid',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + pos = gridPos{4, 7}; + pos(3) = pos(3) * 2; + + h35 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Dose Grid Resolution: ', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Interruptible', 'off', ... + 'Tag', 'textDoseGrid', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + pos = gridPos{5, 7}; + % pos(3) = pos(3)*2; + + h36 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'use CT grid', ... + 'TooltipString', txt, ... + 'Style', 'pushbutton', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) applyCtGrid_callback(this, hObject, eventdata), ... + 'Enable', 'on', ... + 'Value', 0, ... + 'Tag', 'buttonUseCtGrid', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); % Edit dose grid x - pos = gridPos{4,8}; - pos(3) = pos(3)*0.5; - - h37 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','5',... - 'TooltipString','Set the size of an individual voxel in the dose cube in x-direction',... - 'Style','edit',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editDoseX',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + pos = gridPos{4, 8}; + pos(3) = pos(3) * 0.5; + + h37 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '5', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube in x-direction', ... + 'Style', 'edit', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editDoseX', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); % positioning dose grid size input boxes pos(1) = pos(1) + pos(3) + 0.005; % Edit dose grid y - h38 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','5',... - 'TooltipString','Set the size of an individual voxel in the dose cube in y-direction',... - 'Style','edit',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editDoseY',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + h38 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '5', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube in y-direction', ... + 'Style', 'edit', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editDoseY', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); pos(1) = pos(1) + pos(3) + 0.005; % Edit dose grid z - h39 = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','5',... - 'TooltipString','Set the size of an individual voxel in the dose cube in z-direction',... - 'Style','edit',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','editDoseZ',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + h39 = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', '5', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube in z-direction', ... + 'Style', 'edit', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'editDoseZ', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); pos(1) = pos(1) + pos(3) + 0.005; % text dose grid dimension [mm] - h42 = uicontrol(... - 'Parent',h12,...setpln - 'Units','normalized',... - 'String','[mm]',... - 'TooltipString','Set the size of an individual voxel in the dose cube',... - 'HorizontalAlignment','left',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Interruptible','off',... - 'Tag','txtGridmm',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - pos = gridPos{3,4}; - - hTxtDoseEngine = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String','Dose Engine',... - 'TooltipString','Set the size of an individual voxel in the dose cube',... - 'HorizontalAlignment','center',... - 'Style','text',... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Interruptible','off',... - 'Tag','textDoseEngine',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); - - pos = gridPos{3,5}; - - hSelectDoseEngine = uicontrol(... - 'Parent',h12,... - 'Units','normalized',... - 'String',{'auto'},... - 'TooltipString',txt,... - 'Style','popupmenu',... - 'Value',1,... - 'Position',pos,... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,.... - 'Callback',@(hObject,eventdata) updatePlnInWorkspace(this,hObject,eventdata),... - 'Tag','popUpMenuDoseEngine',... - 'Enable', 'on',... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight); + h42 = uicontrol( ... + 'Parent', h12, ...setpln + 'Units', 'normalized', ... + 'String', '[mm]', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube', ... + 'HorizontalAlignment', 'left', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Interruptible', 'off', ... + 'Tag', 'txtGridmm', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + pos = gridPos{3, 4}; + + hTxtDoseEngine = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', 'Dose Engine', ... + 'TooltipString', 'Set the size of an individual voxel in the dose cube', ... + 'HorizontalAlignment', 'center', ... + 'Style', 'text', ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Interruptible', 'off', ... + 'Tag', 'textDoseEngine', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); + + pos = gridPos{3, 5}; + + hSelectDoseEngine = uicontrol( ... + 'Parent', h12, ... + 'Units', 'normalized', ... + 'String', {'auto'}, ... + 'TooltipString', txt, ... + 'Style', 'popupmenu', ... + 'Value', 1, ... + 'Position', pos, ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, .... + 'Callback', @(hObject, eventdata) updatePlnInWorkspace(this, hObject, eventdata), ... + 'Tag', 'popUpMenuDoseEngine', ... + 'Enable', 'on', ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight); this.createHandles(); end - function this = doUpdate(this,evt) + function this = doUpdate(this, evt) doUpdate = true; if nargin == 2 - %At pln changes and at cst/cst (for Isocenter and new settings) - %we need to update - doUpdate = this.checkUpdateNecessary({'pln','ct','cst'},evt); + % At pln changes and at cst/cst (for Isocenter and new settings) + % we need to update + doUpdate = this.checkUpdateNecessary({'pln', 'ct', 'cst'}, evt); end if doUpdate - if evalin('base','exist(''pln'')') + if evalin('base', 'exist(''pln'')') getPlnFromWorkspace(this); else setPlnDefaultValues(this); @@ -767,7 +768,7 @@ end end - %Set default values for the PLN on matRadGUI startup + % Set default values for the PLN on matRadGUI startup function this = setPlnDefaultValues(this) handles = this.handles; @@ -775,29 +776,29 @@ this.getMachines(); % - vChar = get(handles.editGantryAngle,'String'); - if strcmp(vChar(1,1),'0') && length(vChar)==6 - set(handles.editGantryAngle,'String','0'); + vChar = get(handles.editGantryAngle, 'String'); + if strcmp(vChar(1, 1), '0') && length(vChar) == 6 + set(handles.editGantryAngle, 'String', '0'); end - vChar = get(handles.editCouchAngle,'String'); - if strcmp(vChar(1,1),'0') && length(vChar)==3 - set(handles.editCouchAngle,'String','0') + vChar = get(handles.editCouchAngle, 'String'); + if strcmp(vChar(1, 1), '0') && length(vChar) == 3 + set(handles.editCouchAngle, 'String', '0'); end % do not calculate / suggest isoCenter new by default - %this.checkIsoCenter_Callback(handles.checkIsoCenter); - set(handles.editIsoCenter,'Enable','on'); - set(handles.popMenuQuantityOpt,'Value',1); - set(handles.popMenuBioModel,'Value',1); - set(handles.popMenuMultScen,'Value',1); - this.handles=handles; + % this.checkIsoCenter_Callback(handles.checkIsoCenter); + set(handles.editIsoCenter, 'Enable', 'on'); + set(handles.popMenuQuantityOpt, 'Value', 1); + set(handles.popMenuBioModel, 'Value', 1); + set(handles.popMenuMultScen, 'Value', 1); + this.handles = handles; updatePlnInWorkspace(this); this.getPlnFromWorkspace(); end - %Get pln from workspace and update the Settings displayed in GUI + % Get pln from workspace and update the Settings displayed in GUI function this = getPlnFromWorkspace(this) pln = evalin('base', 'pln'); handles = this.handles; @@ -806,141 +807,128 @@ stfGen = matRad_StfGeneratorBase.getGeneratorFromPln(pln, false); - set(handles.editBixelWidth,'String',num2str(stfGen.bixelWidth)); - set(handles.editGantryAngle,'String',num2str(stfGen.gantryAngles)); - set(handles.editCouchAngle,'String',num2str(stfGen.couchAngles)); + set(handles.editBixelWidth, 'String', num2str(stfGen.bixelWidth)); + set(handles.editGantryAngle, 'String', num2str(stfGen.gantryAngles)); + set(handles.editCouchAngle, 'String', num2str(stfGen.couchAngles)); - modIx = find(strcmp(pln.radiationMode,this.modalities)); - set(handles.popupRadMode,'Value',modIx); + modIx = find(strcmp(pln.radiationMode, this.modalities)); + set(handles.popupRadMode, 'Value', modIx); getMachines(this); - modIy = find(strcmp(pln.machine,this.Machines(this.modalities{modIx}))); - set(handles.popUpMachine,'Value',modIy); + modIy = find(strcmp(pln.machine, this.Machines(this.modalities{modIx}))); + set(handles.popUpMachine, 'Value', modIy); availableEngines = DoseEngines.matRad_DoseEngineBase.getAvailableEngines(pln); - set(handles.popUpMenuDoseEngine,'String',{availableEngines(:).shortName}); - selectedEngineIx = get(handles.popUpMenuDoseEngine,'Value'); + set(handles.popUpMenuDoseEngine, 'String', {availableEngines(:).shortName}); + selectedEngineIx = get(handles.popUpMenuDoseEngine, 'Value'); selectedEngine = availableEngines(selectedEngineIx); - if matRad_ispropCompat(stfGen,'numOfBeams') + if matRad_ispropCompat(stfGen, 'numOfBeams') numOfBeams = stfGen.numOfBeams; else numOfBeams = 1; end - if isfield(pln.propStf,'isoCenter') + if isfield(pln.propStf, 'isoCenter') % sanity check of isoCenter - if size(pln.propStf.isoCenter,1) ~= numOfBeams && size(pln.propStf.isoCenter,1) == 1 - pln.propStf.isoCenter = ones(numOfBeams,1) * pln.propStf.isoCenter(1,:); - elseif size(pln.propStf.isoCenter,1) ~= numOfBeams && size(pln.propStf.isoCenter,1) ~= 1 + if size(pln.propStf.isoCenter, 1) ~= numOfBeams && size(pln.propStf.isoCenter, 1) == 1 + pln.propStf.isoCenter = ones(numOfBeams, 1) * pln.propStf.isoCenter(1, :); + elseif size(pln.propStf.isoCenter, 1) ~= numOfBeams && size(pln.propStf.isoCenter, 1) ~= 1 this.showError('Isocenter in plan file are inconsistent.'); end - if size(unique(pln.propStf.isoCenter,'rows'),1) == 1 - set(handles.editIsoCenter,'String',regexprep(num2str((round(pln.propStf.isoCenter(1,:)*10))./10), '\s+', ' ')); - set(handles.checkIsoCenter,'Enable','on'); - if get(handles.checkIsoCenter,'Value') - set(handles.editIsoCenter,'Enable','off'); + if size(unique(pln.propStf.isoCenter, 'rows'), 1) == 1 + set(handles.editIsoCenter, 'String', regexprep(num2str((round(pln.propStf.isoCenter(1, :) * 10)) ./ 10), '\s+', ' ')); + set(handles.checkIsoCenter, 'Enable', 'on'); + if get(handles.checkIsoCenter, 'Value') + set(handles.editIsoCenter, 'Enable', 'off'); else - set(handles.editIsoCenter,'Enable','on'); + set(handles.editIsoCenter, 'Enable', 'on'); end else - set(handles.editIsoCenter,'String','multiple isoCenter'); - set(handles.editIsoCenter,'Enable','off'); - set(handles.checkIsoCenter,'Value',0); - set(handles.checkIsoCenter,'Enable','off'); + set(handles.editIsoCenter, 'String', 'multiple isoCenter'); + set(handles.editIsoCenter, 'Enable', 'off'); + set(handles.checkIsoCenter, 'Value', 0); + set(handles.checkIsoCenter, 'Enable', 'off'); end end - set(handles.editFraction,'String',num2str(pln.numOfFractions)); + set(handles.editFraction, 'String', num2str(pln.numOfFractions)); - if ~isfield(pln,'propOpt') + if ~isfield(pln, 'propOpt') pln.propOpt = struct(); end - %biological model - if isfield(matRad_cfg.defaults.bioModel,pln.radiationMode) + % biological model + if isfield(matRad_cfg.defaults.bioModel, pln.radiationMode) defaultModel = matRad_cfg.defaults.bioModel.(pln.radiationMode); else defaultModel = matRad_cfg.defaults.bioModel.fallback; end - if ~isfield(pln,'bioModel') + if ~isfield(pln, 'bioModel') pln.bioModel = defaultModel; end - bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode); + bioModel = matRad_BiologicalModel.validate(pln.bioModel, pln.radiationMode); fHandle = str2func([selectedEngine.className '.providedQuantities']); qs = fHandle(this.currentMachine); - availableModels = matRad_BiologicalModel.getAvailableModels(pln.radiationMode,qs); + availableModels = matRad_BiologicalModel.getAvailableModels(pln.radiationMode, qs); modelNames = {availableModels.model}; - ix = find(strcmp(bioModel.model,modelNames)); + ix = find(strcmp(bioModel.model, modelNames)); if isempty(ix) this.showWarning('Bio Model seems to be invalid! Choosing none!'); pln.bioModel = 'none'; - ix = find(strcmp('none',modelNames)); + ix = find(strcmp('none', modelNames)); end - set(handles.popMenuBioModel,'String',modelNames,'Value',ix); + set(handles.popMenuBioModel, 'String', modelNames, 'Value', ix); - %Biological optimization dose quantity - contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt,'String'); - if ~isfield(pln.propOpt,'quantityOpt') + % Biological optimization dose quantity + contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt, 'String'); + if ~isfield(pln.propOpt, 'quantityOpt') pln.propOpt.quantityOpt = 'physicalDose'; end - ix = find(strcmp(pln.propOpt.quantityOpt,contentPopUpQuantityOpt)); + ix = find(strcmp(pln.propOpt.quantityOpt, contentPopUpQuantityOpt)); if isempty(ix) ix = 1; end - set(handles.popMenuQuantityOpt,'Value',ix); + set(handles.popMenuQuantityOpt, 'Value', ix); - if evalin('base','exist(''ct'')') - contentPopUpMultScen = get(handles.popMenuMultScen,'String'); + if evalin('base', 'exist(''ct'')') + contentPopUpMultScen = get(handles.popMenuMultScen, 'String'); try scenModel = matRad_ScenarioModel.create(pln.multScen); - ix = find(strcmp(scenModel.shortName,contentPopUpMultScen)); + ix = find(strcmp(scenModel.shortName, contentPopUpMultScen)); catch ix = 1; end - set(handles.popMenuMultScen,'Value',ix); - end - - if strcmp(pln.radiationMode,'photons') && isfield(pln.propOpt,'runDAO') - set(handles.btnRunDAO,'Value',pln.propOpt.runDAO); - else - set(handles.btnRunDAO,'Value', 0 ); - end - - if isfield(pln, 'propSeq') && isfield(pln.propSeq, 'sequencingLevel') - set(handles.btnRunSequencing,'Value',pln.propSeq.runSequencing); - set(handles.editSequencingLevel,'String',num2str(pln.propSeq.sequencingLevel)); - else - set(handles.btnRunSequencing,'Value', 0 ); + set(handles.popMenuMultScen, 'Value', ix); end if isfield (pln.propOpt, 'conf3D') - set(handles.radiobutton3Dconf,'Value',pln.propOpt.conf3D); + set(handles.radiobutton3Dconf, 'Value', pln.propOpt.conf3D); end - if ~isfield(pln,'propDoseCalc') || ~isfield(pln.propDoseCalc,'doseGrid') + if ~isfield(pln, 'propDoseCalc') || ~isfield(pln.propDoseCalc, 'doseGrid') pln.propDoseCalc.doseGrid.resolution = matRad_cfg.defaults.propDoseCalc.doseGrid.resolution; end - set(handles.editDoseX,'String',num2str(pln.propDoseCalc.doseGrid.resolution.x)); - set(handles.editDoseY,'String',num2str(pln.propDoseCalc.doseGrid.resolution.y)); - set(handles.editDoseZ,'String',num2str(pln.propDoseCalc.doseGrid.resolution.z)); + set(handles.editDoseX, 'String', num2str(pln.propDoseCalc.doseGrid.resolution.x)); + set(handles.editDoseY, 'String', num2str(pln.propDoseCalc.doseGrid.resolution.y)); + set(handles.editDoseZ, 'String', num2str(pln.propDoseCalc.doseGrid.resolution.z)); - this.handles=handles; + this.handles = handles; this.switchEnables(); end - %Update the workspace pln from the Widget - function updatePlnInWorkspace(this,hObject,evtData) + % Update the workspace pln from the Widget + function updatePlnInWorkspace(this, hObject, evtData) if nargin < 3 evtData = []; @@ -958,33 +946,33 @@ function updatePlnInWorkspace(this,hObject,evtData) % evalin pln (if existant) in order to decide whether isoCenter should be calculated % automatically - if evalin('base','exist(''pln'',''var'')') - pln = evalin('base','pln'); - if isfield(pln.propStf,'gantryAngles') && isfield(pln.propStf,'couchAngles') + if evalin('base', 'exist(''pln'',''var'')') + pln = evalin('base', 'pln'); + if isfield(pln.propStf, 'gantryAngles') && isfield(pln.propStf, 'couchAngles') oldGantryAngles = pln.propStf.gantryAngles; - oldCouchAngles = pln.propStf.couchAngles ; + oldCouchAngles = pln.propStf.couchAngles; end end - pln.propStf.bixelWidth = this.parseStringAsNum(get(handles.editBixelWidth,'String'),false); % [mm] / also corresponds to lateral spot spacing for particles - - pln.propStf.gantryAngles = this.parseStringAsNum(get(handles.editGantryAngle,'String'),true); % [°] - pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle,'String'),true); % [°] + pln.propStf.bixelWidth = this.parseStringAsNum(get(handles.editBixelWidth, 'String'), false); % [mm] / also corresponds to lateral spot spacing for particles + + pln.propStf.gantryAngles = this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true); % [°] + pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true); % [°] - if ~isequal(pln.propStf.gantryAngles,oldGantryAngles) || ~isequal(pln.propStf.couchAngles,oldCouchAngles) + if ~isequal(pln.propStf.gantryAngles, oldGantryAngles) || ~isequal(pln.propStf.couchAngles, oldCouchAngles) - pln.propStf.gantryAngles = this.parseStringAsNum(get(handles.editGantryAngle,'String'),true); % [°] - pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle,'String'),true); % [°] + pln.propStf.gantryAngles = this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true); % [°] + pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true); % [°] - objectTag = get(hObject,'Tag'); % Returns empty if hObject is empty, no check required + objectTag = get(hObject, 'Tag'); % Returns empty if hObject is empty, no check required - if ~isempty(objectTag) && (strcmp(objectTag,'editGantryAngle')||strcmp(objectTag,'editCouchAngle')) - if numel(this.parseStringAsNum(get(handles.editCouchAngle,'String'),true))numel(this.parseStringAsNum(get(handles.editGantryAngle,'String'),true)) % Feature: autofill couch angles to single plane by entering a single value - couchGantryDifference = numel(this.parseStringAsNum(get(handles.editCouchAngle,'String'),true))-numel(this.parseStringAsNum(get(handles.editGantryAngle,'String'),true)); - pln.propStf.couchAngles = pln.propStf.couchAngles(1:end-couchGantryDifference); + if ~isempty(objectTag) && (strcmp(objectTag, 'editGantryAngle') || strcmp(objectTag, 'editCouchAngle')) + if numel(this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true)) < numel(this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true)) % Feature: autofill couch angles to single plane by entering a single value + couchGantryDifference = numel(this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true)) - numel(this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true)); + pln.propStf.couchAngles = [this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true) zeros(1, couchGantryDifference)]; + elseif numel(this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true)) > numel(this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true)) % Feature: autofill couch angles to single plane by entering a single value + couchGantryDifference = numel(this.parseStringAsNum(get(handles.editCouchAngle, 'String'), true)) - numel(this.parseStringAsNum(get(handles.editGantryAngle, 'String'), true)); + pln.propStf.couchAngles = pln.propStf.couchAngles(1:end - couchGantryDifference); end end this.plotPlan = true; @@ -992,113 +980,106 @@ function updatePlnInWorkspace(this,hObject,evtData) numOfBeams = numel(pln.propStf.gantryAngles); - isoStr = get(handles.editIsoCenter,'String'); - if ~isequal(isoStr,'multiple isoCenter') - pln.propStf.isoCenter = this.parseStringAsNum(isoStr,true); + isoStr = get(handles.editIsoCenter, 'String'); + if ~isequal(isoStr, 'multiple isoCenter') + pln.propStf.isoCenter = this.parseStringAsNum(isoStr, true); end % switch machines depending on radmode selection - selectedMachine = get(handles.popUpMachine,'Value'); - popupMachines = get(handles.popUpMachine,'String'); + selectedMachine = get(handles.popUpMachine, 'Value'); + popupMachines = get(handles.popUpMachine, 'String'); pln.machine = popupMachines{selectedMachine}; + pln.propDoseCalc.doseGrid.resolution.x = this.parseStringAsNum(get(handles.editDoseX, 'String'), false); + pln.propDoseCalc.doseGrid.resolution.y = this.parseStringAsNum(get(handles.editDoseY, 'String'), false); + pln.propDoseCalc.doseGrid.resolution.z = this.parseStringAsNum(get(handles.editDoseZ, 'String'), false); - pln.propDoseCalc.doseGrid.resolution.x = this.parseStringAsNum(get(handles.editDoseX,'String'),false); - pln.propDoseCalc.doseGrid.resolution.y = this.parseStringAsNum(get(handles.editDoseY,'String'),false); - pln.propDoseCalc.doseGrid.resolution.z = this.parseStringAsNum(get(handles.editDoseZ,'String'),false); - - engines = get(handles.popUpMenuDoseEngine,'String'); - selectedEngine = get(handles.popUpMenuDoseEngine,'Value'); + engines = get(handles.popUpMenuDoseEngine, 'String'); + selectedEngine = get(handles.popUpMenuDoseEngine, 'Value'); - if ~strcmp(engines{selectedEngine},'auto') + if ~strcmp(engines{selectedEngine}, 'auto') pln.propDoseCalc.engine = engines{selectedEngine}; else - if isfield(pln.propDoseCalc,'engine') - pln.propDoseCal = rmfield(pln.propDoseCalc,'engine'); + if isfield(pln.propDoseCalc, 'engine') + pln.propDoseCal = rmfield(pln.propDoseCalc, 'engine'); end end - - - pln.numOfFractions = this.parseStringAsNum(get(handles.editFraction,'String'),false); - contents = get(handles.popupRadMode,'String'); - pln.radiationMode = contents{get(handles.popupRadMode,'Value')}; % either photons / protons / carbon - contents = get(handles.popUpMachine,'String'); + pln.numOfFractions = this.parseStringAsNum(get(handles.editFraction, 'String'), false); + contents = get(handles.popupRadMode, 'String'); + pln.radiationMode = contents{get(handles.popupRadMode, 'Value')}; % either photons / protons / carbon + contents = get(handles.popUpMachine, 'String'); % Biological model set - contentQuantityOpt = get(handles.popMenuQuantityOpt,'String'); - contentBioModel = get(handles.popMenuBioModel,'String'); - contentMultScen = get(handles.popMenuMultScen,'String'); + contentQuantityOpt = get(handles.popMenuQuantityOpt, 'String'); + contentBioModel = get(handles.popMenuBioModel, 'String'); + contentMultScen = get(handles.popMenuMultScen, 'String'); try - pln.bioModel = contentBioModel{get(handles.popMenuBioModel,'Value')}; + pln.bioModel = contentBioModel{get(handles.popMenuBioModel, 'Value')}; catch ME - set(handles.popMenuBioModel,'Value',find(strcmp(contentBioModel,'none'))); + set(handles.popMenuBioModel, 'Value', find(strcmp(contentBioModel, 'none'))); pln.bioModel = 'none'; this.showWarning(ME.message); end - pln.propOpt.quantityOpt = contentQuantityOpt{get(handles.popMenuQuantityOpt,'Value')}; - + pln.propOpt.quantityOpt = contentQuantityOpt{get(handles.popMenuQuantityOpt, 'Value')}; - if evalin('base','exist(''ct'')') - ct = evalin('base','ct'); + if evalin('base', 'exist(''ct'')') + ct = evalin('base', 'ct'); pln.numOfVoxels = prod(ct.cubeDim); pln.voxelDimensions = ct.cubeDim; - pln.multScen = matRad_multScen(ct,contentMultScen{get(handles.popMenuMultScen,'Value')}); + pln.multScen = matRad_multScen(ct, contentMultScen{get(handles.popMenuMultScen, 'Value')}); end % checkIsoCenter checkbox - W = evalin('base','whos'); - doesPlnExist = ismember('pln',{W(:).name}) && evalin('base','exist(''cst'')') && evalin('base','exist(''ct'')'); - if get(handles.checkIsoCenter,'Value') && doesPlnExist + W = evalin('base', 'whos'); + doesPlnExist = ismember('pln', {W(:).name}) && evalin('base', 'exist(''cst'')') && evalin('base', 'exist(''ct'')'); + if get(handles.checkIsoCenter, 'Value') && doesPlnExist try - if ~isfield(pln.propStf,'isoCenter') + if ~isfield(pln.propStf, 'isoCenter') pln.propStf.isoCenter = NaN; end - tmpIsoCenter = matRad_getIsoCenter(evalin('base','cst'),evalin('base','ct')); - if ~isequal(tmpIsoCenter,pln.propStf.isoCenter) - pln.propStf.isoCenter = ones(numOfBeams,1)*tmpIsoCenter; - %handles.State = 1; - %UpdateState(handles); + tmpIsoCenter = matRad_getIsoCenter(evalin('base', 'cst'), evalin('base', 'ct')); + if ~isequal(tmpIsoCenter, pln.propStf.isoCenter) + pln.propStf.isoCenter = ones(numOfBeams, 1) * tmpIsoCenter; + % handles.State = 1; + % UpdateState(handles); end - set(handles.editIsoCenter,'String',regexprep(num2str((round(tmpIsoCenter*10))./10), '\s+', ' ')); - set(handles.editIsoCenter,'Enable','off') - assignin('base','pln',pln); + set(handles.editIsoCenter, 'String', regexprep(num2str((round(tmpIsoCenter * 10)) ./ 10), '\s+', ' ')); + set(handles.editIsoCenter, 'Enable', 'off'); + assignin('base', 'pln', pln); catch ME - this.showWarning('could not set isocenter in pln update! Reason: %s\n',ME.message) + this.showWarning('could not set isocenter in pln update! Reason: %s\n', ME.message); end else - set(handles.editIsoCenter,'Enable','on') + set(handles.editIsoCenter, 'Enable', 'on'); end - contents = get(handles.popUpMenuSequencer,'String'); - pln.propSeq.sequencer = contents{get(handles.popUpMenuSequencer,'Value')}; - pln.propSeq.runSequencing = logical(get(handles.btnRunSequencing,'Value')); - pln.propSeq.sequencingLevel = this.parseStringAsNum(get(handles.editSequencingLevel,'String'),false); - pln.propOpt.runDAO = logical(get(handles.btnRunDAO,'Value')); - pln.propOpt.conf3D = logical(get(handles.radiobutton3Dconf,'Value')); + contents = get(handles.popUpMenuSequencer, 'String'); + pln.propSeq.sequencer = contents{get(handles.popUpMenuSequencer, 'Value')}; + pln.propSeq.sequencingLevel = this.parseStringAsNum(get(handles.editSequencingLevel, 'String'), false); + pln.propOpt.conf3D = logical(get(handles.radiobutton3Dconf, 'Value')); - - if evalin('base','exist(''cst'')') + if evalin('base', 'exist(''cst'')') try - cst = evalin('base','cst'); - if (sum(strcmp('TARGET',cst(:,3))) > 0 && get(handles.checkIsoCenter,'Value')) || ... - (sum(strcmp('TARGET',cst(:,3))) > 0 && ~isfield(pln.propStf,'isoCenter')) - pln.propStf.isoCenter = ones(numOfBeams,1) * matRad_getIsoCenter(cst,ct); - set(handles.checkIsoCenter,'Value',1); + cst = evalin('base', 'cst'); + if (sum(strcmp('TARGET', cst(:, 3))) > 0 && get(handles.checkIsoCenter, 'Value')) || ... + (sum(strcmp('TARGET', cst(:, 3))) > 0 && ~isfield(pln.propStf, 'isoCenter')) + pln.propStf.isoCenter = ones(numOfBeams, 1) * matRad_getIsoCenter(cst, ct); + set(handles.checkIsoCenter, 'Value', 1); else - if ~strcmp(get(handles.editIsoCenter,'String'),'multiple isoCenter') - pln.propStf.isoCenter = ones(numOfBeams,1) * str2num(get(handles.editIsoCenter,'String')); + if ~strcmp(get(handles.editIsoCenter, 'String'), 'multiple isoCenter') + pln.propStf.isoCenter = ones(numOfBeams, 1) * str2num(get(handles.editIsoCenter, 'String')); end end catch ME - this.showWarning('Could not set isocenter in pln update! Reason: %s\n',ME.message) + this.showWarning('Could not set isocenter in pln update! Reason: %s\n', ME.message); end end handles.pln = pln; - assignin('base','pln',pln); + assignin('base', 'pln', pln); this.handles = handles; this.changedWorkspace('pln'); if this.plotPlan @@ -1107,9 +1088,10 @@ function updatePlnInWorkspace(this,hObject,evtData) this.plotPlan = false; end end + end - methods(Access = private) + methods (Access = private) % Enable/disable functionality in PlnWidget depending on the active % Radmode @@ -1117,110 +1099,110 @@ function switchEnables(this) handles = this.handles; hObject = handles.popupRadMode; - contents = cellstr(get(hObject,'String')); - RadIdentifier = contents{get(hObject,'Value')}; - contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt,'String'); - contentPopUpBioModel = get(handles.popMenuBioModel,'String'); + contents = cellstr(get(hObject, 'String')); + RadIdentifier = contents{get(hObject, 'Value')}; + contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt, 'String'); + contentPopUpBioModel = get(handles.popMenuBioModel, 'String'); switch RadIdentifier case 'photons' - set(handles.popMenuQuantityOpt,'Enable','on'); + set(handles.popMenuQuantityOpt, 'Enable', 'on'); % ix = find(strcmp(contentPopUpQuantityOpt,'physicalDose')); % set(handles.popMenuQuantityOpt,'Value',ix); - ix = find(strcmp(contentPopUpBioModel,'none')); - set(handles.popMenuBioModel,'Value',ix); - set(handles.popMenuBioModel,'Enable','off'); - set(handles.btnSetTissue,'Enable','off'); - - set(handles.btnRunSequencing,'Enable','on'); - set(handles.btnRunDAO,'Enable','on'); - set(handles.radiobutton3Dconf,'Enable','on'); - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); - set(handles.popUpMenuSequencer,'Enable','on'); - set(handles.txtSequencer,'Enable','on'); - set(handles.popMenuMultScen, 'Enable','on'); - - if ~(get(handles.btnRunSequencing,'Value') || get(handles.btnRunDAO,'Value')) - - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); - set(handles.popUpMenuSequencer,'Enable','off'); - set(handles.txtSequencer,'Enable','off'); + ix = find(strcmp(contentPopUpBioModel, 'none')); + set(handles.popMenuBioModel, 'Value', ix); + set(handles.popMenuBioModel, 'Enable', 'off'); + set(handles.btnSetTissue, 'Enable', 'off'); + + set(handles.btnRunSequencing, 'Enable', 'on'); + set(handles.btnRunDAO, 'Enable', 'on'); + set(handles.radiobutton3Dconf, 'Enable', 'on'); + set(handles.txtSequencing, 'Enable', 'on'); + set(handles.editSequencingLevel, 'Enable', 'on'); + set(handles.popUpMenuSequencer, 'Enable', 'on'); + set(handles.txtSequencer, 'Enable', 'on'); + set(handles.popMenuMultScen, 'Enable', 'on'); + + if ~(get(handles.btnRunSequencing, 'Value') || get(handles.btnRunDAO, 'Value')) + + set(handles.txtSequencing, 'Enable', 'off'); + set(handles.editSequencingLevel, 'Enable', 'off'); + set(handles.popUpMenuSequencer, 'Enable', 'off'); + set(handles.txtSequencer, 'Enable', 'off'); else - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); - set(handles.popUpMenuSequencer,'Enable','on'); - set(handles.txtSequencer,'Enable','on'); + set(handles.txtSequencing, 'Enable', 'on'); + set(handles.editSequencingLevel, 'Enable', 'on'); + set(handles.popUpMenuSequencer, 'Enable', 'on'); + set(handles.txtSequencer, 'Enable', 'on'); end case 'protons' - set(handles.popMenuQuantityOpt,'Enable','on'); - set(handles.popMenuBioModel,'Enable','on'); - set(handles.popMenuMultScen, 'Enable','on'); - set(handles.btnSetTissue,'Enable','on'); - - set(handles.btnRunSequencing,'Enable','off'); - set(handles.btnRunDAO,'Enable','off'); - set(handles.radiobutton3Dconf,'Enable','on'); - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); - set(handles.popUpMenuSequencer,'Enable','off'); - set(handles.txtSequencer,'Enable','off'); + set(handles.popMenuQuantityOpt, 'Enable', 'on'); + set(handles.popMenuBioModel, 'Enable', 'on'); + set(handles.popMenuMultScen, 'Enable', 'on'); + set(handles.btnSetTissue, 'Enable', 'on'); + + set(handles.btnRunSequencing, 'Enable', 'off'); + set(handles.btnRunDAO, 'Enable', 'off'); + set(handles.radiobutton3Dconf, 'Enable', 'on'); + set(handles.txtSequencing, 'Enable', 'off'); + set(handles.editSequencingLevel, 'Enable', 'off'); + set(handles.popUpMenuSequencer, 'Enable', 'off'); + set(handles.txtSequencer, 'Enable', 'off'); case 'carbon' - set(handles.popMenuQuantityOpt,'Enable','on'); - set(handles.popMenuBioModel,'Enable','on'); - set(handles.btnSetTissue,'Enable','on'); - set(handles.popMenuMultScen, 'Enable','on'); + set(handles.popMenuQuantityOpt, 'Enable', 'on'); + set(handles.popMenuBioModel, 'Enable', 'on'); + set(handles.btnSetTissue, 'Enable', 'on'); + set(handles.popMenuMultScen, 'Enable', 'on'); - set(handles.btnRunSequencing,'Enable','off'); - set(handles.btnRunDAO,'Enable','off'); - set(handles.radiobutton3Dconf,'Enable','on'); - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); - set(handles.popUpMenuSequencer,'Enable','off'); - set(handles.txtSequencer,'Enable','off'); + set(handles.btnRunSequencing, 'Enable', 'off'); + set(handles.btnRunDAO, 'Enable', 'off'); + set(handles.radiobutton3Dconf, 'Enable', 'on'); + set(handles.txtSequencing, 'Enable', 'off'); + set(handles.editSequencingLevel, 'Enable', 'off'); + set(handles.popUpMenuSequencer, 'Enable', 'off'); + set(handles.txtSequencer, 'Enable', 'off'); case 'brachy' - set(handles.popMenuQuantityOpt,'Enable','on'); + set(handles.popMenuQuantityOpt, 'Enable', 'on'); % ix = find(strcmp(contentPopUpQuantityOpt,'physicalDose')); % set(handles.popMenuQuantityOpt,'Value',ix); - ix = find(strcmp(contentPopUpBioModel,'none')); - set(handles.popMenuBioModel,'Value',ix); - set(handles.popMenuBioModel,'Enable','off'); - set(handles.btnSetTissue,'Enable','off'); - - set(handles.btnRunSequencing,'Enable','on'); - set(handles.btnRunDAO,'Enable','on'); - set(handles.radiobutton3Dconf,'Enable','on'); - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); - set(handles.popUpMenuSequencer,'Enable','on'); - set(handles.txtSequencer,'Enable','on'); - set(handles.popMenuMultScen, 'Enable','on'); - - if ~(get(handles.btnRunSequencing,'Value') || get(handles.btnRunDAO,'Value')) - - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); - set(handles.popUpMenuSequencer,'Enable','off'); - set(handles.txtSequencer,'Enable','off'); + ix = find(strcmp(contentPopUpBioModel, 'none')); + set(handles.popMenuBioModel, 'Value', ix); + set(handles.popMenuBioModel, 'Enable', 'off'); + set(handles.btnSetTissue, 'Enable', 'off'); + + set(handles.btnRunSequencing, 'Enable', 'on'); + set(handles.btnRunDAO, 'Enable', 'on'); + set(handles.radiobutton3Dconf, 'Enable', 'on'); + set(handles.txtSequencing, 'Enable', 'on'); + set(handles.editSequencingLevel, 'Enable', 'on'); + set(handles.popUpMenuSequencer, 'Enable', 'on'); + set(handles.txtSequencer, 'Enable', 'on'); + set(handles.popMenuMultScen, 'Enable', 'on'); + + if ~(get(handles.btnRunSequencing, 'Value') || get(handles.btnRunDAO, 'Value')) + + set(handles.txtSequencing, 'Enable', 'off'); + set(handles.editSequencingLevel, 'Enable', 'off'); + set(handles.popUpMenuSequencer, 'Enable', 'off'); + set(handles.txtSequencer, 'Enable', 'off'); else - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); - set(handles.popUpMenuSequencer,'Enable','on'); - set(handles.txtSequencer,'Enable','on'); + set(handles.txtSequencing, 'Enable', 'on'); + set(handles.editSequencingLevel, 'Enable', 'on'); + set(handles.popUpMenuSequencer, 'Enable', 'on'); + set(handles.txtSequencer, 'Enable', 'on'); end end - selectedQuantityOpt = get(handles.popMenuQuantityOpt,'Value'); - if strcmp(contentPopUpQuantityOpt{selectedQuantityOpt},'physicalDose') - set(handles.btnSetTissue,'Enable','off'); + selectedQuantityOpt = get(handles.popMenuQuantityOpt, 'Value'); + if strcmp(contentPopUpQuantityOpt{selectedQuantityOpt}, 'physicalDose') + set(handles.btnSetTissue, 'Enable', 'off'); else - set(handles.btnSetTissue,'Enable','on'); + set(handles.btnSetTissue, 'Enable', 'on'); end this.handles = handles; @@ -1232,6 +1214,7 @@ function manageRadModeSpecificDisplay(this) this.handles = handles; end + %% CALLBACKS function popupRadMode_Callback(this, hObject, eventdata) handles = this.handles; @@ -1241,52 +1224,52 @@ function popupRadMode_Callback(this, hObject, eventdata) defaultMachines = matRad_cfg.defaults.machine; defaultBioModels = matRad_cfg.defaults.bioModel; - allRadiationModes = cellstr(get(hObject,'String')); - newRadiationMode = allRadiationModes{get(hObject,'Value')}; - optimizationQuantityPopUpContents = get(handles.popMenuQuantityOpt,'String'); + allRadiationModes = cellstr(get(hObject, 'String')); + newRadiationMode = allRadiationModes{get(hObject, 'Value')}; + optimizationQuantityPopUpContents = get(handles.popMenuQuantityOpt, 'String'); - %Now get pln and stop if nothing changed + % Now get pln and stop if nothing changed try - pln = evalin('base','pln'); - if strcmp(pln.radiationMode,newRadiationMode) - %Nothing changed - return; + pln = evalin('base', 'pln'); + if strcmp(pln.radiationMode, newRadiationMode) + % Nothing changed + return end catch this.setPlnDefaultValues(); - pln = evalin('base','pln'); + pln = evalin('base', 'pln'); end - if any(strcmp(newRadiationMode,{'protons','helium','carbon'})) - ix = find(strcmp(optimizationQuantityPopUpContents,'RBExDose')); - set(handles.popMenuQuantityOpt,'Value',ix); + if any(strcmp(newRadiationMode, {'protons', 'helium', 'carbon'})) + ix = find(strcmp(optimizationQuantityPopUpContents, 'RBExDose')); + set(handles.popMenuQuantityOpt, 'Value', ix); end - if any(strcmp(newRadiationMode,{'photons','VHEE','brachy'})) - ix = find(strcmp(optimizationQuantityPopUpContents,'physicalDose')); - set(handles.popMenuQuantityOpt,'Value',ix); + if any(strcmp(newRadiationMode, {'photons', 'VHEE', 'brachy'})) + ix = find(strcmp(optimizationQuantityPopUpContents, 'physicalDose')); + set(handles.popMenuQuantityOpt, 'Value', ix); end pln.radiationMode = newRadiationMode; - if isfield(defaultMachines,newRadiationMode) + if isfield(defaultMachines, newRadiationMode) pln.machine = defaultMachines.(newRadiationMode); else pln.machine = defaultMachines.fallback; end - %Update machine storages + % Update machine storages this.getMachines(); % Get the dose engines for the current pln selection try availableEngines = DoseEngines.matRad_DoseEngineBase.getAvailableEngines(pln); - set(handles.popUpMenuDoseEngine,'String',{availableEngines(:).shortName},'Value',1); + set(handles.popUpMenuDoseEngine, 'String', {availableEngines(:).shortName}, 'Value', 1); fHandle = str2func([availableEngines(1).className '.providedQuantities']); qs = fHandle(this.currentMachine); - %With the dose engines available, we can now manage biological - %models - models = matRad_BiologicalModel.getAvailableModels(newRadiationMode,qs); + % With the dose engines available, we can now manage biological + % models + models = matRad_BiologicalModel.getAvailableModels(newRadiationMode, qs); modelNames = {models.model}; if isfield(defaultBioModels, newRadiationMode) @@ -1295,36 +1278,50 @@ function popupRadMode_Callback(this, hObject, eventdata) bioModel = defaultBioModels.fallback; end - bioMenuIx = find(strcmp(modelNames,bioModel)); + bioMenuIx = find(strcmp(modelNames, bioModel)); if isempty(bioMenuIx) bioMenuIx = 1; end - set(handles.popMenuBioModel,'String',modelNames,'Value',bioMenuIx); + set(handles.popMenuBioModel, 'String', modelNames, 'Value', bioMenuIx); catch ME - this.showWarning('Dose Engine & Bio Model Update Failed!',ME); + this.showWarning('Dose Engine & Bio Model Update Failed!', ME); end % new radiation modality is photons -> just keep physicalDose try - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - radMode = allRadiationModes(get(hObject,'Value')); - if any(strcmp(radMode,{'photons','brachy','VHEE'})) - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha'); end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBExDose'); resultGUI = rmfield(resultGUI,'RBExDose');end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end - assignin('base','resultGUI',resultGUI); - %handles = updateIsoDoseLineCache(handles); - elseif strcmp(radMode,'protons') - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha');end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end + AllVarNames = evalin('base', 'who'); + if ismember('resultGUI', AllVarNames) + resultGUI = evalin('base', 'resultGUI'); + radMode = allRadiationModes(get(hObject, 'Value')); + if any(strcmp(radMode, {'photons', 'brachy', 'VHEE'})) + if isfield(resultGUI, 'alpha') + resultGUI = rmfield(resultGUI, 'alpha'); + end + if isfield(resultGUI, 'beta') + resultGUI = rmfield(resultGUI, 'beta'); + end + if isfield(resultGUI, 'RBExDose') + resultGUI = rmfield(resultGUI, 'RBExDose'); + end + if isfield(resultGUI, 'RBE') + resultGUI = rmfield(resultGUI, 'RBE'); + end + assignin('base', 'resultGUI', resultGUI); + % handles = updateIsoDoseLineCache(handles); + elseif strcmp(radMode, 'protons') + if isfield(resultGUI, 'alpha') + resultGUI = rmfield(resultGUI, 'alpha'); + end + if isfield(resultGUI, 'beta') + resultGUI = rmfield(resultGUI, 'beta'); + end + if isfield(resultGUI, 'RBE') + resultGUI = rmfield(resultGUI, 'RBE'); + end end - assignin('base','resultGUI',resultGUI); - %handles = updateIsoDoseLineCache(handles); + assignin('base', 'resultGUI', resultGUI); + % handles = updateIsoDoseLineCache(handles); end catch ME this.showWarning("Result Cleanup Failed!", ME); @@ -1339,26 +1336,25 @@ function editIsocenter_Callback(this, hObject, eventdata) handles = this.handles; % checkIsoCenter checkbox - W = evalin('base','whos'); - doesPlnExist = ismember('pln',{W(:).name}) && evalin('base','exist(''cst'')') && evalin('base','exist(''ct'')'); + W = evalin('base', 'whos'); + doesPlnExist = ismember('pln', {W(:).name}) && evalin('base', 'exist(''cst'')') && evalin('base', 'exist(''ct'')'); % evalin pln (if existant) in order to decide whether isoCenter should be calculated % automatically if doesPlnExist - pln = evalin('base','pln'); + pln = evalin('base', 'pln'); end - % editIsoCenter textbox - tmpIsoCenter = str2num(get(handles.editIsoCenter,'String')); + tmpIsoCenter = str2num(get(handles.editIsoCenter, 'String')); numOfBeams = numel(pln.propStf.gantryAngles); if length(tmpIsoCenter) == 3 - if sum(any(unique(pln.propStf.isoCenter,'rows')~=tmpIsoCenter)) - pln.propStf.isoCenter = ones(numOfBeams,1)*tmpIsoCenter; + if sum(any(unique(pln.propStf.isoCenter, 'rows') ~= tmpIsoCenter)) + pln.propStf.isoCenter = ones(numOfBeams, 1) * tmpIsoCenter; end else - handles = showError(this,'EditIsoCenterCallback: Could not set iso center'); + handles = showError(this, 'EditIsoCenterCallback: Could not set iso center'); end this.handles = handles; updatePlnInWorkspace(this); @@ -1369,13 +1365,13 @@ function applyCtGrid_callback(this, hObject, eventdata) handles = this.handles; try - ct = evalin('base','ct'); + ct = evalin('base', 'ct'); resolution = ct.resolution; - %We use mat2str here because for some reason it prints to - %the required precision (not like num2str which rounds) - set(handles.editDoseX,'String',mat2str(resolution.x)); - set(handles.editDoseY,'String',mat2str(resolution.y)); - set(handles.editDoseZ,'String',mat2str(resolution.z)); + % We use mat2str here because for some reason it prints to + % the required precision (not like num2str which rounds) + set(handles.editDoseX, 'String', mat2str(resolution.x)); + set(handles.editDoseY, 'String', mat2str(resolution.y)); + set(handles.editDoseZ, 'String', mat2str(resolution.z)); catch ME this.showWarning('Could not load resolution from CT!'); end @@ -1384,25 +1380,24 @@ function applyCtGrid_callback(this, hObject, eventdata) function popUpMenuSequencer_Callback(this, hObject, eventdata) handles = this.handles; - contents = cellstr(get(hObject,'String')); - SeqIdentifier = contents{get(hObject,'Value')}; - contentPopUp = get(handles.popUpMenuSequencer,'String'); + contents = cellstr(get(hObject, 'String')); + SeqIdentifier = contents{get(hObject, 'Value')}; + contentPopUp = get(handles.popUpMenuSequencer, 'String'); switch SeqIdentifier case 'siochi' - ix = find(strcmp(contentPopUp,'siochi')); - set(handles.popUpMenuSequencer,'Value',ix); + ix = find(strcmp(contentPopUp, 'siochi')); + set(handles.popUpMenuSequencer, 'Value', ix); case 'xia' - ix = find(strcmp(contentPopUp,'xia')); - set(handles.popUpMenuSequencer,'Value',ix); + ix = find(strcmp(contentPopUp, 'xia')); + set(handles.popUpMenuSequencer, 'Value', ix); case 'engel' - ix = find(strcmp(contentPopUp,'engel')); - set(handles.popUpMenuSequencer,'Value',ix); + ix = find(strcmp(contentPopUp, 'engel')); + set(handles.popUpMenuSequencer, 'Value', ix); end - pln = evalin('base','pln'); - + pln = evalin('base', 'pln'); this.handles = handles; updatePlnInWorkspace(this); @@ -1411,65 +1406,79 @@ function popUpMenuSequencer_Callback(this, hObject, eventdata) function popUpMachine_Callback(this, hObject, eventdata) % MOEGLICHER FEHLER WEGEN VALUE WERT! handles = this.handles; - contents = cellstr(get(hObject,'String')); - MachineIdentifier = contents{get(hObject,'Value')}; + contents = cellstr(get(hObject, 'String')); + MachineIdentifier = contents{get(hObject, 'Value')}; % contentPopUp = get(handles.) - flag=checkRadiationComposition(this); + flag = checkRadiationComposition(this); if ~flag this.showWarning(['No base data available for machine: ' MachineIdentifier '. Selecting default machine.']); - set(handles.popUpMachine,'Value',1); + set(handles.popUpMachine, 'Value', 1); end this.getMachines(); - pln = evalin('base','pln'); + pln = evalin('base', 'pln'); % MOEGLICHEE FEHLER HIER VALUE UND GENERIC WERDEN VERGLICHEN - if strcmp(contents(get(hObject,'Value')),'Generic') + if strcmp(contents(get(hObject, 'Value')), 'Generic') try - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha'); end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBExDose'); resultGUI = rmfield(resultGUI,'RBExDose');end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end - assignin('base','resultGUI',resultGUI); - %handles = updateIsoDoseLineCache(handles); + AllVarNames = evalin('base', 'who'); + if ismember('resultGUI', AllVarNames) + resultGUI = evalin('base', 'resultGUI'); + if isfield(resultGUI, 'alpha') + resultGUI = rmfield(resultGUI, 'alpha'); + end + if isfield(resultGUI, 'beta') + resultGUI = rmfield(resultGUI, 'beta'); + end + if isfield(resultGUI, 'RBExDose') + resultGUI = rmfield(resultGUI, 'RBExDose'); + end + if isfield(resultGUI, 'RBE') + resultGUI = rmfield(resultGUI, 'RBE'); + end + assignin('base', 'resultGUI', resultGUI); + % handles = updateIsoDoseLineCache(handles); end catch end % MOEGLICHEE FEHLER HIER VALUE UND GENERIC WERDEN VERGLICHEN - elseif strcmp(contents(get(hObject,'Value')),'generic_MCsquare') + elseif strcmp(contents(get(hObject, 'Value')), 'generic_MCsquare') try - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha');end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end - assignin('base','resultGUI',resultGUI); - %handles = updateIsoDoseLineCache(handles); + AllVarNames = evalin('base', 'who'); + if ismember('resultGUI', AllVarNames) + resultGUI = evalin('base', 'resultGUI'); + if isfield(resultGUI, 'alpha') + resultGUI = rmfield(resultGUI, 'alpha'); + end + if isfield(resultGUI, 'beta') + resultGUI = rmfield(resultGUI, 'beta'); + end + if isfield(resultGUI, 'RBE') + resultGUI = rmfield(resultGUI, 'RBE'); + end + assignin('base', 'resultGUI', resultGUI); + % handles = updateIsoDoseLineCache(handles); end catch end end availableEngines = DoseEngines.matRad_DoseEngineBase.getAvailableEngines(pln); - set(handles.popUpMenuDoseEngine,'String',{availableEngines(:).shortName}); - engineIx = get(handles.popUpMenuDoseEngine,'Value'); + set(handles.popUpMenuDoseEngine, 'String', {availableEngines(:).shortName}); + engineIx = get(handles.popUpMenuDoseEngine, 'Value'); if engineIx > numel(availableEngines) engineIx = 1; - set(handles.popUpMenuDoseEngine,'Value',1); + set(handles.popUpMenuDoseEngine, 'Value', 1); end try fHandle = str2func([availableEngines(engineIx).className '.providedQuantities']); providedQuantities = fHandle(this.currentMachine); - availableBioModels = matRad_BiologicalModel.getAvailableModels(pln.radiationMode,providedQuantities); + availableBioModels = matRad_BiologicalModel.getAvailableModels(pln.radiationMode, providedQuantities); catch ME availableBioModels = matRad_BiologicalModel.getAvailableModels(pln.radiationMode); end - set(handles.popMenuBioModel,'String',{availableBioModels(:).model}); + set(handles.popMenuBioModel, 'String', {availableBioModels(:).model}); this.handles = handles; this.updatePlnInWorkspace(); @@ -1478,133 +1487,131 @@ function popUpMachine_Callback(this, hObject, eventdata) function btnSetTissue_Callback(this, hObject, eventdata) handles = this.handles; - if evalin('base','exist(''cst'')') && evalin('base','exist(''pln'')') + if evalin('base', 'exist(''cst'')') && evalin('base', 'exist(''pln'')') try matRad_cfg = MatRad_Config.instance(); - %parse variables from base-workspace - cst = evalin('base','cst'); - pln = evalin('base','pln'); - + % parse variables from base-workspace + cst = evalin('base', 'cst'); + pln = evalin('base', 'pln'); fileName = [pln.radiationMode '_' pln.machine]; load(fileName); - %biological model - if isfield(matRad_cfg.defaults.bioModel,pln.radiationMode) + % biological model + if isfield(matRad_cfg.defaults.bioModel, pln.radiationMode) defaultModel = matRad_cfg.defaults.bioModel.(pln.radiationMode); else defaultModel = matRad_cfg.defaults.bioModel.fallback; end - if ~isfield(pln,'bioModel') + if ~isfield(pln, 'bioModel') pln.bioModel = defaultModel; end - bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode); + bioModel = matRad_BiologicalModel.validate(pln.bioModel, pln.radiationMode); [availableAlphaX, availableBetaX] = bioModel.getAvailableTissueParameters(pln); if ~isempty(availableAlphaX) && ~isempty(availableBetaX) - for i = 1:size(availableAlphaX,2) + for i = 1:size(availableAlphaX, 2) CellType{i} = [num2str(availableAlphaX(i)) ' ' num2str(availableBetaX(i))]; - columnformat = {'char',CellType,'numeric'}; + columnformat = {'char', CellType, 'numeric'}; end else - columnformat = {'char','numeric','numeric'}; + columnformat = {'char', 'numeric', 'numeric'}; end - %fill table data array - for i = 1:size(cst,1) - data{i,1} = cst{i,2}; - data{i,2} = [num2str(cst{i,5}.alphaX) ' ' num2str(cst{i,5}.betaX)]; - data{i,3} = (cst{i,5}.alphaX / cst{i,5}.betaX ); + % fill table data array + for i = 1:size(cst, 1) + data{i, 1} = cst{i, 2}; + data{i, 2} = [num2str(cst{i, 5}.alphaX) ' ' num2str(cst{i, 5}.betaX)]; + data{i, 3} = (cst{i, 5}.alphaX / cst{i, 5}.betaX); end Width = 400; - Height = 300 + 20*size(data,1); - ScreenSize = get(0,'ScreenSize'); + Height = 300 + 20 * size(data, 1); + ScreenSize = get(0, 'ScreenSize'); % show "set tissue parameter" window - figHandles = get(0,'Children'); + figHandles = get(0, 'Children'); if ~isempty(figHandles) - IdxHandle = strcmp(get(figHandles,'Name'),'Set Tissue Parameters'); + IdxHandle = strcmp(get(figHandles, 'Name'), 'Set Tissue Parameters'); else IdxHandle = []; end - %check if window is already exists + % check if window is already exists if any(IdxHandle) - IdxTable = find(strcmp({figHandles(IdxHandle).Children.Type},'uitable')); + IdxTable = find(strcmp({figHandles(IdxHandle).Children.Type}, 'uitable')); set(figHandles(IdxHandle).Children(IdxTable), 'Data', []); figTissue = figHandles(IdxHandle); - %set focus + % set focus figure(figTissue); else - figTissue = figure('Name','Set Tissue Parameters', ... - 'NumberTitle','off', ... - 'OuterPosition',[ceil(ScreenSize(3)/2) 100 Width Height],... - 'Color',matRad_cfg.gui.backgroundColor); + figTissue = figure('Name', 'Set Tissue Parameters', ... + 'NumberTitle', 'off', ... + 'OuterPosition', [ceil(ScreenSize(3) / 2) 100 Width Height], ... + 'Color', matRad_cfg.gui.backgroundColor); end % define the tissue parameter table - cNames = {'VOI','alphaX betaX','alpha beta ratio'}; + cNames = {'VOI', 'alphaX betaX', 'alpha beta ratio'}; % columnformat = {'char',CellType,'numeric'}; - - %design table colors - colorMatrix = repmat(matRad_cfg.gui.elementColor,size(data,1),1); - ix2 = 2:2:size(data,1); - if ~isempty(ix2) + + % design table colors + colorMatrix = repmat(matRad_cfg.gui.elementColor, size(data, 1), 1); + ix2 = 2:2:size(data, 1); + if ~isempty(ix2) shadeColor = rgb2hsv(matRad_cfg.gui.elementColor); if shadeColor(3) < 0.5 - shadeColor(3) = shadeColor(3)*1.5+0.1; + shadeColor(3) = shadeColor(3) * 1.5 + 0.1; else - shadeColor(3) = shadeColor(3)*0.5-0.1; + shadeColor(3) = shadeColor(3) * 0.5 - 0.1; end - - colorMatrix(ix2,:) = repmat(hsv2rgb(shadeColor),numel(ix2),1); + + colorMatrix(ix2, :) = repmat(hsv2rgb(shadeColor), numel(ix2), 1); end - - + % Create the uitable tissueTable = uitable('Parent', figTissue, ... - 'Data', data, ... - 'ColumnEditable',[false true false],... - 'ColumnName',cNames, ... - 'ColumnFormat',columnformat, ... - 'Position',[50 150 10 10], ... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'BackgroundColor',colorMatrix,... - 'RowStriping','on'); - - set(tissueTable,'CellEditCallback',@(hObject,eventdata) tissueTable_CellEditCallback(this,hObject,eventdata)); + 'Data', data, ... + 'ColumnEditable', [false true false], ... + 'ColumnName', cNames, ... + 'ColumnFormat', columnformat, ... + 'Position', [50 150 10 10], ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'BackgroundColor', colorMatrix, ... + 'RowStriping', 'on'); + + set(tissueTable, 'CellEditCallback', @(hObject, eventdata) tissueTable_CellEditCallback(this, hObject, eventdata)); % set width and height - currTablePos = get(tissueTable,'Position'); - currTableExt = get(tissueTable,'Extent'); + currTablePos = get(tissueTable, 'Position'); + currTableExt = get(tissueTable, 'Extent'); currTablePos(3) = currTableExt(3); currTablePos(4) = currTableExt(4); - set(tissueTable,'Position',currTablePos); + set(tissueTable, 'Position', currTablePos); - themeParams = {'BackgroundColor', matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight}; + themeParams = {'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight}; % define two buttons with callbacks uicontrol('Parent', figTissue, ... - 'Style', 'pushbutton', ... - 'String', 'Save&Close',... - 'Position', [Width-(0.25*Width) 0.1 * Height 70 30],... - 'Callback', @(hpb,eventdata)SaveTissueParameters(this,hpb,eventdata),... - themeParams{:}); + 'Style', 'pushbutton', ... + 'String', 'Save&Close', ... + 'Position', [Width - (0.25 * Width) 0.1 * Height 70 30], ... + 'Callback', @(hpb, eventdata)SaveTissueParameters(this, hpb, eventdata), ... + themeParams{:}); uicontrol('Parent', ... - figTissue,'Style', ... - 'pushbutton', ... - 'String', 'Cancel&Close',... - 'Position', [Width-(0.5*Width) 0.1 * Height 80 30],... - 'Callback', 'close', ... - themeParams{:}); + figTissue, 'Style', ... + 'pushbutton', ... + 'String', 'Cancel&Close', ... + 'Position', [Width - (0.5 * Width) 0.1 * Height 80 30], ... + 'Callback', 'close', ... + themeParams{:}); catch ME - this.showWarning('Could not set Tissue parameter update! Reason: %s\n',ME.message) + this.showWarning('Could not set Tissue parameter update! Reason: %s\n', ME.message); end end this.handles = handles; @@ -1614,9 +1621,9 @@ function btnSetTissue_Callback(this, hObject, eventdata) function popMenuBioModel_Callback(this, hObject, eventdata) handles = this.handles; - pln = evalin('base','pln'); - contentBioModel = get(handles.popMenuBioModel,'String'); - NewBioModel = contentBioModel(get(handles.popMenuBioModel,'Value'),:); + pln = evalin('base', 'pln'); + contentBioModel = get(handles.popMenuBioModel, 'String'); + NewBioModel = contentBioModel(get(handles.popMenuBioModel, 'Value'), :); % if (strcmp(pln.propOpt.bioOptimization,'LEMIV_effect') && strcmp(NewBioOptimization,'LEMIV_RBExDose')) ||... % (strcmp(pln.propOpt.bioOptimization,'LEMIV_RBExDose') && strcmp(NewBioOptimization,'LEMIV_effect')) @@ -1629,6 +1636,7 @@ function popMenuBioModel_Callback(this, hObject, eventdata) this.handles = handles; updatePlnInWorkspace(this); end + function popMenuMultScen_Callback(this, hObject, eventdata) updatePlnInWorkspace(this); @@ -1639,37 +1647,38 @@ function popMenuQuantityOpt_Callback(this, hObject, eventdata) updatePlnInWorkspace(this); end - function tissueTable_CellEditCallback(this,hObject, eventdata) + function tissueTable_CellEditCallback(this, hObject, eventdata) if eventdata.Indices(2) == 2 alphaXBetaX = str2num(eventdata.NewData); - data = get(hObject,'Data'); - data{eventdata.Indices(1),3} = alphaXBetaX(1)/alphaXBetaX(2); - set(hObject,'Data',data); + data = get(hObject, 'Data'); + data{eventdata.Indices(1), 3} = alphaXBetaX(1) / alphaXBetaX(2); + set(hObject, 'Data', data); end end + %% END OF CALLBACKS % load Machine File function getMachines(this) - %matRad_cfg = MatRad_Config.instance(); - %seach for availabes machines + % matRad_cfg = MatRad_Config.instance(); + % seach for availabes machines handles = this.handles; this.Machines = matRad_getAvailableMachines(this.modalities); - selectedRadMod = get(handles.popupRadMode,'Value'); + selectedRadMod = get(handles.popupRadMode, 'Value'); nMachines = numel(this.Machines(this.modalities{selectedRadMod})); - selectedMachine = get(handles.popUpMachine,'Value'); + selectedMachine = get(handles.popUpMachine, 'Value'); - if get(handles.popUpMachine,'Value') > nMachines + if get(handles.popUpMachine, 'Value') > nMachines selectedMachine = 1; end - set(handles.popUpMachine,'Value',selectedMachine,'String',this.Machines(this.modalities{selectedRadMod})); + set(handles.popUpMachine, 'Value', selectedMachine, 'String', this.Machines(this.modalities{selectedRadMod})); availableMachines = this.Machines(this.modalities{selectedRadMod}); try - this.currentMachine = matRad_loadMachine(struct('radiationMode',this.modalities{selectedRadMod},'machine',availableMachines{selectedMachine})); + this.currentMachine = matRad_loadMachine(struct('radiationMode', this.modalities{selectedRadMod}, 'machine', availableMachines{selectedMachine})); catch ME this.currentMachine = []; end @@ -1677,8 +1686,8 @@ function getMachines(this) this.handles = handles; end - %String to num parser for edit fields - function number = parseStringAsNum(this,stringIn,isVector) + % String to num parser for edit fields + function number = parseStringAsNum(this, stringIn, isVector) if isnumeric(stringIn) number = stringIn; else @@ -1692,18 +1701,18 @@ function getMachines(this) end end - %Check if Machine File is available and correct + % Check if Machine File is available and correct function flag = checkRadiationComposition(this) matRad_cfg = MatRad_Config.instance(); handles = this.handles; flag = true; - contents = cellstr(get(handles.popUpMachine,'String')); - Machine = contents{get(handles.popUpMachine,'Value')}; - contents = cellstr(get(handles.popupRadMode,'String')); - radMod = contents{get(handles.popupRadMode,'Value')}; + contents = cellstr(get(handles.popUpMachine, 'String')); + Machine = contents{get(handles.popUpMachine, 'Value')}; + contents = cellstr(get(handles.popupRadMode, 'String')); + radMod = contents{get(handles.popupRadMode, 'Value')}; - FoundFile = ismember(Machine,this.Machines(radMod)); + FoundFile = ismember(Machine, this.Machines(radMod)); if ~FoundFile this.showWarning(['No base data available for machine: ' Machine '. Selecting default machine.']); @@ -1713,32 +1722,33 @@ function getMachines(this) this.handles = handles; end - %Save Tissue Parameters to cst - function SaveTissueParameters(this,~, ~) - cst = evalin('base','cst'); + % Save Tissue Parameters to cst + function SaveTissueParameters(this, ~, ~) + cst = evalin('base', 'cst'); % get handle to uiTable - figHandles = get(0,'Children'); - IdxHandle = find(strcmp(get(figHandles,'Name'),'Set Tissue Parameters')); + figHandles = get(0, 'Children'); + IdxHandle = find(strcmp(get(figHandles, 'Name'), 'Set Tissue Parameters')); % find table in window - figHandleChildren = get(figHandles(IdxHandle),'Children'); - IdxTable = find(strcmp(get(figHandleChildren,'Type'),'uitable')); + figHandleChildren = get(figHandles(IdxHandle), 'Children'); + IdxTable = find(strcmp(get(figHandleChildren, 'Type'), 'uitable')); uiTable = figHandleChildren(IdxTable); % retrieve data from uitable - data = get(uiTable,'data'); - - for i = 1:size(cst,1) - for j = 1:size(data,1) - if strcmp(cst{i,2},data{j,1}) - alphaXBetaX = str2num(data{j,2}); - cst{i,5}.alphaX = alphaXBetaX(1); - cst{i,5}.betaX = alphaXBetaX(2); + data = get(uiTable, 'data'); + + for i = 1:size(cst, 1) + for j = 1:size(data, 1) + if strcmp(cst{i, 2}, data{j, 1}) + alphaXBetaX = str2num(data{j, 2}); + cst{i, 5}.alphaX = alphaXBetaX(1); + cst{i, 5}.betaX = alphaXBetaX(2); end end end - assignin('base','cst',cst); - close + assignin('base', 'cst', cst); + close; updatePlnInWorkspace(this); end + end end diff --git a/matRad/gui/widgets/matRad_WorkflowWidget.m b/matRad/gui/widgets/matRad_WorkflowWidget.m index 26c7a9020..241f5f9a4 100644 --- a/matRad/gui/widgets/matRad_WorkflowWidget.m +++ b/matRad/gui/widgets/matRad_WorkflowWidget.m @@ -1,7 +1,7 @@ classdef matRad_WorkflowWidget < matRad_Widget % matRad_WorkflowWidget class to generate GUI widget to run through the % treatment planning workflow - % + % % % References % - @@ -20,241 +20,240 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% properties - savedResultTag = {}; - end - + savedResultTag = {} + end + methods + function this = matRad_WorkflowWidget(handleParent) if nargin < 1 matRad_cfg = MatRad_Config.instance(); - handleParent = figure(... - 'Units','characters',... - 'Position',[130 45 150 15],... - 'Visible','on',... - 'Color',matRad_cfg.gui.backgroundColor,... - 'IntegerHandle','off',... - 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... - 'MenuBar','none',... - 'Name','MatRad Workflow',... - 'NumberTitle','off',... - 'HandleVisibility','callback',... - 'Tag','figure1',... - 'PaperSize',[20.99999864 29.69999902]); - - set(handleParent,'Units','normalized') - pos = get(handleParent,'Position'); - pos(1:2) = [0.5 0.5] - pos(3:4)./2; - set(handleParent,'Position',pos); - + handleParent = figure( ... + 'Units', 'characters', ... + 'Position', [130 45 150 15], ... + 'Visible', 'on', ... + 'Color', matRad_cfg.gui.backgroundColor, ... + 'IntegerHandle', 'off', ... + 'Colormap', [0 0 0.5625; 0 0 0.625; 0 0 0.6875; 0 0 0.75; 0 0 0.8125; 0 0 0.875; 0 0 0.9375; 0 0 1; 0 0.0625 1; 0 0.125 1; 0 0.1875 1; 0 0.25 1; 0 0.3125 1; 0 0.375 1; 0 0.4375 1; 0 0.5 1; 0 0.5625 1; 0 0.625 1; 0 0.6875 1; 0 0.75 1; 0 0.8125 1; 0 0.875 1; 0 0.9375 1; 0 1 1; 0.0625 1 1; 0.125 1 0.9375; 0.1875 1 0.875; 0.25 1 0.8125; 0.3125 1 0.75; 0.375 1 0.6875; 0.4375 1 0.625; 0.5 1 0.5625; 0.5625 1 0.5; 0.625 1 0.4375; 0.6875 1 0.375; 0.75 1 0.3125; 0.8125 1 0.25; 0.875 1 0.1875; 0.9375 1 0.125; 1 1 0.0625; 1 1 0; 1 0.9375 0; 1 0.875 0; 1 0.8125 0; 1 0.75 0; 1 0.6875 0; 1 0.625 0; 1 0.5625 0; 1 0.5 0; 1 0.4375 0; 1 0.375 0; 1 0.3125 0; 1 0.25 0; 1 0.1875 0; 1 0.125 0; 1 0.0625 0; 1 0 0; 0.9375 0 0; 0.875 0 0; 0.8125 0 0; 0.75 0 0; 0.6875 0 0; 0.625 0 0; 0.5625 0 0], ... + 'MenuBar', 'none', ... + 'Name', 'MatRad Workflow', ... + 'NumberTitle', 'off', ... + 'HandleVisibility', 'callback', ... + 'Tag', 'figure1', ... + 'PaperSize', [20.99999864 29.69999902]); + + set(handleParent, 'Units', 'normalized'); + pos = get(handleParent, 'Position'); + pos(1:2) = [0.5 0.5] - pos(3:4) ./ 2; + set(handleParent, 'Position', pos); + end this = this@matRad_Widget(handleParent); end - + function this = initialize(this) this.update(); end - - + % moved so it can be called from the toolbar button % H74 Callback function btnLoadMat_Callback(this, hObject, event) handles = this.handles; matRad_cfg = MatRad_Config.instance(); - [FileName, FilePath] = uigetfile(fullfile(matRad_cfg.matRadSrcRoot,'phantoms','*.mat')); + [FileName, FilePath] = uigetfile(fullfile(matRad_cfg.matRadSrcRoot, 'phantoms', '*.mat')); if FileName == 0 % user pressed cancel --> do nothing. - return; + return end try % delete existing workspace - parse variables from base workspace - AllVarNames = evalin('base','who'); - RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; - + AllVarNames = evalin('base', 'who'); + RefVarNames = {'ct', 'cst', 'pln', 'stf', 'dij', 'resultGUI'}; + for i = 1:length(RefVarNames) - if sum(ismember(AllVarNames,RefVarNames{i}))>0 - evalin('base',['clear ', RefVarNames{i}]); + if sum(ismember(AllVarNames, RefVarNames{i})) > 0 + evalin('base', ['clear ', RefVarNames{i}]); end end - + % read new data load([FilePath FileName]); - + catch ME - this.handles=handles; + this.handles = handles; getFromWorkspace(this); - showError(this,'LoadMatFileFnc: Could not load *.mat file',ME); + showError(this, 'LoadMatFileFnc: Could not load *.mat file', ME); return end - + try - %cst = generateCstTable(this,cst); - %handles.TableChanged = false; - %set(handles.popupTypeOfPlot,'Value',1); - %cst = matRad_computeVoiContoursWrapper(cst,ct); - - assignin('base','ct',ct); - assignin('base','cst',cst); - + % cst = generateCstTable(this,cst); + % handles.TableChanged = false; + % set(handles.popupTypeOfPlot,'Value',1); + % cst = matRad_computeVoiContoursWrapper(cst,ct); + + assignin('base', 'ct', ct); + assignin('base', 'cst', cst); + catch ME - showError(this,'LoadMatFileFnc: Could not load *.mat file',ME); + showError(this, 'LoadMatFileFnc: Could not load *.mat file', ME); end - + % check if a optimized plan was loaded - if exist('stf','var') - assignin('base','stf',stf); + if exist('stf', 'var') + assignin('base', 'stf', stf); end - if exist('pln','var') - assignin('base','pln',pln); + if exist('pln', 'var') + assignin('base', 'pln', pln); end - if exist('dij','var') - assignin('base','dij',dij); + if exist('dij', 'var') + assignin('base', 'dij', dij); end - - if exist('resultGUI','var') - assignin('base','resultGUI',resultGUI); + + if exist('resultGUI', 'var') + assignin('base', 'resultGUI', resultGUI); end - - this.handles=handles; - %updateInWorkspace(this); + + this.handles = handles; + % updateInWorkspace(this); this.changedWorkspace(); - %getFromWorkspace(this); %update the buttons + % getFromWorkspace(this); %update the buttons end + end - + methods (Access = protected) + function this = createLayout(this) - + parent = this.widgetHandle; - + matRad_cfg = MatRad_Config.instance(); - - - h72 = this.addControlToGrid([2 4],... - 'Style','text',... - 'String','Status:',... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Tag','txtStatus',... - 'FontSize',round(matRad_cfg.gui.fontSize*1.2)); - - h73 = this.addControlToGrid([3 4],... - 'String','no data loaded',... - 'Style','text',... - 'BackgroundColor',matRad_cfg.gui.backgroundColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Tag','txtInfo',... - 'FontSize',round(matRad_cfg.gui.fontSize*1.2)); - - hMatLoad = this.addControlToGrid([2 1],... - 'String','Load *.mat data',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnLoadMat_Callback(this,hObject,eventdata),... - 'Tag','btnLoadMat'); - - hDijCalc = this.addControlToGrid([3 1],... - 'String','Calc. Dose Influence',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnCalcDose_Callback(this,hObject,eventdata),... - 'Tag','btnCalcDose'); - - hOpt = this.addControlToGrid([4 1],... - 'Parent',parent,... - 'String','Optimize',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnOptimize_Callback(this,hObject,eventdata),... - 'Tag','btnOptimize'); - - hLoadDicom = this.addControlToGrid([2 2],... - 'String','Load DICOM',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnLoadDicom_Callback(this,hObject,eventdata),... - 'Tag','btnLoadDicom'); - - - hRefresh = this.addControlToGrid([1 1],... - 'String','Refresh',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnRefresh_Callback(this,hObject,eventdata),... - 'Tag','btnRefresh'); - - hRecalc = this.addControlToGrid([4 2],... - 'String','Recalculate Dose',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) pushbutton_recalc_Callback(this,hObject,eventdata),... - 'Tag','pushbutton_recalc'); - - hKeep = this.addControlToGrid([5 1],... - 'String','Save/Keep Result',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btnSaveToGUI_Callback(this,hObject,eventdata),... - 'Tag','btnSaveToGUI'); - - hExportBin = this.addControlToGrid([5 2],... - 'String','Export Binary',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) btn_export_Callback(this,hObject,eventdata),... - 'Children',[],... - 'Tag','btn_export'); - - hImportDose = this.addControlToGrid([4 3],... - 'String','Import Dose',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) importDoseButton_Callback(this, hObject,eventdata),... - 'Tag','importDoseButton'); - - hImportBin = this.addControlToGrid([2 3],... - 'String','Import from Binary',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) pushbutton_importFromBinary_Callback(this,hObject,eventdata),... - 'TooltipString','Imports a patient data set from binary datafiles describing CT and segmentations',... - 'Tag','pushbutton_importFromBinary'); - - hExportDicom = this.addControlToGrid([5 3],... - 'String','Export Dicom',... - 'BackgroundColor',matRad_cfg.gui.elementColor,... - 'ForegroundColor',matRad_cfg.gui.textColor,... - 'Callback',@(hObject,eventdata) exportDicomButton_Callback(this, hObject,eventdata),... - 'Tag','exportDicomButton'); - + h72 = this.addControlToGrid([2 4], ... + 'Style', 'text', ... + 'String', 'Status:', ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Tag', 'txtStatus', ... + 'FontSize', round(matRad_cfg.gui.fontSize * 1.2)); + + h73 = this.addControlToGrid([3 4], ... + 'String', 'no data loaded', ... + 'Style', 'text', ... + 'BackgroundColor', matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Tag', 'txtInfo', ... + 'FontSize', round(matRad_cfg.gui.fontSize * 1.2)); + + hMatLoad = this.addControlToGrid([2 1], ... + 'String', 'Load *.mat data', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnLoadMat_Callback(this, hObject, eventdata), ... + 'Tag', 'btnLoadMat'); + + hDijCalc = this.addControlToGrid([3 1], ... + 'String', 'Calc. Dose Influence', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnCalcDose_Callback(this, hObject, eventdata), ... + 'Tag', 'btnCalcDose'); + + hOpt = this.addControlToGrid([4 1], ... + 'Parent', parent, ... + 'String', 'Optimize', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnOptimize_Callback(this, hObject, eventdata), ... + 'Tag', 'btnOptimize'); + + hLoadDicom = this.addControlToGrid([2 2], ... + 'String', 'Load DICOM', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnLoadDicom_Callback(this, hObject, eventdata), ... + 'Tag', 'btnLoadDicom'); + + hRefresh = this.addControlToGrid([1 1], ... + 'String', 'Refresh', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnRefresh_Callback(this, hObject, eventdata), ... + 'Tag', 'btnRefresh'); + + hRecalc = this.addControlToGrid([4 2], ... + 'String', 'Recalculate Dose', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) pushbutton_recalc_Callback(this, hObject, eventdata), ... + 'Tag', 'pushbutton_recalc'); + + hKeep = this.addControlToGrid([5 1], ... + 'String', 'Save/Keep Result', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btnSaveToGUI_Callback(this, hObject, eventdata), ... + 'Tag', 'btnSaveToGUI'); + + hExportBin = this.addControlToGrid([5 2], ... + 'String', 'Export Binary', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) btn_export_Callback(this, hObject, eventdata), ... + 'Children', [], ... + 'Tag', 'btn_export'); + + hImportDose = this.addControlToGrid([4 3], ... + 'String', 'Import Dose', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) importDoseButton_Callback(this, hObject, eventdata), ... + 'Tag', 'importDoseButton'); + + hImportBin = this.addControlToGrid([2 3], ... + 'String', 'Import from Binary', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) pushbutton_importFromBinary_Callback(this, hObject, eventdata), ... + 'TooltipString', 'Imports a patient data set from binary datafiles describing CT and segmentations', ... + 'Tag', 'pushbutton_importFromBinary'); + + hExportDicom = this.addControlToGrid([5 3], ... + 'String', 'Export Dicom', ... + 'BackgroundColor', matRad_cfg.gui.elementColor, ... + 'ForegroundColor', matRad_cfg.gui.textColor, ... + 'Callback', @(hObject, eventdata) exportDicomButton_Callback(this, hObject, eventdata), ... + 'Tag', 'exportDicomButton'); + this.createHandles(); - - handles=this.handles; + + handles = this.handles; matRad_cfg = MatRad_Config.instance(); if matRad_cfg.eduMode - %Visisbility in Educational Mode - eduHideHandles = {handles.pushbutton_importFromBinary,... - handles.btnLoadDicom,... - handles.btn_export,... - handles.exportDicomButton,... - handles.importDoseButton}; - cellfun(@(h) set(h,'Visible','Off'),eduHideHandles); - end - this.handles=handles; + % Visisbility in Educational Mode + eduHideHandles = {handles.pushbutton_importFromBinary, ... + handles.btnLoadDicom, ... + handles.btn_export, ... + handles.exportDicomButton, ... + handles.importDoseButton}; + cellfun(@(h) set(h, 'Visible', 'Off'), eduHideHandles); + end + this.handles = handles; end - - function this = doUpdate(this,evt) - %If the pln was changed, we do not do a consistency check (or - %at least we do not throw a warning when it is inconsistent) - if nargin < 2 || any(strcmp(evt.changedVariables,'pln')) + + function this = doUpdate(this, evt) + % If the pln was changed, we do not do a consistency check (or + % at least we do not throw a warning when it is inconsistent) + if nargin < 2 || any(strcmp(evt.changedVariables, 'pln')) noCheck = true; else noCheck = false; end - this.getFromWorkspace(noCheck); - end - - function this = getFromWorkspace(this,noCheck) + this.getFromWorkspace(noCheck); + end + + function this = getFromWorkspace(this, noCheck) if nargin < 2 noCheck = false; @@ -263,221 +262,218 @@ function btnLoadMat_Callback(this, hObject, event) handles = this.handles; matRad_cfg = MatRad_Config.instance(); % no data loaded, disable the buttons - set(handles.txtInfo,'String','no data loaded'); - set(handles.btnCalcDose,'Enable','off'); - set(handles.btnOptimize ,'Enable','off'); - set(handles.pushbutton_recalc,'Enable','off'); - set(handles.btnSaveToGUI,'Enable','off'); - set(handles.importDoseButton,'Enable','off'); - set(handles.btn_export,'Enable','off'); - set(handles.exportDicomButton,'Enable','off'); - + set(handles.txtInfo, 'String', 'no data loaded'); + set(handles.btnCalcDose, 'Enable', 'off'); + set(handles.btnOptimize, 'Enable', 'off'); + set(handles.pushbutton_recalc, 'Enable', 'off'); + set(handles.btnSaveToGUI, 'Enable', 'off'); + set(handles.importDoseButton, 'Enable', 'off'); + set(handles.btn_export, 'Enable', 'off'); + set(handles.exportDicomButton, 'Enable', 'off'); + + if evalin('base', 'exist(''ct'')') && evalin('base', 'exist(''cst'')') + + set(handles.txtInfo, 'String', 'loaded and ready'); + + if evalin('base', 'exist(''pln'')') + pln = evalin('base', 'pln'); - if evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') - - set(handles.txtInfo,'String','loaded and ready'); - - if evalin('base','exist(''pln'')') - pln = evalin('base','pln'); - % ct cst and pln available; ready for dose calculation - set(handles.txtInfo,'String','ready for dose calculation'); - set(handles.btnCalcDose,'Enable','on'); - set(handles.btn_export,'Enable','on'); - set(handles.exportDicomButton,'Enable','on'); + set(handles.txtInfo, 'String', 'ready for dose calculation'); + set(handles.btnCalcDose, 'Enable', 'on'); + set(handles.btn_export, 'Enable', 'on'); + set(handles.exportDicomButton, 'Enable', 'on'); % check if stf exists - if evalin('base','exist(''stf'')') - stf = evalin('base','stf'); + if evalin('base', 'exist(''stf'')') + stf = evalin('base', 'stf'); % check if dij, stf and pln match - [plnStfMatch, msg] = matRad_comparePlnStf(pln,stf); + [plnStfMatch, msg] = matRad_comparePlnStf(pln, stf); if plnStfMatch % plan is ready for optimization - set(handles.txtInfo,'String','ready for dose calculation'); - set(handles.btnOptimize ,'Enable','on'); - elseif ~noCheck + set(handles.txtInfo, 'String', 'ready for dose calculation'); + set(handles.btnOptimize, 'Enable', 'on'); + elseif ~noCheck this.showWarning(msg); else - %Nothing + % Nothing end % check if dij exist - conf3D = isfield(pln,'propOpt') && isfield(pln.propOpt,'conf3D') && pln.propOpt.conf3D; + conf3D = isfield(pln, 'propOpt') && isfield(pln.propOpt, 'conf3D') && pln.propOpt.conf3D; - if evalin('base','exist(''dij'')') && plnStfMatch && ~conf3D - dij = evalin('base','dij'); - [dijStfMatch, msg] = matRad_compareDijStf(dij,stf); + if evalin('base', 'exist(''dij'')') && plnStfMatch && ~conf3D + dij = evalin('base', 'dij'); + [dijStfMatch, msg] = matRad_compareDijStf(dij, stf); if dijStfMatch - set(handles.txtInfo,'String','ready for optimization'); - set(handles.btnOptimize ,'Enable','on'); - elseif ~noCheck + set(handles.txtInfo, 'String', 'ready for optimization'); + set(handles.btnOptimize, 'Enable', 'on'); + elseif ~noCheck this.showWarning(msg); else - %Nothing + % Nothing end end - + end % does resultGUI exist - if evalin('base','exist(''resultGUI'')') - set(handles.pushbutton_recalc,'Enable','on'); - set(handles.btnSaveToGUI,'Enable','on'); + if evalin('base', 'exist(''resultGUI'')') + set(handles.pushbutton_recalc, 'Enable', 'on'); + set(handles.btnSaveToGUI, 'Enable', 'on'); % resultGUI struct needs to be available to import dose % otherwise inconsistent states can be achieved - set(handles.importDoseButton,'Enable','on'); + set(handles.importDoseButton, 'Enable', 'on'); end end else % Do Nothing end - this.handles=handles; + this.handles = handles; end - - + end methods (Access = private) - - function h = addControlToGrid(this,gridPos,varargin) + + function h = addControlToGrid(this, gridPos, varargin) matRad_cfg = MatRad_Config.instance(); parent = this.widgetHandle; - - %Use a 5 x 5 grid - pos = this.computeGridPos(gridPos,[5 5]); - - h = uicontrol('Parent',parent,... - 'Units','normalized',... - 'Position',pos,... - 'FontSize',matRad_cfg.gui.fontSize,... - 'FontName',matRad_cfg.gui.fontName,... - 'FontWeight',matRad_cfg.gui.fontWeight,... - varargin{:}); + + % Use a 5 x 5 grid + pos = this.computeGridPos(gridPos, [5 5]); + + h = uicontrol('Parent', parent, ... + 'Units', 'normalized', ... + 'Position', pos, ... + 'FontSize', matRad_cfg.gui.fontSize, ... + 'FontName', matRad_cfg.gui.fontName, ... + 'FontWeight', matRad_cfg.gui.fontWeight, ... + varargin{:}); end - - + % H75 Callback function btnCalcDose_Callback(this, hObject, eventdata) % hObject handle to btnCalcDose (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - + % http://stackoverflow.com/questions/24703962/trigger-celleditcallback-before-button-callback % http://www.mathworks.com/matlabcentral/newsreader/view_thread/332613 % wait some time until the CallEditCallback is finished % Callback triggers the cst saving mechanism from GUI - + handles = this.handles; try % indicate that matRad is busy % change mouse pointer to hour glass - Figures = gcf;%findobj('type','figure'); + Figures = gcf; % findobj('type','figure'); set(Figures, 'pointer', 'watch'); drawnow; % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - - + InterfaceObj = findobj(Figures, 'Enable', 'on'); + set(InterfaceObj, 'Enable', 'off'); + % read plan from gui and save it to workspace - %handles=getPlnFromGUI(this); - + % handles=getPlnFromGUI(this); + % get default iso center as center of gravity of all targets if not % already defined - pln = evalin('base','pln'); - + pln = evalin('base', 'pln'); + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'CalcDoseCallback: Error in preprocessing!',ME); - return; + showError(this, 'CalcDoseCallback: Error in preprocessing!', ME); + return end - + % generate steering file try - currPln = evalin('base','pln'); + currPln = evalin('base', 'pln'); % % if we run 3d conf opt -> hijack runDao to trigger computation of % % connected bixels % if strcmp(pln.radiationMode,'photons') && get(handles.radiobutton3Dconf,'Value') % currpln.propOpt.runDAO = true; % end - stf = matRad_generateStf(evalin('base','ct'),... - evalin('base','cst'),... - currPln); - assignin('base','stf',stf); + stf = matRad_generateStf(evalin('base', 'ct'), ... + evalin('base', 'cst'), ... + currPln); + assignin('base', 'stf', stf); catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'CalcDoseCallback: Error in steering file generation!',ME); - return; + showError(this, 'CalcDoseCallback: Error in steering file generation!', ME); + return end - + % carry out dose calculation try - dij = matRad_calcDoseInfluence(evalin('base','ct'),evalin('base','cst'),stf,pln); - + dij = matRad_calcDoseInfluence(evalin('base', 'ct'), evalin('base', 'cst'), stf, pln); + % prepare dij for 3d conformal - if isfield(pln,'propOpt') && isfield(pln.propOpt,'conf3D') && pln.propOpt.conf3D - dij = matRad_collapseDij(dij); - stf = matRad_collapseStf(stf); + if isfield(pln, 'propOpt') && isfield(pln.propOpt, 'conf3D') && pln.propOpt.conf3D + dij = matRad_collapseDij(dij); + stf = matRad_collapseStf(stf); end % assign results to base worksapce - assignin('base','dij',dij); - assignin('base','stf',stf); - - + assignin('base', 'dij', dij); + assignin('base', 'stf', stf); + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'CalcDoseCallback: Error in dose calculation!',ME); - return; + showError(this, 'CalcDoseCallback: Error in dose calculation!', ME); + return end - + % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - this.changedWorkspace('stf','dij'); - %getFromWorkspace(this); + this.changedWorkspace('stf', 'dij'); + % getFromWorkspace(this); end - + % H76 Callback function btnOptimize_Callback(this, hObject, eventdata) % hObject handle to btnOptimize (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) handles = this.handles; + runDAO = this.widgetHandle.Parent.Children(9).Children(13).Value; + runSeq = this.widgetHandle.Parent.Children(9).Children(18).Value; try % indicate that matRad is busy % change mouse pointer to hour glass - Figures = gcf;%findobj('type','figure'); + Figures = gcf; % findobj('type','figure'); set(Figures, 'pointer', 'watch'); drawnow; % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - - pln = evalin('base','pln'); - dij = evalin('base','dij'); - cst = evalin('base','cst'); + InterfaceObj = findobj(Figures, 'Enable', 'on'); + set(InterfaceObj, 'Enable', 'off'); + + pln = evalin('base', 'pln'); + dij = evalin('base', 'dij'); + cst = evalin('base', 'cst'); % optimize - [resultGUIcurrentRun,usedOptimizer] = matRad_fluenceOptimization(dij,cst,pln); - if isfield(pln,'propOpt') && isfield(pln.propOpt,'conf3D') && pln.propOpt.conf3D && strcmp(pln.radiationMode,'photons') - resultGUIcurrentRun.w = resultGUIcurrentRun.w .* ones(dij.totalNumOfBixels,1); + [resultGUIcurrentRun, usedOptimizer] = matRad_fluenceOptimization(dij, cst, pln); + if isfield(pln, 'propOpt') && isfield(pln.propOpt, 'conf3D') && pln.propOpt.conf3D && strcmp(pln.radiationMode, 'photons') + resultGUIcurrentRun.w = resultGUIcurrentRun.w .* ones(dij.totalNumOfBixels, 1); resultGUIcurrentRun.wUnsequenced = resultGUIcurrentRun.w; end - - %if resultGUI already exists then overwrite the "standard" fields - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); + + % if resultGUI already exists then overwrite the "standard" fields + AllVarNames = evalin('base', 'who'); + if ismember('resultGUI', AllVarNames) + resultGUI = evalin('base', 'resultGUI'); oldNames = fieldnames(resultGUI); if ~isempty(this.savedResultTag) @@ -488,79 +484,77 @@ function btnOptimize_Callback(this, hObject, eventdata) end end end - end + end end resultGUI = resultGUIcurrentRun; - assignin('base','resultGUI',resultGUI); + assignin('base', 'resultGUI', resultGUI); - if ~pln.propOpt.runDAO || ~strcmp(pln.radiationMode,'photons') - CheckOptimizerStatus(this,usedOptimizer,'Fluence') + if ~runDAO || ~strcmp(pln.radiationMode, 'photons') + CheckOptimizerStatus(this, usedOptimizer, 'Fluence'); end - + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'OptimizeCallback: Could not optimize!',ME); - return; + showError(this, 'OptimizeCallback: Could not optimize!', ME); + return end - + % perform sequencing and DAO try %% sequencing - - resultGUI = matRad_sequencing(resultGUI,evalin('base','stf'),dij,pln); - assignin('base','resultGUI',resultGUI); - - + + resultGUI = matRad_sequencing(resultGUI, evalin('base', 'stf'), pln, dij); + assignin('base', 'resultGUI', resultGUI); + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'OptimizeCallback: Could not perform sequencing',ME); - return; + showError(this, 'OptimizeCallback: Could not perform sequencing', ME); + return end - + try %% DAO - if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO + if strcmp(pln.radiationMode, 'photons') && runDAO - showWarning(this,['Observe: You are running direct aperture optimization' filesep 'This is experimental code that has not been thoroughly debugged - especially in combination with constrained optimization.']); % was assigned to handles WHY ? - [resultGUI,usedOptimizer] = matRad_directApertureOptimization(evalin('base','dij'),evalin('base','cst'),... - resultGUI.apertureInfo,resultGUI,pln); - assignin('base','resultGUI',resultGUI); + showWarning(this, ['Observe: You are running direct aperture optimization' filesep 'This is experimental code that has not been thoroughly debugged - especially in combination with constrained optimization.']); % was assigned to handles WHY ? + [resultGUI, usedOptimizer] = matRad_directApertureOptimization(evalin('base', 'dij'), evalin('base', 'cst'), ... + resultGUI.sequencing.apertureInfo, pln); + assignin('base', 'resultGUI', resultGUI); % check IPOPT status and return message for GUI user - CheckOptimizerStatus(this,usedOptimizer,'DAO'); + CheckOptimizerStatus(this, usedOptimizer, 'DAO'); end - - if strcmp(pln.radiationMode,'photons') && (pln.propSeq.runSequencing || pln.propOpt.runDAO) + if strcmp(pln.radiationMode, 'photons') && runSeq || runDAO - matRad_visApertureInfo(resultGUI.apertureInfo); + matRad_visApertureInfo(resultGUI.sequencing.apertureInfo); end - + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'OptimizeCallback: Could not perform direct aperture optimization',ME); - return; + showError(this, 'OptimizeCallback: Could not perform direct aperture optimization', ME); + return end - + % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - + this.changedWorkspace('resultGUI'); - %getFromWorkspace(this); + % getFromWorkspace(this); end - + % H77 Callback function btnLoadDicom_Callback(this, hObject, event) % hObject handle to btnLoadDicom (see GCBO) @@ -569,35 +563,34 @@ function btnLoadDicom_Callback(this, hObject, event) handles = this.handles; try % delete existing workspace - parse variables from base workspace - AllVarNames = evalin('base','who'); - RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; + AllVarNames = evalin('base', 'who'); + RefVarNames = {'ct', 'cst', 'pln', 'stf', 'dij', 'resultGUI'}; for i = 1:length(RefVarNames) - if sum(ismember(AllVarNames,RefVarNames{i}))>0 - evalin('base',['clear ', RefVarNames{i}]); + if sum(ismember(AllVarNames, RefVarNames{i})) > 0 + evalin('base', ['clear ', RefVarNames{i}]); end end matRad_importDicomWidget; - + catch ME - showError(this,'DicomImport: Could not import data', ME); + showError(this, 'DicomImport: Could not import data', ME); end - + this.handles = handles; end - + % H78 Callback - button: refresh function btnRefresh_Callback(this, hObject, event) % notify so all widgets refresh this.changedWorkspace(); end - - + % H79 Callback function pushbutton_recalc_Callback(this, hObject, eventdata) - + handles = this.handles; - + try % indicate that matRad is busy % change mouse pointer to hour glass @@ -605,284 +598,274 @@ function pushbutton_recalc_Callback(this, hObject, eventdata) set(Figures, 'pointer', 'watch'); drawnow; % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - + InterfaceObj = findobj(Figures, 'Enable', 'on'); + set(InterfaceObj, 'Enable', 'off'); + % get all data from workspace - pln = evalin('base','pln'); - stf = evalin('base','stf'); - ct = evalin('base','ct'); - cst = evalin('base','cst'); - resultGUI = evalin('base','resultGUI'); - - - if sum([stf.totalNumOfBixels]) ~= length(resultGUI.w)%(['w' Suffix])) + pln = evalin('base', 'pln'); + stf = evalin('base', 'stf'); + ct = evalin('base', 'ct'); + cst = evalin('base', 'cst'); + resultGUI = evalin('base', 'resultGUI'); + + if sum([stf.totalNumOfBixels]) ~= length(resultGUI.w) % (['w' Suffix])) this.showWarning('weight vector does not corresponding to current steering file'); return end - + % change isocenter if that was changed and do _not_ recreate steering % information for i = 1:numel(pln.propStf.gantryAngles) - stf(i).isoCenter = pln.propStf.isoCenter(i,:); + stf(i).isoCenter = pln.propStf.isoCenter(i, :); end - - resultGUIreCalc = matRad_calcDoseForward(ct,cst,stf,pln,resultGUI.w); - + + resultGUIreCalc = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); + % delete old variables to avoid confusion - if isfield(resultGUI,'effect') - resultGUI = rmfield(resultGUI,'effect'); - resultGUI = rmfield(resultGUI,'RBExDose'); - resultGUI = rmfield(resultGUI,'RBE'); - resultGUI = rmfield(resultGUI,'alpha'); - resultGUI = rmfield(resultGUI,'beta'); + if isfield(resultGUI, 'effect') + resultGUI = rmfield(resultGUI, 'effect'); + resultGUI = rmfield(resultGUI, 'RBExDose'); + resultGUI = rmfield(resultGUI, 'RBE'); + resultGUI = rmfield(resultGUI, 'alpha'); + resultGUI = rmfield(resultGUI, 'beta'); end - if isfield(resultGUI,'LET') - resultGUI = rmfield(resultGUI,'LET'); + if isfield(resultGUI, 'LET') + resultGUI = rmfield(resultGUI, 'LET'); end - + % overwrite the "standard" fields sNames = fieldnames(resultGUIreCalc); for j = 1:length(sNames) resultGUI.(sNames{j}) = resultGUIreCalc.(sNames{j}); end - + % assign results to base worksapce - %assignin('base','dij',dij); - assignin('base','resultGUI',resultGUI); + % assignin('base','dij',dij); + assignin('base', 'resultGUI', resultGUI); - % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - + set(InterfaceObj, 'Enable', 'on'); + this.handles = handles; this.changedWorkspace('resultGUI'); - + catch ME % change state from busy to normal set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); + set(InterfaceObj, 'Enable', 'on'); this.handles = handles; - showError(this,'CalcDoseCallback: Error in dose recalculation!',ME); - return; - + showError(this, 'CalcDoseCallback: Error in dose recalculation!', ME); + return + end - + end - + % H80 Callback function btnSaveToGUI_Callback(this, hObject, eventdata) handles = this.handles; - + Width = 400; Height = 200; - ScreenSize = get(0,'ScreenSize'); - + ScreenSize = get(0, 'ScreenSize'); + % show "Provide result name" window - figHandles = get(0,'Children'); + figHandles = get(0, 'Children'); if ~isempty(figHandles) - IdxHandle = strcmp(get(figHandles,'Name'),'Provide result name'); + IdxHandle = strcmp(get(figHandles, 'Name'), 'Provide result name'); else IdxHandle = []; end - - %check if window is already exists + + % check if window is already exists if any(IdxHandle) figDialog = figHandles(IdxHandle); - %set focus + % set focus figure(figDialog); else - figDialog = dialog('Position',[ceil(ScreenSize(3)/2) ceil(ScreenSize(4)/2) Width Height],'Name','Provide result name','Color',[0.5 0.5 0.5]); - - uicontrol('Parent',figDialog,... - 'Style','text',... - 'Position',[20 Height - (0.35*Height) 350 60],... - 'String','Please provide a decriptive name for your optimization result:','FontSize',10,'BackgroundColor',[0.5 0.5 0.5]); - - try - pln = evalin('base','pln'); - numOfBeams = evalin('base','numel(stf)'); + figDialog = dialog('Position', [ceil(ScreenSize(3) / 2) ceil(ScreenSize(4) / 2) Width Height], 'Name', 'Provide result name', 'Color', [0.5 0.5 0.5]); + + try + pln = evalin('base', 'pln'); + numOfBeams = evalin('base', 'numel(stf)'); radMode = pln.radiationMode; fractions = pln.numOfFractions; - saveString = sprintf('%s_%dbeams_%dfrac',radMode,numOfBeams,fractions); + saveString = sprintf('%s_%dbeams_%dfrac', radMode, numOfBeams, fractions); catch - saveString = datestr(now,'mmddyyHHMM'); + saveString = datestr(now, 'mmddyyHHMM'); end - - hFocus = uicontrol('Parent',figDialog,... - 'Style','edit',... - 'Position',[30 60 350 60],... - 'String',saveString,'FontSize',10,'BackgroundColor',[0.55 0.55 0.55],... - 'Callback', @(hpb,eventdata)SaveResultToGUI(this,hpb,eventdata)); - - uicontrol('Parent', figDialog,'Style', 'pushbutton', 'String', 'Save','FontSize',10,... - 'Position', [0.42*Width 0.1 * Height 70 30],... - 'Callback', @(hpb,eventdata)SaveResultToGUI(this,hpb,eventdata)); + + hFocus = uicontrol('Parent', figDialog, ... + 'Style', 'edit', ... + 'Position', [30 60 350 60], ... + 'String', saveString, 'FontSize', 10, 'BackgroundColor', [0.55 0.55 0.55], ... + 'Callback', @(hpb, eventdata)SaveResultToGUI(this, hpb, eventdata)); + + uicontrol('Parent', figDialog, 'Style', 'pushbutton', 'String', 'Save', 'FontSize', 10, ... + 'Position', [0.42 * Width 0.1 * Height 70 30], ... + 'Callback', @(hpb, eventdata)SaveResultToGUI(this, hpb, eventdata)); uicontrol(hFocus); end - + uiwait(figDialog); this.handles = handles; end - + function SaveResultToGUI(this, ~, ~) - AllFigHandles = get(0,'Children'); - ixHandle = strcmp(get(AllFigHandles,'Name'),'Provide result name'); - uiEdit = get(AllFigHandles(ixHandle),'Children'); - - + AllFigHandles = get(0, 'Children'); + ixHandle = strcmp(get(AllFigHandles, 'Name'), 'Provide result name'); + uiEdit = get(AllFigHandles(ixHandle), 'Children'); + % delete special characters - Suffix = get(uiEdit(2),'String'); - logIx = isstrprop(Suffix,'alphanum'); + Suffix = get(uiEdit(2), 'String'); + logIx = isstrprop(Suffix, 'alphanum'); Suffix = ['_' Suffix(logIx)]; - this.savedResultTag{end+1}= Suffix; + this.savedResultTag{end + 1} = Suffix; - pln = evalin('base','pln'); - resultGUI = evalin('base','resultGUI'); - - if isfield(resultGUI,'physicalDose') + pln = evalin('base', 'pln'); + resultGUI = evalin('base', 'resultGUI'); + + if isfield(resultGUI, 'physicalDose') resultGUI.(['physicalDose' Suffix]) = resultGUI.physicalDose; end - if isfield(resultGUI,'w') + if isfield(resultGUI, 'w') resultGUI.(['w' Suffix]) = resultGUI.w; end - if isfield(resultGUI,'LET') + if isfield(resultGUI, 'LET') resultGUI.(['LET' Suffix]) = resultGUI.LET; end - - - if isfield(pln,'propOpt') && ~strcmp(pln.propOpt.quantityOpt,'none') - - if isfield(resultGUI,'RBExDose') + + if isfield(pln, 'propOpt') && ~strcmp(pln.propOpt.quantityOpt, 'none') + + if isfield(resultGUI, 'RBExDose') resultGUI.(['RBExDose' Suffix]) = resultGUI.RBExDose; end - - if strcmp(pln.radiationMode,'carbon') == 1 - if isfield(resultGUI,'effect') - resultGUI.(['effect' Suffix])= resultGUI.effect; + + if strcmp(pln.radiationMode, 'carbon') == 1 + if isfield(resultGUI, 'effect') + resultGUI.(['effect' Suffix]) = resultGUI.effect; end - - if isfield(resultGUI,'RBE') + + if isfield(resultGUI, 'RBE') resultGUI.(['RBE' Suffix]) = resultGUI.RBE; end - if isfield(resultGUI,'alpha') + if isfield(resultGUI, 'alpha') resultGUI.(['alpha' Suffix]) = resultGUI.alpha; end - if isfield(resultGUI,'beta') + if isfield(resultGUI, 'beta') resultGUI.(['beta' Suffix]) = resultGUI.beta; end end end - + close(AllFigHandles(ixHandle)); - assignin('base','resultGUI',resultGUI); - + assignin('base', 'resultGUI', resultGUI); + this.changedWorkspace('resultGUI'); - %getFromWorkspace(this); + % getFromWorkspace(this); end - + % H81 Callback function btn_export_Callback(this, hObject, eventdata) % hObject handle to btn_export (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - + try matRad_exportWidget; catch ME - showError(this,'Could not export data. Reason: ', ME); + showError(this, 'Could not export data. Reason: ', ME); end end - + % H82 Callback function importDoseButton_Callback(this, hObject, eventdata) % hObject handle to importDoseButton (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) handles = this.handles; - + extensions{1} = '*.nrrd'; - [filenames,filepath,~] = uigetfile(extensions,'MultiSelect','on'); - - %Import aborted + [filenames, filepath, ~] = uigetfile(extensions, 'MultiSelect', 'on'); + + % Import aborted if filenames == 0 - return; + return end - - %Something was selected + + % Something was selected try if ~iscell(filenames) tmp = filenames; filenames = cell(1); filenames{1} = tmp; end - - ct = evalin('base','ct'); - resultGUI = evalin('base','resultGUI'); - + + ct = evalin('base', 'ct'); + resultGUI = evalin('base', 'resultGUI'); + for filename = filenames - [~,name,~] = fileparts(filename{1}); - [cube,~] = matRad_readCube(fullfile(filepath,filename{1})); + [~, name, ~] = fileparts(filename{1}); + [cube, ~] = matRad_readCube(fullfile(filepath, filename{1})); if ~isequal(ct.cubeDim, size(cube)) - this.showError('Dimensions of the imported cube do not match with ct','Import failed!','modal'); - continue; + this.showError('Dimensions of the imported cube do not match with ct', 'Import failed!', 'modal'); + continue end - - fieldname = ['import_' matlab.lang.makeValidName(name, 'ReplacementStyle','delete')]; + + fieldname = ['import_' matlab.lang.makeValidName(name, 'ReplacementStyle', 'delete')]; resultGUI.(fieldname) = cube; end - - assignin('base','resultGUI',resultGUI); + + assignin('base', 'resultGUI', resultGUI); catch ME this.handles = handles; - showError(this,'Dose Import: Could not import data. Reason: ', ME); - return; + showError(this, 'Dose Import: Could not import data. Reason: ', ME); + return end this.handles = handles; this.changedWorkspace('resultGUI'); - %getFromWorkspace(this); + % getFromWorkspace(this); end - + % H83 Callback function pushbutton_importFromBinary_Callback(this, hObject, eventdata) % hObject handle to pushbutton_importFromBinary (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) handles = this.handles; - - try - %call the gui - h=matRad_importWidget; + + try + % call the gui + h = matRad_importWidget; uiwait(h.widgetHandle); - + this.handles = handles; this.changedWorkspace(); - catch ME + catch ME this.handles = handles; getFromWorkspace(this); - showError(this,'Binary Patient Import: Could not import data. Reason: ', ME); - return; + showError(this, 'Binary Patient Import: Could not import data. Reason: ', ME); + return end - - - %getFromWorkspace(this); + + % getFromWorkspace(this); end - + % H84 Callback function exportDicomButton_Callback(this, hObject, eventdata) % hObject handle to exportDicom (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - try + try matRad_exportDicomWidget; catch ME - showError(this,'DicomImport: Could not export data', ME); + showError(this, 'DicomImport: Could not export data', ME); end end @@ -895,10 +878,9 @@ function CheckOptimizerStatus(this, usedOptimizer,OptCase) else statusIcon = 'warn'; end - - this.showMessage(sprintf('Optimizer finished with status %d (%s)',statusflag,statusmsg),'Optimization finished!',statusIcon,'modal'); + + this.showMessage(sprintf('Optimizer finished with status %d (%s)', statusflag, statusmsg), 'Optimization finished!', statusIcon, 'modal'); end + end end - - diff --git a/matRad/matRad_directApertureOptimization.m b/matRad/matRad_directApertureOptimization.m index dc52bbd5c..65edaae07 100644 --- a/matRad/matRad_directApertureOptimization.m +++ b/matRad/matRad_directApertureOptimization.m @@ -1,4 +1,4 @@ -function [optResult,optimizer] = matRad_directApertureOptimization(dij,cst,apertureInfo,optResult,pln) +function [resultGUI, optimizer] = matRad_directApertureOptimization(dij, cst, apertureInfo, pln) % matRad function to run direct aperture optimization % % call: @@ -10,7 +10,7 @@ % apertureInfo: aperture shape info struct % optResult: resultGUI struct to which the output data will be added, if % this field is empty optResult struct will be created -% +% % pln: matRad pln struct % % output: @@ -36,43 +36,41 @@ matRad_cfg = MatRad_Config.instance(); - % adjust overlap priorities cst = matRad_setOverlapPriorities(cst); -% check & adjust objectives and constraints internally for fractionation -for i = 1:size(cst,1) - for j = 1:numel(cst{i,6}) - obj = cst{i,6}{j}; - - %In case it is a default saved struct, convert to object - %Also intrinsically checks that we have a valid optimization - %objective or constraint function in the end - if ~isa(obj,'matRad_DoseOptimizationFunction') +% check & adjust objectives and constraints internally for fractionation +for i = 1:size(cst, 1) + for j = 1:numel(cst{i, 6}) + obj = cst{i, 6}{j}; + + % In case it is a default saved struct, convert to object + % Also intrinsically checks that we have a valid optimization + % objective or constraint function in the end + if ~isa(obj, 'matRad_DoseOptimizationFunction') try obj = matRad_DoseOptimizationFunction.createInstanceFromStruct(obj); catch - matRad_cfg.dispError('cst{%d,6}{%d} is not a valid Objective/constraint! Remove or Replace and try again!',i,j); + matRad_cfg.dispError('cst{%d,6}{%d} is not a valid Objective/constraint! Remove or Replace and try again!', i, j); end end - - obj = obj.setDoseParameters(obj.getDoseParameters()/pln.numOfFractions); - - cst{i,6}{j} = obj; + + obj = obj.setDoseParameters(obj.getDoseParameters() / pln.numOfFractions); + + cst{i, 6}{j} = obj; end end -% resizing cst to dose cube resolution -cst = matRad_resizeCstToGrid(cst,dij.ctGrid.x,dij.ctGrid.y,dij.ctGrid.z,... - dij.doseGrid.x,dij.doseGrid.y,dij.doseGrid.z); - +% resizing cst to dose cube resolution +cst = matRad_resizeCstToGrid(cst, dij.ctGrid.x, dij.ctGrid.y, dij.ctGrid.z, ... + dij.doseGrid.x, dij.doseGrid.y, dij.doseGrid.z); -if ~isfield(pln,'bioModel') +if ~isfield(pln, 'bioModel') pln.bioModel = 'none'; end -if ~isa(pln.bioModel,'matRad_BiologicalModel') - pln.bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode); +if ~isa(pln.bioModel, 'matRad_BiologicalModel') + pln.bioModel = matRad_BiologicalModel.validate(pln.bioModel, pln.radiationMode); end % set optimization options @@ -83,14 +81,14 @@ options.model = pln.bioModel.model; % update aperture info vector -apertureInfo = matRad_OptimizationProblemDAO.matRad_daoVec2ApertureInfo(apertureInfo,apertureInfo.apertureVector); +apertureInfo = matRad_OptimizationProblemDAO.matRad_daoVec2ApertureInfo(apertureInfo, apertureInfo.apertureVector); -%Use Dose Projection only +% Use Dose Projection only backProjection = matRad_DoseProjection(); -optiProb = matRad_OptimizationProblemDAO(backProjection,apertureInfo); +optiProb = matRad_OptimizationProblemDAO(backProjection, apertureInfo); -if ~isfield(pln.propOpt,'optimizer') +if ~isfield(pln.propOpt, 'optimizer') pln.propOpt.optimizer = 'IPOPT'; end @@ -100,21 +98,20 @@ case 'fmincon' optimizer = matRad_OptimizerFmincon; otherwise - matRad_cfg.dispWarning('Optimizer ''%s'' not known! Fallback to IPOPT!',pln.propOpt.optimizer); + matRad_cfg.dispWarning('Optimizer ''%s'' not known! Fallback to IPOPT!', pln.propOpt.optimizer); optimizer = matRad_OptimizerIPOPT; end % Run IPOPT. -optimizer = optimizer.optimize(apertureInfo.apertureVector,optiProb,dij,cst); +optimizer = optimizer.optimize(apertureInfo.apertureVector, optiProb, dij, cst); wOpt = optimizer.wResult; % update the apertureInfoStruct and calculate bixel weights -apertureInfo = matRad_OptimizationProblemDAO.matRad_daoVec2ApertureInfo(apertureInfo,wOpt); +apertureInfo = matRad_OptimizationProblemDAO.matRad_daoVec2ApertureInfo(apertureInfo, wOpt); % logging final results matRad_cfg.dispInfo('Calculating final cubes...\n'); -resultGUI = matRad_calcCubes(apertureInfo.bixelWeights,dij); +resultGUI = matRad_calcCubes(apertureInfo.bixelWeights, dij); resultGUI.w = apertureInfo.bixelWeights; resultGUI.wDAO = apertureInfo.bixelWeights; -resultGUI.apertureInfo = apertureInfo; - +resultGUI.sequencing.apertureInfo = apertureInfo; diff --git a/matRad/matRad_sequencing.m b/matRad/matRad_sequencing.m index 37f1274cc..1d51023d9 100644 --- a/matRad/matRad_sequencing.m +++ b/matRad/matRad_sequencing.m @@ -1,4 +1,4 @@ -function resultGUI = matRad_sequencing(resultGUI,stf,dij,pln,visBool) +function resultGUI = matRad_sequencing(resultGUI, stf, pln, dij, visMode) % matRad inverse planning wrapper function % % call: @@ -30,42 +30,25 @@ % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - matRad_cfg = MatRad_Config.instance(); -if nargin < 5 - visBool = 0; -end - -if ~isfield(pln,'propSeq') - pln.propSeq = struct('runSequencing',false); -end +sequencer = matRad_SequencerBase.getSequencerFromPln(pln); -if strcmp(pln.radiationMode,'photons') && (pln.propSeq.runSequencing || pln.propOpt.runDAO) - - if ~isfield(pln.propSeq, 'sequencer') - pln.propSeq.sequencer = 'siochi'; % default: siochi sequencing algorithm - matRad_cfg.dispWarning ('pln.propSeq.sequencer not specified. Using siochi leaf sequencing (default).') - end - - if ~isfield(pln.propSeq, 'sequencingLevel') - pln.propSeq.sequencingLevel = 5; - matRad_cfg.dispWarning ('pln.propSeq.sequencingLevel not specified. Using 5 sequencing levels (default).') - end - - switch pln.propSeq.sequencer - case 'xia' - resultGUI = matRad_xiaLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); - case 'engel' - resultGUI = matRad_engelLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); - case 'siochi' - resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); - otherwise - matRad_cfg.dispError('Could not find specified sequencing algorithm ''%s''',pln.propSeq.sequencer); - end -elseif (pln.propSeq.runSequencing || pln.propOpt.runDAO) && ~strcmp(pln.radiationMode,'photons') - matRad_cfg.dispWarning('Sequencing is only specified for pln.radiationMode = "photons". Continuing with out sequencing ... ') +% Handle optional inputs +if nargin == 5 && ~isempty(visMode) + sequencer.visMode = visMode; end +if nargin < 4 || isempty(dij) + dij = []; end +sequence = sequencer.sequence(resultGUI.w, stf); +if ~isempty(dij) + resultGUI = matRad_calcCubes(sequence.w, dij); +else + matRad_cfg.dispWarning('Dose not recalcaulted with sequenced fluence'); +end +resultGUI.sequencing = sequence; + +end diff --git a/matRad/scenarios/matRad_ScenarioModel.m b/matRad/scenarios/matRad_ScenarioModel.m index d96376a4a..64fb17b78 100644 --- a/matRad/scenarios/matRad_ScenarioModel.m +++ b/matRad/scenarios/matRad_ScenarioModel.m @@ -48,6 +48,7 @@ numOfCtScen % total number of CT scenarios used numOfAvailableCtScen % total number of CT scenarios existing in ct structure ctScenIx % map of all ct scenario indices per scenario + motionPeriod = Inf % motion period of 4D CT, if 4D CT % these parameters will be filled according to the chosen scenario type isoShift @@ -77,6 +78,9 @@ else this.numOfCtScen = ct.numOfCtScen; this.numOfAvailableCtScen = ct.numOfCtScen; + if isfield(ct, 'motionPeriod') + this.motionPeriod = ct.motionPeriod; + end end % Equal probability to be in each phase of the 4D ct diff --git a/matRad/sequencing/matRad_ParticleSequencer.m b/matRad/sequencing/matRad_ParticleSequencer.m new file mode 100644 index 000000000..a66963953 --- /dev/null +++ b/matRad/sequencing/matRad_ParticleSequencer.m @@ -0,0 +1,259 @@ +classdef matRad_ParticleSequencer < matRad_SequencerBase + % UNTITLED2 Summary of this class goes here + % Detailed explanation goes here + + properties (Constant) + name = 'Particle IMPT Scanning Sequencing' + shortName = 'IMPT' + possibleRadiationModes = {'protons', 'helium', 'carbon'} + weightPencilBeam = 1e6 + end + + properties + esTime = 3 * 10^6 % [\mu s] time required for synchrotron to recharge it' spill + spillRechargeTime = 2 * 10^6 % [\mu s] number of particles generated in each spill + spillSize = 4 * 10^10 + scanSpeed = 10 % [m/s] speed of synchrotron's lateral scanning in an IES + spillIntensity = 4 * 10^8 % number of particles per second + end + + methods + + function sequence = sequence(this, w, stf) + + sequence = this.calcSpotOrder(stf); + sequence = this.calcSpotTime(sequence, w, stf); + end + + function sequence = calcSpotOrder(~, stf) + + sequence = struct; + + wOffset = 0; + % first loop loops over all bixels to store their position and ray number in each IES + for i = 1:length(stf) + + usedEnergies = unique([stf(i).ray(:).energy]); + usedEnergiesSorted = sort(usedEnergies, 'descend'); + + sequence(i).orderToSTF = zeros(stf(i).totalNumOfBixels, 1); + sequence(i).orderToSS = zeros(stf(i).totalNumOfBixels, 1); + sequence(i).time = zeros(stf(i).totalNumOfBixels, 1); + sequence(i).e = zeros(stf(i).totalNumOfBixels, 1); + + for e = 1:length(usedEnergies) % looping over IES's + + s = 1; + + for j = 1:stf(i).numOfRays % looping over all rays + + % find the rays which are active in current IES + if any(stf(i).ray(j).energy == usedEnergiesSorted(e)) + + x = stf(i).ray(j).rayPos_bev(1); + y = stf(i).ray(j).rayPos_bev(3); + + sequence(i).IES(e).x(s) = x; % store x position + sequence(i).IES(e).y(s) = y; % store y position + sequence(i).IES(e).wIndex(s) = wOffset + ... + sum(stf(i).numOfBixelsPerRay(1:(j - 1))) + ... + find(stf(i).ray(j).energy == usedEnergiesSorted(e)); % store index + + s = s + 1; + + end + end + end + + wOffset = wOffset + sum(stf(i).numOfBixelsPerRay); + + end + + end + + function sequence = calcSpotTime(this, sequence, w, stf) + steerTime = [stf.bixelWidth] * (10^3) / this.scanSpeed; % [\mu s] + % after storing all the required information, + % same loop over all bixels will put each bixel in it's order + + spillUsage = 0; + offset = 0; + + for i = 1:length(stf) + + usedEnergies = unique([stf(i).ray(:).energy]); + + t = 0; + orderCount = 1; + + for e = 1:length(usedEnergies) + + % sort the y positions from high to low (backforth is up do down) + y_sorted = sort(unique(sequence(i).IES(e).y), 'descend'); + x_sorted = sort(sequence(i).IES(e).x, 'ascend'); + + for k = 1:length(y_sorted) + + y = y_sorted(k); + % find indexes corresponding to current y position + % in other words, number of bixels in the current row + ind_y = find(sequence(i).IES(e).y == y); + + % since backforth fasion is zig zag like, flip the order every + % second row + if ~rem(k, 2) + ind_y = fliplr(ind_y); + end + + % loop over all the bixels in the row + for is = 1:length(ind_y) + + s = ind_y(is); + + x = x_sorted(s); + + wIndex = sequence(i).IES(e).wIndex(s); + + % in case there were holes inside the plan "multi" + % multiplies the steertime to take it into account: + if k == 1 && is == 1 + x_prev = x; + y_prev = y; + end + % x direction + multi = abs(x_prev - x) / stf(i).bixelWidth; + % y direction + multi = multi + abs(y_prev - y) / stf(i).bixelWidth; + % + x_prev = x; + y_prev = y; + + % calculating the time: + + % required spot fluence + numOfParticles = w(wIndex) * this.weightPencilBeam; + % time spent to spill the required spot fluence + spillTime = numOfParticles * 10^6 / this.spillIntensity; + + % spotTime:time spent to steer scan along IES per bixel + t = t + multi * steerTime(i) + spillTime; + + % taking account of the time to recharge the spill in case + % the required fluence was more than spill size + if spillUsage + numOfParticles > this.spillSize + t = t + this.spillRechargeTime; + spillUsage = 0; + end + + % used amount of fluence from current spill + spillUsage = spillUsage + numOfParticles; + + % storing the time and the order of bixels + + % make the both counter and index 'per beam' - help index + wInd = wIndex - offset; + + % timeline according to the spot scanning order + sequence(i).time(orderCount) = t; + % IES of bixels according to the spot scanning order + sequence(i).e(orderCount) = e; + % according to spot scanning order, sorts w index of all + % bixels, use this order to transfer STF order to Spot + % Scanning order + sequence(i).orderToSS(orderCount) = wInd; + + % according to STF order, gives us order of irradiation of + % each bixel, use this order to transfer Spot Scanning + % order to STF order + % orderToSTF(orderToSS) = orderToSS(orderToSTF) = 1:#bixels + sequence(i).orderToSTF(wInd) = orderCount; + + orderCount = orderCount + 1; + + end + end + + t = t + this.esTime; + + end + + % storing the fluence per beam + sequence(i).w = w(offset + 1:offset + stf(i).totalNumOfBixels); + + offset = offset + stf(i).totalNumOfBixels; + end + end + + end + + methods (Static) + + function [available, msg] = isAvailable(pln, machine) + % see superclass for information + + if nargin < 2 + machine = matRad_loadMachine(pln); + end + % checkBasic + available = isfield(machine, 'meta') && isfield(machine, 'data'); + + available = available && any(isfield(machine.meta, {'machine', 'radiationMode'})); + + if ~available + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + else + msg = []; + end + + % check modality + checkModality = any(strcmp(matRad_ParticleSequencer.possibleRadiationModes, machine.meta.radiationMode)) && any(strcmp(matRad_ParticleSequencer.possibleRadiationModes, pln.radiationMode)); + + % Sanity check compatibility + if checkModality + checkModality = strcmp(machine.meta.radiationMode, pln.radiationMode); + end + + available = available && checkModality; + + end + + function sequence = makePhaseMatrix(sequence, numOfPhases, motionPeriod) + + phaseTime = motionPeriod * 10^6 / numOfPhases; % time of each phase [/mu s] + + for i = 1:length(sequence) + + realTime = phaseTime; + sequence(i).phaseMatrix = zeros(length(sequence(i).time), numOfPhases); + + iPhase = 1; + iTime = 1; + + while iTime <= length(sequence(i).time) + if sequence(i).time(iTime) < realTime + while iTime <= length(sequence(i).time) && sequence(i).time(iTime) < realTime + sequence(i).phaseMatrix(iTime, iPhase) = 1; + iTime = iTime + 1; + end + else + + iPhase = iPhase + 1; + % back to 1 after going over all phases + if iPhase > numOfPhases + iPhase = 1; + end + realTime = realTime + phaseTime; + end + end + + % permuatation of phaseMatrix from SS order to STF order + sequence(i).phaseMatrix = sequence(i).phaseMatrix(sequence(i).orderToSTF, :); + sequence(i).phaseNum = find(sequence(i).phaseMatrix'); + % inserting the fluence in phaseMatrix + sequence(i).phaseMatrix = sequence(i).phaseMatrix .* sequence(i).w; + end + end + + end + +end diff --git a/matRad/sequencing/matRad_PhotonSequencerAbstract.m b/matRad/sequencing/matRad_PhotonSequencerAbstract.m new file mode 100644 index 000000000..4cb67c955 --- /dev/null +++ b/matRad/sequencing/matRad_PhotonSequencerAbstract.m @@ -0,0 +1,414 @@ +classdef (Abstract) matRad_PhotonSequencerAbstract < matRad_SequencerBase + + % UNTITLED Summary of this class goes here + % Detailed explanation goes her + properties + numOfMLCLeafPairs = 80 + sequencingLevel = 5 + end + + methods + + function sequence = sequence(this, w, stf) + + throw(MException('MATLAB:class:AbstractMember', 'Abstract function sequence needs to be implemented!')); + end + + function [D_0, D_k, shapes, calFac, indInMx] = initBeam(this, stf, wCurr) + + numOfRaysPerBeam = size(stf.ray, 2); + X = ones(numOfRaysPerBeam, 1) * NaN; + Z = ones(numOfRaysPerBeam, 1) * NaN; + + for j = 1:numOfRaysPerBeam + X(j) = stf.ray(j).rayPos_bev(:, 1); + Z(j) = stf.ray(j).rayPos_bev(:, 3); + end + + % sort bixels into matrix + minX = min(X); + maxX = max(X); + minZ = min(Z); + maxZ = max(Z); + + dimOfFluenceMxX = (maxX - minX) / stf.bixelWidth + 1; + dimOfFluenceMxZ = (maxZ - minZ) / stf.bixelWidth + 1; + + % Create the fluence matrix. + fluenceMx = zeros(dimOfFluenceMxZ, dimOfFluenceMxX); + + % Calculate X and Z position of every fluence's matrix spot z axis = + % axis of leaf movement! + xPos = (X - minX) / stf.bixelWidth + 1; + zPos = (Z - minZ) / stf.bixelWidth + 1; + + % Make subscripts for fluence matrix + indInMx = zPos + (xPos - 1) * dimOfFluenceMxZ; + + % Save weights in fluence matrix. + fluenceMx(indInMx) = wCurr .* ones(numOfRaysPerBeam, 1); + + % Stratification + calFac = max(fluenceMx(:)); + D_k = round(fluenceMx / calFac * this.sequencingLevel); + + % Save the stratification in the initial intensity matrix D_0. + D_0 = D_k; + + % container to remember generated shapes; allocate space for 10000 shapes + shapes = NaN * ones(dimOfFluenceMxZ, dimOfFluenceMxX, 10000); + + end + + function sequence = sequencing2ApertureInfo(this, sequence, stf) + % MLC parameters: + bixelWidth = stf(1).bixelWidth; % [mm] + % define central leaf pair (here we want the 0mm position to be in the + % center of a leaf pair (e.g. leaf 41 stretches from -2.5mm to 2.5mm + % for a bixel/leafWidth of 5mm and 81 leaf pairs) + centralLeafPair = ceil(this.numOfMLCLeafPairs / 2); + + % initializing variables + bixelIndOffset = 0; % used for creation of bixel index maps + totalNumOfBixels = sum([stf(:).totalNumOfBixels]); + totalNumOfShapes = sum([sequence.beam.numOfShapes]); + vectorOffset = totalNumOfShapes + 1; % used for bookkeeping in the vector for optimization + + % loop over all beams + for i = 1:size(stf, 2) + + %% 1. read stf and create maps (Ray & Bixelindex) + + % get x- and z-coordinates of bixels + rayPos_bev = reshape([stf(i).ray.rayPos_bev], 3, []); + X = rayPos_bev(1, :)'; + Z = rayPos_bev(3, :)'; + + % create ray-map + maxX = max(X); + minX = min(X); + maxZ = max(Z); + minZ = min(Z); + + dimX = (maxX - minX) / stf(i).bixelWidth + 1; + dimZ = (maxZ - minZ) / stf(i).bixelWidth + 1; + + rayMap = zeros(dimZ, dimX); + + % get indices for x and z positions + xPos = (X - minX) / stf(i).bixelWidth + 1; + zPos = (Z - minZ) / stf(i).bixelWidth + 1; + + % get indices in the ray-map + indInRay = zPos + (xPos - 1) * dimZ; + + % fill ray-map + rayMap(indInRay) = 1; + + % create map of bixel indices + bixelIndMap = NaN * ones(dimZ, dimX); + bixelIndMap(indInRay) = [1:stf(i).numOfRays] + bixelIndOffset; + bixelIndOffset = bixelIndOffset + stf(i).numOfRays; + + % store physical position of first entry in bixelIndMap + posOfCornerBixel = [minX 0 minZ]; + + % get leaf limits from the leaf map + lim_l = NaN * ones(dimZ, 1); + lim_r = NaN * ones(dimZ, 1); + % looping oder leaf pairs + for l = 1:dimZ + lim_lInd = find(rayMap(l, :), 1, 'first'); + lim_rInd = find(rayMap(l, :), 1, 'last'); + % the physical position [mm] can be calculated from the indices + lim_l(l) = (lim_lInd - 1) * bixelWidth + minX - 1 / 2 * bixelWidth; + lim_r(l) = (lim_rInd - 1) * bixelWidth + minX + 1 / 2 * bixelWidth; + end + + % get leaf positions for all shapes + % leaf positions can be extracted from the shapes created in Sequencing + for m = 1:sequence.beam(i).numOfShapes + + % loading shape from Sequencing result + shapeMap = sequence.beam(i).shapes(:, :, m); + % get left and right leaf indices from shapemap + % initializing limits + leftLeafPos = NaN * ones(dimZ, 1); + rightLeafPos = NaN * ones(dimZ, 1); + % looping over leaf pairs + for l = 1:dimZ + leftLeafPosInd = find(shapeMap(l, :), 1, 'first'); + rightLeafPosInd = find(shapeMap(l, :), 1, 'last'); + + if isempty(leftLeafPosInd) && isempty(rightLeafPosInd) % if no bixel is open, use limits from Ray positions + leftLeafPos(l) = (lim_l(l) + lim_r(l)) / 2; + rightLeafPos(l) = leftLeafPos(l); + else + % the physical position [mm] can be calculated from the indices + leftLeafPos(l) = (leftLeafPosInd - 1) * bixelWidth ... + + minX - 1 / 2 * bixelWidth; + rightLeafPos(l) = (rightLeafPosInd - 1) * bixelWidth ... + + minX + 1 / 2 * bixelWidth; + + end + end + + % save data for each shape of this beam + sequence.apertureInfo.beam(i).shape(m).leftLeafPos = leftLeafPos; + sequence.apertureInfo.beam(i).shape(m).rightLeafPos = rightLeafPos; + sequence.apertureInfo.beam(i).shape(m).weight = sequence.beam(i).shapesWeight(m); + sequence.apertureInfo.beam(i).shape(m).shapeMap = shapeMap; + sequence.apertureInfo.beam(i).shape(m).vectorOffset = vectorOffset; + + % update index for bookkeeping + vectorOffset = vectorOffset + dimZ; + + end + + % z-coordinates of active leaf pairs + % get z-coordinates from bixel positions + leafPairPos = unique(Z); + + % find upmost and downmost leaf pair + topLeafPairPos = maxZ; + bottomLeafPairPos = minZ; + + topLeafPair = centralLeafPair - topLeafPairPos / bixelWidth; + bottomLeafPair = centralLeafPair - bottomLeafPairPos / bixelWidth; + + % create bool map of active leaf pairs + isActiveLeafPair = zeros(this.numOfMLCLeafPairs, 1); + isActiveLeafPair(topLeafPair:bottomLeafPair) = 1; + + % create MLC window + % getting the dimensions of the MLC in order to be able to plot the + % shapes using physical coordinates + MLCWindow = [minX - bixelWidth / 2 maxX + bixelWidth / 2 ... + minZ - bixelWidth / 2 maxZ + bixelWidth / 2]; + + % save data for each beam + sequence.apertureInfo.beam(i).numOfShapes = sequence.beam(i).numOfShapes; + sequence.apertureInfo.beam(i).numOfActiveLeafPairs = dimZ; + sequence.apertureInfo.beam(i).leafPairPos = leafPairPos; + sequence.apertureInfo.beam(i).isActiveLeafPair = isActiveLeafPair; + sequence.apertureInfo.beam(i).centralLeafPair = centralLeafPair; + sequence.apertureInfo.beam(i).lim_l = lim_l; + sequence.apertureInfo.beam(i).lim_r = lim_r; + sequence.apertureInfo.beam(i).bixelIndMap = bixelIndMap; + sequence.apertureInfo.beam(i).posOfCornerBixel = posOfCornerBixel; + sequence.apertureInfo.beam(i).MLCWindow = MLCWindow; + + end + + % save global data + sequence.apertureInfo.bixelWidth = bixelWidth; + sequence.apertureInfo.numOfMLCLeafPairs = this.numOfMLCLeafPairs; + sequence.apertureInfo.totalNumOfBixels = totalNumOfBixels; + sequence.apertureInfo.totalNumOfShapes = sum([sequence.apertureInfo.beam.numOfShapes]); + sequence.apertureInfo.totalNumOfLeafPairs = sum([sequence.apertureInfo.beam.numOfShapes] * [sequence.apertureInfo.beam.numOfActiveLeafPairs]'); + + % create vectors for optimization + [sequence.apertureInfo.apertureVector, sequence.apertureInfo.mappingMx, sequence.apertureInfo.limMx] = matRad_OptimizationProblemDAO.matRad_daoApertureInfo2Vec(sequence.apertureInfo); + end + + function plotSegments(this, sequencing) + % create the sequencing figure + sz = [800 1000]; % figure size + screensize = get(0, 'ScreenSize'); + xpos = ceil((screensize(3) - sz(2)) / 2); % center the figure on the screen horizontally + ypos = ceil((screensize(4) - sz(1)) / 2); % center the figure on the screen vertically + seqFig = figure('position', [xpos, ypos, sz(2), sz(1)]); + + for i = 1:numel(sequencing) + + D_0 = sequencing.beam(i).fluence; + + clf(seqFig); + colormap(seqFig, 'jet'); + + seqSubPlots(1) = subplot(2, 2, 1, 'parent', seqFig); + imagesc(sequencing.beam(i).fluence, 'parent', seqSubPlots(1)); + set(seqSubPlots(1), 'CLim', [0 this.sequencingLevel], 'YDir', 'normal'); + title(seqSubPlots(1), ['Beam # ' num2str(i) ': max(D_0) = ' num2str(max(D_0(:))) ' - ' num2str(numel(unique(D_0))) ' intensity levels']); + xlabel(seqSubPlots(1), 'x - direction parallel to leaf motion '); + ylabel(seqSubPlots(1), 'z - direction perpendicular to leaf motion '); + colorbar; + drawnow; + + % show the leaf positions + D_k = sequencing.beam(i).fluence; + for k = 1:sequencing.beam(i).numOfShapes + shape_k = sequencing.beam(i).shapes(:, :, k); + [dimZ, dimX] = size(sequencing.beam(i).fluence); + seqSubPlots(4) = subplot(2, 2, 3.5, 'parent', seqFig); + imagesc(shape_k, 'parent', seqSubPlots(4)); + hold(seqSubPlots(4), 'on'); + set(seqSubPlots(4), 'YDir', 'normal'); + xlabel(seqSubPlots(4), 'x - direction parallel to leaf motion '); + ylabel(seqSubPlots(4), 'z - direction perpendicular to leaf motion '); + title(seqSubPlots(4), ['beam # ' num2str(i) ' shape # ' num2str(k) ' d_k = ' num2str(sequencing.beam(i).shapesWeight(k))]); + for j = 1:dimZ + leftLeafIx = find(shape_k(j, :) > 0, 1, 'first'); + rightLeafIx = find(shape_k(j, :) > 0, 1, 'last'); + if leftLeafIx > 1 + plot(seqSubPlots(4), [.5 leftLeafIx - .5], j - [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), [.5 leftLeafIx - .5], j + [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), [leftLeafIx - .5 leftLeafIx - .5], j + [.5 -.5], 'w', 'LineWidth', 2); + end + if rightLeafIx < dimX + plot(seqSubPlots(4), [dimX + .5 rightLeafIx + .5], j - [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), [dimX + .5 rightLeafIx + .5], j + [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), [rightLeafIx + .5 rightLeafIx + .5], j + [.5 -.5], 'w', 'LineWidth', 2); + end + if isempty(rightLeafIx) && isempty (leftLeafIx) + plot(seqSubPlots(4), [dimX + .5 .5], j - [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), [dimX + .5 .5], j + [.5 .5], 'w', 'LineWidth', 2); + plot(seqSubPlots(4), .5 * dimX * [1 1] + [0.5], j + [.5 -.5], 'w', 'LineWidth', 2); + end + end + pause(1); + + % Plot residual intensity matrix. + D_k = D_k - shape_k; % residual intensity matrix for visualization + seqSubPlots(2) = subplot(2, 2, 2, 'parent', seqFig); + imagesc(D_k, 'parent', seqSubPlots(2)); + set(seqSubPlots(2), 'CLim', [0 this.sequencingLevel], 'YDir', 'normal'); + title(seqSubPlots(2), ['k = ' num2str(k)]); + colorbar; + drawnow; + + axis tight; + drawnow; + end + + end + end + + end + methods (Static) + + function [available, msg] = isAvailable(pln, machine) + + if nargin < 2 + machine = matRad_loadMachine(pln); + end + % checkBasic + available = isfield(machine, 'meta') && isfield(machine, 'data'); + + available = available && any(isfield(machine.meta, {'machine', 'name'})); + + if ~available + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + else + msg = []; + end + end + + function [pln, stf] = aperture2collimation(pln, stf, sequence, visBool) + + if nargin < 4 + visBool = false; + end + + bixelWidth = sequence.apertureInfo.bixelWidth; + leafWidth = bixelWidth; + convResolution = 0.5; % [mm] + + % The collimator limits are infered here from the apertureInfo. This could + % be handled differently by explicitly storing collimator info in the base + % data? + symmetricMLClimits = vertcat(sequence.apertureInfo.beam.MLCWindow); + symmetricMLClimits = max(abs(symmetricMLClimits)); + fieldWidth = 2 * max(symmetricMLClimits); + + % modify basic pln variables + pln.propStf.bixelWidth = 'field'; + pln.propStf.collimation.convResolution = 0.5; % [mm] + pln.propStf.collimation.fieldWidth = fieldWidth; + pln.propStf.collimation.leafWidth = leafWidth; + + % + % [bixelFieldX,bixelFieldY] = ndgrid(-fieldWidth/2:bixelWidth:fieldWidth/2,-fieldWidth/2:leafWidth:fieldWidth/2); + [convFieldX, convFieldY] = meshgrid(-fieldWidth / 2:convResolution:fieldWidth / 2); + + % TODO: Not used in calcPhotonDose but imported from DICOM + % pln.propStf.collimation.Devices ... + % pln.propStf.collimation.numOfFields + % pln.propStf.collimation.beamMeterset + + for iBeam = 1:numel(stf) + stfTmp = stf(iBeam); + beamSequencing = sequence.beam(iBeam); + beamAperture = sequence.apertureInfo.beam(iBeam); + + stfTmp.bixelWidth = 'field'; + + nShapes = beamSequencing.numOfShapes; + + stfTmp.numOfRays = 1; % + stfTmp.numOfBixelsPerRay = nShapes; + stfTmp.totalNumOfBixels = nShapes; + + ray = struct(); + ray.rayPos_bev = [0 0 0]; + ray.targetPoint_bev = [0 stfTmp.SAD 0]; + ray.weight = 1; + ray.energy = stfTmp.ray(1).energy; + ray.beamletCornersAtIso = stfTmp.ray(1).beamletCornersAtIso; + ray.rayCorners_SCD = stfTmp.ray(1).rayCorners_SCD; + + % ray.shape = beamSequencing.sum; + shapeTotalF = zeros(size(convFieldX)); + + ray.shapes = struct(); + for iShape = 1:nShapes + currShape = beamAperture.shape(iShape); + activeLeafPairPosY = beamAperture.leafPairPos; + F = zeros(size(convFieldX)); + if visBool + hF = figure; + imagesc(F); + title(sprintf('Beam %d, Shape %d', iBeam, iShape)); + hold on; + end + for iLeafPair = 1:numel(activeLeafPairPosY) + posY = activeLeafPairPosY(iLeafPair); + ixY = convFieldY >= posY - leafWidth / 2 & convFieldY < posY + leafWidth / 2; + ixX = convFieldX >= currShape.leftLeafPos(iLeafPair) & convFieldX < currShape.rightLeafPos(iLeafPair); + ix = ixX & ixY; + F(ix) = 1; + if visBool + figure(hF); + imagesc(F); + drawnow; + pause(0.1); + end + end + + if visBool + pause(1); + close(hF); + end + + F = F * currShape.weight; + shapeTotalF = shapeTotalF + F; + + ray.shapes(iShape).convFluence = F; + ray.shapes(iShape).shapeMap = currShape.shapeMap; + ray.shapes(iShape).weight = currShape.weight; + ray.shapes(iShape).leftLeafPos = currShape.leftLeafPos; + ray.shapes(iShape).rightLeafPos = currShape.rightLeafPos; + ray.shapes(iShape).leafPairCenterPos = activeLeafPairPosY; + end + + ray.shape = shapeTotalF; + ray.weight = ones(1, nShapes); + ray.collimation = pln.propStf.collimation; + stfTmp.ray = ray; + + stf(iBeam) = stfTmp; + end + end + + end +end diff --git a/matRad/sequencing/matRad_SequencerBase.m b/matRad/sequencing/matRad_SequencerBase.m new file mode 100644 index 000000000..07ee44d08 --- /dev/null +++ b/matRad/sequencing/matRad_SequencerBase.m @@ -0,0 +1,315 @@ +classdef (Abstract) matRad_SequencerBase < handle + % UNTITLED2 Summary of this class goes here + % Detailed explanation goes here + + properties (Constant) + isSequencer = true % const boolean for inheritance quick check + end + + properties (Constant, Abstract) + name % Descriptive Name + shortName % Short name for referencing + possibleRadiationModes % Possible radiation modes for the respective Sequencer + end + + properties (Access = public) + radiationMode % Radiation Mode + visMode = 0 % vis bool + end + + methods + + function this = matRad_SequencerBase(pln) + % Constructs standalone sequencer with or without pln + + this.setDefaults(); + if nargin == 1 && ~isempty(pln) + this.assignPropertiesFromPln(pln); + end + + end + + function setDefaults(this) + % set default values from MatRad_Config + + matRad_cfg = MatRad_Config.instance(); + defaultPropSeq = matRad_cfg.defaults.propSeq; + fields = fieldnames(defaultPropSeq); + for i = 1:numel(fields) + fName = fields{i}; + if matRad_ispropCompat(this, fName) + try + this.(fName) = defaultPropSeq.(fName); + catch + matRad_cfg.dispWarning('Could not assign default property %s', fName); + end + end + end + end + + function warnDeprecatedProperty(this, oldProp, msg, newProp) + matRad_cfg = MatRad_Config.instance(); + if nargin < 3 || isempty(msg) + msg = ''; + end + + if nargin < 4 + dep2 = ''; + else + dep2 = sprintf('Use Property ''%s'' instead!', newProp); + end + + matRad_cfg.dispDeprecationWarning('Property ''%s'' of sequencer ''%s'' is deprecated! %s%s', oldProp, this.name, msg, dep2); + end + + function assignPropertiesFromPln(this, pln, warnWhenPropertyChanged) + % Assign properties from pln.propSeq to the sequencer + + matRad_cfg = MatRad_Config.instance(); + + % Must haves in pln struct + % Set/validate radiation Mode + if ~isfield(pln, 'radiationMode') && isempty(this.radiationMode) + matRad_cfg.dispError('No radiation mode specified in pln struct!'); + else + this.radiationMode = pln.radiationMode; + end + + if nargin < 3 || ~isscalar(warnWhenPropertyChanged) || ~islogical(warnWhenPropertyChanged) + warnWhenPropertyChanged = false; + end + + % Overwrite default properties within the sequencer with the + % ones given in the propSeq struct + if isfield(pln, 'propSeq') && isstruct(pln.propSeq) + plnStruct = pln.propSeq; % get remaining fields + if isfield(plnStruct, 'sequencer') && ~isempty(plnStruct.sequencer) && ~any(strcmp(plnStruct.sequencer, this.shortName)) + matRad_cfg.dispWarning('Inconsistent sequencers given! pln asks for ''%s'', but you are using ''%s''!', plnStruct.sequencer, this.shortName); + end + if isfield(plnStruct, 'sequencer') + plnStruct = rmfield(plnStruct, 'sequencer'); % sequencer field is no longer needed and would throw an exception + end + else + plnStruct = struct(); + end + + fields = fieldnames(plnStruct); + + % Set up warning message + if warnWhenPropertyChanged + warningMsg = 'Property in sequencer overwritten from pln.propSeq'; + else + warningMsg = ''; + end + + % iterate over all fieldnames and try to set the + % corresponding properties inside the sequencer + if matRad_cfg.isOctave + c2sWarningState = warning('off', 'Octave:classdef-to-struct'); + end + + for i = 1:length(fields) + try + field = fields{i}; + if matRad_ispropCompat(this, field) + this.(field) = matRad_recursiveFieldAssignment(this.(field), plnStruct.(field), true, warningMsg); + else + matRad_cfg.dispWarning('Not able to assign property ''%s'' from pln.propSeq to sequencer', field); + end + catch ME + % catch exceptions when the sequencing has no + % properties which are defined in the struct. + % When defining an engine with custom setter and getter + % methods, custom exceptions can be caught here. Be + % careful with Octave exceptions! + if ~isempty(warningMsg) + matRad_cfg = MatRad_Config.instance(); + switch ME.identifier + case 'MATLAB:noPublicFieldForClass' + matRad_cfg.dispWarning('Not able to assign property from pln.propSeq to sequencing: %s', ME.message); + otherwise + matRad_cfg.dispWarning('Problem while setting up sequencing from struct:%s %s', field, ME.message); + end + end + end + end + + if matRad_cfg.isOctave + warning(c2sWarningState.state, 'Octave:classdef-to-struct'); + end + end + + function sequence = sequence(this, w, stf) + + matRad_cfg = MatRad_Config.instance(); + matRad_cfg.dispError('This is an Abstract Base class! Function needs to be called for instantiable subclasses!'); + end + + end + methods (Static) + + function sequencer = getSequencerFromPln(pln, warnDefault) + % GETENGINE Summary of this function goes here + % Detailed explanation goes here + + if nargin < 2 + warnDefault = true; + end + + matRad_cfg = MatRad_Config.instance(); + + sequencer = []; + + initDefaultSequencer = false; + % get all available Sequencers for given pln struct, could be done conditional + classList = matRad_SequencerBase.getAvailableSequencers(pln); + + % Check for a valid engine, and if the given engine isn't valid set boolean + % to initiliaze default engine at the end of this function + if isfield(pln, 'propSeq') && isa(pln.propSeq, mfilename('class')) + sequencer = pln.propSeq; + elseif isfield(pln, 'propSeq') && isstruct(pln.propSeq) && isfield(pln.propSeq, 'sequencer') + if ischar(pln.propSeq.sequencer) || isstring(pln.propSeq.sequencer) + matchSequencers = strcmpi({classList(:).shortName}, pln.propSeq.sequencer); + if any(matchSequencers) + % instantiate engine + sequencerHandle = classList(matchSequencers).handle; + sequencer = sequencerHandle(pln); + else + initDefaultSequencer = true; + end + else + initDefaultSequencer = true; + matRad_cfg.dispWarning('pln.propSeq.sequencer field not valid!'); + end + else + initDefaultSequencer = true; + end + + % trying to use a default engine which fits + % the given radiation mode, when no valid engine was defined. + % Default Engines are defined in matRad_Config. + if initDefaultSequencer + matchSequencers = ismember({classList(:).shortName}, matRad_cfg.defaults.propSeq.sequencer); + if any(matchSequencers) + sequencerHandle = classList(matchSequencers).handle; + + % unlikely event that multiple engines fit just take the first + if length(sequencerHandle) > 1 + sequencerHandle = sequencerHandle{1}; + end + sequencer = sequencerHandle(pln); + if warnDefault + matRad_cfg.dispWarning('Using default sequencer %s!', sequencer.name); + end + elseif ~isempty(classList) + sequencerHandle = classList(1).handle; + sequencer = sequencerHandle(pln); + matRad_cfg.dispWarning('Default sequencer not available! Using %s.', sequencer.name); + else + matRad_cfg.dispError('Default sequencer not found!'); + end + end + + if isempty(sequencer) + matRad_cfg.dispError('No suitable sequencer found!'); + end + + end + + function classList = getAvailableSequencers(pln, optionalPaths) + + matRad_cfg = MatRad_Config.instance(); + + % Parse inputs + if nargin < 2 + optionalPaths = {fileparts(mfilename("fullpath"))}; + else + if ~(iscellstr(optionalPaths) && all(optionalPaths)) + matRad_cfg.dispError('Invalid path array!'); + end + + optionalPaths = horzcat(fileparts(mfilename("fullpath")), optionalPaths); + end + + if nargin < 1 + pln = []; + else + if ~(isstruct(pln) || isempty(pln)) + matRad_cfg.dispError('Invalid pln!'); + end + end + + % Get available, valid classes through call to matRad helper function + % for finding subclasses + persistent allAvailableSequencers lastOptionalPaths + + % First we do a sanity check if persistently stored metaclasses are valid + if ~matRad_cfg.isOctave && ~isempty(allAvailableSequencers) && ~all(cellfun(@isvalid, allAvailableSequencers)) + matRad_cfg.dispWarning('Found invalid Sequencing Sequencers, updating cache.'); + allAvailableSequencers = []; + end + + if isempty(allAvailableSequencers) || (~isempty(lastOptionalPaths) && ~isequal(lastOptionalPaths, optionalPaths)) + lastOptionalPaths = optionalPaths; + allAvailableSequencers = matRad_findSubclasses(mfilename('class'), 'folders', optionalPaths, 'includeAbstract', false); + end + + availableSequencers = allAvailableSequencers; + + % Now filter for pln + ix = []; + + if nargin >= 1 && ~isempty(pln) + machine = matRad_loadMachine(pln); + machineMode = machine.meta.radiationMode; + + for cIx = 1:length(availableSequencers) + mc = availableSequencers{cIx}; + availabilityFuncStr = [mc.Name '.isAvailable']; + % availabilityFunc = str2func(availabilityFuncStr); %str2func does not seem to work on static class functions in Octave 5.2.0 + try + % available = availabilityFunc(pln,machine); + available = eval([availabilityFuncStr '(pln,machine)']); + catch + available = false; + mpList = mc.PropertyList; + if matRad_cfg.isMatlab + loc = find(arrayfun(@(x) strcmp('possibleRadiationModes', x.Name), mpList)); + propValue = mpList(loc).DefaultValue; + else + loc = find(cellfun(@(x) strcmp('possibleRadiationModes', x.Name), mpList)); + propValue = mpList{loc}.DefaultValue; + end + + if any(strcmp(propValue, pln.radiationMode)) + % get radiation mode from the in pln proposed basedata machine file + % add current class to return lists if the + % radiation mode is compatible + if any(strcmp(propValue, machineMode)) + available = true; + + end + end + end + if available + ix = [ix cIx]; + end + end + + availableSequencers = availableSequencers (ix); + end + + classList = matRad_identifyClassesByConstantProperties(availableSequencers, 'shortName', 'defaults', matRad_cfg.defaults.propSeq.sequencer, 'additionalPropertyNames', {'name'}); + + end + + function [available, msg] = isAvailable(pln, machine) + + matRad_cfg = MatRad_Config.instance(); + matRad_cfg.dispError('This is an Abstract Base class! Function needs to be called for instantiable subclasses!'); + end + + end + +end diff --git a/matRad/sequencing/matRad_SequencingPhotonsEngelLeaf.m b/matRad/sequencing/matRad_SequencingPhotonsEngelLeaf.m new file mode 100644 index 000000000..cad85f904 --- /dev/null +++ b/matRad/sequencing/matRad_SequencingPhotonsEngelLeaf.m @@ -0,0 +1,302 @@ +classdef matRad_SequencingPhotonsEngelLeaf < matRad_PhotonSequencerAbstract + + % multileaf collimator leaf sequencing algorithm + % for intensity modulated beams with multiple static segments accroding + % to Engel et al. 2005 Discrete Applied Mathematics + % + % References + % [1] http://www.sciencedirect.com/science/article/pii/S0166218X05001411 + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2015 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSE.md. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties (Constant) + name = 'Photons Engel Leaf Sequenceer' + shortName = 'engel' + possibleRadiationModes = {'photons'} + end + + methods + + function sequence = sequence(this, w, stf) + matRad_cfg = MatRad_Config.instance(); + numOfBeams = numel(stf); + offset = 0; + + for i = 1:numOfBeams + + [D_0, D_k, shapes, calFac, indInMx] = this.initBeam(stf(i), w(1 + offset:stf(i).numOfRays + offset)); + + k = 0; + + % start sequencer + while max(D_k(:) > 0) + + % calculate the difference matrix diffMat + diffMat = diff([zeros(size(D_k, 1), 1) D_k zeros(size(D_k, 1), 1)], [], 2); + + % calculate complexities + c = sum(max(0, diffMat), 2); % TNMU-row-complexity + com = max(c); % TNMU complexity + g = com - c; % row complexity gap + + % initialize segment + segment = zeros(size(D_k)); + + k = k + 1; + + % loop over all rows + for j = 1:size(D_0, 1) + + % determine essential intervals + data(j).left(1) = 0; % left interval limit, actual for an empty interval + data(j).right(1) = 0; % right interal limit, actual for an empty interval + data(j).v(1) = g(j); % greatest number such that the inequalities (6) resp. (7) is satisfied with u=v + data(j).w(1) = inf; % smallest number in the interval + data(j).u(1) = data(j).v(1); % min(v,w) + + [~, pos, ~] = find(diffMat(j, :) > 0); % indices of all positive elements in the j. row of diffmat + [~, neg, ~] = find(diffMat(j, :) < 0); % indices of all negative elements in the j. row of diffMat + + n = 2; + + % loop over the positive elements in the j. row of diffmat -> + % possible left interval limits + for m = 1:size(pos, 2) + + % loop over the negative elements in the j. row of diffMat -> + % possible right interval limit + for l = 1:size(neg, 2) + + % take only intervals I=[l,r] with l<=r + if pos(m) <= neg(l) - 1 + + % set interval limits + data(j).left(n) = pos(m); + data(j).right(n) = neg(l) - 1; + + % calculate v according to Lemma 8 + if g(j) <= abs(diffMat(j, pos(m)) + diffMat(j, neg(l))) + data(j).v(n) = min(diffMat(j, pos(m)), -diffMat(j, neg(l))) + g(j); + else + data(j).v(n) = (diffMat(j, pos(m)) - diffMat(j, neg(l)) + g(j)) / 2; + end + + % calculate w and u according to equality (11) and + % (12) + data(j).w(n) = min(D_k(j, pos(m):(neg(l) - 1))); + data(j).u(n) = min(data(j).v(n), data(j).w(n)); + + n = n + 1; + end + end + end + + u(j) = max(data(j).u); + + end + + % calculate u_max from theorem 9 + d_k = min(u); + + % loop over all rows + for j = 1:size(D_0, 1) + + % find all possible (and essential) intervals + candidate = find(data(j).u >= d_k); + + % calculate the potential of the possible intervals + + % initialize p as -Inf + data(j).p(1:length(data(j).left)) = -Inf; + + % loop over all possible intervals + for s = 1:size(candidate, 2) + + if s == 1 && data(j).left(candidate(s)) == 0 + data(j).p(candidate(1)) = 0; + + else + % calculate p1 according to equality (17) + if d_k == diffMat(j, data(j).left(candidate(s))) && d_k ~= D_k(j, data(j).left(candidate(s))) + p1 = 1; + + else + p1 = 0; + + end + + % calculate p2 according to equalitiy (18) + % if data(j).right(candidate(s)) < size(D_0, 2) + + if d_k == -diffMat(j, data(j).right(candidate(s)) + 1) && d_k ~= D_k(j, data(j).right(candidate(s))) + p2 = 1; + else + p2 = 0; + end + + % else + % + % if d_k == -diffMat(j, data(j).right(candidate(s))+1) + % p2 = 1; + % else + % p2 = 0; + % end + % + % end + + % calculate p3 according to equality (19) + p3 = size(find(D_k(j, data(j).left(candidate(s)):data(j).right(candidate(s))) == d_k), 2); + + data(j).p(candidate(s)) = p1 + p2 + p3; + + end + + end + + % determinate intervals with maximum potential + maxPot = find(data(j).p == max(data(j).p)); + + % if several intervals have maximum potential, select + % the interval which has maximum length + if size(maxPot, 2) > 1 + + for t = 1:size(maxPot, 2) + if t == 1 && data(j).left(maxPot(t)) == 0 + data(j).l(1) = 0; + else + data(j).l(maxPot(t)) = data(j).right(maxPot(t)) - data(j).left(maxPot(t)) + 1; + end + end + + % data(j).l(maxPot) = data(j).right(maxPot) - data(j).left(maxPot) + 1; + + maxLength = find(data(j).l == max(data(j).l)); + + % left and right interval limits of the selected + % interval + leftIntLimit(j) = data(j).left(maxLength(1)); + rightIntLimit(j) = data(j).right(maxLength(1)); + + else + + % left and right interval limits of the selected + % interval + leftIntLimit(j) = data(j).left(maxPot); + rightIntLimit(j) = data(j).right(maxPot); + + end + + % create segment associated by the selected interval + if leftIntLimit(j) ~= 0 + + segment(j, leftIntLimit(j):rightIntLimit(j)) = 1; + + end + + end + + % write the segment in shape_k + shape_k = segment; + + % save shape_k in container + shapes(:, :, k) = shape_k; + + % save the calculated MU + shapesWeight(k) = d_k; + + % calculate new matrix, the diference matrix and complexities + D_k = D_k - d_k * shape_k; + + % delete variables + clear data; + clear segment; + clear u; + clear leftIntLimit; + clear rightIntLimit; + + end + if sum(w(1 + offset:stf(i).numOfRays + offset)) > 0 + sequence.beam(i).numOfShapes = k; + sequence.beam(i).shapes = shapes(:, :, 1:k); + sequence.beam(i).shapesWeight = shapesWeight(1:k) / this.sequencingLevel * calFac; + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2) + offset; + sequence.beam(i).fluence = D_0; + else + sequence.beam(i).numOfShapes = 1; + sequence.beam(i).shapes = zeros(size(D_0)); + sequence.beam(i).shapesWeight = zeros(size(D_0)); + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2) + offset; + sequence.beam(i).fluence = zeros(size(D_0)); + end + if stf(i).numOfRays > 1 + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = D_0(indInMx) / this.sequencingLevel * calFac; + else + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = D_0(indInMx(1)) / this.sequencingLevel * calFac; + end + offset = offset + stf(i).numOfRays; + + end + if this.visMode + this.plotSegments(sequence); + end + sequence = this.sequencing2ApertureInfo(sequence, stf); + end + + end + methods (Static) + + function [available, msg] = isAvailable(pln, machine) + % see superclass for information + + if nargin < 2 + machine = matRad_loadMachine(pln); + end + + % Check superclass availability + [available, msg] = matRad_PhotonSequencerAbstract.isAvailable(pln, machine); + + if ~available + return + else + available = false; + msg = []; + end + + % checkBasic + try + checkBasic = isfield(machine, 'meta') && isfield(machine, 'data'); + + % check modality + checkModality = any(strcmp(matRad_SequencingPhotonsEngelLeaf.possibleRadiationModes, machine.meta.radiationMode)) && any(strcmp(matRad_SequencingPhotonsEngelLeaf.possibleRadiationModes, pln.radiationMode)); + + % Sanity check compatibility + if checkModality + checkModality = strcmp(machine.meta.radiationMode, pln.radiationMode); + end + + preCheck = checkBasic && checkModality; + + if ~preCheck + return + end + catch + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + return + end + + available = preCheck; + end + + end +end diff --git a/matRad/sequencing/matRad_SequencingPhotonsSiochiLeaf.m b/matRad/sequencing/matRad_SequencingPhotonsSiochiLeaf.m new file mode 100644 index 000000000..6fa373e39 --- /dev/null +++ b/matRad/sequencing/matRad_SequencingPhotonsSiochiLeaf.m @@ -0,0 +1,252 @@ +classdef matRad_SequencingPhotonsSiochiLeaf < matRad_PhotonSequencerAbstract + + % UNTITLED Summary of this class goes here + % Detailed explanation goes here + + properties (Constant) + name = 'Photons Siochi Leaf Sequenceer' + shortName = 'siochi' + possibleRadiationModes = {'photons'} + + end + + methods + + function sequence = sequence(this, w, stf) + + offset = 0; + + for i = 1:numel(stf) + + [D_0, D_k, shapes, calFac, indInMx] = this.initBeam(stf(i), w(1 + offset:stf(i).numOfRays + offset)); + + shapesWeight = zeros(10000, 1); + k = 0; + if sum(w(1 + offset:stf(i).numOfRays + offset)) > 0 + + % Decompose the port, do rod pushing + [tops, bases] = this.decomposePort(D_k); + % Form segments + [shapes, shapesWeight, k] = this.convertToSegments(shapes, shapesWeight, k, tops, bases); + + sequence.beam(i).numOfShapes = k; + sequence.beam(i).shapes = shapes(:, :, 1:k); + sequence.beam(i).shapesWeight = shapesWeight(1:k) / this.sequencingLevel * calFac; + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2) + offset; + sequence.beam(i).fluence = D_0; + sequence.beam(i).sum = zeros(size(D_0)); + + for j = 1:k + sequence.beam(i).sum = sequence.beam(i).sum + sequence.beam(i).shapes(:, :, j) * sequence.beam(i).shapesWeight(j); + end + else + sequence.beam(i).numOfShapes = 1; + sequence.beam(i).shapes = zeros(size(D_0)); + sequence.beam(i).shapesWeight = zeros(size(D_0)); + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2) + offset; + sequence.beam(i).fluence = zeros(size(D_0)); + sequence.beam(i).sum = zeros(size(D_0)); + end + if stf(i).numOfRays > 1 + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = sequence.beam(i).sum(indInMx); + else + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = w(1 + offset:stf(i).numOfRays + offset); + end + offset = offset + stf(i).numOfRays; + + end + + if this.visMode + this.plotSegments(sequence); + end + sequence = this.sequencing2ApertureInfo(sequence, stf); + end + + function [tops, bases] = decomposePort(~, map) + % Returns tops and bases of a fluence matrix "map" for Siochi leaf + % sequencing algorithm (rod pushing part). Accounts for collisions and + % tongue and groove (Tng) effects. + + [dimZ, dimX] = size(map); + map_nonZero = (map ~= 0); + + [D_k_Z, D_k_X] = ind2sub([dimZ, dimX], find(map_nonZero)); + minZ = min(D_k_Z); + maxZ = max(D_k_Z); + minX = min(D_k_X); + maxX = max(D_k_X); + + tops = zeros(dimZ, dimX); + bases = zeros(dimZ, dimX); + + for i = minX:maxX + maxTop = -1; + TnG = 1; + for j = minZ:maxZ + if i == minX + bases(j, i) = 1; + tops(j, i) = bases(j, i) + map(j, i) - 1; + else % assign trial base positions + if map(j, i) >= map(j, i - 1) % current rod >= previous, match the bases + bases(j, i) = bases(j, i - 1); + tops(j, i) = bases(j, i) + map(j, i) - 1; + else % current rod maxTop + maxTop = tops(j, i); + maxRow = j; + end + end + + % Correct for collision and tongue and groove error + while TnG + % go from maxRow down checking for TnG. This occurs when a shorter + % rod is "peeking over" a longer one in the direction transverse to + % the leaf motion. To fix this, match either the tops or bases of + % the rods. + for j = (maxRow - 1):-1:minZ + if map(j, i) < map(j + 1, i) + if tops(j, i) > tops(j + 1, i) + tops(j + 1, i) = tops(j, i); + bases(j + 1, i) = tops(j + 1, i) - map(j + 1, i) + 1; + elseif bases(j, i) < bases(j + 1, i) + bases(j, i) = bases(j + 1, i); + tops(j, i) = bases(j, i) + map(j, i) - 1; + end + else + if tops(j, i) < tops(j + 1, i) + tops(j, i) = tops(j + 1, i); + bases(j, i) = tops(j, i) - map(j, i) + 1; + elseif bases(j, i) > bases(j + 1, i) + bases(j + 1, i) = bases(j, i); + tops(j + 1, i) = bases(j + 1, i) + map(j + 1, i) - 1; + end + end + end + % go from maxRow up checking for TnG + for j = (maxRow + 1):maxZ + if map(j, i) < map(j - 1, i) + if tops(j, i) > tops(j - 1, i) + tops(j - 1, i) = tops(j, i); + bases(j - 1, i) = tops(j - 1, i) - map(j - 1, i) + 1; + elseif bases(j, i) < bases(j - 1, i) + bases(j, i) = bases(j - 1, i); + tops(j, i) = bases(j, i) + map(j, i) - 1; + end + else + if tops(j, i) < tops(j - 1, i) + tops(j, i) = tops(j - 1, i); + bases(j, i) = tops(j, i) - map(j, i) + 1; + elseif bases(j, i) > bases(j - 1, i) + bases(j - 1, i) = bases(j, i); + tops(j - 1, i) = bases(j - 1, i) + map(j - 1, i) - 1; + end + end + end + % now check if all TnG conditions have been removed + TnG = 0; + for j = (minZ + 1):maxZ + if map(j, i) < map(j - 1, i) + if tops(j, i) > tops(j - 1, i) + TnG = 1; + elseif bases(j, i) < bases(j - 1, i) + TnG = 1; + end + else + if tops(j, i) < tops(j - 1, i) + TnG = 1; + elseif bases(j, i) > bases(j - 1, i) + TnG = 1; + end + end + end + end + end + end + + function [shapes, shapesWeight, k] = convertToSegments(this, shapes, shapesWeight, k, tops, bases) + % Convert tops and bases to shape matrices. These are taken as to be the + % shapes of uniform level/elevation after the rods are pushed. + + levels = max(tops(:)); + + for level = 1:levels + % check if slab is new + if this.differentSlab(tops, bases, level) + k = k + 1; % increment number of unique slabs + shape_k = (bases <= level) .* (level <= tops); % shape of current slab + shapes(:, :, k) = shape_k; + end + shapesWeight(k) = shapesWeight(k) + 1; % if slab is not unique, this increments weight again + end + end + + function diffSlab = differentSlab(~, tops, bases, level) + + % Returns 1 if slab level is different than slab level-1 0 otherwise + + if level == 1 % first slab is automatically different + diffSlab = 1; + else + shapeLevel = (bases <= level) .* (level <= tops); % shape of slab with current level + shapeLevel_1 = (bases <= level - 1) .* (level - 1 <= tops); % shape of slab with previous level + diffSlab = ~isequal(shapeLevel, shapeLevel_1); % tests if slabs are equal; isequaln was not giving correct results + end + end + + end + methods (Static) + + function [available, msg] = isAvailable(pln, machine) + % see superclass for information + + if nargin < 2 + machine = matRad_loadMachine(pln); + end + + % Check superclass availability + [available, msg] = matRad_PhotonSequencerAbstract.isAvailable(pln, machine); + + if ~available + return + else + available = false; + msg = []; + end + + % checkBasic + try + checkBasic = isfield(machine, 'meta') && isfield(machine, 'data'); + + % check modality + checkModality = any(strcmp(matRad_SequencingPhotonsSiochiLeaf.possibleRadiationModes, machine.meta.radiationMode)) && any(strcmp(matRad_SequencingPhotonsSiochiLeaf.possibleRadiationModes, pln.radiationMode)); + + % Sanity check compatibility + if checkModality + checkModality = strcmp(machine.meta.radiationMode, pln.radiationMode); + end + + preCheck = checkBasic && checkModality; + + if ~preCheck + return + end + catch + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + return + end + + available = preCheck; + end + + end +end diff --git a/matRad/sequencing/matRad_SequencingPhotonsXiaLeaf.m b/matRad/sequencing/matRad_SequencingPhotonsXiaLeaf.m new file mode 100644 index 000000000..124235d98 --- /dev/null +++ b/matRad/sequencing/matRad_SequencingPhotonsXiaLeaf.m @@ -0,0 +1,187 @@ +classdef matRad_SequencingPhotonsXiaLeaf < matRad_PhotonSequencerAbstract + + % multileaf collimator leaf sequence algorithm + % for intensity modulated beams with multiple static segments according to + % Xia et al. (1998) Medical Physics + % + % [1] http://online.medphys.org/resource/1/mphya6/v25/i8/p1424_s1 + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2015 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSE.md. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties (Constant) + name = 'Photons Xia Leaf Sequenceer' + shortName = 'xia' + possibleRadiationModes = {'photons'} + end + properties + mode = 'rl' % sliding window (sw) or reducing level (rl) + end + + methods + + function sequence = sequence(this, w, stf) + matRad_cfg = MatRad_Config.instance(); + numOfBeams = numel(stf); + offset = 0; + + for i = 1:numOfBeams + + [D_0, D_k, shapes, calFac, indInMx] = this.initBeam(stf(i), w(1 + offset:stf(i).numOfRays + offset)); + + % Save the maximun intensity (Equation 5) + L_k = max(D_k(:)); + + % Save the maximun initial intensity matrix value in L_0. + L_0 = L_k; + + % Set k=0, this variable is used for residuals intensity matrices D_k. + k = 0; + + % start sequencer + while L_k > 0 + + k = k + 1; + + % Rounded off integer. Equation 7. + m = floor(log2(L_k)); + + % Convert m=1 if is less than 1. This happens when L_k belong to ]0,2[ + if m < 1 + m = 1; + end + + % Calculate the delivery intensity unit. Equation 6. + d_k = floor(2^(m - 1)); + + % Opening matrix. + openingMx = D_k >= d_k; + + dimOfFluenceMxZ = size(shapes, 1); + + switch this.mode + case 'sw' % sliding window technique! + for j = 1:dimOfFluenceMxZ + openIx = find(openingMx(j, :) == 1, 1, 'first'); + if ~isempty(openIx) + closeIx = find(openingMx(j, openIx + 1:end) == 0, 1, 'first'); + if ~isempty(closeIx) + openingMx(j, openIx + closeIx:end) = 0; + end + end + + end + case 'rl' % reducing levels technique! + for j = 1:dimOfFluenceMxZ + [maxVal, maxIx] = max(openingMx(j, :) .* D_k(j, :)); + if maxVal > 0 + closeIx = maxIx + find(openingMx(j, maxIx + 1:end) == 0, 1, 'first'); + if ~isempty(closeIx) + openingMx(j, closeIx:end) = 0; + end + openIx = find(openingMx(j, 1:maxIx - 1) == 0, 1, 'last'); + if ~isempty(openIx) + openingMx(j, 1:openIx) = 0; + end + end + + end + otherwise + matRad_cfg.dispError('unknown sequencing mode'); + end + + shape_k = openingMx * d_k; + + shapes(:, :, k) = shape_k; + shapesWeight(k) = d_k; + D_k = D_k - shape_k; + + L_k = max(D_k(:)); % eq 5 + + end + + if sum(w(1 + offset:stf(i).numOfRays + offset)) > 0 + + sequence.beam(i).numOfShapes = k; + sequence.beam(i).shapes = shapes(:, :, 1:k); + sequence.beam(i).shapesWeight = shapesWeight(1:k) / this.sequencingLevel * calFac; + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2) + offset; + sequence.beam(i).fluence = D_0; + + else + sequence.beam(i).numOfShapes = 1; + sequence.beam(i).shapes = zeros(size(D_0)); + sequence.beam(i).shapesWeight = zeros(size(D_0)); + sequence.beam(i).bixelIx = 1 + offset:size(stf(i).ray, 2); + sequence.beam(i).fluence = zeros(size(D_0)); + end + if stf(i).numOfRays > 1 + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = D_0(indInMx) / this.sequencingLevel * calFac; + else + sequence.w(1 + offset:stf(i).numOfRays + offset, 1) = D_0(indInMx(1)) / this.sequencingLevel * calFac; + end + offset = offset + stf(i).numOfRays; + end + if this.visMode + this.plotSegments(sequence); + end + sequence = this.sequencing2ApertureInfo(sequence, stf); + end + + end + methods (Static) + + function [available, msg] = isAvailable(pln, machine) + % see superclass for information + + if nargin < 2 + machine = matRad_loadMachine(pln); + end + + % Check superclass availability + [available, msg] = matRad_PhotonSequencerAbstract.isAvailable(pln, machine); + + if ~available + return + else + available = false; + msg = []; + end + + % checkBasic + try + checkBasic = isfield(machine, 'meta') && isfield(machine, 'data'); + + % check modality + checkModality = any(strcmp(matRad_SequencingPhotonsXiaLeaf.possibleRadiationModes, machine.meta.radiationMode)) && any(strcmp(matRad_SequencingPhotonsXiaLeaf.possibleRadiationModes, pln.radiationMode)); + + % Sanity check compatibility + if checkModality + checkModality = strcmp(machine.meta.radiationMode, pln.radiationMode); + end + + preCheck = checkBasic && checkModality; + + if ~preCheck + return + end + catch + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + return + end + + available = preCheck; + end + + end +end diff --git a/matRad/sequencing/matRad_aperture2collimation.m b/matRad/sequencing/matRad_aperture2collimation.m index 64ef91cc6..f069f1cf8 100644 --- a/matRad/sequencing/matRad_aperture2collimation.m +++ b/matRad/sequencing/matRad_aperture2collimation.m @@ -1,4 +1,4 @@ -function [pln,stf] = matRad_aperture2collimation(pln,stf,sequencing,apertureInfo) +function [pln, stf] = matRad_aperture2collimation(pln, stf, sequencing, apertureInfo) % matRad function to convert sequencing information / aperture information % into collimation information in pln and stf for field-based dose % calculation @@ -14,7 +14,7 @@ % % output: % pln: matRad pln struct with collimation information -% stf: matRad stf struct with shapes instead of beamlets +% stf: matRad stf struct with shapes instead of beamlets % % References % - @@ -23,109 +23,27 @@ % % Copyright 2022-2026 the matRad development team. % Author: wahln -% -% This file is part of the matRad project. It is subject to the license -% terms in the LICENSE file found in the top-level directory of this -% distribution and at https://github.com/e0404/matRad/LICENSE.md. No part -% of the matRad project, including this file, may be copied, modified, -% propagated, or distributed except according to the terms contained in the +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSE.md. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%Debug visualization -visBool = false; - -bixelWidth = apertureInfo.bixelWidth; -leafWidth = bixelWidth; -convResolution = 0.5; %[mm] - -%The collimator limits are infered here from the apertureInfo. This could -%be handled differently by explicitly storing collimator info in the base -%data? -symmetricMLClimits = vertcat(apertureInfo.beam.MLCWindow); -symmetricMLClimits = max(abs(symmetricMLClimits)); -fieldWidth = 2*max(symmetricMLClimits); - -%modify basic pln variables -pln.propStf.bixelWidth = 'field'; -pln.propStf.collimation.convResolution = 0.5; %[mm] -pln.propStf.collimation.fieldWidth = fieldWidth; -pln.propStf.collimation.leafWidth = leafWidth; - -% -%[bixelFieldX,bixelFieldY] = ndgrid(-fieldWidth/2:bixelWidth:fieldWidth/2,-fieldWidth/2:leafWidth:fieldWidth/2); -[convFieldX,convFieldY] = meshgrid(-fieldWidth/2:convResolution:fieldWidth/2); - -%TODO: Not used in calcPhotonDose but imported from DICOM -%pln.propStf.collimation.Devices ... -%pln.propStf.collimation.numOfFields -%pln.propStf.collimation.beamMeterset +matRad_cfg = MatRad_Config.instance(); -for iBeam = 1:numel(stf) - stfTmp = stf(iBeam); - beamSequencing = sequencing.beam(iBeam); - beamAperture = apertureInfo.beam(iBeam); - - stfTmp.bixelWidth = 'field'; - - nShapes = beamSequencing.numOfShapes; +matRad_cfg.dispWarning('This function is outdated use, class intead'); - stfTmp.numOfRays = 1;% - stfTmp.numOfBixelsPerRay = nShapes; - stfTmp.totalNumOfBixels = nShapes; - - ray = struct(); - ray.rayPos_bev = [0 0 0]; - ray.targetPoint_bev = [0 stfTmp.SAD 0]; - ray.weight = 1; - ray.energy = stfTmp.ray(1).energy; - ray.beamletCornersAtIso = stfTmp.ray(1).beamletCornersAtIso; - ray.rayCorners_SCD = stfTmp.ray(1).rayCorners_SCD; +sequencer = matRad_SequencerBase.getSequencerFromPln(pln); - %ray.shape = beamSequencing.sum; - shapeTotalF = zeros(size(convFieldX)); - - ray.shapes = struct(); - for iShape = 1:nShapes - currShape = beamAperture.shape(iShape); - activeLeafPairPosY = beamAperture.leafPairPos; - F = zeros(size(convFieldX)); - if visBool - hF = figure; imagesc(F); title(sprintf('Beam %d, Shape %d',iBeam,iShape)); hold on; - end - for iLeafPair = 1:numel(activeLeafPairPosY) - posY = activeLeafPairPosY(iLeafPair); - ixY = convFieldY >= posY-leafWidth/2 & convFieldY < posY + leafWidth/2; - ixX = convFieldX >= currShape.leftLeafPos(iLeafPair) & convFieldX < currShape.rightLeafPos(iLeafPair); - ix = ixX & ixY; - F(ix) = 1; - if visBool - figure(hF); imagesc(F); drawnow; pause(0.1); - end - end - - if visBool - pause(1); close(hF); - end - - F = F*currShape.weight; - shapeTotalF = shapeTotalF + F; - - ray.shapes(iShape).convFluence = F; - ray.shapes(iShape).shapeMap = currShape.shapeMap; - ray.shapes(iShape).weight = currShape.weight; - ray.shapes(iShape).leftLeafPos = currShape.leftLeafPos; - ray.shapes(iShape).rightLeafPos = currShape.rightLeafPos; - ray.shapes(iShape).leafPairCenterPos = activeLeafPairPosY; +if ~exist("apertureInfo") + if ~isfield(sequencing, 'apertureInfo') + sequencing.aperatureInfo = aperaturInfo; end - - ray.shape = shapeTotalF; - ray.weight = ones(1,nShapes); - ray.collimation = pln.propStf.collimation; - stfTmp.ray = ray; - - stf(iBeam) = stfTmp; end +[pln, stf] = sequencer.aperture2collimation(pln, stf, sequencing); - +end diff --git a/matRad/sequencing/matRad_engelLeafSequencing.m b/matRad/sequencing/matRad_engelLeafSequencing.m index 528b72d15..5f418c6d8 100644 --- a/matRad/sequencing/matRad_engelLeafSequencing.m +++ b/matRad/sequencing/matRad_engelLeafSequencing.m @@ -1,6 +1,6 @@ -function resultGUI = matRad_engelLeafSequencing(resultGUI,stf,dij,numOfLevels,visBool) -% multileaf collimator leaf sequencing algorithm -% for intensity modulated beams with multiple static segments accroding +function resultGUI = matRad_engelLeafSequencing(resultGUI, stf, dij, numOfLevels, visBool) +% multileaf collimator leaf sequencing algorithm +% for intensity modulated beams with multiple static segments accroding % to Engel et al. 2005 Discrete Applied Mathematics % % call: @@ -34,362 +34,21 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% if visBool not set toogle off visualization -if nargin < 5 - visBool = 0; -end - -numOfBeams = numel(stf); - -if visBool - % create the sequencing figure - sz = [800 1000]; % figure size - screensize = get(0,'ScreenSize'); - xpos = ceil((screensize(3)-sz(2))/2); % center the figure on the screen horizontally - ypos = ceil((screensize(4)-sz(1))/2); % center the figure on the screen vertically - seqFig = figure('position',[xpos,ypos,sz(2),sz(1)]); -end - -offset = 0; - -for i = 1:numOfBeams - - numOfRaysPerBeam = stf(i).numOfRays; - - % get relevant weights for current beam - wOfCurrBeams = resultGUI.w(1+offset:numOfRaysPerBeam+offset).* ones(size(stf(i).ray,2),1); - - X = ones(size(stf(i).ray,2),1)*NaN; %this way it also works with3dconformal - Z = ones(size(stf(i).ray,2),1)*NaN; - - for j=1:size(stf(i).ray,2) - X(j) = stf(i).ray(j).rayPos_bev(:,1); - Z(j) = stf(i).ray(j).rayPos_bev(:,3); - end - - % sort bixels into matrix - minX = min(X); - maxX = max(X); - minZ = min(Z); - maxZ = max(Z); - - dimOfFluenceMxX = (maxX-minX)/stf(i).bixelWidth + 1; - dimOfFluenceMxZ = (maxZ-minZ)/stf(i).bixelWidth + 1; - - %Create the fluence matrix. - fluenceMx = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - - % Calculate X and Z position of every fluence's matrix spot - % z axis = axis of leaf movement! - xPos = (X-minX)/stf(i).bixelWidth+1; - zPos = (Z-minZ)/stf(i).bixelWidth+1; - - % Make subscripts for fluence matrix - indInFluenceMx = zPos + (xPos-1)*dimOfFluenceMxZ; - - %Save weights in fluence matrix. - fluenceMx(indInFluenceMx) = wOfCurrBeams; - - % Stratification - calFac = max(fluenceMx(:)); - D_k = round(fluenceMx/calFac*numOfLevels); - - % Save the stratification in the initial intensity matrix D_0. - D_0 = D_k; - - % container to remember generated shapes; allocate space for 10000 shapes - shapes = NaN*ones(dimOfFluenceMxZ,dimOfFluenceMxX,10000); - - k = 0; - - if visBool - clf(seqFig); - colormap(seqFig,'jet'); - - seqSubPlots(1) = subplot(2,2,1,'parent',seqFig); - imagesc(D_k,'parent',seqSubPlots(1)); - set(seqSubPlots(1),'CLim',[0 numOfLevels],'YDir','normal'); - title(seqSubPlots(1),['Beam # ' num2str(i) ': max(D_0) = ' num2str(max(D_0(:))) ' - ' num2str(numel(unique(D_0))) ' intensity levels']); - xlabel(seqSubPlots(1),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(1),'z - direction perpendicular to leaf motion ') - colorbar; - drawnow - end - - % start sequencer - while max(D_k(:) > 0) - - %calculate the difference matrix diffMat - diffMat = diff([zeros(size(D_k,1),1) D_k zeros(size(D_k,1),1)],[],2); - - %calculate complexities - c = sum(max(0,diffMat),2); %TNMU-row-complexity - com = max(c); %TNMU complexity - g = com - c; %row complexity gap - - %initialize segment - segment = zeros(size(D_k)); - - k = k + 1; - - %Plot residual intensity matrix. - if visBool - seqSubPlots(2) = subplot(2,2,2,'parent',seqFig); - imagesc(D_k,'parent',seqSubPlots(2)); - set(seqSubPlots(2),'CLim',[0 numOfLevels],'YDir','normal'); - title(seqSubPlots(2),['k = ' num2str(k)]); - colorbar - drawnow - end - - - %loop over all rows - for j=1:size(D_0,1) - - %determine essential intervals - data(j).left(1) = 0; %left interval limit, actual for an empty interval - data(j).right(1) = 0; %right interal limit, actual for an empty interval - data(j).v(1) = g(j); %greatest number such that the inequalities (6) resp. (7) is satisfied with u=v - data(j).w(1) = inf; %smallest number in the interval - data(j).u(1) = data(j).v(1); %min(v,w) - - [~, pos, ~] = find(diffMat(j,:) > 0); % indices of all positive elements in the j. row of diffmat - [~, neg, ~] = find(diffMat(j,:) < 0); % indices of all negative elements in the j. row of diffMat - - n=2; - - %loop over the positive elements in the j. row of diffmat -> - %possible left interval limits - for m=1:size(pos,2) - - %loop over the negative elements in the j. row of diffMat -> - %possible right interval limit - for l=1:size(neg,2) - - %take only intervals I=[l,r] with l<=r - if pos(m) <= neg(l)-1 - - %set interval limits - data(j).left(n) = pos(m); - data(j).right(n) = neg(l)-1; - - %calculate v according to Lemma 8 - if g(j) <= abs( diffMat(j,pos(m)) + diffMat(j,neg(l)) ) - data(j).v(n) = min( diffMat(j,pos(m)), -diffMat(j,neg(l)) ) + g(j); - else - data(j).v(n) = ( diffMat(j, pos(m)) - diffMat(j, neg(l)) + g(j)) / 2; - end - - %calculate w and u according to equality (11) and - %(12) - data(j).w(n) = min(D_k(j,pos(m):(neg(l)-1))); - data(j).u(n) = min(data(j).v(n), data(j).w(n)); - - n = n+1; - end - end - end - - u(j) = max(data(j).u); - - end - - %calculate u_max from theorem 9 - d_k = min(u); - - %loop over all rows - for j=1:size(D_0,1) - - %find all possible (and essential) intervals - candidate = find(data(j).u >= d_k); - - %calculate the potential of the possible intervals - - %initialize p as -Inf - data(j).p(1:length(data(j).left)) = -Inf; - - %loop over all possible intervals - for s=1:size(candidate,2) - - if (s==1 && data(j).left(candidate(s)) == 0) - data(j).p(candidate(1)) = 0; - - - else - %calculate p1 according to equality (17) - if (d_k == diffMat(j, data(j).left(candidate(s))) && d_k ~= D_k(j, data(j).left(candidate(s)))) - p1 = 1; - - else - p1 = 0; - - end - - %calculate p2 according to equalitiy (18) - % if data(j).right(candidate(s)) < size(D_0, 2) - - if (d_k == -diffMat(j, data(j).right(candidate(s))+1) && d_k ~= D_k(j, data(j).right(candidate(s)))) - p2 = 1; - else - p2 = 0; - end - -% else -% -% if d_k == -diffMat(j, data(j).right(candidate(s))+1) -% p2 = 1; -% else -% p2 = 0; -% end -% -% end - - %calculate p3 according to equality (19) - p3 = size(find(D_k(j, data(j).left(candidate(s)):data(j).right(candidate(s))) == d_k),2); - - data(j).p(candidate(s)) = p1 + p2+ p3; - - end - - end - - %determinate intervals with maximum potential - maxPot = find(data(j).p == max(data(j).p)); - - %if several intervals have maximum potential, select - %the interval which has maximum length - if size(maxPot,2) > 1 - - for t=1:size(maxPot,2) - if t==1 && data(j).left(maxPot(t)) == 0 - data(j).l(1) = 0; - else - data(j).l(maxPot(t)) = data(j).right(maxPot(t)) - data(j).left(maxPot(t)) + 1; - end - end - - %data(j).l(maxPot) = data(j).right(maxPot) - data(j).left(maxPot) + 1; - - maxLength = find(data(j).l == max(data(j).l)); - - %left and right interval limits of the selected - %interval - leftIntLimit(j) = data(j).left(maxLength(1)); - rightIntLimit(j) = data(j).right(maxLength(1)); - - - else - - %left and right interval limits of the selected - %interval - leftIntLimit(j) = data(j).left(maxPot); - rightIntLimit(j) = data(j).right(maxPot); - - - end - - %create segment associated by the selected interval - if leftIntLimit(j) ~= 0 - - segment(j,leftIntLimit(j):rightIntLimit(j)) = 1; - - end - - end - - %write the segment in shape_k - shape_k = segment; - - %show the leaf positions - if visBool - seqSubPlots(4) = subplot(2,2,3.5,'parent',seqFig); - imagesc(shape_k,'parent',seqSubPlots(4)); - hold(seqSubPlots(4),'on'); - set(seqSubPlots(4),'YDir','normal') - xlabel(seqSubPlots(4),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(4),'z - direction perpendicular to leaf motion ') - title(seqSubPlots(4),['beam # ' num2str(i) ' shape # ' num2str(k) ' d_k = ' num2str(d_k)]); - for j = 1:dimOfFluenceMxZ - leftLeafIx = find(shape_k(j,:)>0,1,'first'); - rightLeafIx = find(shape_k(j,:)>0,1,'last'); - if leftLeafIx > 1 - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j-[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j+[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[ leftLeafIx-.5 leftLeafIx-.5],j+[.5 -.5] ,'w','LineWidth',2) - end - if rightLeafIx0 - - sequencing.beam(i).numOfShapes = k; - sequencing.beam(i).shapes = shapes(:,:,1:k); - sequencing.beam(i).shapesWeight = shapesWeight(1:k)/numOfLevels*calFac; - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = D_0; - - else - sequencing.beam(i).numOfShapes = 1; - sequencing.beam(i).shapes = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).shapesWeight = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - end - - sequencing.w(1+offset:numOfRaysPerBeam+offset,1) = D_0(indInFluenceMx)/numOfLevels*calFac; - - offset = offset + numOfRaysPerBeam; - -end - -resultGUI.w = sequencing.w; -resultGUI.wSequenced = sequencing.w; +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +matRad_cfg = MatRad_Config.instance(); -resultGUI.sequencing = sequencing; -resultGUI.apertureInfo = matRad_sequencing2ApertureInfo(sequencing,stf); +matRad_cfg.dispWarning('This function is Outdated use new SequencingClass'); -doseSequencedDoseGrid = reshape(dij.physicalDose{1} * sequencing.w,dij.doseGrid.dimensions); -% interpolate to ct grid for visualiation & analysis -resultGUI.physicalDose = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ... - doseSequencedDoseGrid, ... - dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z); +sequencer = matRad_SequencingPhotonsEngelLeaf(); +sequencer.visMode = visBool; +sequencer.sequencingLevel = numOfLevels; -% if weights exists from an former DAO remove it -if isfield(resultGUI,'wDao') - resultGUI = rmfield(resultGUI,'wDao'); +sequence = sequencer.sequence(resultGUI.w, stf); +if ~isempty(dij) + resultGUI = matRad_calcCubes(sequence.w, dij); +else + matRad_cfg.dispWarning('Dose not recalcaulted with sequenced fluence'); end +resultGUI.sequencing = sequence; end - diff --git a/matRad/sequencing/matRad_siochiLeafSequencing.m b/matRad/sequencing/matRad_siochiLeafSequencing.m index 96a0444b8..425ce0ed4 100644 --- a/matRad/sequencing/matRad_siochiLeafSequencing.m +++ b/matRad/sequencing/matRad_siochiLeafSequencing.m @@ -1,6 +1,6 @@ -function resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,numOfLevels,visBool) -% multileaf collimator leaf sequencing algorithm -% for intensity modulated beams with multiple static segments according to +function resultGUI = matRad_siochiLeafSequencing(resultGUI, stf, dij, numOfLevels, visBool) +% multileaf collimator leaf sequencing algorithm +% for intensity modulated beams with multiple static segments according to % Siochi (1999)International Journal of Radiation Oncology * Biology * Physics, % originally implemented in PLUNC (https://sites.google.com/site/planunc/) % @@ -40,340 +40,20 @@ % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +matRad_cfg = MatRad_Config.instance(); -% if visBool not set toogle off visualization -if nargin < 5 - visBool = 0; -end - -numOfBeams = numel(stf); - -if visBool - % create the sequencing figure - sz = [800 1000]; % figure size - screensize = get(0,'ScreenSize'); - xpos = ceil((screensize(3)-sz(2))/2); % center the figure on the screen horizontally - ypos = ceil((screensize(4)-sz(1))/2); % center the figure on the screen vertically - seqFig = figure('position',[xpos,ypos,sz(2),sz(1)]); -end - -offset = 0; - -if ~isfield(resultGUI,'wUnsequenced') - wUnsequenced = resultGUI.w; -else - wUnsequenced = resultGUI.wUnsequenced; -end - -for i = 1:numOfBeams - - numOfRaysPerBeam = stf(i).numOfRays; - - % get relevant weights for current beam - wOfCurrBeams = wUnsequenced(1+offset:numOfRaysPerBeam+offset).* ones(size(stf(i).ray,2),1);%REVIEW OFFSET - - X = ones(size(stf(i).ray,2),1)*NaN; %this way it also works with3dconformal - Z = ones(size(stf(i).ray,2),1)*NaN; - - for j = 1:size(stf(i).ray,2) - X(j) = stf(i).ray(j).rayPos_bev(:,1); - Z(j) = stf(i).ray(j).rayPos_bev(:,3); - end - - % sort bixels into matrix - minX = min(X); - maxX = max(X); - minZ = min(Z); - maxZ = max(Z); - - dimOfFluenceMxX = (maxX-minX)/stf(i).bixelWidth + 1; - dimOfFluenceMxZ = (maxZ-minZ)/stf(i).bixelWidth + 1; - - %Create the fluence matrix. - fluenceMx = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - - % Calculate X and Z position of every fluence's matrix spot z axis = - % axis of leaf movement! - xPos = (X-minX)/stf(i).bixelWidth+1; - zPos = (Z-minZ)/stf(i).bixelWidth+1; - - % Make subscripts for fluence matrix - indInFluenceMx = zPos + (xPos-1)*dimOfFluenceMxZ; - - %Save weights in fluence matrix. - fluenceMx(indInFluenceMx) = wOfCurrBeams; - - % Stratification - calFac = max(fluenceMx(:)); - D_k = round(fluenceMx/calFac*numOfLevels); - - % Save the stratification in the initial intensity matrix D_0. - D_0 = D_k; - - % container to remember generated shapes; allocate space for 10000 - % shapes - shapes = NaN*ones(dimOfFluenceMxZ,dimOfFluenceMxX,10000); - shapesWeight = zeros(10000,1); - k = 0; - - if visBool - clf(seqFig); - colormap(seqFig,'jet'); - - seqSubPlots(1) = subplot(2,2,1,'parent',seqFig); - imagesc(D_k,'parent',seqSubPlots(1)); - set(seqSubPlots(1),'CLim',[0 numOfLevels],'YDir','normal'); - title(seqSubPlots(1),['Beam # ' num2str(i) ': max(D_0) = ' num2str(max(D_0(:))) ' - ' num2str(numel(unique(D_0))) ' intensity levels']); - xlabel(seqSubPlots(1),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(1),'z - direction perpendicular to leaf motion ') - colorbar; - drawnow - end - - D_k_nonZero = (D_k~=0); - [D_k_Z, D_k_X] = ind2sub([dimOfFluenceMxZ,dimOfFluenceMxX],find(D_k_nonZero)); - D_k_MinZ = min(D_k_Z); - D_k_MaxZ = max(D_k_Z); - D_k_MinX = min(D_k_X); - D_k_MaxX = max(D_k_X); - - if sum(wOfCurrBeams)>0 - %Decompose the port, do rod pushing - [tops, bases] = matRad_siochiDecomposePort(D_k,dimOfFluenceMxZ,dimOfFluenceMxX,D_k_MinZ,D_k_MaxZ,D_k_MinX,D_k_MaxX); - %Form segments with and without visualization - if visBool - [shapes,shapesWeight,k,D_k]=matRad_siochiConvertToSegments(shapes,shapesWeight,k,tops,bases,visBool,i,D_k,numOfLevels,seqFig,seqSubPlots); - else - [shapes,shapesWeight,k]=matRad_siochiConvertToSegments(shapes,shapesWeight,k,tops,bases); - end - - sequencing.beam(i).numOfShapes = k; - sequencing.beam(i).shapes = shapes(:,:,1:k); - sequencing.beam(i).shapesWeight = shapesWeight(1:k)/numOfLevels*calFac; - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = D_0; - sequencing.beam(i).sum = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - - for j = 1:k - sequencing.beam(i).sum = sequencing.beam(i).sum+sequencing.beam(i).shapes(:,:,j)*sequencing.beam(i).shapesWeight(j); - end - - else - sequencing.beam(i).numOfShapes = 1; - sequencing.beam(i).shapes = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).shapesWeight = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).sum = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - end - - if numOfRaysPerBeam >1 - sequencing.w(1+offset:numOfRaysPerBeam+offset,1) = sequencing.beam(i).sum(indInFluenceMx); - else - sequencing.w(1+offset:numOfRaysPerBeam+offset,1) = wOfCurrBeams(1); - end - offset = offset + numOfRaysPerBeam; - -end - -resultGUI.w = sequencing.w; -resultGUI.wSequenced = sequencing.w; - -resultGUI.sequencing = sequencing; -resultGUI.apertureInfo = matRad_sequencing2ApertureInfo(sequencing,stf); - -doseSequencedDoseGrid = reshape(dij.physicalDose{1} * sequencing.w,dij.doseGrid.dimensions); -% interpolate to ct grid for visualiation & analysis -resultGUI.physicalDose = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ... - doseSequencedDoseGrid, ... - dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z); - -% if weights exists from an former DAO remove it -if isfield(resultGUI,'wDao') - resultGUI = rmfield(resultGUI,'wDao'); -end +matRad_cfg.dispWarning('This function is Outdated use new SequencingClass'); -end - -function [tops, bases] = matRad_siochiDecomposePort(map,dimZ,dimX,minZ,maxZ,minX,maxX) -%Returns tops and bases of a fluence matrix "map" for Siochi leaf -%sequencing algorithm (rod pushing part). Accounts for collisions and -%tongue and groove (Tng) effects. - -tops = zeros(dimZ, dimX); -bases = zeros(dimZ, dimX); - -for i = minX:maxX - maxTop = -1; - TnG = 1; - for j = minZ:maxZ - if i == minX - bases(j,i) = 1; - tops(j,i) = bases(j,i)+map(j,i)-1; - else %assign trial base positions - if map(j,i) >= map(j,i-1) %current rod >= previous, match the bases - bases(j,i) = bases(j,i-1); - tops(j,i) = bases(j,i)+map(j,i)-1; - else %current rod maxTop - maxTop = tops(j,i); - maxRow = j; - end - end - - %Correct for collision and tongue and groove error - while(TnG) - %go from maxRow down checking for TnG. This occurs when a shorter - %rod is "peeking over" a longer one in the direction transverse to - %the leaf motion. To fix this, match either the tops or bases of - %the rods. - for j = (maxRow-1):-1:minZ - if map(j,i) < map(j+1,i) - if tops(j,i) > tops(j+1,i) - tops(j+1,i) = tops(j,i); - bases(j+1,i) = tops(j+1,i)-map(j+1,i)+1; - elseif bases(j,i) < bases(j+1,i) - bases(j,i) = bases(j+1,i); - tops(j,i) = bases(j,i)+map(j,i)-1; - end - else - if tops(j,i) < tops(j+1,i) - tops(j,i) = tops(j+1,i); - bases(j,i) = tops(j,i)-map(j,i)+1; - elseif bases(j,i) > bases(j+1,i) - bases(j+1,i) = bases(j,i); - tops(j+1,i) = bases(j+1,i)+map(j+1,i)-1; - end - end - end - %go from maxRow up checking for TnG - for j = (maxRow+1):maxZ - if map(j,i) < map(j-1,i) - if tops(j,i) > tops(j-1,i) - tops(j-1,i) = tops(j,i); - bases(j-1,i) = tops(j-1,i)-map(j-1,i)+1; - elseif bases(j,i) < bases(j-1,i) - bases(j,i) = bases(j-1,i); - tops(j,i) = bases(j,i)+map(j,i)-1; - end - else - if tops(j,i) < tops(j-1,i) - tops(j,i) = tops(j-1,i); - bases(j,i) = tops(j,i)-map(j,i)+1; - elseif bases(j,i) > bases(j-1,i) - bases(j-1,i) = bases(j,i); - tops(j-1,i) = bases(j-1,i)+map(j-1,i)-1; - end - end - end - %now check if all TnG conditions have been removed - TnG = 0; - for j = (minZ+1):maxZ - if map(j,i) < map(j-1,i); - if tops(j,i) > tops(j-1,i) - TnG = 1; - elseif bases(j,i) < bases(j-1,i) - TnG = 1; - end - else - if tops(j,i) < tops(j-1,i) - TnG = 1; - elseif bases(j,i) > bases(j-1,i) - TnG = 1; - end - end - end - end -end - -end - -function [shapes,shapesWeight,k,D_k] = matRad_siochiConvertToSegments(shapes,shapesWeight,k,tops,bases,visBool,i,D_k,numOfLevels,seqFig,seqSubPlots) -%Convert tops and bases to shape matrices. These are taken as to be the -%shapes of uniform level/elevation after the rods are pushed. -if nargin < 6 - visBool = 0; -end +sequencer = matRad_SequencingPhotonsSiochiLeaf(); +sequencer.visMode = visBool; +sequencer.sequencingLevel = numOfLevels; - -levels = max(tops(:)); - -for level = 1:levels - %check if slab is new - if matRad_siochiDifferentSlab(tops,bases,level) - k = k+1; %increment number of unique slabs - shape_k = (bases <= level).*(level <= tops); %shape of current slab - shapes(:,:,k) = shape_k; - end - shapesWeight(k) = shapesWeight(k)+1; %if slab is not unique, this increments weight again - - if visBool - %show the leaf positions - [dimZ,dimX] = size(tops); - seqSubPlots(4) = subplot(2,2,3.5,'parent',seqFig); - imagesc(shape_k,'parent',seqSubPlots(4)); - hold(seqSubPlots(4),'on'); - set(seqSubPlots(4),'YDir','normal') - xlabel(seqSubPlots(4),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(4),'z - direction perpendicular to leaf motion ') - title(seqSubPlots(4),['beam # ' num2str(i) ' shape # ' num2str(k) ' d_k = ' num2str(shapesWeight(k))]); - for j = 1:dimZ - leftLeafIx = find(shape_k(j,:)>0,1,'first'); - rightLeafIx = find(shape_k(j,:)>0,1,'last'); - if leftLeafIx > 1 - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j-[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j+[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[ leftLeafIx-.5 leftLeafIx-.5],j+[.5 -.5] ,'w','LineWidth',2) - end - if rightLeafIx 0 - - k = k + 1; - - %Plot residual intensity matrix. - if visBool - seqSubPlots(2) = subplot(2,2,2,'parent',seqFig); - imagesc(D_k,'parent',seqSubPlots(2)); - set(seqSubPlots(2),'CLim',[0 L_0],'YDir','normal'); - title(seqSubPlots(2),['k = ' num2str(k) ' - ' num2str(numel(unique(D_k))) ' intensity levels remaining...']); - xlabel(seqSubPlots(2),'x - direction parallel to leaf motion '); - ylabel(seqSubPlots(2),'z - direction perpendicular to leaf motion '); - colorbar - drawnow - end - - %Rounded off integer. Equation 7. - m = floor(log2(L_k)); - - % Convert m=1 if is less than 1. This happens when L_k belong to ]0,2[ - if m < 1 - m = 1; - end - - %Calculate the delivery intensity unit. Equation 6. - d_k = floor(2^(m-1)); - - % Opening matrix. - openingMx = D_k >= d_k; - - % Plot opening matrix. - if visBool - seqSubPlots(3) = subplot(2,2,3,'parent',seqFig); - imagesc(openingMx,'parent',seqSubPlots(3)); - set(seqSubPlots(3),'YDir','normal') - xlabel(seqSubPlots(3),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(3),'z - direction perpendicular to leaf motion ') - title(seqSubPlots(3),'Opening matrix'); - drawnow - end - - if strcmp(mode,'sw') % sliding window technique! - for j = 1:dimOfFluenceMxZ - openIx = find(openingMx(j,:) == 1,1,'first'); - if ~isempty(openIx) - closeIx = find(openingMx(j,openIx+1:end) == 0,1,'first'); - if ~isempty(closeIx) - openingMx(j,openIx+closeIx:end) = 0; - end - end - - end - elseif strcmp(mode,'rl') % reducing levels technique! - for j = 1:dimOfFluenceMxZ - [maxVal,maxIx] = max(openingMx(j,:) .* D_k(j,:)); - if maxVal > 0 - closeIx = maxIx + find(openingMx(j,maxIx+1:end) == 0,1,'first'); - if ~isempty(closeIx) - openingMx(j,closeIx:end) = 0; - end - openIx = find(openingMx(j,1:maxIx-1) == 0,1,'last'); - if ~isempty(openIx) - openingMx(j,1:openIx) = 0; - end - end - - end - - end - - shape_k = openingMx * d_k; - - if visBool - seqSubPlots(4) = subplot(2,2,4,'parent',seqFig); - imagesc(shape_k,'parent',seqSubPlots(4)); - set(seqSubPlots(4),'YDir','normal') - hold(seqSubPlots(4),'on'); - xlabel(seqSubPlots(4),'x - direction parallel to leaf motion ') - ylabel(seqSubPlots(4),'z - direction perpendicular to leaf motion ') - title(seqSubPlots(4),['beam # ' num2str(i) ' shape # ' num2str(k) ' d_k = ' num2str(d_k)]); - for j = 1:dimOfFluenceMxZ - leftLeafIx = find(shape_k(j,:)>0,1,'first'); - rightLeafIx = find(shape_k(j,:)>0,1,'last'); - if leftLeafIx > 1 - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j-[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[.5 leftLeafIx-.5],j+[.5 .5] ,'w','LineWidth',2) - plot(seqSubPlots(4),[ leftLeafIx-.5 leftLeafIx-.5],j+[.5 -.5] ,'w','LineWidth',2) - end - if rightLeafIx0 - - sequencing.beam(i).numOfShapes = k; - sequencing.beam(i).shapes = shapes(:,:,1:k); - sequencing.beam(i).shapesWeight = shapesWeight(1:k)/numOfLevels*calFac; - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = D_0; - - else - sequencing.beam(i).numOfShapes = 1; - sequencing.beam(i).shapes = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).shapesWeight = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - sequencing.beam(i).bixelIx = 1+offset:numOfRaysPerBeam+offset; - sequencing.beam(i).fluence = zeros(dimOfFluenceMxZ,dimOfFluenceMxX); - end - - - sequencing.w(1+offset:numOfRaysPerBeam+offset,1) = D_0(indInFluenceMx)/numOfLevels*calFac; - - offset = offset + numOfRaysPerBeam; - -end - -resultGUI.w = sequencing.w; -resultGUI.wSequenced = sequencing.w; - -resultGUI.sequencing = sequencing; -resultGUI.apertureInfo = matRad_sequencing2ApertureInfo(sequencing,stf); +matRad_cfg.dispWarning('This function is Outdated use new SequencingClass'); -doseSequencedDoseGrid = reshape(dij.physicalDose{1} * sequencing.w,dij.doseGrid.dimensions); -% interpolate to ct grid for visualiation & analysis -resultGUI.physicalDose = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ... - doseSequencedDoseGrid, ... - dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z); +sequencer = matRad_SequencingPhotonsXiaLeaf(); +sequencer.visMode = visBool; +sequencer.sequencingLevel = numOfLevels; -% if weights exists from an former DAO remove it -if isfield(resultGUI,'wDao') - resultGUI = rmfield(resultGUI,'wDao'); +sequence = sequencer.sequence(resultGUI.w, stf); +if ~isempty(dij) + resultGUI = matRad_calcCubes(sequence.w, dij); +else + matRad_cfg.dispWarning('Dose not recalcaulted with sequenced fluence'); end +resultGUI.sequencing = sequence; end - diff --git a/matRad/steering/matRad_StfGeneratorExternalRayBixelAbstract.m b/matRad/steering/matRad_StfGeneratorExternalRayBixelAbstract.m index a47dd0203..a3f48420c 100644 --- a/matRad/steering/matRad_StfGeneratorExternalRayBixelAbstract.m +++ b/matRad/steering/matRad_StfGeneratorExternalRayBixelAbstract.m @@ -16,39 +16,39 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% properties - gantryAngles = 0; - couchAngles = 0; - bixelWidth = 0; + gantryAngles = 0 + couchAngles = 0 + bixelWidth = 0 isoCenter fillEmptyBixels centered end properties (Dependent) - numOfBeams; + numOfBeams end - properties (Access = protected, Hidden) - lockAngleUpdate = false; + properties (Access = protected, Hidden) + lockAngleUpdate = false end + methods - methods - function this = matRad_StfGeneratorExternalRayBixelAbstract(pln) + function this = matRad_StfGeneratorExternalRayBixelAbstract(pln) % Constructs ExternalStfGenerator with or without pln if nargin < 1 pln = []; end this@matRad_StfGeneratorBase(pln); - end + end - function setDefaults(this) + function setDefaults(this) % Set default values for ExternalStfGenerator this.setDefaults@matRad_StfGeneratorBase(); - end + end - function nBeams = get.numOfBeams(this) + function nBeams = get.numOfBeams(this) % Return number of beams obtained from angles nBeams = numel(this.gantryAngles); @@ -56,88 +56,90 @@ function setDefaults(this) matRad_cfg = MatRad_Config.instance(); matRad_cfg.dispWarning('For some reason, we have a different number of beam and couch angles!'); end - end + end - function set.gantryAngles(this,angles) + function set.gantryAngles(this, angles) % Set gantry angles and update couch angles if necessary - validateattributes(angles,{'numeric'},{'vector','nonempty','nonnan'}); + validateattributes(angles, {'numeric'}, {'vector', 'nonempty', 'nonnan'}); oldAngles = this.gantryAngles; this.gantryAngles = angles; if ~this.lockAngleUpdate this.lockAngleUpdate = true; if numel(this.gantryAngles) > numel(this.couchAngles) - %Append Couch angles with zeros - this.couchAngles = [this.couchAngles zeros(1,numel(this.gantryAngles)-numel(this.couchAngles))]; + % Append Couch angles with zeros + this.couchAngles = [this.couchAngles zeros(1, numel(this.gantryAngles) - numel(this.couchAngles))]; elseif numel(this.couchAngles) > numel(this.gantryAngles) - %Try to identify the removed beam angles - [removedAngles,ix] = setdiff(oldAngles,this.gantryAngles); - + % Try to identify the removed beam angles + [removedAngles, ix] = setdiff(oldAngles, this.gantryAngles); + nRemovedAngles = numel(this.couchAngles) - numel(this.gantryAngles); - + if ~isempty(ix) && numel(ix) == nRemovedAngles - %Remove corresponding couch angles - this.couchAngles(ix) = []; - else - this.couchAngles(end-nRemovedAngles+1:end) = []; + % Remove corresponding couch angles + this.couchAngles(ix) = []; + else + this.couchAngles(end - nRemovedAngles + 1:end) = []; end end this.lockAngleUpdate = false; - end - end + end + end - function set.couchAngles(this,angles) + function set.couchAngles(this, angles) % Set couch angles and update gantry angles if necessary - validateattributes(angles,{'numeric'},{'vector','nonempty','nonnan'}); + validateattributes(angles, {'numeric'}, {'vector', 'nonempty', 'nonnan'}); oldAngles = this.couchAngles; this.couchAngles = angles; if ~this.lockAngleUpdate this.lockAngleUpdate = true; if numel(this.couchAngles) > numel(this.gantryAngles) - %Append Gantry angles with zeros - this.gantryAngles = [this.gantryAngles zeros(1,numel(this.couchAngles)-numel(this.gantryAngles))]; + % Append Gantry angles with zeros + this.gantryAngles = [this.gantryAngles zeros(1, numel(this.couchAngles) - numel(this.gantryAngles))]; elseif numel(this.gantryAngles) > numel(this.couchAngles) - %Try to identify the removed couch angles - [removedAngles,ix] = setdiff(oldAngles,this.couchAngles); + % Try to identify the removed couch angles + [removedAngles, ix] = setdiff(oldAngles, this.couchAngles); nRemovedAngles = numel(this.gantryAngles) - numel(this.couchAngles); if ~isempty(ix) && numel(ix) == nRemovedAngles - %Remove corresponding gantry angles - this.gantryAngles(ix) = []; - else - this.gantryAngles(end-nRemovedAngles+1:end) = []; - end + % Remove corresponding gantry angles + this.gantryAngles(ix) = []; + else + this.gantryAngles(end - nRemovedAngles + 1:end) = []; + end end this.lockAngleUpdate = false; - end - end + end + end + end methods (Access = protected) + function initialize(this) - + this.initialize@matRad_StfGeneratorBase(); if this.visMode > 1 visBool = true; - else + else visBool = false; end if isempty(this.isoCenter) - this.isoCenter = matRad_getIsoCenter(this.cst,this.ct,visBool); + this.isoCenter = matRad_getIsoCenter(this.cst, this.ct, visBool); end - if ~isequal(size(this.isoCenter),[this.numOfBeams,3]) && size(this.isoCenter,1) ~= 1 + if ~isequal(size(this.isoCenter), [this.numOfBeams, 3]) && size(this.isoCenter, 1) ~= 1 matRad_cfg = MatRad_Config.instance(); matRad_cfg.dispWarning('IsoCenter invalid, creating new one automatically!'); - this.isoCenter = matRad_getIsoCenter(this.cst,this.ct,visBool); + this.isoCenter = matRad_getIsoCenter(this.cst, this.ct, visBool); end - - if size(this.isoCenter,1) == 1 - this.isoCenter = repmat(this.isoCenter,this.numOfBeams,1); + + if size(this.isoCenter, 1) == 1 + this.isoCenter = repmat(this.isoCenter, this.numOfBeams, 1); end end @@ -145,67 +147,67 @@ function initialize(this) % Correct for iso center position. Whit this correction Isocenter is % (0,0,0) [mm] isoCoords = this.voxTargetWorldCoords - beam.isoCenter; - + % Get the (active) rotation matrix. We perform a passive/system % rotation with row vector coordinates, which would introduce two % inversions / transpositions of the matrix, thus no changes to the - % rotation matrix are necessary - rotMat_system_T = matRad_getRotationMatrix(beam.gantryAngle,beam.couchAngle); + % rotation matrix are necessary + rotMat_system_T = matRad_getRotationMatrix(beam.gantryAngle, beam.couchAngle); - coordsAtIsoCenterPlane = isoCoords*rotMat_system_T; - coordsAtIsoCenterPlane = (coordsAtIsoCenterPlane*beam.SAD)./(beam.SAD+coordsAtIsoCenterPlane(:,2)); - coordsAtIsoCenterPlane(:,2) = 0; + coordsAtIsoCenterPlane = isoCoords * rotMat_system_T; + coordsAtIsoCenterPlane = (coordsAtIsoCenterPlane * beam.SAD) ./ (beam.SAD + coordsAtIsoCenterPlane(:, 2)); + coordsAtIsoCenterPlane(:, 2) = 0; - rayPos = unique(beam.bixelWidth*round(coordsAtIsoCenterPlane./beam.bixelWidth),'rows'); + rayPos = unique(beam.bixelWidth * round(coordsAtIsoCenterPlane ./ beam.bixelWidth), 'rows'); % pad ray position array if resolution of target voxel grid not sufficient maxCtResolution = max([this.ct.resolution.x this.ct.resolution.y this.ct.resolution.z]); if beam.bixelWidth < maxCtResolution origRayPos = rayPos; - for j = -floor(maxCtResolution/beam.bixelWidth):floor(maxCtResolution/beam.bixelWidth) - for k = -floor(maxCtResolution/beam.bixelWidth):floor(maxCtResolution/beam.bixelWidth) - if abs(j)+abs(k)==0 - continue; + for j = -floor(maxCtResolution / beam.bixelWidth):floor(maxCtResolution / beam.bixelWidth) + for k = -floor(maxCtResolution / beam.bixelWidth):floor(maxCtResolution / beam.bixelWidth) + if abs(j) + abs(k) == 0 + continue end - rayPos = [rayPos; origRayPos(:,1)+j*beam.bixelWidth origRayPos(:,2) origRayPos(:,3)+k*beam.bixelWidth]; + rayPos = [rayPos; origRayPos(:, 1) + j * beam.bixelWidth origRayPos(:, 2) origRayPos(:, 3) + k * beam.bixelWidth]; end end end % remove spaces within rows of bixels for DAO - %TODO Only Photons + % TODO Only Photons if this.fillEmptyBixels % create single x,y,z vectors - x = rayPos(:,1); - y = rayPos(:,2); - z = rayPos(:,3); + x = rayPos(:, 1); + y = rayPos(:, 2); + z = rayPos(:, 3); uniZ = unique(z); for j = 1:numel(uniZ) x_loc = x(z == uniZ(j)); x_min = min(x_loc); x_max = max(x_loc); x = [x; (x_min:beam.bixelWidth:x_max)']; - y = [y; zeros((x_max-x_min)/beam.bixelWidth+1,1)]; - z = [z; uniZ(j)*ones((x_max-x_min)/beam.bixelWidth+1,1)]; + y = [y; zeros((x_max - x_min) / beam.bixelWidth + 1, 1)]; + z = [z; uniZ(j) * ones((x_max - x_min) / beam.bixelWidth + 1, 1)]; end - rayPos = [x,y,z]; + rayPos = [x, y, z]; end - % remove double rays - rayPos = unique(rayPos,'rows'); + % remove double rays + rayPos = unique(rayPos, 'rows'); end - function beam = initBeamData(this,beamIndex) + function beam = initBeamData(this, beamIndex) beam.gantryAngle = this.gantryAngles(beamIndex); beam.couchAngle = this.couchAngles(beamIndex); - beam.isoCenter = this.isoCenter(beamIndex,:); + beam.isoCenter = this.isoCenter(beamIndex, :); beam.bixelWidth = this.bixelWidth; beam.radiationMode = this.radiationMode; % Newer machine files have "name" instead of "machine" - if ~isfield(this.machine.meta,'machine') - machineName = this.machine.meta.name; + if ~isfield(this.machine.meta, 'machine') + machineName = this.machine.meta.name; else machineName = this.machine.meta.machine; end @@ -213,275 +215,274 @@ function initialize(this) beam.SAD = this.machine.meta.SAD; end - - + function stf = generateSourceGeometry(this) % Generate basic source geometry (beams & rays) for external beam therapy matRad_cfg = MatRad_Config.instance; - + % loop over all angles for i = 1:length(this.gantryAngles) % Save meta information for treatment plan beam = this.initBeamData(i); - beam = this.initRays(beam); + beam = this.initRays(beam); beam = this.setBeamletEnergies(beam); - if ~isfield(beam.ray,'energy') + if ~isfield(beam.ray, 'energy') matRad_cfg.dispError('Error generating stf struct: no suitable energies found. Check if bixelwidth is too large.'); - end + end - beam = this.finalizeBeam(beam); % Post Processing for Ions + beam = this.finalizeBeam(beam); % Post Processing for Ions stf(i) = beam; % Show progress if matRad_cfg.logLevel > 2 - matRad_progress(i,length(this.gantryAngles)); + matRad_progress(i, length(this.gantryAngles)); end %% visualization if this.visMode > 0 - - %center coordinates + + % center coordinates x = this.ct.x - stf(i).isoCenter(1); y = this.ct.y - stf(i).isoCenter(2); z = this.ct.z - stf(i).isoCenter(3); d = sqrt(sum(([x(1) y(2) z(3)] - [x(end) y(end) z(end)]).^2)); - limits = [-d/2 d/2 -d/2 d/2 -d/2 d/2]; + limits = [-d / 2 d / 2 -d / 2 d / 2 -d / 2 d / 2]; clf; % first subplot: visualization in bev - hAxBEV = subplot(1,2,1); - set(hAxBEV,'DataAspectRatioMode','manual'); + hAxBEV = subplot(1, 2, 1); + set(hAxBEV, 'DataAspectRatioMode', 'manual'); hold on; isoCoords = this.voxTargetWorldCoords - stf(i).isoCenter; - + % Get the (active) rotation matrix. We perform a passive/system % rotation with row vector coordinates, which would introduce two % inversions / transpositions of the matrix, thus no changes to the - % rotation matrix are necessary - rotMat_system_T = matRad_getRotationMatrix(stf(i).gantryAngle,stf(i).couchAngle); + % rotation matrix are necessary + rotMat_system_T = matRad_getRotationMatrix(stf(i).gantryAngle, stf(i).couchAngle); - rot_coords = isoCoords*rotMat_system_T; + rot_coords = isoCoords * rotMat_system_T; % plot rotated target coordinates - plot3(rot_coords(:,1),rot_coords(:,2),rot_coords(:,3),'r.') + plot3(rot_coords(:, 1), rot_coords(:, 2), rot_coords(:, 3), 'r.'); % surface rendering if this.visMode == 2 % computes surface - patSurfCube = 0*this.ct.cube{1}; - idx = [this.cst{:,4}]; + patSurfCube = 0 * this.ct.cube{1}; + idx = [this.cst{:, 4}]; idx = unique(vertcat(idx{:})); patSurfCube(idx) = 1; - [f,v] = isosurface(x,y,z,patSurfCube,.5); + [f, v] = isosurface(x, y, z, patSurfCube, .5); + + vRot = v * rotMat_system_T; - vRot = v*rotMat_system_T; - % rotate surface - rotated_surface = v*rotMat_system_T; + rotated_surface = v * rotMat_system_T; % surface rendering - surface = patch('Faces',f,'Vertices',rotated_surface); - set(surface,'FaceColor',[0 0 1],'EdgeColor','none','FaceAlpha',.4); + surface = patch('Faces', f, 'Vertices', rotated_surface); + set(surface, 'FaceColor', [0 0 1], 'EdgeColor', 'none', 'FaceAlpha', .4); lighting gouraud; end % plot projection matrix: coordinates at isocenter - plot3(rayPos_bev(:,1),rayPos_bev(:,2),rayPos_bev(:,3),'k.'); + plot3(rayPos_bev(:, 1), rayPos_bev(:, 2), rayPos_bev(:, 3), 'k.'); % Plot matrix border of matrix at isocenter for j = 1:stf(i).numOfRays % Compute border for every bixels - targetPoint_vox_X_1 = stf(i).ray(j).targetPoint_bev(:,1) + stf(i).bixelWidth; - targetPoint_vox_Y_1 = stf(i).ray(j).targetPoint_bev(:,2); - targetPoint_vox_Z_1 = stf(i).ray(j).targetPoint_bev(:,3) + stf(i).bixelWidth; + targetPoint_vox_X_1 = stf(i).ray(j).targetPoint_bev(:, 1) + stf(i).bixelWidth; + targetPoint_vox_Y_1 = stf(i).ray(j).targetPoint_bev(:, 2); + targetPoint_vox_Z_1 = stf(i).ray(j).targetPoint_bev(:, 3) + stf(i).bixelWidth; - targetPoint_vox_X_2 = stf(i).ray(j).targetPoint_bev(:,1) + stf(i).bixelWidth; - targetPoint_vox_Y_2 = stf(i).ray(j).targetPoint_bev(:,2); - targetPoint_vox_Z_2 = stf(i).ray(j).targetPoint_bev(:,3) - stf(i).bixelWidth; + targetPoint_vox_X_2 = stf(i).ray(j).targetPoint_bev(:, 1) + stf(i).bixelWidth; + targetPoint_vox_Y_2 = stf(i).ray(j).targetPoint_bev(:, 2); + targetPoint_vox_Z_2 = stf(i).ray(j).targetPoint_bev(:, 3) - stf(i).bixelWidth; - targetPoint_vox_X_3 = stf(i).ray(j).targetPoint_bev(:,1) - stf(i).bixelWidth; - targetPoint_vox_Y_3 = stf(i).ray(j).targetPoint_bev(:,2); - targetPoint_vox_Z_3 = stf(i).ray(j).targetPoint_bev(:,3) - stf(i).bixelWidth; + targetPoint_vox_X_3 = stf(i).ray(j).targetPoint_bev(:, 1) - stf(i).bixelWidth; + targetPoint_vox_Y_3 = stf(i).ray(j).targetPoint_bev(:, 2); + targetPoint_vox_Z_3 = stf(i).ray(j).targetPoint_bev(:, 3) - stf(i).bixelWidth; - targetPoint_vox_X_4 = stf(i).ray(j).targetPoint_bev(:,1) - stf(i).bixelWidth; - targetPoint_vox_Y_4 = stf(i).ray(j).targetPoint_bev(:,2); - targetPoint_vox_Z_4 = stf(i).ray(j).targetPoint_bev(:,3) + stf(i).bixelWidth; + targetPoint_vox_X_4 = stf(i).ray(j).targetPoint_bev(:, 1) - stf(i).bixelWidth; + targetPoint_vox_Y_4 = stf(i).ray(j).targetPoint_bev(:, 2); + targetPoint_vox_Z_4 = stf(i).ray(j).targetPoint_bev(:, 3) + stf(i).bixelWidth; % plot - plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_1],[stf(i).sourcePoint_bev(2) targetPoint_vox_Y_1],[stf(i).sourcePoint_bev(3) targetPoint_vox_Z_1],'g') - plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_2],[stf(i).sourcePoint_bev(2) targetPoint_vox_Y_2],[stf(i).sourcePoint_bev(3) targetPoint_vox_Z_2],'g') - plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_3],[stf(i).sourcePoint_bev(2) targetPoint_vox_Y_3],[stf(i).sourcePoint_bev(3) targetPoint_vox_Z_3],'g') - plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_4],[stf(i).sourcePoint_bev(2) targetPoint_vox_Y_4],[stf(i).sourcePoint_bev(3) targetPoint_vox_Z_4],'g') + plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_1], [stf(i).sourcePoint_bev(2) targetPoint_vox_Y_1], [stf(i).sourcePoint_bev(3) targetPoint_vox_Z_1], 'g'); + plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_2], [stf(i).sourcePoint_bev(2) targetPoint_vox_Y_2], [stf(i).sourcePoint_bev(3) targetPoint_vox_Z_2], 'g'); + plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_3], [stf(i).sourcePoint_bev(2) targetPoint_vox_Y_3], [stf(i).sourcePoint_bev(3) targetPoint_vox_Z_3], 'g'); + plot3([stf(i).sourcePoint_bev(1) targetPoint_vox_X_4], [stf(i).sourcePoint_bev(2) targetPoint_vox_Y_4], [stf(i).sourcePoint_bev(3) targetPoint_vox_Z_4], 'g'); end % Plot properties - view(hAxBEV,0,-90); - xlabel(hAxBEV,'X [mm]'); - ylabel(hAxBEV,'Y [mm]'); - zlabel(hAxBEV,'Z [mm]'); - title(hAxBEV,'Beam''s eye view'); + view(hAxBEV, 0, -90); + xlabel(hAxBEV, 'X [mm]'); + ylabel(hAxBEV, 'Y [mm]'); + zlabel(hAxBEV, 'Z [mm]'); + title(hAxBEV, 'Beam''s eye view'); - axis(hAxBEV,limits); + axis(hAxBEV, limits); % second subplot: visualization in lps coordinate system - hAxLPS = subplot(1,2,2); + hAxLPS = subplot(1, 2, 2); % Plot target coordinates whitout any rotation - plot3(isoCoords(:,1),isoCoords(:,2),isoCoords(:,3),'r.') + plot3(isoCoords(:, 1), isoCoords(:, 2), isoCoords(:, 3), 'r.'); hold on; % Rotated projection matrix at isocenter - isocenter_plane_coor = rayPos_bev*rotMat_vectors_T; + isocenter_plane_coor = rayPos_bev * rotMat_vectors_T; % Plot isocenter plane - plot3(isocenter_plane_coor(:,1),isocenter_plane_coor(:,2),isocenter_plane_coor(:,3),'y.'); + plot3(isocenter_plane_coor(:, 1), isocenter_plane_coor(:, 2), isocenter_plane_coor(:, 3), 'y.'); % Plot rotated bixels border. for j = 1:stf(i).numOfRays % Generate rotated projection target points. - targetPoint_vox_1_rotated = [stf(i).ray(j).targetPoint_bev(:,1) + stf(i).bixelWidth,stf(i).ray(j).targetPoint_bev(:,2),stf(i).ray(j).targetPoint_bev(:,3) + stf(i).bixelWidth]*rotMat_vectors_T; - targetPoint_vox_2_rotated = [stf(i).ray(j).targetPoint_bev(:,1) + stf(i).bixelWidth,stf(i).ray(j).targetPoint_bev(:,2),stf(i).ray(j).targetPoint_bev(:,3) - stf(i).bixelWidth]*rotMat_vectors_T; - targetPoint_vox_3_rotated = [stf(i).ray(j).targetPoint_bev(:,1) - stf(i).bixelWidth,stf(i).ray(j).targetPoint_bev(:,2),stf(i).ray(j).targetPoint_bev(:,3) - stf(i).bixelWidth]*rotMat_vectors_T; - targetPoint_vox_4_rotated = [stf(i).ray(j).targetPoint_bev(:,1) - stf(i).bixelWidth,stf(i).ray(j).targetPoint_bev(:,2),stf(i).ray(j).targetPoint_bev(:,3) + stf(i).bixelWidth]*rotMat_vectors_T; + targetPoint_vox_1_rotated = [stf(i).ray(j).targetPoint_bev(:, 1) + stf(i).bixelWidth, stf(i).ray(j).targetPoint_bev(:, 2), stf(i).ray(j).targetPoint_bev(:, 3) + stf(i).bixelWidth] * rotMat_vectors_T; + targetPoint_vox_2_rotated = [stf(i).ray(j).targetPoint_bev(:, 1) + stf(i).bixelWidth, stf(i).ray(j).targetPoint_bev(:, 2), stf(i).ray(j).targetPoint_bev(:, 3) - stf(i).bixelWidth] * rotMat_vectors_T; + targetPoint_vox_3_rotated = [stf(i).ray(j).targetPoint_bev(:, 1) - stf(i).bixelWidth, stf(i).ray(j).targetPoint_bev(:, 2), stf(i).ray(j).targetPoint_bev(:, 3) - stf(i).bixelWidth] * rotMat_vectors_T; + targetPoint_vox_4_rotated = [stf(i).ray(j).targetPoint_bev(:, 1) - stf(i).bixelWidth, stf(i).ray(j).targetPoint_bev(:, 2), stf(i).ray(j).targetPoint_bev(:, 3) + stf(i).bixelWidth] * rotMat_vectors_T; % Plot rotated target points. - plot3([stf(i).sourcePoint(1) targetPoint_vox_1_rotated(:,1)],[stf(i).sourcePoint(2) targetPoint_vox_1_rotated(:,2)],[stf(i).sourcePoint(3) targetPoint_vox_1_rotated(:,3)],'g') - plot3([stf(i).sourcePoint(1) targetPoint_vox_2_rotated(:,1)],[stf(i).sourcePoint(2) targetPoint_vox_2_rotated(:,2)],[stf(i).sourcePoint(3) targetPoint_vox_2_rotated(:,3)],'g') - plot3([stf(i).sourcePoint(1) targetPoint_vox_3_rotated(:,1)],[stf(i).sourcePoint(2) targetPoint_vox_3_rotated(:,2)],[stf(i).sourcePoint(3) targetPoint_vox_3_rotated(:,3)],'g') - plot3([stf(i).sourcePoint(1) targetPoint_vox_4_rotated(:,1)],[stf(i).sourcePoint(2) targetPoint_vox_4_rotated(:,2)],[stf(i).sourcePoint(3) targetPoint_vox_4_rotated(:,3)],'g') + plot3([stf(i).sourcePoint(1) targetPoint_vox_1_rotated(:, 1)], [stf(i).sourcePoint(2) targetPoint_vox_1_rotated(:, 2)], [stf(i).sourcePoint(3) targetPoint_vox_1_rotated(:, 3)], 'g'); + plot3([stf(i).sourcePoint(1) targetPoint_vox_2_rotated(:, 1)], [stf(i).sourcePoint(2) targetPoint_vox_2_rotated(:, 2)], [stf(i).sourcePoint(3) targetPoint_vox_2_rotated(:, 3)], 'g'); + plot3([stf(i).sourcePoint(1) targetPoint_vox_3_rotated(:, 1)], [stf(i).sourcePoint(2) targetPoint_vox_3_rotated(:, 2)], [stf(i).sourcePoint(3) targetPoint_vox_3_rotated(:, 3)], 'g'); + plot3([stf(i).sourcePoint(1) targetPoint_vox_4_rotated(:, 1)], [stf(i).sourcePoint(2) targetPoint_vox_4_rotated(:, 2)], [stf(i).sourcePoint(3) targetPoint_vox_4_rotated(:, 3)], 'g'); end % surface rendering if this.visMode == 2 - surface = patch('Faces',f,'Vertices',v); - set(surface,'FaceColor',[0 0 1],'EdgeColor','none','FaceAlpha',.4); + surface = patch('Faces', f, 'Vertices', v); + set(surface, 'FaceColor', [0 0 1], 'EdgeColor', 'none', 'FaceAlpha', .4); lighting gouraud; end % labels etc. daspect([1 1 1]); - view(0,-90); - xlabel(hAxBEV,'X [mm]'); - ylabel(hAxBEV,'Y [mm]'); - zlabel(hAxBEV,'Z [mm]'); + view(0, -90); + xlabel(hAxBEV, 'X [mm]'); + ylabel(hAxBEV, 'Y [mm]'); + zlabel(hAxBEV, 'Z [mm]'); title('lps coordinate system'); axis(limits); drawnow(); pause(1); end - % Show progress if matRad_cfg.logLevel > 2 - matRad_progress(i,length(this.gantryAngles)); + matRad_progress(i, length(this.gantryAngles)); end end end - function beam = initRays(this,beam) + function beam = initRays(this, beam) % source position in bev beam.sourcePoint_bev = [0 -beam.SAD 0]; rayPos_bev = this.getRayPositionMatrix(beam); % Save the number of rays - beam.numOfRays = size(rayPos_bev,1); + beam.numOfRays = size(rayPos_bev, 1); % Save ray and target position in beam eye's view (bev) for j = 1:beam.numOfRays - beam.ray(j).rayPos_bev = rayPos_bev(j,:); - beam.ray(j).targetPoint_bev = [2*beam.ray(j).rayPos_bev(1) ... - beam.SAD ... - 2*beam.ray(j).rayPos_bev(3)]; + beam.ray(j).rayPos_bev = rayPos_bev(j, :); + beam.ray(j).targetPoint_bev = [2 * beam.ray(j).rayPos_bev(1) ... + beam.SAD ... + 2 * beam.ray(j).rayPos_bev(3)]; end % get (active) rotation matrix % transpose matrix because we are working with row vectors - rotMat_vectors_T = transpose(matRad_getRotationMatrix(beam.gantryAngle,beam.couchAngle)); + rotMat_vectors_T = transpose(matRad_getRotationMatrix(beam.gantryAngle, beam.couchAngle)); + + % Source point in LPS system + beam.sourcePoint = beam.sourcePoint_bev * rotMat_vectors_T; - %Source point in LPS system - beam.sourcePoint = beam.sourcePoint_bev*rotMat_vectors_T; - % Save ray and target position in lps system. for j = 1:beam.numOfRays - beam.ray(j).rayPos = beam.ray(j).rayPos_bev*rotMat_vectors_T; - beam.ray(j).targetPoint = beam.ray(j).targetPoint_bev*rotMat_vectors_T; - end + beam.ray(j).rayPos = beam.ray(j).rayPos_bev * rotMat_vectors_T; + beam.ray(j).targetPoint = beam.ray(j).targetPoint_bev * rotMat_vectors_T; + end end - function beam = setBeamletEnergies(this,beam) + function beam = setBeamletEnergies(this, beam) % Abstract method to set source energy on beam. Implemented in Subclasses - throw(MException('MATLAB:class:AbstractMember','This method is not implemented in the base class')); + throw(MException('MATLAB:class:AbstractMember', 'This method is not implemented in the base class')); end - - function beam = finalizeBeam(this,beam) - %Finalize meta data of beam + function beam = finalizeBeam(this, beam) + % Finalize meta data of beam matRad_cfg = MatRad_Config.instance(); numOfRays = numel(beam.ray); - if ~isfield(beam,'numOfRays') + if ~isfield(beam, 'numOfRays') beam.numOfRays = numOfRays; end - if ~isequal(numOfRays,beam.numOfRays) + if ~isequal(numOfRays, beam.numOfRays) matRad_cfg.dispError('Validation of number of rays failed!'); end % Calculate the number of bixels per ray - numOfBixelsPerRay = arrayfun(@(r) numel(r.energy),beam.ray); - if ~isfield(beam,'numOfBixelsPerRay') + numOfBixelsPerRay = arrayfun(@(r) numel(r.energy), beam.ray); + if ~isfield(beam, 'numOfBixelsPerRay') beam.numOfBixelsPerRay = numOfBixelsPerRay; end - if ~isequal(numOfBixelsPerRay,beam.numOfBixelsPerRay) + if ~isequal(numOfBixelsPerRay, beam.numOfBixelsPerRay) matRad_cfg.dispError('Validation of number of bixels per ray failed!'); end - - % Calculate the total number of bixels + + % Calculate the total number of bixels totalNumOfBixels = sum(beam.numOfBixelsPerRay); - if ~isfield(beam,'totalNumOfBixels') + if ~isfield(beam, 'totalNumOfBixels') beam.totalNumOfBixels = totalNumOfBixels; end - if ~isequal(totalNumOfBixels,beam.totalNumOfBixels) + if ~isequal(totalNumOfBixels, beam.totalNumOfBixels) matRad_cfg.dispError('Validation of total number of bixels failed!'); end - + end + end methods (Static) - function [available,msg] = isAvailable(pln,machine) - % see superclass for information - + + function [available, msg] = isAvailable(pln, machine) + % see superclass for information + if nargin < 2 machine = matRad_loadMachine(pln); end - %checkBasic - available = isfield(machine,'meta') && isfield(machine,'data'); + % checkBasic + available = isfield(machine, 'meta') && isfield(machine, 'data'); - available = available && any(isfield(machine.meta,{'machine','name'})); + available = available && any(isfield(machine.meta, {'machine', 'name'})); + + available = available && isfield(machine.meta, 'SAD') && isscalar(machine.meta.SAD); - available = available && isfield(machine.meta,'SAD') && isscalar(machine.meta.SAD); - if ~available - msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; + msg = 'Your machine file is invalid and does not contain the basic field (meta/data/radiationMode)!'; else msg = []; end end + end end - diff --git a/matRad/steering/matRad_StfGeneratorPhotonRayBixelAbstract.m b/matRad/steering/matRad_StfGeneratorPhotonRayBixelAbstract.m index b1ff73e8c..524cfafd4 100644 --- a/matRad/steering/matRad_StfGeneratorPhotonRayBixelAbstract.m +++ b/matRad/steering/matRad_StfGeneratorPhotonRayBixelAbstract.m @@ -14,7 +14,6 @@ % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - methods function this = matRad_StfGeneratorPhotonRayBixelAbstract(pln) % Constructs ExternalStfGenerator with or without pln @@ -22,10 +21,12 @@ pln = []; end this@matRad_StfGeneratorExternalRayBixelAbstract(pln); + end function setDefaults(this) % Set default values for ExternalStfGenerator + this.fillEmptyBixels = true; this.setDefaults@matRad_StfGeneratorExternalRayBixelAbstract(); end end diff --git a/test/doseCalc/test_TopasMCEngine.m b/test/doseCalc/test_TopasMCEngine.m index a19ce6660..35df1888b 100644 --- a/test/doseCalc/test_TopasMCEngine.m +++ b/test/doseCalc/test_TopasMCEngine.m @@ -1,4 +1,4 @@ -function test_suite = test_TopasMCEngine +function test_suite = test_topasMCEngine test_functions = localfunctions(); @@ -22,7 +22,7 @@ assertTrue(isa(engine, 'DoseEngines.matRad_TopasMCEngine')); end -function test_TopasMCdoseCalcBasic +function test_topasMCdoseCalcBasic % test if all the necessary output files are written for a couple of cases. % i am not using the default number of histories for testing her, instead 1e6. % Because the files are just written and not simulated so we dont care about simulation time. @@ -43,13 +43,15 @@ w = ones(1, sum([stf(:).totalNumOfBixels])); if strcmp(radModes{i}, 'photons') - pln.propOpt.runSequencing = 1; pln.propOpt.runDAO = 1; dij = matRad_calcDoseInfluence(ct, cst, stf, pln); resultGUI = matRad_calcCubes(ones(dij.totalNumOfBixels, 1), dij); resultGUI.wUnsequenced = ones(dij.totalNumOfBixels, 1); - resultGUI = matRad_siochiLeafSequencing(resultGUI, stf, dij, 5); - [pln, stf] = matRad_aperture2collimation(pln, stf, resultGUI.sequencing, resultGUI.apertureInfo); + pln.propSeq.sequencer = 'siochi'; + pln.propSeq.sequencingLevel = 5; + sequencer = matRad_SequencerBase.getSequencerFromPln(pln); + resultGUI = matRad_sequencing(resultGUI, stf, pln); + [pln, stf] = sequencer.aperture2collimation(pln, stf, resultGUI.sequencing); w = resultGUI.w; pln.propDoseCalc.beamProfile = 'phasespace'; end @@ -74,7 +76,7 @@ rmdir(folderName, 's'); % clean up end -function test_TopasMCdoseCalcBasicRBE +function test_topasMCdoseCalcBasicRBE % test if all the necessary output files are written for a couple of cases. % i am not using the default number of histories for testing her, instead 1e6. % Because the files are just written and not simulated so we dont care about simulation time. @@ -123,7 +125,7 @@ rmdir(folderName, 's'); % clean up end -function test_TopasMCdoseCalcMultRuns +function test_topasMCdoseCalcMultRuns numOfRuns = 5; radModes = DoseEngines.matRad_TopasMCEngine.possibleRadiationModes; matRad_cfg = MatRad_Config.instance(); @@ -138,13 +140,14 @@ w = ones(1, sum([stf(:).totalNumOfBixels])); if strcmp(radModes{i}, 'photons') - pln.propOpt.runSequencing = 1; - pln.propOpt.runDAO = 1; dij = matRad_calcDoseInfluence(ct, cst, stf, pln); resultGUI = matRad_calcCubes(ones(dij.totalNumOfBixels, 1), dij); resultGUI.wUnsequenced = ones(dij.totalNumOfBixels, 1); - resultGUI = matRad_siochiLeafSequencing(resultGUI, stf, dij, 5); - [pln, stf] = matRad_aperture2collimation(pln, stf, resultGUI.sequencing, resultGUI.apertureInfo); + pln.propSeq.sequencer = 'siochi'; + pln.propSeq.sequencingLevel = 5; + sequencer = matRad_SequencerBase.getSequencerFromPln(pln); + resultGUI = matRad_sequencing(resultGUI, stf, pln); + [pln, stf] = sequencer.aperture2collimation(pln, stf, resultGUI.sequencing); pln.propDoseCalc.beamProfile = 'phasespace'; w = resultGUI.w; end @@ -174,7 +177,7 @@ rmdir(folderName, 's'); % clean up end -function test_TopasMCdoseCalc4D +function test_topasMCdoseCalc4D numOfPhases = 5; radModes = DoseEngines.matRad_TopasMCEngine.possibleRadiationModes; matRad_cfg = MatRad_Config.instance(); @@ -185,17 +188,18 @@ % physical Dose for i = 1:numel(radModes) - if ~strcmp(radModes{i}, 'photons') + if ~ismember(radModes{i}, {'photons', 'VHEE'}) load([radModes{i} '_testData.mat']); [ct, cst] = matRad_addMovement(ct, cst, 5, numOfPhases, [0 3 0], 'dvfType', 'pull'); pln.bioModel = matRad_bioModel(radModes{i}, 'none'); resultGUI.w = ones(1, sum([stf(:).totalNumOfBixels]))'; - timeSequence = matRad_makeBixelTimeSeq(stf, resultGUI); - timeSequence = matRad_makePhaseMatrix(timeSequence, ct.numOfCtScen, ct.motionPeriod, 'linear'); + resultGUI = matRad_sequencing(resultGUI, stf, pln); + sequencer = matRad_ParticleSequencer(); + resultGUI.sequencing = sequencer.makePhaseMatrix(resultGUI.sequencing, ct.numOfCtScen, ct.motionPeriod); pln.propDoseCalc.engine = 'TOPAS'; pln.propDoseCalc.externalCalculation = 'write'; pln.propDoseCalc.calc4DInterplay = true; - pln.propDoseCalc.calcTimeSequence = timeSequence; + pln.propDoseCalc.calcTimeSequence = resultGUI.sequencing; pln.propDoseCalc.numHistoriesDirect = 1e6; resultGUI = matRad_calcDoseForward(ct, cst, stf, pln, resultGUI.w); @@ -231,12 +235,13 @@ [ct, cst] = matRad_addMovement(ct, cst, 5, numOfPhases, [0 3 0], 'dvfType', 'pull'); pln.bioModel = matRad_bioModel(radModes{i}, 'none'); resultGUI.w = ones(1, sum([stf(:).totalNumOfBixels]))'; - timeSequence = matRad_makeBixelTimeSeq(stf, resultGUI); - timeSequence = matRad_makePhaseMatrix(timeSequence, ct.numOfCtScen, ct.motionPeriod, 'linear'); + resultGUI = matRad_sequencing(resultGUI, stf, pln); + sequencer = matRad_ParticleSequencer(); + resultGUI.sequencing = sequencer.makePhaseMatrix(resultGUI.sequencing, ct.numOfCtScen, ct.motionPeriod); pln.propDoseCalc.engine = 'TOPAS'; pln.propDoseCalc.externalCalculation = 'write'; pln.propDoseCalc.calc4DInterplay = true; - pln.propDoseCalc.calcTimeSequence = timeSequence; + pln.propDoseCalc.calcTimeSequence = resultGUI.sequencing; pln.propDoseCalc.numHistoriesDirect = 1e6; pln.propDoseCalc.scorer.RBE = true; pln.propDoseCalc.scorer.RBE_model = RBEmodel; @@ -259,7 +264,7 @@ rmdir(folderName, 's'); % clean up end -function test_TopasMCdoseCalc_multiAlphaBeta +function test_topasMultiAlphaBeta radModes = {'protons'}; matRad_cfg = MatRad_Config.instance(); diff --git a/test/sequencing/test_engelLeafSequencing.m b/test/sequencing/test_engelLeafSequencing.m index a07546c29..a46cbcf40 100644 --- a/test/sequencing/test_engelLeafSequencing.m +++ b/test/sequencing/test_engelLeafSequencing.m @@ -1,79 +1,69 @@ function test_suite = test_engelLeafSequencing - %The output should always be test_suite, and the function name the same as - %your file name - - %To collect all tests defined below, this is needed in newer Matlab - %versions. test_functions will collect function handles to below test - %functions - test_functions=localfunctions(); - - % This will initialize the test suite, i.e., take the functions from - % test_functions, check if they contain "test", convert them into a MOxUnit - % Test Case, and add them to the test-runner - initTestSuite; - - function [resultGUI,stf,dij] = helper_getTestData() - p = load('photons_testData.mat'); - resultGUI = p.resultGUI; - stf = p.stf; - dij = p.dij; - - - function test_run_sequencing_basic - [resultGUI,stf,dij] = helper_getTestData(); - fn_old = fieldnames(resultGUI); - - numOfLevels = [1,10]; - - for levels = numOfLevels - resultGUI_sequenced = matRad_engelLeafSequencing(resultGUI,stf,dij,levels); - - fn_new = fieldnames(resultGUI_sequenced); - for i = 1:numel(fn_old) - assertTrue(any(strcmp(fn_old{i},fn_new))); - assertElementsAlmostEqual(resultGUI.(fn_old{i}),resultGUI_sequenced.(fn_old{i})); - end - - % Basic additions to resultGUI - assertTrue(isvector(resultGUI_sequenced.wSequenced)); - assertTrue(isstruct(resultGUI_sequenced.apertureInfo)); - assertTrue(isstruct(resultGUI_sequenced.sequencing)); - - %Sequencing Struct - seq = resultGUI_sequenced.sequencing; - assertTrue(isstruct(seq.beam)); - assertTrue(numel(seq.beam) == numel(stf)); - for i = 1:numel(seq.beam) - assertTrue(isscalar(seq.beam(i).numOfShapes)); - assertTrue(isnumeric(seq.beam(i).shapes)); - shapeSize = size(seq.beam(i).shapes); - assertEqual(shapeSize(3),seq.beam(i).numOfShapes); - assertTrue(isvector(seq.beam(i).shapesWeight)); - assertTrue(isvector(seq.beam(i).bixelIx)); - assertTrue(ismatrix(seq.beam(i).fluence)); - assertEqual(size(seq.beam(i).fluence),shapeSize([1 2])); - end - - %ApertureInfo Sturct - apInfo = resultGUI_sequenced.apertureInfo; - assertTrue(isscalar(apInfo.bixelWidth)); - assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); - assertTrue(isscalar(apInfo.totalNumOfBixels)); - assertTrue(isscalar(apInfo.totalNumOfShapes)); - assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); - assertTrue(isvector(apInfo.apertureVector)); - assertTrue(ismatrix(apInfo.mappingMx)); - assertTrue(ismatrix(apInfo.limMx)); - assertTrue(isstruct(apInfo.beam)) - assertTrue(numel(apInfo.beam) == numel(stf)); - end - - - - - - - - - - \ No newline at end of file +% The output should always be test_suite, and the function name the same as +% your file name + +% To collect all tests defined below, this is needed in newer Matlab +% versions. test_functions will collect function handles to below test +% functions +test_functions = localfunctions(); + +% This will initialize the test suite, i.e., take the functions from +% test_functions, check if they contain "test", convert them into a MOxUnit +% Test Case, and add them to the test-runner +initTestSuite; + +function [resultGUI, stf, dij, pln] = helper_getTestData() +p = load('photons_testData.mat'); +pln = p.pln; +pln.propSeq.sequencer = 'engel'; +resultGUI = p.resultGUI; +stf = p.stf; +dij = p.dij; + +function test_run_sequencing_basic +[resultGUI, stf, dij, pln] = helper_getTestData(); +fn_old = fieldnames(resultGUI); + +numOfLevels = [1, 10]; + +for levels = numOfLevels + resultGUI_sequenced = matRad_sequencing(resultGUI, stf, pln); + + fn_new = fieldnames(resultGUI_sequenced); + for i = 1:numel(fn_old) + assertTrue(any(strcmp(fn_old{i}, fn_new))); + assertElementsAlmostEqual(resultGUI.(fn_old{i}), resultGUI_sequenced.(fn_old{i})); + end + + % Basic additions to resultGUI + assertTrue(isstruct(resultGUI_sequenced.sequencing.apertureInfo)); + assertTrue(isstruct(resultGUI_sequenced.sequencing)); + + % Sequencing Struct + seq = resultGUI_sequenced.sequencing; + assertTrue(isstruct(seq.beam)); + assertTrue(numel(seq.beam) == numel(stf)); + for i = 1:size(seq.beam, 2) + assertTrue(isscalar(seq.beam(i).numOfShapes)); + assertTrue(isnumeric(seq.beam(i).shapes)); + assertEqual(size(seq.beam(i).shapes, 3), seq.beam(i).numOfShapes); + assertTrue(isvector(seq.beam(i).shapesWeight)); + assertTrue(isvector(seq.beam(i).bixelIx)); + assertTrue(ismatrix(seq.beam(i).fluence)); + assertEqual(size(seq.beam(i).fluence, 1), size(seq.beam(i).shapes, 1)); + assertEqual(size(seq.beam(i).fluence, 2), size(seq.beam(i).shapes, 2)); + end + + % ApertureInfo Sturct + apInfo = resultGUI_sequenced.sequencing.apertureInfo; + assertTrue(isscalar(apInfo.bixelWidth)); + assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); + assertTrue(isscalar(apInfo.totalNumOfBixels)); + assertTrue(isscalar(apInfo.totalNumOfShapes)); + assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); + assertTrue(isvector(apInfo.apertureVector)); + assertTrue(ismatrix(apInfo.mappingMx)); + assertTrue(ismatrix(apInfo.limMx)); + assertTrue(isstruct(apInfo.beam)); + assertTrue(numel(apInfo.beam) == numel(stf)); +end diff --git a/test/sequencing/test_siochiLeafSequencing.m b/test/sequencing/test_siochiLeafSequencing.m index 60f6bf7db..88f847357 100644 --- a/test/sequencing/test_siochiLeafSequencing.m +++ b/test/sequencing/test_siochiLeafSequencing.m @@ -1,80 +1,69 @@ -function test_suite = test_siochiLeafSequencing - %The output should always be test_suite, and the function name the same as - %your file name - - %To collect all tests defined below, this is needed in newer Matlab - %versions. test_functions will collect function handles to below test - %functions - test_functions=localfunctions(); - - % This will initialize the test suite, i.e., take the functions from - % test_functions, check if they contain "test", convert them into a MOxUnit - % Test Case, and add them to the test-runner - initTestSuite; - - function [resultGUI,stf,dij] = helper_getTestData() - p = load('photons_testData.mat'); - resultGUI = p.resultGUI; - stf = p.stf; - dij = p.dij; - - - function test_run_sequencing_basic - [resultGUI,stf,dij] = helper_getTestData(); - fn_old = fieldnames(resultGUI); - - numOfLevels = [1,10]; - - for levels = numOfLevels - resultGUI_sequenced = matRad_siochiLeafSequencing(resultGUI,stf,dij,levels); - - fn_new = fieldnames(resultGUI_sequenced); - for i = 1:numel(fn_old) - assertTrue(any(strcmp(fn_old{i},fn_new))); - assertElementsAlmostEqual(resultGUI.(fn_old{i}),resultGUI_sequenced.(fn_old{i})); - end - - % Basic additions to resultGUI - assertTrue(isvector(resultGUI_sequenced.wSequenced)); - assertTrue(isstruct(resultGUI_sequenced.apertureInfo)); - assertTrue(isstruct(resultGUI_sequenced.sequencing)); - - %Sequencing Struct - seq = resultGUI_sequenced.sequencing; - assertTrue(isstruct(seq.beam)); - assertTrue(numel(seq.beam) == numel(stf)); - for i = 1:numel(seq.beam) - assertTrue(isscalar(seq.beam(i).numOfShapes)); - assertTrue(isnumeric(seq.beam(i).shapes)); - shapeSize = size(seq.beam(i).shapes); - assertEqual(shapeSize(3),seq.beam(i).numOfShapes); - assertTrue(isvector(seq.beam(i).shapesWeight)); - assertTrue(isvector(seq.beam(i).bixelIx)); - assertTrue(ismatrix(seq.beam(i).fluence)); - assertEqual(size(seq.beam(i).fluence),shapeSize([1 2])); - end - - %ApertureInfo Sturct - apInfo = resultGUI_sequenced.apertureInfo; - assertTrue(isscalar(apInfo.bixelWidth)); - assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); - assertTrue(isscalar(apInfo.totalNumOfBixels)); - assertTrue(isscalar(apInfo.totalNumOfShapes)); - assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); - assertTrue(isvector(apInfo.apertureVector)); - assertTrue(ismatrix(apInfo.mappingMx)); - assertTrue(ismatrix(apInfo.limMx)); - assertTrue(isstruct(apInfo.beam)) - assertTrue(numel(apInfo.beam) == numel(stf)); - end - - - - - - - - - - - \ No newline at end of file +function test_suite = test_xiaLeafSequencing +% The output should always be test_suite, and the function name the same as +% your file name + +% To collect all tests defined below, this is needed in newer Matlab +% versions. test_functions will collect function handles to below test +% functions +test_functions = localfunctions(); + +% This will initialize the test suite, i.e., take the functions from +% test_functions, check if they contain "test", convert them into a MOxUnit +% Test Case, and add them to the test-runner +initTestSuite; + +function [resultGUI, stf, dij, pln] = helper_getTestData() +p = load('photons_testData.mat'); +pln = p.pln; +pln.propSeq.sequencer = 'siochi'; +resultGUI = p.resultGUI; +stf = p.stf; +dij = p.dij; + +function test_run_sequencing_basic +[resultGUI, stf, dij, pln] = helper_getTestData(); +fn_old = fieldnames(resultGUI); + +numOfLevels = [1, 10]; + +for levels = numOfLevels + resultGUI_sequenced = matRad_sequencing(resultGUI, stf, pln); + + fn_new = fieldnames(resultGUI_sequenced); + for i = 1:numel(fn_old) + assertTrue(any(strcmp(fn_old{i}, fn_new))); + assertElementsAlmostEqual(resultGUI.(fn_old{i}), resultGUI_sequenced.(fn_old{i})); + end + + % Basic additions to resultGUI + assertTrue(isstruct(resultGUI_sequenced.sequencing.apertureInfo)); + assertTrue(isstruct(resultGUI_sequenced.sequencing)); + + % Sequencing Struct + seq = resultGUI_sequenced.sequencing; + assertTrue(isstruct(seq.beam)); + assertTrue(numel(seq.beam) == numel(stf)); + for i = 1:size(seq.beam, 2) + assertTrue(isscalar(seq.beam(i).numOfShapes)); + assertTrue(isnumeric(seq.beam(i).shapes)); + assertEqual(size(seq.beam(i).shapes, 3), seq.beam(i).numOfShapes); + assertTrue(isvector(seq.beam(i).shapesWeight)); + assertTrue(isvector(seq.beam(i).bixelIx)); + assertTrue(ismatrix(seq.beam(i).fluence)); + assertEqual(size(seq.beam(i).fluence, 1), size(seq.beam(i).shapes, 1)); + assertEqual(size(seq.beam(i).fluence, 2), size(seq.beam(i).shapes, 2)); + end + + % ApertureInfo Sturct + apInfo = resultGUI_sequenced.sequencing.apertureInfo; + assertTrue(isscalar(apInfo.bixelWidth)); + assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); + assertTrue(isscalar(apInfo.totalNumOfBixels)); + assertTrue(isscalar(apInfo.totalNumOfShapes)); + assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); + assertTrue(isvector(apInfo.apertureVector)); + assertTrue(ismatrix(apInfo.mappingMx)); + assertTrue(ismatrix(apInfo.limMx)); + assertTrue(isstruct(apInfo.beam)); + assertTrue(numel(apInfo.beam) == numel(stf)); +end diff --git a/test/sequencing/test_xiaLeafSequencing.m b/test/sequencing/test_xiaLeafSequencing.m index a4fabcc2d..b00d0f3a9 100644 --- a/test/sequencing/test_xiaLeafSequencing.m +++ b/test/sequencing/test_xiaLeafSequencing.m @@ -1,80 +1,69 @@ function test_suite = test_xiaLeafSequencing -%The output should always be test_suite, and the function name the same as -%your file name - -%To collect all tests defined below, this is needed in newer Matlab -%versions. test_functions will collect function handles to below test -%functions -test_functions=localfunctions(); +% The output should always be test_suite, and the function name the same as +% your file name + +% To collect all tests defined below, this is needed in newer Matlab +% versions. test_functions will collect function handles to below test +% functions +test_functions = localfunctions(); % This will initialize the test suite, i.e., take the functions from % test_functions, check if they contain "test", convert them into a MOxUnit % Test Case, and add them to the test-runner initTestSuite; -function [resultGUI,stf,dij] = helper_getTestData() - p = load('photons_testData.mat'); - resultGUI = p.resultGUI; - stf = p.stf; - dij = p.dij; - +function [resultGUI, stf, dij, pln] = helper_getTestData() +p = load('photons_testData.mat'); +pln = p.pln; +pln.propSeq.sequencer = 'xia'; +resultGUI = p.resultGUI; +stf = p.stf; +dij = p.dij; function test_run_sequencing_basic - [resultGUI,stf,dij] = helper_getTestData(); - fn_old = fieldnames(resultGUI); - - numOfLevels = [1,10]; - - for levels = numOfLevels - resultGUI_sequenced = matRad_xiaLeafSequencing(resultGUI,stf,dij,levels); - - fn_new = fieldnames(resultGUI_sequenced); - for i = 1:numel(fn_old) - assertTrue(any(strcmp(fn_old{i},fn_new))); - assertElementsAlmostEqual(resultGUI.(fn_old{i}),resultGUI_sequenced.(fn_old{i})); - end - - % Basic additions to resultGUI - assertTrue(isvector(resultGUI_sequenced.wSequenced)); - assertTrue(isstruct(resultGUI_sequenced.apertureInfo)); - assertTrue(isstruct(resultGUI_sequenced.sequencing)); - - %Sequencing Struct - seq = resultGUI_sequenced.sequencing; - assertTrue(isstruct(seq.beam)); - assertTrue(numel(seq.beam) == numel(stf)); - for i = 1:numel(seq.beam) - assertTrue(isscalar(seq.beam(i).numOfShapes)); - assertTrue(isnumeric(seq.beam(i).shapes)); - shapeSize = size(seq.beam(i).shapes); - assertEqual(shapeSize(3),seq.beam(i).numOfShapes); - assertTrue(isvector(seq.beam(i).shapesWeight)); - assertTrue(isvector(seq.beam(i).bixelIx)); - assertTrue(ismatrix(seq.beam(i).fluence)); - assertEqual(size(seq.beam(i).fluence),shapeSize([1 2])); - end - - %ApertureInfo Sturct - apInfo = resultGUI_sequenced.apertureInfo; - assertTrue(isscalar(apInfo.bixelWidth)); - assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); - assertTrue(isscalar(apInfo.totalNumOfBixels)); - assertTrue(isscalar(apInfo.totalNumOfShapes)); - assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); - assertTrue(isvector(apInfo.apertureVector)); - assertTrue(ismatrix(apInfo.mappingMx)); - assertTrue(ismatrix(apInfo.limMx)); - assertTrue(isstruct(apInfo.beam)) - assertTrue(numel(apInfo.beam) == numel(stf)); - end +[resultGUI, stf, dij, pln] = helper_getTestData(); +fn_old = fieldnames(resultGUI); +numOfLevels = [1, 10]; - - - - +for levels = numOfLevels + resultGUI_sequenced = matRad_sequencing(resultGUI, stf, pln); + fn_new = fieldnames(resultGUI_sequenced); + for i = 1:numel(fn_old) + assertTrue(any(strcmp(fn_old{i}, fn_new))); + assertElementsAlmostEqual(resultGUI.(fn_old{i}), resultGUI_sequenced.(fn_old{i})); + end + % Basic additions to resultGUI + assertTrue(isstruct(resultGUI_sequenced.sequencing.apertureInfo)); + assertTrue(isstruct(resultGUI_sequenced.sequencing)); + % Sequencing Struct + seq = resultGUI_sequenced.sequencing; + assertTrue(isstruct(seq.beam)); + assertTrue(numel(seq.beam) == numel(stf)); + for i = 1:size(seq.beam, 2) + assertTrue(isscalar(seq.beam(i).numOfShapes)); + assertTrue(isnumeric(seq.beam(i).shapes)); + assertEqual(size(seq.beam(i).shapes, 3), seq.beam(i).numOfShapes); + assertTrue(isvector(seq.beam(i).shapesWeight)); + assertTrue(isvector(seq.beam(i).bixelIx)); + assertTrue(ismatrix(seq.beam(i).fluence)); + assertEqual(size(seq.beam(i).fluence, 1), size(seq.beam(i).shapes, 1)); + assertEqual(size(seq.beam(i).fluence, 2), size(seq.beam(i).shapes, 2)); + end - \ No newline at end of file + % ApertureInfo Sturct + apInfo = resultGUI_sequenced.sequencing.apertureInfo; + assertTrue(isscalar(apInfo.bixelWidth)); + assertTrue(isscalar(apInfo.numOfMLCLeafPairs)); + assertTrue(isscalar(apInfo.totalNumOfBixels)); + assertTrue(isscalar(apInfo.totalNumOfShapes)); + assertTrue(isscalar(apInfo.totalNumOfLeafPairs)); + assertTrue(isvector(apInfo.apertureVector)); + assertTrue(ismatrix(apInfo.mappingMx)); + assertTrue(ismatrix(apInfo.limMx)); + assertTrue(isstruct(apInfo.beam)); + assertTrue(numel(apInfo.beam) == numel(stf)); +end diff --git a/test/testData/helper_testDataCreater.m b/test/testData/helper_testDataCreater.m index 023240316..0e70b8c8a 100644 --- a/test/testData/helper_testDataCreater.m +++ b/test/testData/helper_testDataCreater.m @@ -3,67 +3,67 @@ %% create ct ct = struct(); -ct.cubeDim = [20,10,10]; +ct.cubeDim = [20, 10, 10]; ct.resolution.x = 10; ct.resolution.y = 10; ct.resolution.z = 10; ct.numOfCtScen = 1; ct.cubeHU{1} = ones(ct.cubeDim) * -1000; -ct.cubeHU{1}(2:19,2:9,2:9) = 0; +ct.cubeHU{1}(2:19, 2:9, 2:9) = 0; VolHelper = false(ct.cubeDim); -VolHelper(2:19,2:9,2:9) = true; +VolHelper(2:19, 2:9, 2:9) = true; ixBody = find(VolHelper); VolHelper = false(ct.cubeDim); -VolHelper(10:11,5:6,5:6) = true; +VolHelper(10:11, 5:6, 5:6) = true; ixTarget = find(VolHelper); for i = 1:2 - cst{i,1} = i-1; - cst{i,5}.TissueClass = 1; - cst{i,5}.alphaX = 0.1000; - cst{i,5}.betaX = 0.0500; - cst{i,5}.Priority = i; - cst{i,5}.Visible = 1; - cst{i,5}.visibleColor = [0 0 0]; + cst{i, 1} = i - 1; + cst{i, 5}.TissueClass = 1; + cst{i, 5}.alphaX = 0.1000; + cst{i, 5}.betaX = 0.0500; + cst{i, 5}.Priority = i; + cst{i, 5}.Visible = 1; + cst{i, 5}.visibleColor = [0 0 0]; end -cst{1,2} = 'Target'; -cst{1,3} = 'TARGET'; -cst{1,4}{1} = ixTarget; -cst{1,6}{1} = struct(DoseObjectives.matRad_SquaredDeviation(800,45)); -cst{2,2} = 'Body'; -cst{2,3} = 'OAR'; -cst{2,4}{1} = ixBody; -cst{2,6}{1} = struct(DoseObjectives.matRad_SquaredOverdosing(400,0)); +cst{1, 2} = 'Target'; +cst{1, 3} = 'TARGET'; +cst{1, 4}{1} = ixTarget; +cst{1, 6}{1} = struct(DoseObjectives.matRad_SquaredDeviation(800, 45)); +cst{2, 2} = 'Body'; +cst{2, 3} = 'OAR'; +cst{2, 4}{1} = ixBody; +cst{2, 6}{1} = struct(DoseObjectives.matRad_SquaredOverdosing(400, 0)); -clear VolHelper ixBody ixTarget i +clear VolHelper ixBody ixTarget i; %% create pln, stf -radModes = ["protons","helium","carbon","VHEE"]; +radModes = ["photons", "protons", "helium", "carbon", "VHEE"]; for radMode = radModes - %radMode = 'carbon'; %protons,helium,carbon; - - pln.radiationMode = char(radMode); - pln.machine = 'Generic'; + % radMode = 'carbon'; %protons,helium,carbon; + + pln.radiationMode = char(radMode); + pln.machine = 'Generic'; pln.numOfFractions = 30; - pln.propStf.gantryAngles = [0,180]; + pln.propStf.gantryAngles = [0, 180]; pln.propStf.couchAngles = zeros(size(pln.propStf.gantryAngles)); - pln.propStf.isoCenter = matRad_getIsoCenter(cst,ct,0); - + pln.propStf.isoCenter = matRad_getIsoCenter(cst, ct, 0); + pln.propStf.longitudinalSpotSpacing = 8; pln.propStf.bixelWidth = 10; - pln.propDoseCalc.doseGrid.resolution = struct('x',10,'y',10,'z',10); %[mm] - - %pln.bioModel = matRad_bioModel(pln.radiationMode,'none'); - + pln.propDoseCalc.doseGrid.resolution = struct('x', 10, 'y', 10, 'z', 10); % [mm] + + % pln.bioModel = matRad_bioModel(pln.radiationMode,'none'); + %% Generate Beam Geometry STF - pln.propStf.addMargin = false; %to make smaller stf, les bixel - stf = matRad_generateStf(ct,cst,pln); + pln.propStf.addMargin = false; % to make smaller stf, les bixel + stf = matRad_generateStf(ct, cst, pln); ct = matRad_calcWaterEqD(ct, pln); %% Dose Calculation - dij = matRad_calcDoseInfluence(ct,cst,stf,pln); - resultGUI = matRad_calcCubes(ones(dij.totalNumOfBixels,1),dij); - + dij = matRad_calcDoseInfluence(ct, cst, stf, pln); + resultGUI = matRad_calcCubes(ones(dij.totalNumOfBixels, 1), dij); + %% save basic data - save([char(radMode) '_testData.mat'],'ct','cst','pln','stf','dij','resultGUI','-v7'); -end \ No newline at end of file + save([char(radMode) '_testData.mat'], 'ct', 'cst', 'pln', 'stf', 'dij', 'resultGUI', '-v7'); +end diff --git a/test/testData/photons_testData.mat b/test/testData/photons_testData.mat index c595876bc..ca11ba114 100644 Binary files a/test/testData/photons_testData.mat and b/test/testData/photons_testData.mat differ