@@ -210,10 +210,6 @@ def uvcalibrate(
210210 Returns if not inplace
211211
212212 """
213- if uvcal .cal_type == "gain" and uvcal .wide_band :
214- raise ValueError (
215- "uvcalibrate currently does not support wide-band calibrations"
216- )
217213 if uvcal .cal_type == "delay" and uvcal .Nspws > 1 :
218214 # To fix this, need to make UVCal.convert_to_gain support multiple spws
219215 raise ValueError (
@@ -319,7 +315,7 @@ def uvcalibrate(
319315 )
320316
321317 uvdata_times , uvd_time_ri = np .unique (uvdata .time_array , return_inverse = True )
322- downselect_cal_times = False
318+ uvcal_times_to_keep = None
323319 # time_range supercedes time_array.
324320 if uvcal .time_range is not None :
325321 if np .min (uvdata_times ) < np .min (uvcal .time_range [:, 0 ]) or np .max (
@@ -402,43 +398,44 @@ def uvcalibrate(
402398 raise ValueError (
403399 f"Time { this_time } exists on UVData but not on UVCal."
404400 )
405- if len (uvcal_times_to_keep ) < uvcal .Ntimes :
406- downselect_cal_times = True
407401
408- downselect_cal_freq = False
409- if uvcal .freq_array is not None :
410- uvdata_freq_arr_use = uvdata .freq_array
411- uvcal_freq_arr_use = uvcal .freq_array
412- try :
413- freq_arr_match = np .allclose (
414- np .sort (uvcal_freq_arr_use ),
415- np .sort (uvdata_freq_arr_use ),
416- atol = uvdata ._freq_array .tols [1 ],
417- rtol = uvdata ._freq_array .tols [0 ],
418- )
419- except ValueError :
420- freq_arr_match = False
421-
422- if freq_arr_match is False :
423- # check more carefully
424- uvcal_freqs_to_keep = []
425- for this_freq in uvdata_freq_arr_use :
426- wh_freq_match = np .nonzero (
427- np .isclose (
428- uvcal .freq_array - this_freq ,
429- 0 ,
402+ uvcal_chans_to_keep = None if uvcal .wide_band else []
403+ uvcal_spws_to_keep = [] if uvcal .wide_band else None
404+ for idx , spw in enumerate (uvcal .spw_array ):
405+ if spw in uvdata .spw_array :
406+ if uvcal .wide_band :
407+ freq_array_spw = uvdata .freq_array [uvdata .flex_spw_id_array == spw ]
408+ min_freq = min (uvcal .freq_range [idx ])
409+ max_freq = max (uvcal .freq_range [idx ])
410+ if all ((freq_array_spw < min_freq ) | (freq_array_spw > max_freq )):
411+ raise ValueError (
412+ "SPWs between UVData and UVCal overlap, but the frequencies of "
413+ "channels are inconsistent with frequency ranges."
414+ )
415+ uvcal_spws_to_keep .append (spw )
416+ else :
417+ uvcal_chans = np .where (uvcal .flex_spw_id_array == spw )[0 ]
418+ uvdata_chans = np .where (uvdata .flex_spw_id_array == spw )[0 ]
419+ uvcal_freqs = uvcal .freq_array [uvcal_chans ]
420+ uvdata_freqs = uvdata .freq_array [uvdata_chans ]
421+ for indv_freq in uvdata_freqs :
422+ freq_match = np .isclose (
423+ uvcal_freqs ,
424+ indv_freq ,
430425 atol = uvdata ._freq_array .tols [1 ],
431426 rtol = uvdata ._freq_array .tols [0 ],
432427 )
433- )
434- if wh_freq_match [0 ].size > 0 :
435- uvcal_freqs_to_keep .append (uvcal .freq_array [wh_freq_match ][0 ])
436- else :
437- raise ValueError (
438- f"Frequency { this_freq } exists on UVData but not on UVCal."
439- )
440- if len (uvcal_freqs_to_keep ) < uvcal .Nfreqs :
441- downselect_cal_freq = True
428+ if any (freq_match ):
429+ uvcal_chans_to_keep .append (uvcal_chans [freq_match ][0 ])
430+ else :
431+ raise ValueError (
432+ f"Frequency { indv_freq } exists on UVData but not on UVCal."
433+ )
434+
435+ if np .array_equal (uvcal_chans_to_keep , np .arange (uvcal .Nfreqs )):
436+ uvcal_chans_to_keep = None
437+ if np .array_equal (uvcal_spws_to_keep , uvcal .spw_array ):
438+ uvcal_spws_to_keep = None
442439
443440 # check if x_orientation-equivalent in uvdata isn't set (it's required for uvcal)
444441 uvd_x = uvdata .telescope .get_x_orientation_from_feeds ()
@@ -468,21 +465,20 @@ def uvcalibrate(
468465 )
469466
470467 # downselect UVCal times, frequencies
471- if downselect_cal_freq or downselect_cal_times :
472- if not downselect_cal_times :
473- uvcal_times_to_keep = None
474- elif not downselect_cal_freq :
475- uvcal_freqs_to_keep = None
476-
468+ new_uvcal = not (
469+ uvcal_times_to_keep is None
470+ and uvcal_chans_to_keep is None
471+ and uvcal_spws_to_keep is None
472+ )
473+ uvcal_use = uvcal
474+ if new_uvcal :
477475 uvcal_use = uvcal .select (
478- times = uvcal_times_to_keep , frequencies = uvcal_freqs_to_keep , inplace = False
476+ times = uvcal_times_to_keep ,
477+ freq_chans = uvcal_chans_to_keep ,
478+ spws = uvcal_spws_to_keep ,
479+ inplace = False ,
479480 )
480481
481- new_uvcal = True
482- else :
483- uvcal_use = uvcal
484- new_uvcal = False
485-
486482 # input checks
487483 if uvcal_use .cal_type == "delay" :
488484 if not new_uvcal :
@@ -525,6 +521,7 @@ def uvcalibrate(
525521 strict = False ,
526522 )
527523 )
524+ uvc_spw_map = {spw : idx for idx , spw in enumerate (uvcal_use .spw_array )}
528525
529526 # iterate over keys
530527 for key in uvdata .get_antpairpols ():
@@ -573,20 +570,29 @@ def uvcalibrate(
573570 gain = gain [trange_ind_arr [blt_inds ], :]
574571 flag = flag [trange_ind_arr [blt_inds ], :]
575572
573+ # Use a slice operator to expand out the flags and gains with a wide_band
574+ # calibration solution, otherwise use the Ellipsis to select the whole
575+ # array when using a channel-based soln.
576+ gain_slice = ...
577+ if uvcal_use .wide_band :
578+ gain_slice = np .s_ [
579+ :, [uvc_spw_map [spw ] for spw in uvdata .flex_spw_id_array ]
580+ ]
581+
576582 # propagate flags
577583 if prop_flags :
578584 mask = np .isclose (gain , 0.0 ) | flag
579585 gain [mask ] = 1.0
580- uvdata .flag_array [blt_inds , :, pol_ind ] + = mask
586+ uvdata .flag_array [blt_inds , :, pol_ind ] | = mask [ gain_slice ]
581587
582588 # apply to data
583589 mult_gains = uvcal_use .gain_convention == "multiply"
584590 if undo :
585591 mult_gains = not mult_gains
586592 if mult_gains :
587- uvdata .data_array [blt_inds , :, pol_ind ] *= gain
593+ uvdata .data_array [blt_inds , :, pol_ind ] *= gain [ gain_slice ]
588594 else :
589- uvdata .data_array [blt_inds , :, pol_ind ] /= gain
595+ uvdata .data_array [blt_inds , :, pol_ind ] /= gain [ gain_slice ]
590596
591597 # update attributes
592598 uvdata .history += "\n Calibrated with pyuvdata.utils.uvcalibrate."
0 commit comments