<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Independent confirmation of ACA offsets
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
Step1:  Check dynamic offset file for consistency
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
Step2:  Run the ACA model and get new offsets
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) &gt; 0
Step3:  Compare values to dynamic offset table from Matlab
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.
Step4:  The maximum differences in the offsets between the values via an independent run of the model are within an arcsec.
<ASSISTANT_TASK:>
Python Code:
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
Explanation: Independent confirmation of ACA offsets
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:
the ZERO OFFSET aimpoint table provided by SOT MP
the xija ACA thermal model for a test week
(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)
End of explanation
TEST_DIR = '/proj/sot/ska/ops/SFE/JUL0415O/oflso'
dynam_table = Table.read(glob("{}/*dynamical_offsets.txt".format(TEST_DIR))[0], format='ascii')
# 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))
Explanation: Check dynamic offset file for consistency
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.
End of explanation
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)));
Explanation: Run the ACA model and get new offsets
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) &gt; 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)
End of explanation
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)
Explanation: Compare values to dynamic offset table from Matlab
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.
End of explanation
print "Y offset max difference {:.2f} arcsec".format(np.max(np.abs(y_diff)))
print "Z offset max difference {:.2f} arcsec".format(np.max(np.abs(z_diff)))
Explanation: The maximum differences in the offsets between the values via an independent run of the model are within an arcsec.
End of explanation
<END_TASK>