Skip to content

Commit 162bde8

Browse files
authored
Merge pull request #873 from e0404/rc/v3.2.0
Minor Update 3.2.0
2 parents 0994481 + e455e93 commit 162bde8

File tree

127 files changed

+5833
-1114
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+5833
-1114
lines changed

.gitattributes

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Auto-detect text files and normalize (LF in repo).
2+
* text=auto
3+
4+
# Force files to be binary
5+
*.mat binary
6+
*.pdf binary
7+
*.mex* binary
8+
*.zip binary
9+
*.dll binary
10+
*.exe binary
11+
12+
# Ensure .m files are never marked as executable
13+
*.m -x

.github/actions/test-matlab/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ runs:
1515
uses: matlab-actions/setup-matlab@v2
1616
with:
1717
release: ${{ inputs.matlab-version }}
18-
products: Image_Processing_Toolbox Parallel_Computing_Toolbox Optimization_Toolbox
18+
products: Image_Processing_Toolbox Parallel_Computing_Toolbox Optimization_Toolbox Global_Optimization_Toolbox
1919

2020
# Runs test command
2121
- name: Run Tests

.github/workflows/coverage-report.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ jobs:
6464
6565
- name: Add Coverage PR Comment
6666
uses: marocchino/sticky-pull-request-comment@v2
67+
continue-on-error: true
6768
if: github.event_name == 'pull_request'
6869
with:
6970
recreate: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ coverage.xml
55
coverage.json
66
*.asv
77
build/
8+
.DS_Store

.gitlab-ci.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
variables:
2+
GIT_SUBMODULE_STRATEGY: recursive
3+
GIT_SUBMODULE_DEPTH: 1
4+
GIT_SUBMODULE_PATH: submodules/MOxUnit submodules/MOcov
5+
6+
.matlab_defaults:
7+
image:
8+
name: '${CUSTOM_MATLAB_IMAGE}:r2024b' # Replace the value with the name of the MATLAB container image you want to use
9+
entrypoint: [""]
10+
variables:
11+
MLM_LICENSE_FILE: $E040_MATLAB_LICENSE_SERVER # Replace the value with the port number and DNS address for your network license manager
12+
resource_group: matlab
13+
14+
test:
15+
stage: test
16+
extends: .matlab_defaults
17+
script: matlab -batch "assert(matRad_runTests('test',true));"
18+
artifacts:
19+
reports:
20+
junit: "./testresults.xml"
21+
coverage_report:
22+
coverage_format: cobertura
23+
path: "./coverage.xml"
24+
paths:
25+
- "./*.xml"
26+
- "./*.json"
27+
coverage: '/TOTAL.*? (100(?:\.0+)?%|[1-9]?\d(?:\.\d+)?%)/'
28+
29+
30+
package:
31+
stage: deploy
32+
extends: .matlab_defaults
33+
script:
34+
- |
35+
if [ -n "$CI_COMMIT_TAG" ]; then
36+
matlab -batch "matRad_buildStandalone('isRelease', true);"
37+
PACKAGE_NAME="$CI_COMMIT_TAG"
38+
else
39+
matlab -batch "matRad_buildStandalone();"
40+
PACKAGE_NAME="$CI_COMMIT_BRANCH"
41+
fi
42+
INSTALLER_NAME="matRad_installerLinux64_$PACKAGE_NAME"
43+
- tar -czvf $INSTALLER_NAME.tar.gz build/installer
44+
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $INSTALLER_NAME.tar.gz "$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/matRad_linux64/$CI_COMMIT_BRANCH/matRad/$PACKAGE_NAME/$INSTALLER_NAME.tar.gz"'
45+
# - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
46+
# - docker image tag matRad:develop $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/matRad:develop
47+
# - docker push $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/matRad:develop
48+
rules:
49+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
50+
- if: $CI_COMMIT_BRANCH == "dev"
51+
- if: $CI_COMMIT_TAG

AUTHORS.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
List of all matRad developers that contributed code (alphabetical)
1+
List of all matRad developers that contributed code (alphabetical)
22

33
* Nelly Abbani
44
* Nabe Al-Hasnawi
@@ -11,8 +11,10 @@
1111
* Louis Charton
1212
* Eric Christiansen
1313
* Remo Cristoforetti
14+
* Fabio D'Andrea
1415
* Marios Dallas
1516
* Edgardo Doerner
17+
* Louis Ermeneux
1618
* Simona Facchiano
1719
* Hubert Gabrys
1820
* Josefine Handrack
@@ -26,6 +28,7 @@
2628
* Navid Khaledi
2729
* Thomas Klinge
2830
* Jeremias Kunz
31+
* Mariia Lapaeva
2932
* Paul Anton Meder
3033
* Henning Mescher
3134
* Lucas-Raphael Müller
@@ -36,6 +39,7 @@
3639
* Claus Sarnighausen
3740
* Carsten Scholz
3841
* Camilo Sevilla
42+
* Mateusz Sitarz
3943
* Alexander Stadler
4044
* Uwe Titt
4145
* Niklas Wahl

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
# Changelog
22

