Skip to content

Commit 8ddcf25

Browse files
committed
Resolve merge conflict: keep dvh_method and bound_type in DVH table
2 parents f42d102 + 3196613 commit 8ddcf25

File tree

5 files changed

+54
-35
lines changed

5 files changed

+54
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ docs/_build/
5656
/results/
5757
/metadata/
5858
/examples/hugging_face_data/
59+
.DS_Store

portpy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
# a separate commercial license.
1616
# ----------------------------------------------------------------------
1717

18-
__version__ = "1.1.2"
18+
__version__ = "1.1.3"
1919
# Change version here manually to reflect it everywhere
2020
from portpy import photon

portpy/photon/clinical_criteria.py

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -140,34 +140,38 @@ def get_criteria(self, type: str = None) -> List[dict]:
140140
return all_criteria
141141

142142
def check_criterion_exists(self, criterion, return_ind:bool = False):
143-
criterion_exist = False
144-
criterion_ind = None
145-
for ind, crit in enumerate(self.clinical_criteria_dict['criteria']):
146-
if (crit['type'] == criterion['type']) and crit['parameters'] == criterion['parameters']:
147-
for constraint in crit['constraints']:
148-
if constraint == criterion['constraints']:
149-
criterion_exist = True
150-
criterion_ind = ind
151-
if return_ind:
152-
return criterion_exist,criterion_ind
153-
else:
154-
return criterion_exist
143+
criterion_exist = False
144+
criterion_ind = None
145+
for ind, crit in enumerate(self.clinical_criteria_dict['criteria']):
146+
if (crit['type'] == criterion['type']) and crit['parameters'] == criterion['parameters']:
147+
for constraint in crit['constraints']:
148+
for key, _ in criterion['constraints'].items():
149+
if constraint == key:
150+
criterion_exist = True
151+
criterion_ind = ind
152+
if return_ind:
153+
return criterion_exist,criterion_ind
154+
else:
155+
return criterion_exist
155156

156157
def modify_criterion(self, criterion):
157158
"""
158-
Modify the criterion the clinical criteria
159-
160-
159+
Update constraints for an existing criterion.
160+
Only updates keys that already exist in the original constraint dict.
161161
"""
162-
criterion_found = False
163162
for ind, crit in enumerate(self.clinical_criteria_dict['criteria']):
164-
if (crit['type'] == criterion['type']) and crit['parameters'] == criterion['parameters']:
165-
for constraint in crit['constraints']:
166-
if constraint == criterion['constraints']:
167-
self.clinical_criteria_dict['criteria'][ind]['constraints'][constraint] = criterion['constraints']
168-
criterion_found = True
169-
if not criterion_found:
170-
raise Warning('No criteria for {}'.format(criterion))
163+
164+
if crit['type'] == criterion['type'] and crit['parameters'] == criterion['parameters']:
165+
existing_keys = crit['constraints']
166+
updated = False
167+
for key, value in criterion.get('constraints', {}).items():
168+
if key in existing_keys:
169+
existing_keys[key] = value
170+
updated = True
171+
if updated:
172+
return
173+
174+
raise Warning(f"No criteria found for {criterion}")
171175

172176

