Skip to content

Commit 0bd0544

Browse files
author
sambit-giri
committed
checkpoint variable reintroduced
to progressively save the uv maps
1 parent 2738896 commit 0bd0544

1 file changed

Lines changed: 103 additions & 20 deletions

File tree

src/tools21cm/radio_telescope_noise.py

Lines changed: 103 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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"\nCheckpoint: 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"\nCheckpoint: 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

227272
def 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

Comments
 (0)