Skip to content

Commit

Permalink
implement the field rotation correction
Browse files Browse the repository at this point in the history
  • Loading branch information
araichoor committed Jan 28, 2025
1 parent dd72bc8 commit 005eae3
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 10 deletions.
16 changes: 14 additions & 2 deletions py/fiberassign/scripts/assign.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ def parse_assign(optlist=None):
help="Override obsdate and use this field rotation "
"for all tiles (degrees counter clockwise in CS5)")

parser.add_argument("--fieldrot_corr", type=bool, required=False,
default=None,
help="apply correction to computed fieldrot "
"(default: False if rundate<rundate_cutoff, True else")

parser.add_argument("--dir", type=str, required=False, default=None,
help="Output directory.")

Expand Down Expand Up @@ -264,7 +269,7 @@ def parse_assign(optlist=None):

# Set obsdate
if args.obsdate is None:
args.obsdate = get_obsdate(rundate=args.rundate)
args.obsdate, _ = get_obsdate(rundate=args.rundate)

# convert YEARMMDD to YEAR-MM-DD to be ISO 8601 compatible
if re.match('\d{8}', args.obsdate):
Expand All @@ -274,6 +279,12 @@ def parse_assign(optlist=None):
#- Note: ISO8601 does not require time portion
args.obsdate = '{}-{}-{}'.format(year, mm, dd)

# Apply the field rotation correction?
# - True if rundate!=None and rundate>=rundate_cutoff
# - False else
if args.fieldrot_corr is None:
_, args.fieldrot_corr = get_obsdate(rundate=args.rundate)

# Set output directory
if args.dir is None:
if args.rundate is None:
Expand Down Expand Up @@ -324,7 +335,8 @@ def run_assign_init(args, plate_radec=True):
except ValueError:
pass
tiles = load_tiles(tiles_file=args.footprint, select=tileselect,
obstime=args.obsdate, obstheta=args.fieldrot, obsha=args.ha)
obstime=args.obsdate, obstheta=args.fieldrot, obsha=args.ha,
obsthetacorr=args.fieldrot_corr)

# Before doing significant calculations, check for pre-existing files
if not args.overwrite:
Expand Down
19 changes: 17 additions & 2 deletions py/fiberassign/tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@

import astropy.time

from fiberassign.utils import get_default_static_obsdate
from fiberassign.utils import get_default_static_obsdate, get_obstheta_corr

from ._internal import Tiles


