11import pandas as pd
2- from peptdeep .pretrained_models import ModelManager , model_mgr_settings , MODEL_DOWNLOAD_INSTRUCTIONS , \
2+ from peptdeep .pretrained_models import ModelManager , model_mgr_settings , \
33 psm_sampling_with_important_mods , is_model_zip
4+
5+ # Try to import MODEL_DOWNLOAD_INSTRUCTIONS from peptdeep (version-dependent export)
6+ try :
7+ from peptdeep .pretrained_models import MODEL_DOWNLOAD_INSTRUCTIONS
8+ except ImportError :
9+ MODEL_DOWNLOAD_INSTRUCTIONS = (
10+ "Please download the pretrained models manually from "
11+ "https://github.com/MannLabs/alphapeptdeep/releases and place them in the model directory."
12+ )
413from peptdeep .model .ms2 import pDeepModel , frag_types , max_frag_charge , ModelMS2Bert , calc_ms2_similarity
514from peptdeep .model .rt import AlphaRTModel
615from peptdeep .model .ccs import AlphaCCSModel
@@ -57,11 +66,19 @@ def configure_torch_for_hpc(n_threads: int = 1) -> None:
5766
5867
5968class MS2ModelManager (ModelManager ):
69+ """Extended ModelManager that uses MS2pDeepModel for MS2 predictions.
70+
71+ Note: This class intentionally does not call super().__init__() because it
72+ replaces the ms2_model with MS2pDeepModel. Calling the parent would create
73+ duplicate model instances.
74+ """
75+
6076 def __init__ (self ,
6177 mask_modloss : bool = False ,
6278 device : str = "gpu" ,
6379 model_dir : str = "." ,
6480 ):
81+ # Initialize attributes expected by parent class methods
6582 self ._train_psm_logging = True
6683
6784 self .ms2_model : pDeepModel = MS2pDeepModel (
@@ -75,8 +92,9 @@ def __init__(self,
7592 )
7693 self .model_url = "https://github.com/MannLabs/alphapeptdeep/releases/download/pre-trained-models/pretrained_models_v3.zip"
7794
78- if len (glob .glob (os .path .join (model_dir , "*ms2.pth" ))) > 0 :
79- self .load_external_models (ms2_model_file = glob .glob (os .path .join (model_dir , "*ms2.pth" ))[0 ])
95+ ms2_model_files = glob .glob (os .path .join (model_dir , "*ms2.pth" ))
96+ if ms2_model_files :
97+ self .load_external_models (ms2_model_file = ms2_model_files [0 ])
8098 self .model_str = model_dir
8199 else :
82100 self .download_model_path = os .path .join (model_dir , "pretrained_models_v3.zip" )
@@ -116,7 +134,9 @@ def _download_models(self, model_zip_file_path: str, skip_if_exists: bool = True
116134 else :
117135 logging .info (f"Downloading pretrained models from { url } to { model_zip_file_path } ..." )
118136 try :
119- os .makedirs (os .path .dirname (model_zip_file_path ), exist_ok = True )
137+ parent_dir = os .path .dirname (model_zip_file_path )
138+ if parent_dir :
139+ os .makedirs (parent_dir , exist_ok = True )
120140 context = ssl .create_default_context (cafile = certifi .where ())
121141 # Use streaming download with longer timeout for large model files
122142 # timeout=300s (5 min) for slow connections; stream in 1MB chunks
@@ -129,16 +149,21 @@ def _download_models(self, model_zip_file_path: str, skip_if_exists: bool = True
129149 try :
130150 os .remove (model_zip_file_path )
131151 except OSError :
132- pass
152+ pass # Best-effort cleanup; file may already be removed or locked
133153 raise FileNotFoundError (
134154 f"Downloading model failed: { e } .\n " + MODEL_DOWNLOAD_INSTRUCTIONS
135155 ) from e
136156
137157 logging .info ("Successfully downloaded pretrained models." )
138158 if not is_model_zip (model_zip_file_path ):
159+ # Clean up invalid/corrupted file
160+ try :
161+ os .remove (model_zip_file_path )
162+ except OSError :
163+ pass # Best-effort cleanup; file may already be removed or locked
139164 raise ValueError (
140165 f"Local model file is not a valid zip: { model_zip_file_path } .\n "
141- f"Please delete this file and try again.\n "
166+ f"The invalid file has been removed. Please try again.\n "
142167 f"Or: { MODEL_DOWNLOAD_INSTRUCTIONS } "
143168 )
144169
@@ -184,6 +209,28 @@ def load_installed_models(self, download_model_path: str = "pretrained_models_v3
184209 download_model_path , model_path_in_zip = "generic/charge.pth"
185210 )
186211
212+ def _build_intensity_df (self , matched_intensity_df : pd .DataFrame ) -> pd .DataFrame :
213+ """Build intensity DataFrame with all charged fragment types.
214+
215+ Parameters
216+ ----------
217+ matched_intensity_df : pd.DataFrame
218+ The matched fragment intensities.
219+
220+ Returns
221+ -------
222+ pd.DataFrame
223+ Intensity DataFrame with columns for all charged fragment types,
224+ filled with 0.0 for missing columns.
225+ """
226+ inten_df = pd .DataFrame ()
227+ for frag_type in self .ms2_model .charged_frag_types :
228+ if frag_type in matched_intensity_df .columns :
229+ inten_df [frag_type ] = matched_intensity_df [frag_type ]
230+ else :
231+ inten_df [frag_type ] = 0.0
232+ return inten_df
233+
187234 def train_ms2_model (
188235 self ,
189236 psm_df : pd .DataFrame ,
@@ -215,12 +262,7 @@ def train_ms2_model(
215262 else :
216263 tr_df = psm_df
217264 if len (tr_df ) > 0 :
218- tr_inten_df = pd .DataFrame ()
219- for frag_type in self .ms2_model .charged_frag_types :
220- if frag_type in matched_intensity_df .columns :
221- tr_inten_df [frag_type ] = matched_intensity_df [frag_type ]
222- else :
223- tr_inten_df [frag_type ] = 0.0
265+ tr_inten_df = self ._build_intensity_df (matched_intensity_df )
224266
225267 if self .use_grid_nce_search :
226268 self .nce , self .instrument = self .ms2_model .grid_nce_search (
@@ -254,12 +296,7 @@ def train_ms2_model(
254296 test_psm_df = pd .DataFrame ()
255297 else :
256298 test_psm_df = psm_df .copy ()
257- tr_inten_df = pd .DataFrame ()
258- for frag_type in self .ms2_model .charged_frag_types :
259- if frag_type in matched_intensity_df .columns :
260- tr_inten_df [frag_type ] = matched_intensity_df [frag_type ]
261- else :
262- tr_inten_df [frag_type ] = 0.0
299+ tr_inten_df = self ._build_intensity_df (matched_intensity_df )
263300 self .set_default_nce_instrument (test_psm_df )
264301 else :
265302 test_psm_df = pd .DataFrame ()
@@ -369,7 +406,7 @@ def _set_batch_predict_data(
369406 self ,
370407 batch_df : pd .DataFrame ,
371408 predicts : np .ndarray ,
372- ** kwargs ,
409+ ** _kwargs ,
373410 ):
374411 apex_intens = predicts .reshape ((len (batch_df ), - 1 )).max (axis = 1 )
375412 apex_intens [apex_intens <= 0 ] = 1
0 commit comments