Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision d745e74710ab581d489e815095d0dd4ee91e9c35 authored by Bryna Hazelton on 15 September 2025, 18:00:43 UTC, committed by Jonathan Pober on 15 September 2025, 18:31:58 UTC
remove macos-13 from our CI matrix because it is deprecated
1 parent ea19da5
  • Files
  • Changes
  • 0617af3
  • /
  • tests
  • /
  • utils
  • /
  • test_pol.py
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
revision badge
swh:1:rev:d745e74710ab581d489e815095d0dd4ee91e9c35
directory badge
swh:1:dir:44d91f34b75d4752e8467339185a98db104ff524
content badge
swh:1:cnt:a4c91c76bf9afe14ae50874bb462b61101d8648c

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
test_pol.py
# Copyright (c) 2024 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License
"""Tests for polarization utility functions."""

import copy

import numpy as np
import pytest

from pyuvdata import utils
from pyuvdata.testing import check_warnings
from pyuvdata.uvbeam.uvbeam import _convert_feeds_to_pols


def test_pol_funcs():
    """Test utility functions to convert between polarization strings and numbers"""

    pol_nums = [-8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4]
    pol_str = ["yx", "xy", "yy", "xx", "lr", "rl", "ll", "rr", "pI", "pQ", "pU", "pV"]
    assert pol_nums == utils.polstr2num(pol_str)
    assert pol_str == utils.polnum2str(pol_nums)
    # Check individuals
    assert utils.polstr2num("YY") == -6
    assert utils.polnum2str(4) == "pV"
    # Check errors
    pytest.raises(KeyError, utils.polstr2num, "foo")
    pytest.raises(ValueError, utils.polstr2num, 1)
    pytest.raises(ValueError, utils.polnum2str, 7.3)
    # Check parse
    assert utils.parse_polstr("xX") == "xx"
    assert utils.parse_polstr("XX") == "xx"
    assert utils.parse_polstr("i") == "pI"


def test_pol_funcs_x_orientation():
    """Test functions to convert between pol strings and numbers with x_orientation."""

    pol_nums = [-8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4]

    x_orient1 = "e"
    pol_str = ["ne", "en", "nn", "ee", "lr", "rl", "ll", "rr", "pI", "pQ", "pU", "pV"]
    assert pol_nums == utils.polstr2num(pol_str, x_orientation=x_orient1)
    assert pol_str == utils.polnum2str(pol_nums, x_orientation=x_orient1)
    # Check individuals
    assert utils.polstr2num("NN", x_orientation=x_orient1) == -6
    assert utils.polnum2str(4) == "pV"
    # Check errors
    pytest.raises(KeyError, utils.polstr2num, "foo", x_orientation=x_orient1)
    pytest.raises(ValueError, utils.polstr2num, 1, x_orientation=x_orient1)
    pytest.raises(ValueError, utils.polnum2str, 7.3, x_orientation=x_orient1)
    # Check parse
    assert utils.parse_polstr("eE", x_orientation=x_orient1) == "ee"
    assert utils.parse_polstr("xx", x_orientation=x_orient1) == "ee"
    assert utils.parse_polstr("NN", x_orientation=x_orient1) == "nn"
    assert utils.parse_polstr("yy", x_orientation=x_orient1) == "nn"
    assert utils.parse_polstr("i", x_orientation=x_orient1) == "pI"

    x_orient2 = "n"
    pol_str = ["en", "ne", "ee", "nn", "lr", "rl", "ll", "rr", "pI", "pQ", "pU", "pV"]
    assert pol_nums == utils.polstr2num(pol_str, x_orientation=x_orient2)
    assert pol_str == utils.polnum2str(pol_nums, x_orientation=x_orient2)
    # Check individuals
    assert utils.polstr2num("EE", x_orientation=x_orient2) == -6
    assert utils.polnum2str(4) == "pV"
    # Check errors
    pytest.raises(KeyError, utils.polstr2num, "foo", x_orientation=x_orient2)
    pytest.raises(ValueError, utils.polstr2num, 1, x_orientation=x_orient2)
    pytest.raises(ValueError, utils.polnum2str, 7.3, x_orientation=x_orient2)
    # Check parse
    assert utils.parse_polstr("nN", x_orientation=x_orient2) == "nn"
    assert utils.parse_polstr("xx", x_orientation=x_orient2) == "nn"
    assert utils.parse_polstr("EE", x_orientation=x_orient2) == "ee"
    assert utils.parse_polstr("yy", x_orientation=x_orient2) == "ee"
    assert utils.parse_polstr("i", x_orientation=x_orient2) == "pI"

    # check warnings for non-recognized x_orientation
    with check_warnings(UserWarning, "x_orientation not recognized"):
        assert utils.polstr2num("xx", x_orientation="foo") == -5

    with check_warnings(UserWarning, "x_orientation not recognized"):
        assert utils.polnum2str(-6, x_orientation="foo") == "yy"


