Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 128 additions & 96 deletions matRad/matRad_planAnalysis.m
Original file line number Diff line number Diff line change
@@ -1,144 +1,176 @@
function resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln,varargin)
% matRad plan analysis function
% This function performs analysis on radiation therapy plans, including DVH (Dose-Volume Histogram) and quality indicators.
% It optionally displays these analyses based on input parameters.
function resultGUI = matRad_planAnalysis(resultGUI, ct, cst, stf, pln, varargin)
% matRad_planAnalysis calculates DVH and quality indicators for a plan.
%
% input:
% resultGUI: matRad resultGUI struct containing the analysis results
% call
% resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln)
% resultGUI = matRad_planAnalysis(resultGUI,ct,cst,stf,pln,varargin)
%
% input
% resultGUI: matRad resultGUI struct containing dose cubes
% ct: matRad ct struct with computed tomography data
% cst: matRad cst cell array with structure definitions
% stf: matRad stf struct with beam information
% pln: matRad pln struct with plan information
% name / value pairs: Optional parameters for analysis customization
% refGy: (optional) Dose values for V_XGy calculation (default: [40 50 60])
% refVol:(optional) Volume percentages for D_X calculation (default: [2 5 95 98])
% quantity: (optional) resultGUI dose quantity to analyse
% evaluationMode:(optional) 'perFraction' or 'total' for figures/tables
% doseWindow: (optional) dose axis window for DVH display
% refGy: (optional) Per-fraction dose values for V_XGy calculation
% refVol:(optional) Volume percentages for D_X calculation
%
% output:
% output
% resultGUI: Updated resultGUI with analysis data

% Initialize input parser for function arguments
p = inputParser();

% Define required inputs
p.addRequired('ct',@isstruct);
p.addRequired('cst',@iscell);
p.addRequired('stf',@isstruct);
p.addRequired('pln',@isstruct);

%
% Copyright 2024 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
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2024-2026 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.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% Initialize input parser for optional parameters
p = inputParser();
p.addRequired('ct', @isstruct);
p.addRequired('cst', @iscell);
p.addRequired('stf', @isstruct);
p.addRequired('pln', @isstruct);
p.addParameter('refGy', [], @isnumeric);
p.addParameter('refVol', [2 5 95 98], @isnumeric);
p.addParameter('showDVH', true, @islogical);
p.addParameter('showQI', true, @islogical);
p.addParameter('quantity', '', @(x) ischar(x) || isstring(x));
p.addParameter('evaluationMode', 'perFraction', @(x) ischar(x) || isstring(x));
p.addParameter('doseWindow', [], @(x) isempty(x) || (isnumeric(x) && numel(x) == 2));
p.parse(ct, cst, stf, pln, varargin{:});

% Define required inputs again for clarity
p.addRequired('ct',@isstruct);
p.addRequired('cst',@iscell);
p.addRequired('stf',@isstruct);
p.addRequired('pln',@isstruct);

% Define optional parameters with default values
p.addParameter('refGy',[40 50 60],@isnumeric); % Reference dose values for V_XGy calculation
p.addParameter('refVol',[2 5 95 98],@isnumeric); % Reference volume percentages for D_X calculation
p.addParameter('showDVH',true,@islogical); % Flag to show or hide the DVH plot
p.addParameter('showQI',true,@islogical); % Flag to show or hide the Quality Indicators plot

% Parse input arguments to extract values
p.parse(ct,cst,stf,pln,varargin{:});

% Assign parsed values to variables
ct = p.Results.ct;
cst = p.Results.cst;
stf = p.Results.stf;
pln = p.Results.pln;
refGy = p.Results.refGy;
refVol = p.Results.refVol;
showDVH = p.Results.showDVH;
showQI = p.Results.showQI;
quantity = p.Results.quantity;
doseWindow = p.Results.doseWindow;
evaluationMode = p.Results.evaluationMode;

% Determine which dose cube to use based on resultGUI structure
if isfield(resultGUI,'RBExDose')
visQ = 'RBExDose';
else
visQ = 'physicalDose';
end
[~, evaluationMode, evaluationScale] = matRad_convertToEvaluationMode([], pln, evaluationMode);

if ~isfield(resultGUI,visQ)
matRad_cfg = MatRad_Config.instance();
matRad_cfg.dispError('Unknown quantity ''%s'' to analyse!',visQ);
if ~isempty(doseWindow)
doseWindow = doseWindow(:)';
end

% Validate / Create Scenario model
if ~isfield(pln,'multScen')
visQ = matRad_resolveDoseAnalysisQuantity(resultGUI, pln, quantity);

if ~isfield(pln, 'multScen')
pln.multScen = 'nomScen';
end
if ~isa(pln.multScen,'matRad_ScenarioModel')
pln.multScen = matRad_ScenarioModel.create(pln.multScen,ct);
end

if ~isa(pln.multScen, 'matRad_ScenarioModel')
pln.multScen = matRad_ScenarioModel.create(pln.multScen, ct);
end

doseCube = resultGUI.(visQ);

