|
| 1 | +# %% [markdown] |
| 2 | +"""# Example for proton dose calculation using the FRED engine.""" |
| 3 | +# %% [markdown] |
| 4 | +# This example demonstrates how to use the pyRadPlan library to perform proton dose calculations using the FRED Monte Carlo engine. |
| 5 | + |
| 6 | +# For installation instructions, please refer to https://www.fred-mc.org/ |
| 7 | + |
| 8 | +# To display this script in a Jupyter Notebook, you need to install jupytext via pip and run the following command. |
| 9 | +# This will create a .ipynb file in the same directory: |
| 10 | + |
| 11 | +# ```bash |
| 12 | +# pip install jupytext |
| 13 | +# jupytext --to notebook path/to/this/file/proton_MC_FRED.py |
| 14 | + |
| 15 | +# %% |
| 16 | +# Import necessary libraries |
| 17 | +import logging |
| 18 | +import numpy as np |
| 19 | + |
| 20 | +from pyRadPlan import ( |
| 21 | + IonPlan, |
| 22 | + generate_stf, |
| 23 | + calc_dose_influence, |
| 24 | + calc_dose_forward, |
| 25 | + load_tg119, |
| 26 | + fluence_optimization, |
| 27 | + plot_slice, |
| 28 | +) |
| 29 | + |
| 30 | +from pyRadPlan.optimization.objectives import SquaredDeviation, SquaredOverdosing, MeanDose |
| 31 | + |
| 32 | +logging.basicConfig(level=logging.INFO) |
| 33 | + |
| 34 | +# %% |
| 35 | +# Load TG119 (provided within pyRadPlan) |
| 36 | +ct, cst = load_tg119() |
| 37 | + |
| 38 | +# %% [markdown] |
| 39 | +# In this section, we create a proton therapy plan using the FRED engine. |
| 40 | +# First, we define the parameters for the steering information (`prop_stf`). |
| 41 | +# Next, we specify the settings for dose calculation (`prop_dose_calc`), |
| 42 | +# including compatibility with different FRED versions through `dij_format_version`. |
| 43 | +# Finally, we configure the optimization parameters (`prop_opt`), as demonstrated in previous examples. |
| 44 | + |
| 45 | +# Use less histories and beams when experimenting. This reduces computation time significantly. |
| 46 | + |
| 47 | +# %% |
| 48 | +pln = IonPlan(radiation_mode="protons", machine="Generic") |
| 49 | +pln.prop_stf = { |
| 50 | + "gantry_angles": [0, 180], # define gantry angles for n beams |
| 51 | + "couch_angles": [0, 0], |
| 52 | + "longitudinal_spot_spacing": 2.0, |
| 53 | + "iso_center": np.array([[-5.0, -5.0, -5.0], [-5.0, -5.0, -5.0]]), # two beams |
| 54 | + "bixel_width": 10, |
| 55 | + "add_margin": 1, |
| 56 | +} |
| 57 | +pln.prop_dose_calc = { |
| 58 | + "engine": "FRED", # Use the FRED engine for dose calculation |
| 59 | + "fred_version": "3.76.0", # std: "3.70.0" |
| 60 | + "use_gpu": True, # std: True | one has to set GPU = 1 using 'fred -config' manually! |
| 61 | + "calc_let": False, # std: False |
| 62 | + "scorers": ["Dose"], # std: ["Dose"] |
| 63 | + "room_material": "Air", # vacuum | std: Air |
| 64 | + "print_output": False, |
| 65 | + "num_histories_per_beamlet": 2e3, # use less histories for faster computation (e.g. 2e2) or less beams |
| 66 | + # "dij_format_version": "31", # 20/30 for fred 3.70.0 and 21/31 for fred 3.76.0 |
| 67 | + # "save_input": "path/to/save/input", |
| 68 | + # "save_output": "path/to/save/output", # or True (for current dir)/False | std: False (using temp path) |
| 69 | + # "use_output": "path/to/use/output", # skips output file generation if only 'use_output_file' is set and not 'save_output_file' |
| 70 | +} |
| 71 | + |
| 72 | +pln.prop_opt = {"solver": "scipy"} |
| 73 | + |
| 74 | +# %% [markdown] |
| 75 | +# Generate Steering Geometry ("stf") |
| 76 | +# %% |
| 77 | +stf = generate_stf(ct, cst, pln) |
| 78 | + |
| 79 | +# %% [markdown] |
| 80 | +# Run calc_dose_influence (dose influence matrix) |
| 81 | +# %% |
| 82 | +dij = calc_dose_influence(ct, cst, stf, pln) |
| 83 | + |
| 84 | +# %% [markdown] |
| 85 | +# Define cst objectives and run fluence optimization |
| 86 | +# %% |
| 87 | +cst.vois[0].objectives = [SquaredOverdosing(priority=100.0, d_max=1.0)] # OAR |
| 88 | +cst.vois[1].objectives = [SquaredDeviation(priority=1000.0, d_ref=3.0)] # Target |
| 89 | +cst.vois[2].objectives = [ |
| 90 | + MeanDose(priority=1.0, d_ref=0.0), |
| 91 | + SquaredOverdosing(priority=10.0, d_max=2.0), |
| 92 | +] # BODY |
| 93 | + |
| 94 | +fluence = fluence_optimization(ct, cst, stf, dij, pln) |
| 95 | + |
| 96 | +# %% [markdown] |
| 97 | +# Compute dose distribution and other quantities given in pln.prop_opt |
| 98 | +# %% |
| 99 | +result_opt = dij.compute_result_ct_grid(fluence) |
| 100 | + |
| 101 | +# %% [markdown] |
| 102 | +# Visualize the results |
| 103 | +# %% |
| 104 | + |
| 105 | +# Choose a slice to visualize |
| 106 | +view_slice = int(np.round(ct.size[2] / 2)) |
| 107 | + |
| 108 | +plot_slice( |
| 109 | + ct=ct, |
| 110 | + cst=cst, |
| 111 | + overlay=result_opt["physical_dose"], |
| 112 | + view_slice=view_slice, |
| 113 | + plane="axial", |
| 114 | + overlay_unit="Gy", |
| 115 | +) |
| 116 | + |
| 117 | +# %% [markdown] |
| 118 | +# You can also run calc_dose_forward (direct dose calculation) without optimization. |
| 119 | +# %% |
| 120 | +# Set run_direct_dose_calc to True to perform direct dose calculation |
| 121 | +run_direct_dose_calc = False |
| 122 | +if run_direct_dose_calc: |
| 123 | + result_fwd = calc_dose_forward(ct, cst, stf, pln, np.ones(stf.total_number_of_bixels)) |
0 commit comments