DEV Community

Ahmed Hossam
Ahmed Hossam

Posted on

Good start for the project

First 2 weeks summarize:

This week I began working on refactoring hek.py functions.
I started by migrating the finished work in GSoC2023 to a new PR to start working on it.
My first contribution was creating util.py file to include all utility functions needed for hek.py, a lot of functions that was added in HEKClient at first didn't make sense to remain there.

Now the new util.py file includes:

def parse_times(table)
def parse_values_to_quantities(table)
def parse_columns_to_table(table, attributes, is_coord_prop = False)
def parse_unit(table, attribute, is_coord_prop = False)
def parse_chaincode(value, attribute, unit)
def get_unit(unit)

Enter fullscreen mode Exit fullscreen mode

get_unit has been simplified in terms of implementation and interface, this was the first version:

    def get_unit(attribute, str):
        if attribute["is_coord_prop"]:
            coord1_unit, coord2_unit, coord3_unit = None, None, None
            coord_units = re.split(r'[, ]', str)
            if len(coord_units) == 1: # deg
               coord1_unit = coord2_unit = u.Unit(coord_units[0])
            elif len(coord_units) == 2:
                coord1_unit = u.Unit(coord_units[0])
                coord2_unit = u.Unit(coord_units[1])
            else:
                coord1_unit = u.Unit(coord_units[0])
                coord2_unit = u.Unit(coord_units[1])
                coord3_unit = u.Unit(coord_units[2])
            return locals()[attribute["unit_prop"]]
        else:
            return u.Unit(str)
Enter fullscreen mode Exit fullscreen mode

The first thing that has been done is to use unit aliases inside the function using context manager instead of putting the aliases globally.

The whole goal of this function is to parse a string into an astropy unit, but the big part of the function was splitting the string into more than one unit if the input was coordinate units, and then returning the unit assigned to unit_prop. I decided to just remove all of this and convert the unit into an array and return the first index, like this:

   units = re.split(r'[, ]', unit)
   return u.Unit(units[0].lower())
Enter fullscreen mode Exit fullscreen mode

And actually it works just fine with all HEK features and events, so I will keep it like this until some strange error appears.
And also the interface has been simplified to just take the string of the targeted unit.

This is the current version of get_unit:

def get_unit(unit):
    """
    Converts string into astropy unit.

    Parameters
    ----------
    unit: str
        The targeted unit

    Returns
    -------
    unit
        Astropy unit object (e.g. <class 'astropy.units.core.Unit'> or <class 'astropy.units.core.CompositeUnit'>)

    Raises
    ------
    ValueError
        Because `unit` did not parse as unit.

    Notes
    ----
    For the complete list of HEK parameters: https://www.lmsal.com/hek/VOEvent_Spec.html

    """
    cm2 = u.def_unit("cm2", u.cm**3)
    m2 = u.def_unit("m2", u.m**2)
    m3 = u.def_unit("m3", u.m**3)

    aliases = {
        "steradian": u.sr,
        "arcseconds": u.arcsec,
        "degrees": u.deg,
        "sec": u.s,
        "emx": u.Mx,
        "amperes": u.A,
        "ergs": u.erg,
        "cubic centimeter": u.ml,
        "square centimeter": cm2,
        "cubic meter": m3,
        "square meter": m2,
    }

    with u.add_enabled_units([cm2, m2, m3]), u.set_enabled_aliases(aliases):
        # If they are units of coordinates, it will have more than one unit,
        # otherwise it will be just one unit.
        # NOTE: There is an assumption that coord1_unit, coord2_unit and coord3_unit will be the same.
        units = re.split(r'[, ]', unit)
        return u.Unit(units[0].lower())

Enter fullscreen mode Exit fullscreen mode

Another thing that has been done was adding a documentation string for parse_chaincode function.

def parse_chaincode(value, attribute, unit):
    """
    Parses a string representation of coordinates and convert them into a PolygonSkyRegion object
    using units based on the specified coordinate frame.

    Parameters
    ----------
    value: PolygonSkyRegion
        A polygon defined using vertices in sky coordinates.
    attribute: dict
        An object from coord_properties.json
    unit: str
        The unit of the coordinates

    Returns
    -------
    PolygonSkyRegion
        A polygon defined using vertices in sky coordinates.

    Raises
    ------
    IndexError
        Because `value` does not contain the expected '((' and '))' substrings.
    UnitConversionError
        Because the units set by `coord1_unit` or `coord2_unit` are incompatible with the values being assigned.

    """
    coord1_unit = u.deg
    coord2_unit = u.deg
    if attribute["frame"] == "helioprojective":
        coord1_unit = u.arcsec
        coord2_unit = u.arcsec
    elif attribute["frame"] == "heliocentric":
        coord1_unit = u.R_sun # Nominal solar radius
    elif attribute["frame"] == "icrs":
        coord1_unit = get_unit(unit)
        coord2_unit = get_unit(unit)

    coordinates_str = value.split('((')[1].split('))')[0]
    coord1_list = [float(coord.split()[0]) for coord in coordinates_str.split(',')] * coord1_unit
    coord2_list = [float(coord.split()[1]) for coord in coordinates_str.split(',')] * coord2_unit
    vertices = {}
    if attribute["frame"] == "heliocentric":
        vertices = SkyCoord(coord1_list, coord2_list, [1]* len(coord1_list) * u.AU, representation_type="cylindrical", frame="heliocentric")
    else:
        vertices = SkyCoord(coord1_list, coord2_list, frame=attribute["frame"])

    return PolygonSkyRegion(vertices = vertices)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)