Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
fe06707
Updates to where objectives and constraints are stored
tobiasbecher May 31, 2023
823794f
Merge branch 'dev_Pareto' into dev_ParetoObj
tobiasbecher Jun 5, 2023
4cba6b2
Merge branch 'dev_Pareto' into dev_ParetoObj
tobiasbecher Jul 21, 2023
2e806a9
Merge branch 'dev_varRBErobOpt' into dev_ParetoObj
tobiasbecher Jul 21, 2023
bc7f21d
Fixed bug with constraints and added some comments
tobiasbecher Jul 28, 2023
0faf883
Added Pareto Sandwich support
tobiasbecher Aug 15, 2023
6b8aa22
Added navigation functionality
tobiasbecher Sep 6, 2023
21347fe
Merge branch 'e0404:master' into dev_ParetoNavigation
tobiasbecher Oct 27, 2023
22bb7e6
Changed naming convention
tobiasbecher Oct 27, 2023
fed3c63
Merge branch 'dev_varRBErobOpt' into dev_ParetoNavigation
tobiasbecher Oct 27, 2023
778b4cc
Incorporate scenario change
tobiasbecher Oct 27, 2023
8cb4398
Small fixes
tobiasbecher Oct 27, 2023
d134d85
Final updates
tobiasbecher Oct 30, 2023
23eee6a
File cleanup
tobiasbecher Oct 30, 2023
b9009b1
Delete testRun.mat
tobiasbecher Oct 30, 2023
46ec9b3
Updated documentation
tobiasbecher Nov 2, 2023
267d2b8
Reverted to default values
tobiasbecher Nov 2, 2023
823f44d
First changes based on pull request comments
tobiasbecher Jan 11, 2024
5ddce71
Moved some files and updated GUI
tobiasbecher Jan 24, 2024
e8c6218
Small bug fix
tobiasbecher Jan 24, 2024
b38a859
Added lexicographic optimization
tobiasbecher Jan 30, 2024
f19635f
Update matRad_MinMaxDose.m
tobiasbecher Jan 31, 2024
8004b74
Merge branch 'dev_varRBErobOpt' into pr/669
wahln May 13, 2024
13e4615
fix documentation
wahln May 13, 2024
ece7b7f
fix missing ct scenario index in calcCubesDoseGrid
wahln May 13, 2024
75f7513
Merge branch 'dev_varRBErobOpt' into pr/669
wahln May 13, 2024
c7dc07e
First fixes
tobiasbecher Jun 18, 2024
74ae379
Optimization Preprocessing changes
tobiasbecher Jun 20, 2024
4ceb2bf
Plotting support for Pareto optimization
tobiasbecher Jun 20, 2024
db5d715
Updates to the GUI
tobiasbecher Jun 20, 2024
b8df93d
Readded option to calculate Pareto surface from the given facets
tobiasbecher Jun 20, 2024
5baac7a
Delete matRad_plotPS.m
tobiasbecher Jun 20, 2024
41234fe
Delete matRad_ButtonWidget.m
tobiasbecher Jun 20, 2024
94d2d46
Small updates to plotting functions
tobiasbecher Jun 20, 2024
a0336fe
Removed some unnecessary functions
tobiasbecher Jun 21, 2024
130bcc7
Temporary calcCubes fix for RBExD
tobiasbecher Jun 21, 2024
f0fd213
changed default linestyle
tobiasbecher Jun 21, 2024
2210d7f
Changed linewidth for VOIContours
tobiasbecher Jun 21, 2024
e93381b
Merge branch 'dev_varRBErobOpt' into pr/669
wahln Jun 27, 2024
aa90d6a
Merge branch 'dev' into pr/669
wahln Jun 27, 2024
c0b9594
Small GUI updates
tobiasbecher Jul 4, 2024
7e1653f
Merge branch 'dev_ParetoNav' of https://github.com/tobiasbecher/matRa…
tobiasbecher Jul 4, 2024
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
66 changes: 66 additions & 0 deletions Pareto/NavigationUI/matRad_UIData.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
classdef matRad_UIData < handle
% matRad_UIData implements a class that allows easy storing of
% variables related to the pareto Navigation
%
% References
% -
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2020 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/LICENSES.txt. 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
wRef %weight vector of last calculated plan
fRef %objective function values of last calculated plan
fIndsAll % stores all objective function values
fIndsRed % stores only the "reduced points"
upboundInit
upboundRed
upboundSlider
lowboundSliderInit
lowboundSlider
linestyle
end

methods
function obj = matRad_UIData(wRef,fRef,fInds)
obj.wRef = wRef;
obj.fRef = fRef;
obj.fIndsAll = fInds;
obj.fIndsRed = fInds;
obj.upboundInit = max(fInds,[],1);
obj.upboundRed = max(fInds,[],1);
obj.upboundSlider= max(fInds,[],1);
obj.lowboundSliderInit = min(fInds,[],1);
obj.lowboundSlider = min(fInds,[],1);
obj.linestyle = 2;
end

