@@ -161,7 +161,8 @@ def noise_coeval_power_spectrum_1d(ncells, z, depth_mhz, obs_time=1000, subarray
161161 return pn , kn , n_modes
162162 return pn , kn
163163
164- def get_uv_map_lightcone (ncells , zs , subarray_type = "AA4" , total_int_time = 6. , int_time = 10. , boxsize = None , declination = - 30. , save_uvmap = None , n_jobs = 4 , verbose = True ):
164+ def get_uv_map_lightcone (ncells , zs , subarray_type = "AA4" , total_int_time = 6. , int_time = 10. , boxsize = None , declination = - 30. ,
165+ save_uvmap = None , n_jobs = 4 , verbose = True , checkpoint = 16 ):
165166 """
166167 Generates or loads a lightcone of UV coverage maps, one for each redshift.
167168
@@ -184,6 +185,9 @@ def get_uv_map_lightcone(ncells, zs, subarray_type="AA4", total_int_time=6., int
184185 Number of CPUs for parallel generation of UV maps.
185186 verbose : bool, optional
186187 If True, enables progress bars and informational messages.
188+ checkpoint : int, optional
189+ If provided, the number of redshifts to process before saving the results
190+ to `save_uvmap`. This is useful for long runs to prevent data loss.
187191
188192 Returns
189193 -------
@@ -198,6 +202,7 @@ def get_uv_map_lightcone(ncells, zs, subarray_type="AA4", total_int_time=6., int
198202
199203 # Attempt to load existing UV maps or initialize a new dictionary
200204 if save_uvmap and os .path .exists (save_uvmap ):
205+ if verbose : print (f"Loading existing UV maps from { save_uvmap } " )
201206 uvs = read_dictionary_data (save_uvmap )
202207 else :
203208 uvs = {'ncells' : ncells , 'boxsize' : boxsize , 'total_int_time' : total_int_time , 'int_time' : int_time , 'declination' : declination }
@@ -206,22 +211,62 @@ def get_uv_map_lightcone(ncells, zs, subarray_type="AA4", total_int_time=6., int
206211 z_to_run = [zi for zi in zs if '{:.3f}' .format (zi ) not in uvs ]
207212
208213 if z_to_run :
209- print (f'Generating { len (z_to_run )} new UV maps...' )
214+ if verbose : print (f'Found { len (z_to_run )} new redshifts to generate UV maps for.' )
215+
216+ # Define the worker function for a single redshift
210217 _uvmap_worker = lambda zi : get_uv_map (ncells , zi , subarray_type = antxyz , total_int_time = total_int_time , int_time = int_time , boxsize = boxsize , declination = declination , verbose = False )[0 ]
211218
212- if n_jobs > 1 :
213- results = Parallel (n_jobs = n_jobs , verbose = 10 )(delayed (_uvmap_worker )(i ) for i in z_to_run )
214- for zi , uv_map in zip (z_to_run , results ):
215- uvs ['{:.3f}' .format (zi )] = uv_map
216- else :
217- for zi in tqdm (z_to_run , desc = "Generating UV maps sequentially" ):
218- uvs ['{:.3f}' .format (zi )] = _uvmap_worker (zi )
219+ # Condition for using chunked parallel processing with checkpoints
220+ use_chunked_parallel = n_jobs > 1 and checkpoint is not None and checkpoint > 0
221+
222+ if use_chunked_parallel :
223+ num_chunks = (len (z_to_run ) + checkpoint - 1 ) // checkpoint
224+ if verbose : print (f"Processing in { num_chunks } chunks of size up to { checkpoint } ..." )
225+
226+ for i in tqdm (range (0 , len (z_to_run ), checkpoint ), desc = "Processing Chunks" , disable = not verbose ):
227+ z_chunk = z_to_run [i :i + checkpoint ]
228+
229+ # Run the parallel job on the current chunk
230+ # Set inner verbose to 0 to avoid clutter; outer tqdm handles progress
231+ results_chunk = Parallel (n_jobs = n_jobs , verbose = 0 )(delayed (_uvmap_worker )(zi ) for zi in z_chunk )
232+
233+ # Update the main dictionary with the results from the chunk
234+ for zi , uv_map in zip (z_chunk , results_chunk ):
235+ uvs ['{:.3f}' .format (zi )] = uv_map
236+
237+ # Save after processing the chunk
238+ if save_uvmap :
239+ if verbose : print (f"\n Checkpoint: Saving results to { save_uvmap } " )
240+ write_dictionary_data (uvs , save_uvmap )
241+
242+ else : # Original behavior: either sequential or parallel without checkpoints
243+ if n_jobs > 1 :
244+ # Parallel processing for all z's at once
245+ if verbose : print (f"Generating all { len (z_to_run )} maps in parallel..." )
246+ results = Parallel (n_jobs = n_jobs , verbose = 10 if verbose else 0 )(delayed (_uvmap_worker )(i ) for i in z_to_run )
247+ for zi , uv_map in zip (z_to_run , results ):
248+ uvs ['{:.3f}' .format (zi )] = uv_map
249+ else :
250+ # Sequential processing
251+ iterator = tqdm (z_to_run , desc = "Generating UV maps sequentially" , disable = not verbose )
252+ for i , zi in enumerate (iterator ):
253+ uvs ['{:.3f}' .format (zi )] = _uvmap_worker (zi )
254+ # Checkpoint logic for sequential mode
255+ is_checkpoint_step = checkpoint and (i + 1 ) % checkpoint == 0
256+ is_not_last_step = (i + 1 ) < len (z_to_run )
257+ if save_uvmap and is_checkpoint_step and is_not_last_step :
258+ if verbose : print (f"\n Checkpoint: Saving results to { save_uvmap } " )
259+ write_dictionary_data (uvs , save_uvmap )
219260
220261 uvs ['Nant' ] = N_ant
262+ # Final save to ensure the last chunk or the full result is written
221263 if save_uvmap :
222- print (f"Saving updated UV maps to { save_uvmap } " )
264+ if verbose : print (f"Saving final updated UV maps to { save_uvmap } " )
223265 write_dictionary_data (uvs , save_uvmap )
224266
267+ elif verbose :
268+ print ("All requested redshift UV maps are already present." )
269+
225270 return uvs
226271
227272def noise_map (ncells , z , depth_mhz , obs_time = 1000 , subarray_type = "AA4" , boxsize = None , total_int_time = 6. , int_time = 10. , declination = - 30. , uv_map = None , N_ant = None , uv_weighting = 'natural' , sefd_data = None , nu_data = None , fft_wrap = False , verbose = True , suppress_sharp_features_uv_map = False ):
@@ -643,7 +688,7 @@ def noise_cube_lightcone(ncells, z, obs_time=1000, subarray_type="AA4", boxsize=
643688 # This function body is very similar to `noise_lightcone`. Consider refactoring.
644689 return noise_lightcone (ncells , zs , obs_time , subarray_type , boxsize , save_uvmap , total_int_time , int_time , declination , N_ant , uv_weighting , fft_wrap , verbose , n_jobs , checkpoint , sefd_data , nu_data , suppress_sharp_features_uv_map )
645690
646- def noise_lightcone (ncells , zs , obs_time = 1000 , subarray_type = "AA4" , boxsize = None , save_uvmap = None , total_int_time = 6. , int_time = 10. , declination = - 30. , uv_weighting = 'natural' , fft_wrap = False , verbose = True , n_jobs = 4 , sefd_data = None , nu_data = None , suppress_sharp_features_uv_map = False ):
691+ def noise_lightcone (ncells , zs , obs_time = 1000 , subarray_type = "AA4" , boxsize = None , save_uvmap = None , total_int_time = 6. , int_time = 10. , declination = - 30. , uv_weighting = 'natural' , fft_wrap = False , verbose = True , n_jobs = 4 , checkpoint = 16 , sefd_data = None , nu_data = None , suppress_sharp_features_uv_map = False ):
647692 """
648693 Generates a 3D lightcone of instrumental noise over a list of redshifts.
649694
@@ -654,28 +699,66 @@ def noise_lightcone(ncells, zs, obs_time=1000, subarray_type="AA4", boxsize=None
654699 Parameters
655700 ----------
656701 ncells : int
657- The grid size .
702+ The number of grid cells along each spatial dimension .
658703 zs : np.ndarray or list
659- An array or list of redshift values for each slice of the lightcone.
660- obs_time, subarray_type, etc. : various
661- Observational parameters.
704+ An array of redshift values defining the slices of the lightcone.
705+ obs_time : float, optional
706+ Total observation time in hours. Default is 1000.
707+ subarray_type : str, optional
708+ The type of telescope subarray (e.g., "AA4"). Default is "AA4".
709+ boxsize : float, optional
710+ The comoving size of the simulation box in Mpc. If None, a default
711+ cosmological value is used.
662712 save_uvmap : str, optional
663- File path passed to `get_uv_map_lightcone` for caching UV maps.
713+ File path to save or load the UV coverage maps. Caching these maps can
714+ significantly speed up subsequent runs.
715+ total_int_time : float, optional
716+ Total integration time in hours used for generating the UV coverage.
717+ Default is 6.
718+ int_time : float, optional
719+ The integration time for a single visibility measurement in seconds.
720+ Default is 10.
721+ declination : float, optional
722+ The pointing declination of the telescope in degrees. Default is -30.
723+ uv_weighting : str, optional
724+ The UV weighting scheme to use (e.g., 'natural', 'uniform').
725+ Default is 'natural'.
726+ fft_wrap : bool, optional
727+ If True, use `pyfft_wrap` for FFTs, which can be faster. Default is False.
728+ verbose : bool, optional
729+ If True, print progress bars and status messages. Default is True.
664730 n_jobs : int, optional
665- Number of CPUs for parallel processing of UV maps.
666-
731+ The number of CPU cores to use for parallel generation of UV maps.
732+ Default is 4.
733+ checkpoint : int, optional
734+ Number of redshift slices to process before saving the UV maps to a
735+ checkpoint file. Useful for long runs. Default is 16.
736+ sefd_data : str, optional
737+ Path to a file containing System Equivalent Flux Density (SEFD) data.
738+ nu_data : str, optional
739+ Path to a file containing frequencies corresponding to the SEFD data.
740+ suppress_sharp_features_uv_map : bool, optional
741+ If True, applies a suppression filter to the UV map to mitigate sharp
742+ features. Default is False.
743+
667744 Returns
668745 -------
669746 np.ndarray
670- A 3D `(ncells, ncells, len(zs))` lightcone of noise in mK.
747+ A 3D array of shape `(ncells, ncells, len(zs))` representing the
748+ noise lightcone in units of mK.
749+
750+ See Also
751+ --------
752+ get_uv_map_lightcone : Generates the underlying UV coverage maps.
753+ noise_map : Generates a 2D noise map for a single redshift.
671754 """
672755 if isinstance (zs , list ): zs = np .array (zs )
673756
674757 # Step 1: Get all UV maps efficiently.
675758 uvs = get_uv_map_lightcone (
676759 ncells , zs , subarray_type = subarray_type , total_int_time = total_int_time ,
677760 int_time = int_time , boxsize = boxsize , declination = declination ,
678- save_uvmap = save_uvmap , n_jobs = n_jobs , verbose = verbose
761+ save_uvmap = save_uvmap , n_jobs = n_jobs , verbose = verbose , checkpoint = checkpoint ,
679762 )
680763 N_ant = uvs .get ('Nant' )
681764
0 commit comments