173177
def get_num(self, string: Union[str, float]):
@@ -279,6 +283,7 @@ def get_dvh_table(self, my_plan: Plan, constraint_list: list = None, opt_params:
279283
df.at[count, 'volume_perc'] = dvh_updated_list[i]['constraints'][limit_key]
280284
df.at[count, 'dvh_type'] = 'constraint'
281285
df.at[count, 'dvh_method'] = dvh_method
286+
df.at[count, 'bound_type'] = dvh_updated_list[i]['constraints'].get('bound_type', 'upper')
282287
count = count + 1
283288
goal_key = self.matching_keys(dvh_updated_list[i]['constraints'], 'goal')
284289
if goal_key in dvh_updated_list[i]['constraints']:
@@ -288,6 +293,7 @@ def get_dvh_table(self, my_plan: Plan, constraint_list: list = None, opt_params:
288293
df.at[count, 'dvh_type'] = 'goal'
289294
df.at[count, 'dvh_method'] = dvh_method
290295
df.at[count, 'weight'] = dvh_updated_list[i]['parameters']['weight']
296+
df.at[count, 'bound_type'] = dvh_updated_list[i]['constraints'].get('bound_type', 'upper')
291297
count = count + 1
292298
if 'dose_volume_D' in dvh_updated_list[i]['type']:
293299
limit_key = self.matching_keys(dvh_updated_list[i]['constraints'], 'limit')
@@ -297,6 +303,7 @@ def get_dvh_table(self, my_plan: Plan, constraint_list: list = None, opt_params:
297303
df.at[count, 'dose_gy'] = self.dose_to_gy(limit_key, dvh_updated_list[i]['constraints'][limit_key])
298304
df.at[count, 'dvh_method'] = dvh_method
299305
df.at[count, 'dvh_type'] = 'constraint'
306+
df.at[count, 'bound_type'] = dvh_updated_list[i]['constraints'].get('bound_type', 'upper')
300307
count = count + 1
301308
goal_key = self.matching_keys(dvh_updated_list[i]['constraints'], 'goal')
302309
if goal_key in dvh_updated_list[i]['constraints']:
@@ -306,14 +313,15 @@ def get_dvh_table(self, my_plan: Plan, constraint_list: list = None, opt_params:
306313
df.at[count, 'dvh_method'] = dvh_method
307314
df.at[count, 'dvh_type'] = 'goal'
308315
df.at[count, 'weight'] = dvh_updated_list[i]['parameters']['weight']
316+
df.at[count, 'bound_type'] = dvh_updated_list[i]['constraints'].get('bound_type', 'upper')
309317
count = count + 1
310318
self.dvh_table = df
311319
self.get_max_tol(constraints_list=constraint_list)
312320
self.filter_dvh(my_plan=my_plan)
313321
return self.dvh_table
314322

315323

316-
def get_low_dose_vox_ind(self, my_plan: Plan, dose: np.ndarray, inf_matrix: InfluenceMatrix):
324+
def get_low_dose_vox_ind(self, my_plan: Plan, dose: np.ndarray, inf_matrix: InfluenceMatrix = None):
317325
"""
318326
Identifies and stores the indices of low-dose voxels for each DVH constraint or goal.
319327
@@ -342,15 +350,25 @@ def get_low_dose_vox_ind(self, my_plan: Plan, dose: np.ndarray, inf_matrix: Infl
342350
volume percentage.
343351
"""
344352
dvh_table = self.dvh_table
345-
inf_matrix = my_plan.inf_matrix
353+
if inf_matrix is None:
354+
inf_matrix = my_plan.inf_matrix
346355
for ind in dvh_table.index:
347356
structure_name, dose_gy, vol_perc = dvh_table['structure_name'][ind], dvh_table['dose_gy'][ind], \
348357
dvh_table['volume_perc'][ind]
349358
dvh_type = dvh_table['dvh_type'][ind]
359+
bound_type = dvh_table.at[ind, 'bound_type'] if 'bound_type' in dvh_table.columns else 'upper'
350360
vol_perc = vol_perc / inf_matrix.get_fraction_of_vol_in_calc_box(structure_name)
351361
struct_vox = inf_matrix.get_opt_voxels_idx(structure_name)
352362
n_struct_vox = len(struct_vox)
353-
sort_ind = np.argsort(dose[struct_vox])
363+
# sort_ind = np.argsort(dose[struct_vox])
364+
if bound_type == 'lower':
365+
# For lower bound (e.g., PTV coverage), pick HIGHEST-dose voxels up to p%
366+
sort_ind = np.argsort(-dose[struct_vox]) # descending
367+
target_perc = vol_perc # accumulate to p%
368+
else:
369+
# Original behavior (upper bound): pick LOWEST-dose voxels up to (100 - p)%
370+
sort_ind = np.argsort(dose[struct_vox]) # ascending
371+
target_perc = 100 - vol_perc # accumulate to (100 - p)%
354372
voxel_sort = struct_vox[sort_ind]
355373
weights = inf_matrix.get_opt_voxels_volume_cc(structure_name)
356374
weights_sort = weights[sort_ind]
@@ -360,7 +378,7 @@ def get_low_dose_vox_ind(self, my_plan: Plan, dose: np.ndarray, inf_matrix: Infl
360378
for w_ind in range(n_struct_vox):
361379
w_sum = w_sum + weights_sort[w_ind]
362380
w_ratio = w_sum / weight_all_sum
363-
if w_ratio * 100 >= (100 - vol_perc):
381+
if w_ratio * 100 >= target_perc:
364382
break
365383
low_dose_voxels = voxel_sort[:w_ind+1]
366384
if ind == 0:

portpy/photon/data_explorer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,8 @@ def _load_structure_metadata(self, patient_id: str, temp_download_dir: Optional[
593593
repo_id=self.hf_repo_id,
594594
repo_type="dataset",
595595
filename=hf_path,
596-
local_dir=temp_download_dir,
597-
use_auth_token=self.hf_token
596+
local_dir=temp_download_dir
597+
# use_auth_token=self.hf_token
598598
)
599599
with open(local_file) as f:
600600
return json.load(f)
@@ -634,8 +634,8 @@ def _load_beam_metadata(self, patient_id: str, temp_download_dir: Optional[str]
634634
self.hf_repo_id,
635635
repo_type="dataset",
636636
filename=path,
637-
local_dir=temp_download_dir,
638-
use_auth_token=self.hf_token
637+
local_dir=temp_download_dir
638+
# use_auth_token=self.hf_token
639639
)
640640
with open(local_file) as f:
641641
beam_meta.append(json.load(f))
@@ -666,8 +666,8 @@ def _load_planner_beams(self, patient_id: str, temp_download_dir: Optional[str]
666666
self.hf_repo_id,
667667
repo_type="dataset",
668668
filename=hf_path,
669-
local_dir=temp_download_dir,
670-
use_auth_token=self.hf_token
669+
local_dir=temp_download_dir
670+
# use_auth_token=self.hf_token
671671
)
672672
with open(local_file) as f:
673673
return json.load(f).get("IDs", [])

portpy/photon/vmat_scp/vmat_scp_optimization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ def create_cvxpy_intermediate_problem_prediction(self, pred_dose_1d, final_dose_
11781178
+ cp.multiply(bound_v_r, card_bound_inds_r)]
11791179
# generic constraints for relation between interior and boundary beamlets
11801180
constraints += [leaf_pos_mu_r - leaf_pos_mu_l >= int_v[map_int_v]]
1181-
constraints += [int_v >= self.vmat_params['mu_min']]
1181+
constraints += [int_v*100 >= self.vmat_params['mu_min']] # multiply it by 100 to match eclipse mu
11821182
constraints += [bound_v_l <= int_v[map_int_v]]
11831183
constraints += [bound_v_r <= int_v[map_int_v]]
11841184
if 'minimum_dynamic_leaf_gap_mm' in self.vmat_params:

0 commit comments

Comments
 (0)