Skip to content

Commit da28a5a

Browse files
author
sambit-giri
committed
cached and new parameter values are compared
when save_uvmap file is read inside the get_uv_map_lightcone function is called. An error message is raised if values don't match. This update fixes #76
1 parent 74f3bc0 commit da28a5a

1 file changed

Lines changed: 129 additions & 100 deletions

File tree

src/tools21cm/radio_telescope_noise.py

Lines changed: 129 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -163,111 +163,140 @@ def noise_coeval_power_spectrum_1d(ncells, z, depth_mhz, obs_time=1000, subarray
163163

164164
def get_uv_map_lightcone(ncells, zs, subarray_type="AA4", total_int_time=6., int_time=10., boxsize=None, declination=-30.,
165165
save_uvmap=None, n_jobs=4, verbose=True, checkpoint=16):
166-
"""
167-
Generates or loads a lightcone of UV coverage maps, one for each redshift.
166+
"""
167+
Generates or loads a lightcone of UV coverage maps, one for each redshift.
168168
169-
This function handles the creation of UV maps across a range of redshifts,
170-
using parallel processing for efficiency and providing an option to cache
171-
the results to a file to speed up subsequent runs.
169+
This function handles the creation of UV maps across a range of redshifts,
170+
using parallel processing for efficiency and providing an option to cache
171+
the results to a file to speed up subsequent runs.
172172
173-
Parameters
174-
----------
175-
ncells : int
176-
The number of grid cells.
177-
zs : np.ndarray or list
178-
An array or list of redshift values for the lightcone.
179-
subarray_type, total_int_time, etc. : various, optional
180-
Observational parameters passed to `get_uv_map`.
181-
save_uvmap : str, optional
182-
File path to save or load the UV map dictionary. If the file exists,
183-
maps are loaded; otherwise, they are generated and saved.
184-
n_jobs : int, optional
185-
Number of CPUs for parallel generation of UV maps.
186-
verbose : bool, optional
187-
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.
173+
Parameters
174+
----------
175+
ncells : int
176+
The number of grid cells.
177+
zs : np.ndarray or list
178+
An array or list of redshift values for the lightcone.
179+
subarray_type, total_int_time, etc. : various, optional
180+
Observational parameters passed to `get_uv_map`.
181+
save_uvmap : str, optional
182+
File path to save or load the UV map dictionary. If the file exists,
183+
maps are loaded; otherwise, they are generated and saved.
184+
n_jobs : int, optional
185+
Number of CPUs for parallel generation of UV maps.
186+
verbose : bool, optional
187+
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.
191191
192-
Returns
193-
-------
194-
dict
195-
A dictionary containing the UV maps for each redshift, keyed by the
196-
redshift value formatted to three decimal places, along with metadata
197-
about the simulation parameters.
198-
"""
199-
antxyz, N_ant = subarray_type_to_antxyz(subarray_type, verbose=verbose)
200-
if boxsize is None: boxsize = conv.LB
201-
if isinstance(zs, list): zs = np.array(zs)
192+
Returns
193+
-------
194+
dict
195+
A dictionary containing the UV maps for each redshift, keyed by the
196+
redshift value formatted to three decimal places, along with metadata
197+
about the simulation parameters.
198+
"""
199+
antxyz, N_ant = subarray_type_to_antxyz(subarray_type, verbose=verbose)
200+
if boxsize is None: boxsize = conv.LB
201+
if isinstance(zs, list): zs = np.array(zs)
202+
203+
# Define a dictionary of simulation parameters
204+
params = {
205+
'ncells': ncells,
206+
'boxsize': boxsize,
207+
'total_int_time': total_int_time,
208+
'int_time': int_time,
209+
'declination': declination,
210+
'subarray_type': subarray_type,
211+
'N_ant': N_ant,
212+
}
213+
214+
# Attempt to load existing UV maps
215+
if save_uvmap and os.path.exists(save_uvmap):
216+
if verbose: print(f"Loading existing UV maps from {save_uvmap}")
217+
uvs = read_dictionary_data(save_uvmap)
218+
219+
# Validate using the params dictionary
220+
mismatches = []
221+
for key, current_val in params.items():
222+
cached_val = uvs.get(key)
223+
# Check for a mismatch
224+
if cached_val is not None and cached_val != current_val:
225+
# Add the error message to a list instead of raising immediately
226+
mismatch_msg = f" - '{key}': Cached value is '{cached_val}', but current call requested '{current_val}'."
227+
mismatches.append(mismatch_msg)
228+
229+
if mismatches:
230+
all_errors = "\n".join(mismatches)
231+
raise ValueError(
232+
f"Parameter mismatches found in cached file '{save_uvmap}':\n"
233+
f"{all_errors}\n"
234+
f"Please use a different save_uvmap path or delete the old file."
235+
)
236+
else:
237+
# Initialize from the defined dictionary
238+
uvs = params.copy()
202239

203-
# Attempt to load existing UV maps or initialize a new dictionary
204-
if save_uvmap and os.path.exists(save_uvmap):
205-
if verbose: print(f"Loading existing UV maps from {save_uvmap}")
206-
uvs = read_dictionary_data(save_uvmap)
207-
else:
208-
uvs = {'ncells': ncells, 'boxsize': boxsize, 'total_int_time': total_int_time, 'int_time': int_time, 'declination': declination}
209-
210-
# Identify which redshifts need a UV map to be generated
211-
z_to_run = [zi for zi in zs if '{:.3f}'.format(zi) not in uvs]
240+
# Identify which redshifts need a UV map to be generated
241+
z_to_run = [zi for zi in zs if '{:.3f}'.format(zi) not in uvs]
212242

213-
if z_to_run:
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
217-
_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]
218-
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)
260-
261-
uvs['Nant'] = N_ant
262-
# Final save to ensure the last chunk or the full result is written
263-
if save_uvmap:
264-
if verbose: print(f"Saving final updated UV maps to {save_uvmap}")
265-
write_dictionary_data(uvs, save_uvmap)
266-
267-
elif verbose:
268-
print("All requested redshift UV maps are already present.")
269-
270-
return uvs
243+
if z_to_run:
244+
if verbose: print(f'Found {len(z_to_run)} new redshifts to generate UV maps for.')
245+
246+
# Define the worker function for a single redshift
247+
_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]
248+
249+
# Condition for using chunked parallel processing with checkpoints
250+
use_chunked_parallel = n_jobs > 1 and checkpoint is not None and checkpoint > 0
251+
252+
if use_chunked_parallel:
253+
num_chunks = (len(z_to_run) + checkpoint - 1) // checkpoint
254+
if verbose: print(f"Processing in {num_chunks} chunks of size up to {checkpoint}...")
255+
256+
for i in tqdm(range(0, len(z_to_run), checkpoint), desc="Processing Chunks", disable=not verbose):
257+
z_chunk = z_to_run[i:i + checkpoint]
258+
259+
# Run the parallel job on the current chunk
260+
# Set inner verbose to 0 to avoid clutter; outer tqdm handles progress
261+
results_chunk = Parallel(n_jobs=n_jobs, verbose=0)(delayed(_uvmap_worker)(zi) for zi in z_chunk)
262+
263+
# Update the main dictionary with the results from the chunk
264+
for zi, uv_map in zip(z_chunk, results_chunk):
265+
uvs['{:.3f}'.format(zi)] = uv_map
266+
267+
# Save after processing the chunk
268+
if save_uvmap:
269+
if verbose: print(f"\nCheckpoint: Saving results to {save_uvmap}")
270+
write_dictionary_data(uvs, save_uvmap)
271+
272+
else: # Original behavior: either sequential or parallel without checkpoints
273+
if n_jobs > 1:
274+
# Parallel processing for all z's at once
275+
if verbose: print(f"Generating all {len(z_to_run)} maps in parallel...")
276+
results = Parallel(n_jobs=n_jobs, verbose=10 if verbose else 0)(delayed(_uvmap_worker)(i) for i in z_to_run)
277+
for zi, uv_map in zip(z_to_run, results):
278+
uvs['{:.3f}'.format(zi)] = uv_map
279+
else:
280+
# Sequential processing
281+
iterator = tqdm(z_to_run, desc="Generating UV maps sequentially", disable=not verbose)
282+
for i, zi in enumerate(iterator):
283+
uvs['{:.3f}'.format(zi)] = _uvmap_worker(zi)
284+
# Checkpoint logic for sequential mode
285+
is_checkpoint_step = checkpoint and (i + 1) % checkpoint == 0
286+
is_not_last_step = (i + 1) < len(z_to_run)
287+
if save_uvmap and is_checkpoint_step and is_not_last_step:
288+
if verbose: print(f"\nCheckpoint: Saving results to {save_uvmap}")
289+
write_dictionary_data(uvs, save_uvmap)
290+
291+
# Final save to ensure the last chunk or the full result is written
292+
if save_uvmap:
293+
if verbose: print(f"Saving final updated UV maps to {save_uvmap}")
294+
write_dictionary_data(uvs, save_uvmap)
295+
296+
elif verbose:
297+
print("All requested redshift UV maps are already present.")
298+
299+
return uvs
271300

272301
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):
273302
"""

0 commit comments

Comments
 (0)