def test_jones_num_funcs():
    """Test functions to convert between jones polarization strings and numbers."""

    jnums = [-8, -7, -6, -5, -4, -3, -2, -1]
    jstr = ["Jyx", "Jxy", "Jyy", "Jxx", "Jlr", "Jrl", "Jll", "Jrr"]
    assert jnums == utils.jstr2num(jstr)
    assert jstr, utils.jnum2str(jnums)
    # Check shorthands
    jstr = ["yx", "xy", "yy", "y", "xx", "x", "lr", "rl", "ll", "l", "rr", "r"]
    jnums = [-8, -7, -6, -6, -5, -5, -4, -3, -2, -2, -1, -1]
    assert jnums == utils.jstr2num(jstr)
    # Check individuals
    assert utils.jstr2num("jyy") == -6
    assert utils.jnum2str(-7) == "Jxy"
    # Check errors
    pytest.raises(KeyError, utils.jstr2num, "foo")
    pytest.raises(ValueError, utils.jstr2num, 1)
    pytest.raises(ValueError, utils.jnum2str, 7.3)

    # check parse method
    assert utils.pol.parse_jpolstr("x") == "Jxx"
    assert utils.pol.parse_jpolstr("xy") == "Jxy"
    assert utils.pol.parse_jpolstr("XY") == "Jxy"


def test_jones_num_funcs_x_orientation():
    """Test functions to convert jones pol strings and numbers with x_orientation."""

    jnums = [-8, -7, -6, -5, -4, -3, -2, -1]
    x_orient1 = "east"
    jstr = ["Jne", "Jen", "Jnn", "Jee", "Jlr", "Jrl", "Jll", "Jrr"]
    assert jnums == utils.jstr2num(jstr, x_orientation=x_orient1)
    assert jstr == utils.jnum2str(jnums, x_orientation=x_orient1)
    # Check shorthands
    jstr = ["ne", "en", "nn", "n", "ee", "e", "lr", "rl", "ll", "l", "rr", "r"]
    jnums = [-8, -7, -6, -6, -5, -5, -4, -3, -2, -2, -1, -1]
    assert jnums == utils.jstr2num(jstr, x_orientation=x_orient1)
    # Check individuals
    assert utils.jstr2num("jnn", x_orientation=x_orient1) == -6
    assert utils.jnum2str(-7, x_orientation=x_orient1) == "Jen"
    # Check errors
    pytest.raises(KeyError, utils.jstr2num, "foo", x_orientation=x_orient1)
    pytest.raises(ValueError, utils.jstr2num, 1, x_orientation=x_orient1)
    pytest.raises(ValueError, utils.jnum2str, 7.3, x_orientation=x_orient1)

    # check parse method
    assert utils.pol.parse_jpolstr("e", x_orientation=x_orient1) == "Jee"
    assert utils.pol.parse_jpolstr("x", x_orientation=x_orient1) == "Jee"
    assert utils.pol.parse_jpolstr("y", x_orientation=x_orient1) == "Jnn"
    assert utils.pol.parse_jpolstr("en", x_orientation=x_orient1) == "Jen"
    assert utils.pol.parse_jpolstr("NE", x_orientation=x_orient1) == "Jne"

    jnums = [-8, -7, -6, -5, -4, -3, -2, -1]
    x_orient2 = "north"
    jstr = ["Jen", "Jne", "Jee", "Jnn", "Jlr", "Jrl", "Jll", "Jrr"]
    assert jnums == utils.jstr2num(jstr, x_orientation=x_orient2)
    assert jstr == utils.jnum2str(jnums, x_orientation=x_orient2)
    # Check shorthands
    jstr = ["en", "ne", "ee", "e", "nn", "n", "lr", "rl", "ll", "l", "rr", "r"]
    jnums = [-8, -7, -6, -6, -5, -5, -4, -3, -2, -2, -1, -1]
    assert jnums == utils.jstr2num(jstr, x_orientation=x_orient2)
    # Check individuals
    assert utils.jstr2num("jee", x_orientation=x_orient2) == -6
    assert utils.jnum2str(-7, x_orientation=x_orient2) == "Jne"
    # Check errors
    pytest.raises(KeyError, utils.jstr2num, "foo", x_orientation=x_orient2)
    pytest.raises(ValueError, utils.jstr2num, 1, x_orientation=x_orient2)
    pytest.raises(ValueError, utils.jnum2str, 7.3, x_orientation=x_orient2)

    # check parse method
    assert utils.pol.parse_jpolstr("e", x_orientation=x_orient2) == "Jee"
    assert utils.pol.parse_jpolstr("x", x_orientation=x_orient2) == "Jnn"
    assert utils.pol.parse_jpolstr("y", x_orientation=x_orient2) == "Jee"
    assert utils.pol.parse_jpolstr("en", x_orientation=x_orient2) == "Jen"
    assert utils.pol.parse_jpolstr("NE", x_orientation=x_orient2) == "Jne"

    # check warnings for non-recognized x_orientation
    with check_warnings(UserWarning, "x_orientation not recognized"):
        assert utils.jstr2num("x", x_orientation="foo") == -5

    with check_warnings(UserWarning, "x_orientation not recognized"):
        assert utils.jnum2str(-6, x_orientation="foo") == "Jyy"


