As part of the verification of the dynamic offsets process, SOT/ACA ops has independently confirmed the FOT aimpoint offsets for the JUL0415O test week. For this independent verification, we have used:
(note that in testing, this notebook required PYTHONPATH set to include the 'calc_aca_offsets' branch of starcheck and the 'read_zero_offset' branch of parse_cm)
In [1]:
import os
import sys
from glob import glob
import json
import numpy as np
from astropy.table import Table
from Chandra.Time import DateTime
from Ska.Matplotlib import plot_cxctime
from chandra_aca import drift
import parse_cm
The dynamic target offset file includes the aca offsets calculated by the FOT version of the chandra_aca.drift module. The file also includes inputs used to calculate the offsets values: detector, chip_id, chipx, chipy, a time, and a temperature. As a check of consistency, we recalculate the aca_offset_y and aca_offset_z values using those inputs and the SOT/Ska version of the chandra_aca.drift module. For each row in the table, this tests confirms that the re-calculated values of aca_offset_y and aca_offset_z are within 0.02 arcsecs of the values in the file.
In [2]:
TEST_DIR = '/proj/sot/ska/ops/SFE/JUL0415O/oflso'
dynam_table = Table.read(glob("{}/*dynamical_offsets.txt".format(TEST_DIR))[0], format='ascii')
In [3]:
# first, check table for self-consistent offsets
ys = []
zs = []
for row in dynam_table:
y, z = drift.get_aca_offsets(row['detector'], row['chip_id'], row['chipx'], row['chipy'],
time=row['mean_date'], t_ccd=row['mean_t_ccd'])
ys.append(y)
zs.append(z)
print "Y offsets consistent: {}".format(np.allclose(dynam_table['aca_offset_y'], ys, atol=0.02))
print "Z offsets consistent: {}".format(np.allclose(dynam_table['aca_offset_z'], zs, atol=0.02))
The dynamic offsets in the dynamic offsets / aimpoint file are calculated using the the xija ACA thermal model called from the FOT Matlab tools. To independently verify both the inputs and the outputs reported in the dynamic offsets/aimpoints file, we run the SOT version ACA model over the JUL0415O schedule interval and recalculate the aca_offsets using the calculated ACA ccd temperatures and the zero offset aimpoint information from the OR list. The ACA load review software, starcheck, already has code to determine inputs to the xija ACA model and to run the model over command products. For this test, the code to get the mean aca ccd temperature for each obsid has been extended to also run the offset calculation via chandra_aca.drift_get_aca_offsets
.
+ if interval['obsid'] in obsreqs and len(ok_temps) > 0:
+ obsreq = obsreqs[interval['obsid']]
+ if 'chip_id' in obsreq:
+ ddy, ddz = get_aca_offsets(obsreq['detector'],
+ obsreq['chip_id'],
+ obsreq['chipx'],
+ obsreq['chipy'],
+ time=itimes,
+ t_ccd=ok_temps)
+ obs['aca_offset_y'] = np.mean(ddy)
+ obs['aca_offset_z'] = np.mean(ddz)
(see link to changed starcheck code)
Then, the returned values from that code include these independently calculated values of aca_offset_y and aca_offset_z that correspond to aca_offset_y and aca_offset_z in the dynamic aimpoint text product. (apologies for the starcheck log output)
In [4]:
from starcheck.calc_ccd_temps import get_ccd_temps
obsid_info = json.loads(get_ccd_temps(TEST_DIR,
json_obsids=open("{}/starcheck/obsids.json".format(TEST_DIR)),
model_spec="{}/starcheck/aca_spec.json".format(TEST_DIR),
char_file="/proj/sot/ska/data/starcheck/characteristics.yaml",
orlist="{}/mps/or/JUL0415_A.or".format(TEST_DIR)));
Then, for each entry in the dynamic offset table from the matlab tools, we compare the aca_offset_y and aca_offset_z with the values from the independent run of the model and the offset values calculated from within the starcheck code. For quick review, we print out the offsets and temperatures, with the dynamic aimpoint offset file versions in the first column of each value being checked.
In [5]:
y_diff = []
z_diff = []
for obsid in dynam_table['obsid']:
dyn_rec = dynam_table[dynam_table['obsid'] == obsid][0]
if str(obsid) in obsid_info:
print "{} offset y {: .2f} vs {: .2f} offset z {: .2f} vs {: .2f} t_ccd {: .2f} vs {: .2f}".format(
obsid,
dyn_rec['aca_offset_y'], obsid_info[str(obsid)]['aca_offset_y'],
dyn_rec['aca_offset_z'], obsid_info[str(obsid)]['aca_offset_z'],
dyn_rec['mean_t_ccd'], obsid_info[str(obsid)]['ccd_temp'])
y_diff.append(dyn_rec['aca_offset_y'] - obsid_info[str(obsid)]['aca_offset_y'])
z_diff.append(dyn_rec['aca_offset_z'] - obsid_info[str(obsid)]['aca_offset_z'])
y_diff = np.array(y_diff)
z_diff = np.array(z_diff)
The maximum differences in the offsets between the values via an independent run of the model are within an arcsec.
In [6]:
print "Y offset max difference {:.2f} arcsec".format(np.max(np.abs(y_diff)))
In [7]:
print "Z offset max difference {:.2f} arcsec".format(np.max(np.abs(z_diff)))