diff --git a/README.md b/README.md index a20c511..27813f9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

-![Version](https://img.shields.io/static/v1?label=latest&message=v1.0.4.5&color=darkgreen) +![Version](https://img.shields.io/static/v1?label=latest&message=v1.0.4.6&color=darkgreen) [![Total Downloads](https://static.pepy.tech/personalized-badge/portpy?period=total&units=international_system&left_color=grey&right_color=blue&left_text=total%20downloads)](https://pepy.tech/project/portpy?&left_text=totalusers) [![Monthly Downloads](https://static.pepy.tech/badge/portpy/month)](https://pepy.tech/project/portpy) # What is PortPy? diff --git a/portpy/__init__.py b/portpy/__init__.py index d21fd0a..5caeefc 100644 --- a/portpy/__init__.py +++ b/portpy/__init__.py @@ -1,3 +1,3 @@ -__version__ = "1.0.4.5" +__version__ = "1.0.4.6" from portpy import photon diff --git a/portpy/photon/optimization.py b/portpy/photon/optimization.py index 6466ce8..6d2ea84 100644 --- a/portpy/photon/optimization.py +++ b/portpy/photon/optimization.py @@ -343,7 +343,22 @@ def solve(self, return_cvxpy_prob=False, *args, **kwargs): problem = cp.Problem(cp.Minimize(cp.sum(self.obj)), constraints=self.constraints) print('Running Optimization..') t = time.time() - problem.solve(*args, **kwargs) + # Check if 'solver' is passed in args + solver = kwargs.get('solver', None) + if solver and solver.lower() == 'mosek': + try: + problem.solve(*args, **kwargs) # Attempt to solve with mosek + except cp.error.SolverError as e: + # Raise a custom error if MOSEK is not installed or available + raise ImportError( + "MOSEK solver is not installed. You can obtain the MOSEK license file by applying using an .edu account. \n" + r"The license file should be placed in the directory C:\\Users\\username\\mosek." + "\n To use MOSEK, install it using: pip install portpy[mosek].\n" + "If a license is not available, you may try open-source or free solvers like SCS or ECOS. \n" + "Please refer to the CVXPy documentation for more information about its various solvers.\n" + ) from e + else: + problem.solve(*args, **kwargs) # Continue solving with other solvers elapsed = time.time() - t self.obj_value = problem.value print("Optimal value: %s" % problem.value) diff --git a/portpy/photon/utils/convert_dose_rt_dicom_to_portpy.py b/portpy/photon/utils/convert_dose_rt_dicom_to_portpy.py index 4ce921e..3957cc6 100644 --- a/portpy/photon/utils/convert_dose_rt_dicom_to_portpy.py +++ b/portpy/photon/utils/convert_dose_rt_dicom_to_portpy.py @@ -2,7 +2,11 @@ from __future__ import annotations import os import SimpleITK as sitk -from pydicom import dcmread +try: + from pydicom import dcmread + pydicom_installed = True +except ImportError: + pydicom_installed = False import numpy as np from portpy.photon.ct import CT from typing import TYPE_CHECKING @@ -74,6 +78,10 @@ def get_ct_image(ct: CT): def convert_dose_rt_dicom_to_portpy(my_plan: Plan = None, ct: CT = None, dir_name: str = None, dose_file_name: str = None): + if not pydicom_installed: + raise ImportError( + "Pydicom. To use this function, please install it with `pip install portpy[pydicom]`." + ) dicom_dose_image = read_dicom_dose(dir_name=dir_name, dose_file_name=dose_file_name) if ct is None: ct = my_plan.ct diff --git a/portpy/photon/utils/write_rt_plan_imrt.py b/portpy/photon/utils/write_rt_plan_imrt.py index 5aca1ca..8b4653f 100644 --- a/portpy/photon/utils/write_rt_plan_imrt.py +++ b/portpy/photon/utils/write_rt_plan_imrt.py @@ -2,8 +2,12 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from portpy.photon.plan import Plan -from pydicom import dcmread -from pydicom import Dataset, Sequence +try: + from pydicom import dcmread + from pydicom import Dataset, Sequence + pydicom_installed = True +except ImportError: + pydicom_installed = False def write_rt_plan_imrt(my_plan: Plan, leaf_sequencing: dict, out_rt_plan_file: str, in_rt_plan_file: str): @@ -23,6 +27,11 @@ def write_rt_plan_imrt(my_plan: Plan, leaf_sequencing: dict, out_rt_plan_file: s # bottom_right_y_mm = my_plan.beams.beams_dict['jaw_position'][0]['bottom_right_y_mm'] # top_left_y_mm = my_plan.beams.beams_dict['jaw_position'][0]['top_left_y_mm'] + if not pydicom_installed: + raise ImportError( + "Pydicom. To use this function, please install it with `pip install portpy[pydicom]`." + ) + # read rt plan file using pydicom ds = dcmread(in_rt_plan_file) for b in range(len(ds.BeamSequence)): diff --git a/portpy/photon/utils/write_rt_plan_vmat.py b/portpy/photon/utils/write_rt_plan_vmat.py index 42e951b..cb1f5db 100644 --- a/portpy/photon/utils/write_rt_plan_vmat.py +++ b/portpy/photon/utils/write_rt_plan_vmat.py @@ -3,8 +3,12 @@ if TYPE_CHECKING: from portpy.photon.plan import Plan from portpy.photon.beam import Beams -from pydicom import dcmread -from pydicom import Dataset, Sequence +try: + from pydicom import dcmread + from pydicom import Dataset, Sequence + pydicom_installed = True +except ImportError: + pydicom_installed = False import numpy as np @@ -173,7 +177,10 @@ def write_rt_plan_vmat(my_plan: Plan, out_rt_plan_file: str, in_rt_plan_file: st :param out_rt_plan_file: new rt plan which can be imported in TPS """ - + if not pydicom_installed: + raise ImportError( + "Pydicom. To use this function, please install it with `pip install portpy[pydicom]`." + ) # read rt plan file using pydicom ds = dcmread(in_rt_plan_file) del ds.BeamSequence diff --git a/setup.py b/setup.py index 74e1c07..82cbcc8 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ def _get_portpy_photon_version(): "cvxpy>=1.1.18", "ecos>=2.0.10", "h5py>=3.6.0", - "Mosek>=9.3.14", "natsort>=8.1.0", "numpy>=1.15", "osqp>=0.4.1", @@ -70,7 +69,11 @@ def _get_portpy_photon_version(): "typing-extensions>=3.10.0.0", "scikit-image>=0.17.2", "patchify>=0.2.3", - "pydicom>=2.2.0", ], + extras_require={ + 'mosek': ["Mosek>=9.3.14"], + 'pydicom': ["pydicom>=2.2.0"], + 'full': ["Mosek>=9.3.14", "pydicom>=2.2.0"] + } )