def test_conj_pol():
    """Test function to conjugate pols"""

    pol_nums = [-8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4]
    cpol_nums = [-7, -8, -6, -5, -3, -4, -2, -1, 1, 2, 3, 4]
    assert pol_nums == utils.conj_pol(cpol_nums)
    assert utils.conj_pol(pol_nums) == cpol_nums
    # fmt: off
    pol_str = ['yx', 'xy', 'yy', 'xx', 'ee', 'nn', 'en', 'ne', 'lr', 'rl', 'll',
               'rr', 'pI', 'pQ', 'pU', 'pV']
    cpol_str = ['xy', 'yx', 'yy', 'xx', 'ee', 'nn', 'ne', 'en', 'rl', 'lr', 'll',
                'rr', 'pI', 'pQ', 'pU', 'pV']
    # fmt: on
    assert pol_str == utils.conj_pol(cpol_str)
    assert utils.conj_pol(pol_str) == cpol_str
    assert [pol_str, pol_nums] == utils.conj_pol([cpol_str, cpol_nums])

    # Test error with jones
    cjstr = ["Jxy", "Jyx", "Jyy", "Jxx", "Jrl", "Jlr", "Jll", "Jrr"]
    assert pytest.raises(KeyError, utils.conj_pol, cjstr)

    # Test invalid pol
    with pytest.raises(
        ValueError, match="Polarization not recognized, cannot be conjugated."
    ):
        utils.conj_pol(2.3)


def test_reorder_conj_pols_non_list():
    pytest.raises(ValueError, utils.pol.reorder_conj_pols, 4)


def test_reorder_conj_pols_strings():
    pols = ["xx", "xy", "yx"]
    corder = utils.pol.reorder_conj_pols(pols)
    assert np.array_equal(corder, [0, 2, 1])


def test_reorder_conj_pols_ints():
    pols = [-5, -7, -8]  # 'xx', 'xy', 'yx'
    corder = utils.pol.reorder_conj_pols(pols)
    assert np.array_equal(corder, [0, 2, 1])


def test_reorder_conj_pols_missing_conj():
    pols = ["xx", "xy"]  # Missing 'yx'
    pytest.raises(ValueError, utils.pol.reorder_conj_pols, pols)


def test_determine_pol_order_err():
    with pytest.raises(ValueError, match='order must be either "AIPS" or "CASA".'):
        utils.pol.determine_pol_order([], order="ABC")


@pytest.mark.parametrize(
    "pols,aips_order,casa_order",
    [
        [[-8, -7, -6, -5], [3, 2, 1, 0], [3, 1, 0, 2]],
        [[-5, -6, -7, -8], [0, 1, 2, 3], [0, 2, 3, 1]],
        [[1, 2, 3, 4], [0, 1, 2, 3], [0, 1, 2, 3]],
    ],
)
@pytest.mark.parametrize("order", ["CASA", "AIPS"])
def test_pol_order(pols, aips_order, casa_order, order):
    check = utils.pol.determine_pol_order(pols, order=order)

    if order == "CASA":
        assert all(check == casa_order)
    if order == "AIPS":
        assert all(check == aips_order)


def test_x_orientation_pol_map():
    assert utils.x_orientation_pol_map("north") == {"x": "n", "y": "e"}