% Calculate DVH and quality indicators
resultGUI.dvh = matRad_calcDVH(cst,doseCube,'cum'); % Calculate cumulative DVH
resultGUI.qi = matRad_calcQualityIndicators(cst,pln,doseCube,refGy,refVol); % Calculate quality indicators
resultGUI.dvh = matRad_calcDVH(cst, doseCube, 'cum');
resultGUI.qi = matRad_calcQualityIndicators(cst, pln, doseCube, refGy, refVol);
resultGUI.analysisQuantity = visQ;
resultGUI.evaluationModeBase = 'perFraction';
resultGUI.evaluationMode = evaluationMode;
resultGUI.evaluationScale = evaluationScale;
resultGUI.displayDvh = matRad_convertDvhForEvaluation(resultGUI.dvh, pln, evaluationMode);
resultGUI.displayQi = matRad_convertQiForEvaluation(resultGUI.qi, pln, evaluationMode);

dvhScen = {};


if pln.multScen.totNumScen > 1
for i = 1:pln.multScen.totNumScen
scenFieldName = sprintf('%s_scen%d',visQ,i);
if isfield(resultGUI,scenFieldName)
dvhScen{i} = matRad_calcDVH(cst,resultGUI.(scenFieldName),'cum'); % Calculate cumulative scenario DVH
numScenarios = pln.multScen.totNumScen;
if numScenarios > 1
for i = 1:numScenarios
scenFieldName = sprintf('%s_scen%d', visQ, i);
if isfield(resultGUI, scenFieldName)
dvhScen{i} = matRad_convertDvhForEvaluation( ...
matRad_calcDVH(cst, resultGUI.(scenFieldName), 'cum'), pln, evaluationMode); %#ok<AGROW>
end
end
end

% Configuration for GUI appearance
matRad_cfg = MatRad_Config.instance();

% Create figure for plots with background color from configuration
hF = figure('Color',matRad_cfg.gui.backgroundColor);

colorSpec = {'Color',matRad_cfg.gui.elementColor,...
'XColor',matRad_cfg.gui.textColor,...
'YColor',matRad_cfg.gui.textColor,...
'GridColor',matRad_cfg.gui.textColor,...
'MinorGridColor',matRad_cfg.gui.backgroundColor};

% Determine subplot layout based on flags
if showDVH && showQI
hDVHax = subplot(2,1,1,colorSpec{:}); % DVH plot area
hQIax = subplot(2,1,2,colorSpec{:}); % Quality Indicators plot area
elseif showDVH
hDVHax = subplot(1,1,1,colorSpec{:}); % Only DVH plot
elseif showQI
hQIax = subplot(1,1,1,colorSpec{:}); % Only Quality Indicators plot
if showDVH || showQI
matRad_cfg = MatRad_Config.instance();
figure('Color', matRad_cfg.gui.backgroundColor);

colorSpec = {'Color', matRad_cfg.gui.elementColor, ...
'XColor', matRad_cfg.gui.textColor, ...
'YColor', matRad_cfg.gui.textColor, ...
'GridColor', matRad_cfg.gui.textColor, ...
'MinorGridColor', matRad_cfg.gui.backgroundColor};

if showDVH && showQI
hDVHax = subplot(2, 1, 1, colorSpec{:});
hQIax = subplot(2, 1, 2, colorSpec{:});
elseif showDVH
hDVHax = subplot(1, 1, 1, colorSpec{:});
elseif showQI
hQIax = subplot(1, 1, 1, colorSpec{:});
end
end

% Display DVH if enabled
if showDVH
matRad_showDVH(resultGUI.dvh,cst,pln,'axesHandle',hDVHax,'LineWidth',3); % Show DVH plot
matRad_showDVH(resultGUI.displayDvh, cst, pln, 'axesHandle', hDVHax, 'LineWidth', 3);

for i = 1:numel(dvhScen)
matRad_showDVH(dvhScen{i},cst,pln,'axesHandle',hDVHax,'LineWidth',0.5,'plotLegend',false,'LineStyle','--'); % Show DVH plot
matRad_showDVH(dvhScen{i}, cst, pln, 'axesHandle', hDVHax, ...
'LineWidth', 0.5, 'plotLegend', false, 'LineStyle', '--');
end

if ~isempty(doseWindow)
xlim(hDVHax, doseWindow);
end
end

% Display Quality Indicators if enabled
if showQI
matRad_showQualityIndicators(hQIax,resultGUI.qi); % Show Quality Indicators plot
matRad_showQualityIndicators(hQIax, resultGUI.displayQi);
end

end

function dvh = matRad_convertDvhForEvaluation(dvh, pln, evaluationMode)
if isempty(dvh)
return
end

for i = 1:numel(dvh)
if isfield(dvh(i), 'doseGrid') && ~isempty(dvh(i).doseGrid)
dvh(i).doseGrid = matRad_convertToEvaluationMode( ...
dvh(i).doseGrid, pln, evaluationMode);
end
end
end

function qi = matRad_convertQiForEvaluation(qi, pln, evaluationMode)
if isempty(qi)
return
end

doseFields = {'mean', 'std', 'max', 'min', 'referenceDose'};
for i = 1:numel(qi)
for j = 1:numel(doseFields)
fieldName = doseFields{j};
if isfield(qi(i), fieldName) && isnumeric(qi(i).(fieldName))
qi(i).(fieldName) = matRad_convertToEvaluationMode( ...
qi(i).(fieldName), pln, evaluationMode);
end
end

end
fields = fieldnames(qi(i));
for j = 1:numel(fields)
fieldName = fields{j};
if strncmp(fieldName, 'D_', 2) && isnumeric(qi(i).(fieldName))
qi(i).(fieldName) = matRad_convertToEvaluationMode( ...
qi(i).(fieldName), pln, evaluationMode);
end
end
end
end
Loading