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)
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)
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())
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())
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)