@pytest.mark.parametrize(
    ("input_kwargs", "output"),
    [
        ({"feed_array": ["x"]}, [-5]),
        ({"feed_array": ["x", "y"]}, [-5, -6, -7, -8]),
        ({"feed_array": ["r", "l"]}, [-1, -2, -3, -4]),
        ({"feed_array": ["x", "y"], "include_cross_pols": False}, [-5, -6]),
        (
            {
                "feed_array": ["e", "n"],
                "x_orientation": "east",
                "return_feed_pol_order": True,
            },
            ([-5, -6, -7, -8], [(0, 0), (1, 1), (0, 1), (1, 0)]),
        ),
        (
            {
                "feed_array": ["n", "e"],
                "x_orientation": "north",
                "return_feed_pol_order": True,
            },
            ([-5, -6, -7, -8], [(0, 0), (1, 1), (0, 1), (1, 0)]),
        ),
        (
            {
                "feed_array": ["e", "n"],
                "x_orientation": "north",
                "return_feed_pol_order": True,
            },
            ([-6, -5, -8, -7], [(0, 0), (1, 1), (0, 1), (1, 0)]),
        ),
    ],
)
def test_convert_feeds_to_pols(input_kwargs, output):
    ret_val = utils.pol.convert_feeds_to_pols(**input_kwargs)
    if not isinstance(output, tuple):
        assert ret_val.tolist() == output
    else:
        assert ret_val[0].tolist() == output[0]
        assert ret_val[1] == output[1]

        dep_kwargs = copy.deepcopy(input_kwargs)
        dep_kwargs["calc_cross_pols"] = dep_kwargs.pop("include_cross_pols", True)
        del dep_kwargs["return_feed_pol_order"]
        with check_warnings(
            DeprecationWarning,
            match="This method (uvbeam._convert_feeds_to_pols) is deprecated in "
            "favor of utils.pol.convert_feeds_to_pols. This will become an error "
            "in version 3.3",
        ):
            dep_ret_val = _convert_feeds_to_pols(**dep_kwargs)
        assert dep_ret_val[0].tolist() == output[0]
        assert dep_ret_val[1] == output[1]


def test_convert_feeds_to_pols_errors():
    with pytest.raises(
        ValueError, match="feed_array contains 3 feeds. Only 1 or 2 feeds is supported."
    ):
        utils.pol.convert_feeds_to_pols(["x", "y", "z"])

    with pytest.raises(
        ValueError, match="feed_array contains 0 feeds. Only 1 or 2 feeds is supported."
    ):
        utils.pol.convert_feeds_to_pols([])


@pytest.mark.parametrize(
    "kwargs,err_msg",
    [
        [{"feeds": ["l", "r"], "x_orientation": "xyz"}, "x_orientation not recognized"],
        [
            {"feeds": ["l", "r", "x"], "x_orientation": "n"},
            "feeds must be a list or tuple of length 1 or 2.",
        ],
        [
            {"feeds": ["u", "v"], "x_orientation": "n"},
            'feeds must contain only "x", "y", "l", and/or "r".',
        ],
    ],
)
def test_get_feeds_from_x_orientation_errors(kwargs, err_msg):
    with pytest.raises(ValueError, match=err_msg):
        utils.pol.get_feeds_from_x_orientation(nants=1, **kwargs)


@pytest.mark.parametrize(
    "kwargs,exp_feed_array,exp_feed_angle,has_warn",
    [
        [{"x_orientation": "e", "nants": 1}, [["x", "y"]], [[np.pi / 2, 0]], True],
        [{"x_orientation": None, "nants": 2}, None, None, False],
        [{"x_orientation": "n", "nants": 1, "feeds": "x"}, [["x"]], [[0]], False],
        [
            {
                "x_orientation": "east",
                "nants": 2,
                "polarization_array": [-5, -6, -7, -8],
            },
            [["x", "y"], ["x", "y"]],
            [[np.pi / 2, 0], [np.pi / 2, 0]],
            False,
        ],
        [
            {
                "x_orientation": "east",
                "nants": 2,
                "flex_polarization_array": [-1, -2, -3, -4],
                "polarization_array": [0],
            },
            [["l", "r"], ["l", "r"]],
            [[0, 0], [0, 0]],
            False,
        ],
    ],
)
def test_get_feeds_from_x_orientation(kwargs, exp_feed_array, exp_feed_angle, has_warn):
    with check_warnings(
        UserWarning if has_warn else None,
        match=("Unknown polarization basis" if has_warn else None),
    ):
        Nfeeds, feed_array, feed_angle = utils.pol.get_feeds_from_x_orientation(
            **kwargs
        )

    if exp_feed_angle is None:
        assert Nfeeds is None
        assert feed_array is None
        assert feed_angle is None
    else:
        assert np.array_equal(feed_array, exp_feed_array)
        assert np.array_equal(feed_angle, exp_feed_angle)
        assert Nfeeds == feed_array.shape[1]
        assert Nfeeds == feed_angle.shape[1]
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API