Skip to content

Commit 536ef5b

Browse files
committed
use better names for the new components
feed_aligned_response and feed_aligned_projection
1 parent 442e568 commit 536ef5b

9 files changed

Lines changed: 174 additions & 115 deletions

File tree

src/pyuvdata/analytic_beam.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -391,11 +391,11 @@ def _get_empty_data_array(
391391
self,
392392
grid_shape: tuple[int, int],
393393
beam_type: Literal[
394-
"efield", "power", "feed_iresponse", "feed_projection"
394+
"efield", "power", "feed_aligned_response", "feed_aligned_projection"
395395
] = "efield",
396396
) -> FloatArray:
397397
"""Get the empty data to fill in the eval methods."""
398-
if beam_type in ["efield", "feed_projection"]:
398+
if beam_type in ["efield", "feed_aligned_projection"]:
399399
return np.zeros(
400400
(self.Naxes_vec, self.Nfeeds, *grid_shape), dtype=np.complex128
401401
)
@@ -404,7 +404,7 @@ def _get_empty_data_array(
404404
(1, self.Npols, *grid_shape),
405405
dtype=np.complex128 if self.Npols > self.Nfeeds else np.float64,
406406
)
407-
elif beam_type == "feed_iresponse":
407+
elif beam_type == "feed_aligned_response":
408408
return np.zeros((1, self.Nfeeds, *grid_shape), dtype=np.complex128)
409409