def load_tiles(tiles_file=None, select=None, obstime=None, obstheta=None,
obsha=None):
obsha=None, obsthetacorr=False):
"""Load tiles from a file.
Load tile data either from the specified file or from the default provided
Expand All @@ -42,10 +42,16 @@ def load_tiles(tiles_file=None, select=None, obstime=None, obstheta=None,
obstheta (float): The angle in degrees to override the field rotation
of all tiles.
obsha (float): The Hour Angle in degrees to design the observation of all tiles.
obsthetacorr (optional, defaults to False): apply a correction on obstheta,
based on DocDB-8931 (bool)
Returns:
(Tiles): A Tiles object.
Notes:
2025, Feb.: added the obsthetacorr optional argument; this correction
implemented to avoid the hexapod rotation limit error, is
presented/described in DocDB-8931.
"""
# Read in the tile information
if tiles_file is None:
Expand Down Expand Up @@ -99,6 +105,15 @@ def load_tiles(tiles_file=None, select=None, obstime=None, obstheta=None,
if obsha is not None:
ha_obs[:] = obsha

# AR apply correction on the obstheta?
# AR sign is "minus" here;
# AR see get_obstheta_corr() (and p.5 of DocDB-8931)
if obsthetacorr:
theta_obs -= get_obstheta_corr(
tiles_data["DEC"][keeprows],
ha_obs,
)

tls = Tiles(tiles_data["TILEID"][keeprows], tiles_data["RA"][keeprows],
tiles_data["DEC"][keeprows],
tiles_data["OBSCONDITIONS"][keeprows],
Expand Down
69 changes: 63 additions & 6 deletions py/fiberassign/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,28 +145,35 @@ def get_obsdate(rundate=None):
Returns:
obsdate: "YYYY-MM-DD" format (string)
is_after_cutoff: is rundate after rundate_cutoff? (bool)
"""
obsdate = get_default_static_obsdate()
is_after_cutoff = False
if rundate is None:
log.info("rundate={} -> setting obsdate={}".format(rundate, obsdate))
log.info(
"rundate={} -> (obsdate, is_after_cutoff)=({}, {})".format(
rundate, obsdate, is_after_cutoff
)
)
else:
assert_isoformat_utc(rundate)
rundate_mjd = Time(datetime.strptime(rundate, "%Y-%m-%dT%H:%M:%S%z")).mjd
rundate_cutoff = get_date_cutoff("rundate", "obsdate")
rundate_mjd_cutoff = Time(datetime.strptime(rundate_cutoff, "%Y-%m-%dT%H:%M:%S%z")).mjd
if rundate_mjd >= rundate_mjd_cutoff:
is_after_cutoff = True
yyyy = int(rundate[:4])
obsdate = "{}{}".format(yyyy + 1, rundate[4:10])
log.info(
"rundate={} >= rundate_cutoff={} -> setting obsdate={}".format(
rundate, rundate_cutoff, obsdate)
"rundate={} >= rundate_cutoff={} -> (obsdate, is_after_cutoff)=({}, {})".format(
rundate, rundate_cutoff, obsdate, is_after_cutoff)
)
else:
log.info(
"rundate={} < rundate_cutoff={} -> setting obsdate={}".format(
rundate, rundate_cutoff, obsdate)
"rundate={} < rundate_cutoff={} -> (obsdate, is_after_cutoff)=({}, {})".format(
rundate, rundate_cutoff, obsdate, is_after_cutoff)
)
return obsdate
return obsdate, is_after_cutoff


def get_svn_version(svn_dir):
Expand Down Expand Up @@ -373,3 +380,53 @@ def get_rev_fiberassign_changes(svndir, rev, subdirs=None):
d["REVISION"] = np.array([rev for fn in fns], dtype=int)
d["CHANGE"] = np.array(changes, dtype=str)
return d


def get_obstheta_corr(decs, has, clip_arcsec=600.):
"""
Returns the computed correction to be applied to the field rotation
computed in fiberassign.tiles.load_tiles().
The correction should be applied as: obsthetas[deg] -= obstheta_corrs[deg].
Args:
decs: tile declinations (float or np array of floats)
has: hour angles (float or np array of floats)
clip_arcsec (optional, defaults to 600): abs(obstheta_corrs) is
forced to be <obstheta_corrs (float)
Returns:
obstheta_corrs: correction (in deg) to be applied (float or np array of floats)
Notes:
See DocDB-8931 for details.
During observations, PlateMaker computes the required field rotation,
then asks the hexapod to be rotated by
ROTOFFST = FIELDROT - PM_REQ_FIELDROT,
where FIELDROT is the field rotation coming from fiberassign.tiles.load_tiles().
When abs(ROTOFFST)>600 arcsec, the move is denied, and the exposure aborted.
Correction computed here is a fit to ROTOFFST=f(DEC), plus a fit on the residuals.
"""
assert clip_arcsec >= 0
isoneval = isinstance(decs, float)
if isoneval:
decs, has = np.atleast_1d(decs), np.atleast_1d(has)
# AR fitted function, ROTOFFST[arcsec] = f(DEC)
# AR rescale decs into [0, 1]
xs = (90. - decs) / 180.
rotoffsts = 937.60578 - 697.06513 * xs ** -0.18835
# AR fitted function to the residuals, residuals[arcsec] = f(HA)
xs = (90. + has) / 180.
residuals = -113.90162 + 222.18009 * xs
sel = xs > 0.5
residuals[sel] = -245.49007 + 485.35700 * xs[sel]
# AR total correction
obstheta_corrs = rotoffsts + residuals
# AR clip
obstheta_corrs = np.clip(obstheta_corrs, -clip_arcsec, clip_arcsec)
# AR switch to degrees
obstheta_corrs /= 3600
if isoneval:
decs, has = decs[0], has[0]
return obstheta_corrs[0]
else:
return obstheta_corrs

0 comments on commit 005eae3

Please sign in to comment.