function [sliderLowBound,sliderUpBound] = restrictObjective(obj,i,bound)
obj.upboundRed(i) = bound;
obj.fIndsRed = obj.fIndsRed(obj.fIndsRed(:,i) <= bound,:);
obj.upboundSlider= max([obj.fIndsRed;obj.fRef],[],1);
obj.upboundSlider(i) = bound;
obj.lowboundSlider = min([obj.fIndsRed;obj.fRef],[],1);
sliderLowBound = obj.lowboundSlider;
sliderUpBound = obj.upboundSlider;
end

function releaseObjectiveBounds(obj)
obj.upboundRed = obj.upboundInit;
obj.fIndsRed = obj.fIndsAll;
obj.upboundSlider = obj.upboundInit;
obj.lowboundSlider = obj.lowboundSliderInit;
end

end
end
227 changes: 227 additions & 0 deletions Pareto/NavigationUI/matRad_UIInterpolation.m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should follow the Widget mechanism (check the gui folder). Create a widget derived from matRad_Widget here, you may ask @amitantony for help with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes :D

Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
function matRad_UIInterpolation(data,dij,pln,ct,cst,optiProb)


fInds = data.finds;


%sort function values according to each dimension - still necessary?
[A,I] = sort(fInds(:,1),1,'ascend');
fIndsSorted = fInds(I,:);
weights = data.weights(:,I);
%idx = round(size(fInds,1)/2);
idx = 4;
%%

slice = round(pln.propStf.isoCenter(1,3)./ct.resolution.z);
%%

%%
f = figure('units','normalized','outerposition',[0 0 1 1]);
DosePlot = axes('Position',[.1 .5 .4 .4]);
title('Dose Slice')
DVHPlot = axes('Position',[.6 .5 .35 .4]);
title('DVH');
%

%Create uipanels
p = uipanel(f,'Position',[0.05 0.1 0.3 0.35]);
p2 = uipanel(f,'Position',[0.35 0.3 0.15 0.15]);

%extract the names of the objectives and corresponding VOI
names = {};
for i = (1:numel(optiProb.objectives))
names{end + 1} = optiProb.objectives{i}.name;
end

VOIs = {};
for i = 1:size(cst,1)
if ~isempty(cst{i,6})
VOIs{end + 1} = cst{i,2};
end
end
%Generate reference point (should lie somewhere in the middle of
%objective 1)
fRef = fIndsSorted(idx,:);
wRef = weights(:,idx);
refObj = matRad_UIData(wRef,fRef,fIndsSorted);

%initial UI elements
sliders = {};
namesui = {};
sliderValues = {};
fixButtons = {};
VOIui = {};

%%Create interactive elements
for i = 1:size(fInds,2)
VOIui{i} = uicontrol(p,'Style','text',...
'Units','normalized',...
'Position',[0.02,0.9-(i-1)*0.13, 0.15,0.1],...
'String',VOIs{i});

namesui{i} = uicontrol(p,'Style','text',...
'Units','normalized',...
'Position',[0.17,0.9-(i-1)*0.13, 0.22,0.1],...
'String',names{i});

sliderValues{i} = uicontrol(p,'Style','text',...
'Units','normalized',...
'Position',[0.9,0.9-(i-1)*0.13, 0.1,0.05],...
'String',fIndsSorted(idx,i));

sliders{i} = uicontrol(p,'Style','slider',...
'Units','normalized',...
'Min',min(fIndsSorted(:,i)),'Max',max(fIndsSorted(:,i)),...
'Position',[0.4,0.9-(i-1)*0.13, 0.35,0.05]);


fixButtons{i} = uicontrol(p,'Style','pushbutton',...
'Units','normalized',...
'Position',[0.78,0.9-(i-1)*0.13, 0.1,0.05],...
'String','Fix');

sliders{i}.Value = fIndsSorted(idx,i);

end

DVHButton = uicontrol(p2,'Style','pushbutton',...
'Units','normalized',...
'Position',[0.1 0.6 0.35 0.3],...
'String','Show DVH');

ExportButton = uicontrol(p2,'Style','pushbutton',...
'Units','normalized',...
'Position',[0.55 0.6 0.35 0.3],...
'String','Export to GUI');

ResetConstraintButton = uicontrol(p2,'Style','pushbutton',...
'Units','normalized',...
'Position',[0.2 0.3 0.6 0.2],...
'String','Reset Constraints');

ParetoSurfaceButton = uicontrol(p2,'Style','pushbutton',...
'Units','normalized',...
'Position',[0.2 0.05 0.6 0.2],...
'String','Show PS');


%%Set callback for buttons
for i = 1:size(fInds,2)
set(sliders{i},'Callback',{@slider_callback,sliderValues,sliders,i,weights,dij,slice,DosePlot,refObj,optiProb,data.modcst,cst,pln,ct})
end

for i = 1:numel(fixButtons)
set(fixButtons{i},'Callback',{@FixButton_callback,refObj,sliders,i})
end

set(DVHButton,'Callback',{@DVHButton_callback,cst,refObj,dij,pln})
set(ExportButton,'Callback',{@ExportButton_callback,refObj,dij})
set(ResetConstraintButton,'Callback',{@ResetConstraintButton_callback,refObj,sliders})
set(ParetoSurfaceButton,'Callback',{@ParetoSurfaceButton_callback,refObj})