410410
def efield_eval(
@@ -516,11 +516,23 @@ def power_eval(
516516

517517
return data_array
518518

519-
def feed_iresponse_eval(
519+
def feed_aligned_response_eval(
520520
self, *, az_array: FloatArray, za_array: FloatArray, freq_array: FloatArray
521521
) -> FloatArray:
522522
"""
523-
Evaluate the feed I response at the given coordinates.
523+
Evaluate the feed aligned response at the given coordinates.
524+
525+
The feed aligned response is the complex response of each instrumental
526+
feed to electric fields aligned with it -- i.e. after projecting electric
527+
fields to directions aligned with the feeds (these directions are
528+
non-orthogonal in many directions on the sky even though the feeds are
529+
generally physically orthogonal). Since the projection has already happened,
530+
the feed aligned response only has a feed index, no vector component axis.
531+
Non-zero phase is caused by time delays which can vary spatially but do
532+
not depend on incident polarization.
533+
534+
Unpolarized light is equally spread among all polarization orientations,
535+
so the feed aligned response can be used as the response to unpolarized light.
524536
525537
Parameters
526538
----------
@@ -544,11 +556,11 @@ def feed_iresponse_eval(
544556
az_array=az_array, za_array=za_array, freq_array=freq_array
545557
)
546558

547-
if hasattr(self, "_feed_iresponse_eval"):
559+
if hasattr(self, "_feed_aligned_response_eval"):
548560
za_grid, _ = np.meshgrid(za_array, freq_array)
549561
az_grid, f_grid = np.meshgrid(az_array, freq_array)
550562

551-
return self._feed_iresponse_eval(
563+
return self._feed_aligned_response_eval(
552564
az_grid=az_grid, za_grid=za_grid, f_grid=f_grid
553565
).astype(complex)
554566
else:
@@ -574,11 +586,18 @@ def feed_iresponse_eval(
574586

575587
return data_array.astype(complex)
576588

577-
def feed_projection_eval(
589+
def feed_aligned_projection_eval(
578590
self, *, az_array: FloatArray, za_array: FloatArray, freq_array: FloatArray
579591
) -> FloatArray:
580592
"""
581-
Evaluate the feed projection at the given coordinates.
593+
Evaluate the feed aligned projection at the given coordinates.
594+
595+
The feed aligned projection is the projection from celestial polarization
596+
vector components (orthogonal on the sky) to instrumental feed vector
597+
components (often non-orthogonal on the sky). This is similar to a
598+
rotation matrix in that it just converts between coordinate systems
599+
(the magnitude of the response is removed), but is not unitary because
600+
the projection of the feeds are not orthogonal in all directions on the sky.
582601
583602
Parameters
584603
----------
@@ -605,8 +624,8 @@ def feed_projection_eval(
605624
za_grid, _ = np.meshgrid(za_array, freq_array)
606625
az_grid, f_grid = np.meshgrid(az_array, freq_array)
607626

608-
if hasattr(self, "_feed_projection_eval"):
609-
return self._feed_projection_eval(
627+
if hasattr(self, "_feed_aligned_projection_eval"):
628+
return self._feed_aligned_projection_eval(
610629
az_grid=az_grid, za_grid=za_grid, f_grid=f_grid
611630
).astype(complex)
612631
else:
@@ -615,7 +634,7 @@ def feed_projection_eval(
615634
)
616635

617636
# set f to the magnitude of the I response, assume no time delays
618-
f_vals = self.feed_iresponse_eval(
637+
f_vals = self.feed_aligned_response_eval(
619638
az_array=az_array, za_array=za_array, freq_array=freq_array
620639
)
621640
data_array = self._get_empty_data_array(az_grid.shape)
@@ -631,7 +650,7 @@ def to_uvbeam(
631650
self,
632651
freq_array: FloatArray,
633652
beam_type: Literal[
634-
"efield", "power", "feed_iresponse", "feed_projection"
653+
"efield", "power", "feed_aligned_response", "feed_aligned_projection"
635654
] = "efield",
636655
pixel_coordinate_system: (
637656
Literal["az_za", "orthoslant_zenith", "healpix"] | None
@@ -653,7 +672,8 @@ def to_uvbeam(
653672
freq_array : ndarray of float
654673
Array of frequencies in Hz to evaluate the beam at.
655674
beam_type : str
656-
Beam type, one of "efield", "power", "feed_iresponse" or "feed_projection".
675+
Beam type, one of "efield", "power", "feed_aligned_response" or
676+
"feed_aligned_projection".
657677
pixel_coordinate_system : str
658678
Pixel coordinate system, options are "az_za", "orthoslant_zenith" and
659679
"healpix". Forced to be "healpix" if ``nside`` is given and by
@@ -676,7 +696,12 @@ def to_uvbeam(
676696
Healpix ordering parameter, defaults to "ring" if nside is provided.
677697
678698
"""
679-
allowed_beam_types = ["efield", "power", "feed_iresponse", "feed_projection"]
699+
allowed_beam_types = [
700+
"efield",
701+
"power",
702+
"feed_aligned_response",
703+
"feed_aligned_projection",
704+
]
680705
if beam_type not in allowed_beam_types:
681706
raise ValueError(f"Beam type must be one of {allowed_beam_types}")
682707

@@ -761,7 +786,7 @@ def plot(
761786
self,
762787
*,
763788
beam_type: Literal[
764-
"efield", "power", "feed_iresponse", "feed_projection"
789+
"efield", "power", "feed_aligned_response", "feed_aligned_projection"
765790
] = "efield",
766791
freq: float,
767792
complex_type: str = "real",
@@ -782,7 +807,8 @@ def plot(
782807
Parameters
783808
----------
784809
beam_type : str
785-
Beam type, one of "efield", "power", "feed_iresponse" or "feed_projection".
810+
Beam type, one of "efield", "power", "feed_aligned_response" or
811+
"feed_aligned_projection".
786812
freq : int
787813
The frequency index to plot.
788814
complex_type : str

src/pyuvdata/beam_interface.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class BeamInterface:
3838
Beam object to use for computations. If a BeamInterface is passed, a new
3939
view of the same object is created.
4040
beam_type : str
41-
The beam type, one of "efield", "power", "feed_iresponse" or "feed_projection".
41+
The beam type, one of "efield", "power", "feed_aligned_response" or
42+
"feed_aligned_projection".
4243
include_cross_pols : bool
4344
Option to include the cross polarized beams (e.g. xy and yx or en and ne).
4445
Used if beam is a UVBeam and and the input UVBeam is an Efield beam but
@@ -49,7 +50,8 @@ class BeamInterface:
4950

5051
beam: AnalyticBeam | UVBeam
5152
beam_type: (
52-
Literal["efield", "power", "feed_iresponse", "feed_projection"] | None
53+
Literal["efield", "power", "feed_aligned_response", "feed_aligned_projection"]
54+
| None
5355
) = None
5456
include_cross_pols: InitVar[bool] = True
5557

@@ -82,8 +84,8 @@ def __post_init__(self, include_cross_pols: bool):
8284
elif self.beam.beam_type == "efield":
8385
conv_func = {
8486
"power": "efield_to_power",
85-
"feed_iresponse": "efield_to_feed_iresponse",
86-
"feed_projection": "efield_to_feed_projection",
87+
"feed_aligned_response": "efield_to_feed_aligned_response",
88+
"feed_aligned_projection": "efield_to_feed_aligned_projection",
8789
}
8890
warnings.warn(
8991
"Input beam is an efield UVBeam but beam_type is specified as "

src/pyuvdata/utils/plotting.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ def beam_plot(
414414
freq_title = freq
415415

416416
naxes_vec = beam_obj.Naxes_vec
417-
if beam_type in ["power", "feed_iresponse"]:
417+
if beam_type in ["power", "feed_aligned_response"]:
418418
naxes_vec = 1
419419
reg_grid = True
420420

@@ -440,9 +440,9 @@ def beam_plot(
440440
beam_type_label = "power"
441441
elif beam_type == "efield":
442442
beam_type_label = "E-field"
443-
elif beam_type == "feed_iresponse":
443+
elif beam_type == "feed_aligned_response":
444444
beam_type_label = "Feed I response"
445-
elif beam_type == "feed_projection":
445+
elif beam_type == "feed_aligned_projection":
446446
beam_type_label = "Feed projection"
447447

448448
plot_beam_arrays(

src/pyuvdata/uvbeam/initializers.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ def new_uvbeam(
2424
feed_version: str = "0.0",
2525
model_name: str = "default",
2626
model_version: str = "0.0",
27-
beam_type: Literal["efield", "power", "feed_iresponse", "feed_projection"] = None,
27+
beam_type: Literal[
28+
"efield", "power", "feed_aligned_response", "feed_aligned_projection"
29+
] = None,
2830
feed_array: StrArray | None = None,
2931
feed_angle: FloatArray | None = None,
3032
mount_type: str | None = "fixed",
@@ -80,8 +82,9 @@ def new_uvbeam(
8082
model_version: str
8183
Version of the beam model.
8284
beam_type : str
83-
Beam type, one of "efield", "power", "feed_iresponse" or "feed_projection".
84-
Defaults to "power" if polarization_array is provided and "efield" otherwise.
85+
Beam type, one of "efield", "power", "feed_aligned_response" or
86+
"feed_aligned_projection". Defaults to "power" if polarization_array is
87+
provided and "efield" otherwise.
8588
feed_array : ndarray of str
8689
Array of feed orientations. Options are: n/e or x/y or r/l.
8790
feed_angle : ndarray of float
@@ -268,7 +271,7 @@ def new_uvbeam(
268271
"Either nside or both axis1_array and axis2_array must be provided."
269272
)
270273

271-
if uvb.beam_type in ["power", "feed_iresponse"]:
274+
if uvb.beam_type in ["power", "feed_aligned_response"]:
272275
uvb.Naxes_vec = 1
273276

274277
uvb._set_cs_params()
@@ -305,7 +308,7 @@ def new_uvbeam(
305308
f"expected shape {bv_shape}."
306309
)
307310
uvb.basis_vector_array = basis_vector_array
308-
elif uvb.beam_type in ["efield", "feed_projection"]:
311+
elif uvb.beam_type in ["efield", "feed_aligned_projection"]:
309312
if uvb.pixel_coordinate_system == "healpix":
310313
basis_vector_array = np.zeros(
311314
(uvb.Naxes_vec, uvb.Ncomponents_vec, uvb.Npixels), dtype=float

0 commit comments

Comments
 (0)