3+
## Minor Update 3.2.0
4+
5+
### New Features
6+
- FRED MC interface (if installed)
7+
- VHEE planning with a Generic (unfocused) beam and a focused beam. The Generic beam can be forwarded to TOPAS as well.
8+
- New matRad_plotSlice function with keyword / value synxtax for more intuitive plotting of slices
9+
10+
### Bug Fixes & Performance
11+
- DICOM Import widget allow selection of multiple RTDose files.
12+
- DICOM Import Widget and importer handle selected patient more consistently and robustly.
13+
- DICOM Exporter writes quantities beyond dose, importer tries to import them correctly.
14+
- DICOM Exporter now always writes ReferencedRTPlanSequence. Importer can now survive without it.
15+
- DVH widget does not throw a warning in updates, handle scenarios correctly / more robustly and missing xlabel axesHandle parameter.
16+
- GUI fixes regarding setting of gantry angles and other parameters in the PlanningWidget
17+
- EXTERNAL contours now correctly recognized
18+
- performance improvement for obtaining jacobian structure in optimization
19+
- Available Classes (e.g., dose engines) are now cached for faster loading
20+
21+
### User Experience
22+
- Added new examples for usage of FRED & VHEE and a workflow example for comparing dose calculation on synthetic CT to planning CT
23+
- Updated examples to use matRad_plotSlice
24+
- GUI fixes for use in Matlab Online
25+
- The analyitcal functions from the Bortfeld Bragg Peak Model are now public and can be used to compute standard approximations (e.g. range-energy relationship)
26+
27+
### Development and CI
28+
- Added a new `.gitlab-ci.yml` file to support GitLab CI/CD, including test and package stages, artifact handling, and configuration for MATLAB container images and licensing.
29+
- Added a `.gitattributes` file to standardize line endings, treat certain file types as binary, and ensure `.m` files are not marked as executable.
30+
- In `.github/actions/test-matlab/action.yml`, added `Global_Optimization_Toolbox` to the list of MATLAB products for testing.
31+
- In `.github/workflows/coverage-report.yml`, made the coverage PR comment step tolerant to errors to avoid workflow failures.
32+
- More comprehensive dose calculation tests
33+
- Added new contributors
34+
335
## Version 3.1.0 - "Cleve"
436

537
### Major Changes and New Features

CITATION.cff

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ authors:
2525
given-names: "Eric"
2626
- family-names: "Cristoforetti"
2727
given-names: "Remo"
28+
- family-names: "D'Andrea"
29+
given-names: "Fabio"
2830
- family-names: "Dallas"
2931
given-names: "Marios"
3032
- family-names: "Doerner"
@@ -33,6 +35,8 @@ authors:
3335
given-names: "Swantje"
3436
- family-names: "Ellerbrock"
3537
given-names: "Malte"
38+
- family-names: "Ermeneux"
39+
given-names: "Louis"
3640
- family-names: "Facchiano"
3741
given-names: "Simona"
3842
- family-names: "Gabryś"
@@ -61,6 +65,8 @@ authors:
6165
given-names: "Thomas"
6266
- family-names: "Kunz"
6367
given-names: "Jeremias"
68+
- family-names: "Lapaeva"
69+
given-names: "Mariia"
6470
- family-names: "Mairani"
6571
given-names: "Andrea"
6672
- family-names: "Meder"
@@ -85,6 +91,8 @@ authors:
8591
given-names: "Carsten"
8692
- family-names: "Sevilla"
8793
given-names: "Camilo"
94+
- family-names: "Sitarz"
95+
given-names: "Mateusz"
8896
- family-names: "Stadler"
8997
given-names: "Alexander"
9098
- family-names: "Ulrich"

examples/matRad_example10_4DphotonRobust.m

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
stf = matRad_generateStf(ct,cst,pln);
193193

194194
%% Dose Calculation
195-
dij = matRad_calcPhotonDose(ct,stf,pln,cst);
195+
dij = matRad_calcDoseInfluence(ct,cst,stf,pln);
196196

197197
%% Inverse Optimization for IMPT based on RBE-weighted dose
198198
% The goal of the fluence optimization is to find a set of bixel/spot
@@ -241,30 +241,38 @@
241241
maxDose = max([max(resultGUI.([pln.propOpt.quantityOpt])(:,:,slice)) max(resultGUIrobust.([pln.propOpt.quantityOpt])(:,:,slice))])+1e-4;
242242
doseIsoLevels = linspace(0.1 * maxDose,maxDose,10);
243243
figure,
244-
subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan - beam1')
245-
subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt]) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan')
244+
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')
245+
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')
246+
%subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt '_' 'beam1']) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan - beam1')
247+
%subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI.([pln.propOpt.quantityOpt]) ,plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('conventional plan')
246248

247249
figure
248-
subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan - beam1')
249-
subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan')
250+
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')
251+
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')
252+
%subplot(121),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_' 'beam1']),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan - beam1')
253+
%subplot(122),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan')
250254

251255

252256
figure
253-
subplot(131),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan')
254-
subplot(132),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.('accPhysicalDose'),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan dose accumulation')
257+
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')
258+
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')
259+
%subplot(131),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.([pln.propOpt.quantityOpt]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan')
260+
%subplot(132),matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust4D.('accPhysicalDose'),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);title('robust plan dose accumulation')
255261