%initial plot of first point

cubes = reshape(dij.physicalDose{1}*wRef,dij.doseGrid.dimensions);

cubes = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ...
cubes, ...
dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z,'linear',0);

matRad_plotSliceWrapper(DosePlot,ct,cst,1,cubes,3,slice);
%zoom(DosePlot,1.1);
dvh = matRad_calcDVH(cst,cubes,'cum');
matRad_showDVH(dvh,cst,pln);

end
% End of main file
function DVHButton_callback(~,~,cst,refObj,dij,pln)
%Shows the DVH for the current plan
resultGUI = matRad_calcCubes(refObj.wRef,dij);
%dvh = matRad_calcDVH(cst,doseCube,'cum');
dvh = matRad_calcDVH(cst,resultGUI.physicalDose,'cum');

matRad_showDVH(dvh,cst,pln,refObj.linestyle);
if refObj.linestyle < 4
refObj.linestyle = refObj.linestyle + 1;
else
refObj.linestyle = 1;
end
%

end

function ExportButton_callback(~,~,refObj,dij)
%Export the current plan to the matRadGUi for better inspection
resultGUI = matRad_calcCubes(refObj.wRef,dij);
assignin('base',"resultGUI",resultGUI);
matRadGUI;

end


function FixButton_callback(~,~,refObj,sliders,i)

[lb,ub] = refObj.restrictObjective(i,sliders{i}.Value); %update refObjects bounds


for i = 1:numel(sliders)
set(sliders{i},'Min',lb(i));
set(sliders{i},'Max',ub(i));
end
end


function ResetConstraintButton_callback(~,~,refObj,sliders)
refObj.releaseObjectiveBounds();

for i = 1:numel(sliders)
set(sliders{i},'Min',refObj.lowboundSliderInit(i));
set(sliders{i},'Max',refObj.upboundInit(i));
end
end

function ParetoSurfaceButton_callback(~,~,refObj)
%TODO: Should only show up in low dimensions
matRad_plotPS(refObj);

end


% Callback subfunctions to support UI actions
function slider_callback(slider,~,textFields,sliders,idx,weights,dij,slice,DosePlot,refObj,optiProb,cstMod,cst,pln,ct)
% Update the text for the moved slider
%Calculate combination weights


v = matRad_navigationProblem(refObj.fIndsAll',refObj.fRef,slider.Value,idx,refObj.upboundRed);
%%need to check actual dimensions
if numel(v) == 0
%navigation algorithm didnt find a new point
for i = 1:numel(sliders)
set(sliders{i},'Value',refObj.fRef(i));
set(textFields{i},'String',refObj.fRef(i));
end

else

wnew = weights*v;%new weights

fNew = matRad_objectiveFunctions(optiProb,wnew,dij,cstMod);
fNew = optiProb.normalizeObjectives(fNew');
refObj.fRef = fNew;
refObj.wRef = wnew;

%% should be moved to seperate function
cubes = matRad_calcFastCubes(wnew,dij,pln);
%#Update the slider and text values for objective functions

for i = 1:numel(sliders)
set(sliders{i},'Value',fNew(i));
set(textFields{i},'String',fNew(i));
end

%Plot updated plan
matRad_plotSliceWrapper(DosePlot,ct,cst,1,cubes,3,slice);
end
end
46 changes: 46 additions & 0 deletions Pareto/NavigationUI/matRad_calcFastCubes.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
function cubes = matRad_calcFastCubes(w,dij,pln)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the speedup you gain by this?
Wouldn't it be an option to have additional arguments to calcCubesDoseGrid (e.g. just the field names you want to compute)? Doesn't look very compatible to scenario situations to me...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also alternatively cycle through the projections stored in OptimizationProblem? Are they not fast enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First option would calculate too many quantities if we have something like RBExD etc. Second option might be possible, but I thought the backprojections are mainly meant for usage in the optimization problem

% matRad computation of cube for plan optimization quantity
%
% call
% resultGUI = matRad_calcCubes(w,dij,pln
%
% input
% w: bixel weight vector
% dij: dose influence matrix
% pln: matRad pln struct
%
% output
% cubes: Array storing cubes of optimization quantity
%
% References
% -
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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/LICENSES.txt. 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.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch pln.bioParam.quantityOpt
case 'physicalDose'
cubes = reshape(dij.physicalDose{1}*w,dij.doseGrid.dimensions);
case 'RBExD'
if isfield(dij,'mAlphaDose') && isfield(dij,'mSqrtBetaDose')
%TODO
matRad_cfg.dispError('TODO');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not implemented?

else
cubes = reshape(dij.physicalDose{1}*w,dij.doseGrid.dimensions)*dij.RBE;
end
case 'effect'
cubes = reshape(full(dij.mAlphaDose{1} * wBeam + (dij.mSqrtBetaDose{1} * wBeam).^2),dij.doseGrid.dimensions);
end
cubes = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ...
cubes, ...
dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z,'linear',0);
end
Loading