256262
% create an interactive plot to slide through individual scnearios
257263
f = figure; title('individual scenarios');
258264
numScen = 1;
259265
maxDose = max(max(resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))])(:,:,slice)))+0.2;
260266
doseIsoLevels = linspace(0.1 * maxDose,maxDose,10);
261-
matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);
267+
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);
268+
%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUIrobust.([pln.propOpt.quantityOpt '_scen' num2str(round(numScen))]),plane,slice,[],[],colorcube,[],[0 maxDose],doseIsoLevels);
262269

263270
[env,envver] = matRad_getEnvironment();
264271
if strcmp(env,'MATLAB') || str2double(envver(1)) >= 5
265272
b = uicontrol('Parent',f,'Style','slider','Position',[50,5,419,23],...
266273
'value',numScen, 'min',1, 'max',pln.multScen.totNumScen,'SliderStep', [1/(pln.multScen.totNumScen-1) , 1/(pln.multScen.totNumScen-1)]);
267-
set(b,'Callback',@(es,ed) 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));
274+
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));
275+
%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));
268276
end
269277

270278
%% Indicator calculation and show DVH and QI
@@ -282,10 +290,12 @@
282290
[cstStatRob, resultGUISampRob, metaRob] = matRad_samplingAnalysis(ct,cst,plnSampRob,caSampRob, mSampDoseRob, resultGUInomScen);
283291

284292
figure,title('std dose cube based on sampling - conventional')
285-
matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISamp.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISamp.stdCube(:))]);
293+
matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISamp.stdCube ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISamp.stdCube(:))]);
294+
%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISamp.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISamp.stdCube(:))]);
286295

287296
figure,title('std dose cube based on sampling - robust')
288-
matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISampRob.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISampRob.stdCube(:))]);
297+
matRad_plotSlice(ct, 'axesHandle', gca, 'cst', cst, 'cubeIdx', 1, 'dose', resultGUISampRob.stdCube ,'plane', plane, 'slice', slice, 'contourColorMap', colorcube, 'doseWindow', [0 max(resultGUISampRob.stdCube(:))]);
298+
%matRad_plotSliceWrapper(gca,ct,cst,1,resultGUISampRob.stdCube,plane,slice,[],[],colorcube,[],[0 max(resultGUISampRob.stdCube(:))]);
289299

290300

291301

examples/matRad_example15_brachy.m

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,12 @@
8484
% Here we will use HDR. By this means matRad will look for 'brachy_HDR.mat'
8585
% in our root directory and will use the data provided in there for
8686
% dose calculation.
87-
8887
pln.radiationMode = 'brachy';
8988
pln.machine = 'HDR'; % 'LDR' or 'HDR' for brachy
9089

9190
pln.bioModel = 'none';
9291
pln.multScen = 'nomScen';
9392

94-
9593
%% II.1 - needle and template geometry
9694
% Now we have to set some parameters for the template and the needles.
9795
% Let's start with the needles: Seed distance is the distance between
@@ -110,6 +108,7 @@
110108
% The needles will be positioned right under the target volume pointing up.
111109

112110

111+
pln.propStf.visMode = 1; %Enable visualization for stf generation
113112
pln.propStf.bixelWidth = 5; % [mm] template grid distance
114113

115114
%Template Type
@@ -151,16 +150,12 @@
151150
% needles.
152151
% Calculation time will be reduced by one tenth when we define a dose
153152
% cutoff distance.
154-
155-
156153
pln.propDoseCalc.TG43approximation = '2D'; %'1D' or '2D'
157154

158155
pln.propDoseCalc.doseGrid.resolution.x = 5; % [mm]
159156
pln.propDoseCalc.doseGrid.resolution.y = 5; % [mm]
160157
pln.propDoseCalc.doseGrid.resolution.z = 5; % [mm]
161158

162-
163-
164159
% We can also use other solver for optimization than IPOPT. matRad
165160
% currently supports simulannealbnd from the MATLAB Global Optimization Toolbox. First we
166161
% check if the simulannealbnd-Solver is available, and if it is, we set in in the
@@ -172,7 +167,6 @@
172167
pln.propOpt.optimizer = 'IPOPT';
173168
end
174169

175-
pln.propOpt.optimizer = 'IPOPT';
176170
%% II.1 - book keeping
177171
% Some field names have to be kept although they don't have a direct
178172
% relevance for brachy therapy.
@@ -182,14 +176,12 @@
182176
% Et voila! Our treatment plan structure is ready. Lets have a look:
183177
disp(pln);
184178

185-
186179
%% II.2 Steering Seed Positions From STF
187180
% The steering file struct contains all needls/catheter geometry with the
188181
% target volume, number of needles, seeds and the positions of all needles
189182
% The one in the end enables visualization.
190183
stf = matRad_generateStf(ct,cst,pln);
191184

192-
193185
%% II.2 - view stf
194186
% The 3D view is interesting, but we also want to know how the stf struct
195187
% looks like.

0 commit comments

Comments
 (0)