def __init__(self, tstart, tstop, msids, recent_source="maude", filter_bad=False, stat='5min', user=None, password=None, get_states=True, state_keys=None): msids = ensure_list(msids) tstart = get_time(tstart, fmt='secs') tstop = get_time(tstop, fmt='secs') tmid = 1.0e99 for msid in msids: tm = fetch.get_time_range(msid, format="secs")[-1] tmid = min(tmid, tm) tmid = get_time(tmid, fmt='secs') if tmid < tstop: msids1 = MSIDs.from_database(msids, tstart, tstop=tmid, filter_bad=filter_bad, stat=stat) if recent_source == "maude": msids2 = MSIDs.from_maude(msids, tmid, tstop=tstop, user=user, password=password) elif recent_source == "tracelog": msids2 = _parse_tracelogs(tmid, tstop, ["/data/acis/eng_plots/acis_eng_10day.tl", "/data/acis/eng_plots/acis_dea_10day.tl"], None) msids = ConcatenatedMSIDs(msids1, msids2) else: msids = MSIDs.from_database(msids, tstart, tstop=tstop, filter_bad=filter_bad, stat=stat) if get_states: states = States.from_kadi_states(tstart, tstop, state_keys=state_keys) else: states = EmptyTimeSeries() model = EmptyTimeSeries() super(TelemData, self).__init__(msids, states, model)
def from_states_file(cls, name, states_file, T_init, dt=328.0, model_spec=None, mask_bad_times=False, ephem_file=None, get_msids=True, no_eclipse=False): """ Run a xija thermal model using a states.dat file. Parameters ---------- name : string The name of the model to simulate. Can be "dea", "dpa", "psmc", or "fep1mong". states_file : string A file containing commanded states, in the same format as "states.dat" which is outputted by ACIS thermal model runs for loads. T_init : float The starting temperature for the model in degrees C. model_spec : string, optional Path to the model spec JSON file for the model. Default: None, the standard model path will be used. mask_bad_times : boolean, optional If set, bad times from the data are included in the array masks and plots. Default: False """ states = States.from_load_file(states_file) tstart = get_time(states['tstart'].value[0]) tstop = get_time(states['tstop'].value[-1]) return cls(name, tstart, tstop, states=states, T_init=T_init, dt=dt, model_spec=model_spec, mask_bad_times=mask_bad_times, ephem_file=ephem_file, get_msids=get_msids, no_eclipse=no_eclipse)
def from_database(cls, tstart, tstop, state_keys=None, server=None): from Chandra.cmd_states import fetch_states tstart = get_time(tstart) tstop = get_time(tstop) if state_keys is not None: state_keys = ensure_list(state_keys) t = fetch_states(tstart, tstop, vals=state_keys, server=server) return cls(t)
def from_kadi(cls, name, tstart, tstop, T_init, get_msids=True, dt=328.0, model_spec=None, mask_bad_times=False, ephem_file=None, no_eclipse=False, compute_model=None): tstart = get_time(tstart) tstop = get_time(tstop) states = States.from_kadi_states(tstart, tstop) return cls(name, tstart, tstop, states=states, T_init=T_init, dt=dt, model_spec=model_spec, mask_bad_times=mask_bad_times, ephem_file=ephem_file, get_msids=get_msids, no_eclipse=no_eclipse, compute_model=compute_model)
def from_kadi_states(cls, tstart, tstop, state_keys=None): from kadi.commands import states tstart = get_time(tstart) tstop = get_time(tstop) if state_keys is not None: state_keys = ensure_list(state_keys) t = states.get_states(tstart, tstop, state_keys=state_keys, merge_identical=True).as_array() return cls(t)
def from_commands(cls, tstart, tstop, cmds=None, state_keys=None): from kadi import commands from kadi.commands import states tstart = get_time(tstart) tstop = get_time(tstop) if cmds is None: cmds = commands.get_cmds(tstart, tstop) continuity = states.get_continuity(tstart, state_keys) t = states.get_states(cmds=cmds, continuity=continuity, state_keys=state_keys, merge_identical=True).as_array() return cls(t)
def __init__(self, tstart, tstop, msids, get_states=True, filter_bad=False, stat='5min', state_keys=None, interpolate=None, interpolate_times=None): tstart = get_time(tstart) tstop = get_time(tstop) msids = MSIDs.from_database(msids, tstart, tstop=tstop, filter_bad=filter_bad, stat=stat, interpolate=interpolate, interpolate_times=interpolate_times) if get_states: states = States.from_kadi_states(tstart, tstop, state_keys=state_keys) else: states = EmptyTimeSeries() model = EmptyTimeSeries() super(EngArchiveData, self).__init__(msids, states, model)
def __init__(self, tstart, tstop, msids, get_states=True, user=None, password=None, other_msids=None, state_keys=None): tstart = get_time(tstart) tstop = get_time(tstop) msids = MSIDs.from_maude(msids, tstart, tstop=tstop, user=user, password=password) if other_msids is not None: msids2 = MSIDs.from_database(other_msids, tstart, tstop) msids = CombinedMSIDs([msids, msids2]) if get_states: states = States.from_kadi_states(tstart, tstop, state_keys=state_keys) else: states = EmptyTimeSeries() model = EmptyTimeSeries() super(MaudeData, self).__init__(msids, states, model)
def get_states(self, time): """ Get the commanded states at a given *time*. """ time = get_time(time, 'secs') state = {} for key in self.keys(): state[key] = self[key][time] return state
def from_maude(cls, msids, tstart, tstop=None, user=None, password=None): import maude tstart = get_time(tstart) tstop = get_time(tstop) msids = ensure_list(msids) msids, derived_msids = check_depends(msids) table = {} times = {} state_codes = {} out = maude.get_msids(msids, start=tstart, stop=tstop, user=user, password=password) for msid in out["data"]: k = msid["msid"].lower() table[k] = msid["values"] times[k] = msid['times'] state_codes[k] = get_state_codes(k) return cls(table, times, state_codes=state_codes, derived_msids=derived_msids)
def from_database(cls, msids, tstart, tstop=None, filter_bad=False, stat='5min', interpolate=None, interpolate_times=None): tstart = get_time(tstart) tstop = get_time(tstop) msids = ensure_list(msids) msids, derived_msids = check_depends(msids) msids = [msid.lower() for msid in msids] data = fetch.MSIDset(msids, tstart, stop=tstop, filter_bad=filter_bad, stat=stat) table = {} times = {} state_codes = {} masks = {} if interpolate is not None: if interpolate_times is None: # Get the nominal tstart / tstop range max_fetch_tstart = max(msid.times[0] for msid in data.values()) min_fetch_tstop = min(msid.times[-1] for msid in data.values()) dt = 328.0 start = DateTime(tstart).secs if tstart else data.tstart stop = DateTime(tstop).secs if tstop else data.tstop start = max(start, max_fetch_tstart) stop = min(stop, min_fetch_tstop) interpolate_times = np.arange((stop - start) // dt + 1) * dt + start else: interpolate_times = DateTime(interpolate_times).secs for k, msid in data.items(): if interpolate is not None: indexes = Ska.Numpy.interpolate(np.arange(len(msid.times)), msid.times, interpolate_times, method=interpolate, sorted=True) times[k.lower()] = interpolate_times else: indexes = slice(None, None, None) times[k.lower()] = data[k].times if msid.state_codes: state_codes[k] = dict((k, v) for v, k in msid.state_codes) table[k.lower()] = msid.vals[indexes] if msid.bads is not None: masks[k.lower()] = (~msid.bads)[indexes] return cls(table, times, state_codes=state_codes, masks=masks, derived_msids=derived_msids)
def get_values(self, time): time = get_time(time, fmt='secs') t = Quantity(time, "s") values = {} for key in self.keys(): v = Ska.Numpy.interpolate(self[key].value, self[key].times.value, [time], method='linear')[0] unit = get_units("model", key) values[key] = APQuantity(v, t, unit=unit, dtype=v.dtype) return values
def _parse_tracelogs(tbegin, tend, filenames, other_msids): filenames = ensure_list(filenames) if tbegin is not None: tbegin = get_time(tbegin) if tend is not None: tend = get_time(tend) msid_objs = [] for filename in filenames: # Figure out what kind of file this is f = open(filename, "r") line = f.readline() f.close() if line.startswith("TIME"): msids = MSIDs.from_tracelog(filename, tbegin=tbegin, tend=tend) elif line.startswith("#YEAR") or line.startswith("YEAR"): msids = MSIDs.from_mit_file(filename, tbegin=tbegin, tend=tend) else: raise RuntimeError("I cannot parse this file!") msid_objs.append(msids) if other_msids is not None: msid_objs.append(MSIDs.from_database(other_msids, tbegin, tend)) all_msids = CombinedMSIDs(msid_objs) return all_msids
def plot(self, fields, field2=None, lw=1.5, fontsize=18, color=None, color2='magenta', figsize=(10, 8), plot=None, tbegin=None, tend=None, annotations=None, ymin=None, ymax=None, ymin2=None, ymax2=None): """ Plot temperature and state data from a load review. Parameters ---------- fields : tuple of strings or list of tuples of strings A single field or list of fields to plot on the left y-axis. field2 : tuple of strings, optional A single field to plot on the right y-axis. Default: None lw : float, optional The width of the lines in the plots. Default: 1.5 px. fontsize : integer, optional The font size for the labels in the plot. Default: 18 pt. color : list of strings, optional The color for the lines plotted on the left y-axis. Default: ["blue", "red", "green", "black"] color2 : string, optional The color for the line plotted on the right y-axis. Default: "magenta" figsize : tuple of integers, optional The size of the plot in (width, height) in inches. Default: (10, 8) plot : :class:`~acispy.plots.DatePlot` or :class:`~acispy.plots.CustomDatePlot`, optional An existing DatePlot to add this plot to. Default: None, one will be created if not provided. tbegin : string, float, or DateTime object, optional The start time of the plot. Default is to plot from the beginning of the load. tend : string, float, or DateTime object, optional The end time of the plot. Default is to plot to the ending of the load. annotations : list of strings, optional Additional annotations to add to the plot. Available options are "cti_runs", "comms", "belts", "perigee", "sim_trans", and "apogee". Default: None ymin : float, optional Set the minimum value of the y-axis on the left side of the plot. ymax : float, optional Set the maximum value of the y-axis on the left side of the plot. ymin2 : float, optional Set the minimum value of the y-axis on the right side of the plot. ymax2 : float, optional Set the maximum value of the y-axis on the right side of the plot. """ dp = DatePlot(self.ds, fields, field2=field2, lw=lw, fontsize=fontsize, color=color, color2=color2, figsize=figsize, plot=plot) ylimits = dp.ax.get_ylim() if ymin is None: ymin = ylimits[0] if ymax is None: ymax = ylimits[1] dp.set_ylim(ymin, ymax) if field2 is not None: ylimits2 = dp.ax2.get_ylim() if ymin2 is None: ymin2 = ylimits2[0] if ymax2 is None: ymax2 = ylimits2[1] dp.set_ylim2(ymin2, ymax2) if tbegin is None: tbegin = self.first_time if tend is None: tend = self.last_time tbegin = get_time(tbegin, 'secs') tend = get_time(tend, 'secs') if annotations is not None: self._add_annotations(dp, annotations.copy(), tbegin, tend) dp.set_xlim(secs2date(tbegin), secs2date(tend)) return dp
def __init__(self, name, tstart, hours, T_init, pitch, ccd_count, vehicle_load=None, simpos=-99616.0, off_nom_roll=0.0, dh_heater=0, fep_count=None, clocking=1, q=None, instrument=None, model_spec=None, no_limit=False, no_earth_heat=False): if name in short_name_rev: name = short_name_rev[name] if name == "fptemp_11" and instrument is None: raise RuntimeError("Must specify either 'ACIS-I' or 'ACIS-S' in " "'instrument' if you want to test a focal plane " "temperature prediction!") if fep_count is None: fep_count = ccd_count if q is None and name == "fptemp_11": raise RuntimeError("Please supply an attitude quaternion for the focal plane model!") self.vehicle_load = vehicle_load self.no_limit = no_limit tstart = get_time(tstart) datestart = tstart tstart = DateTime(tstart).secs tstop = tstart+hours*3600.0+10012.0 datestop = secs2date(tstop) tend = tstop+0.5*(tstop-tstart) dateend = secs2date(tend) self.datestart = datestart self.datestop = datestop self.hours = hours self.tstart = Quantity(tstart, "s") self.tstop = Quantity(tstop, "s") self.dateend = dateend self.T_init = Quantity(T_init, "deg_C") self.instrument = instrument self.no_earth_heat = no_earth_heat if vehicle_load is None: states = {"ccd_count": np.array([ccd_count], dtype='int'), "fep_count": np.array([fep_count], dtype='int'), "clocking": np.array([clocking], dtype='int'), 'vid_board': np.array([ccd_count > 0], dtype='int'), "pitch": np.array([pitch]), "simpos": np.array([simpos]), "datestart": np.array([self.datestart]), "datestop": np.array([self.dateend]), "tstart": np.array([self.tstart.value]), "tstop": np.array([tend]), "hetg": np.array(["RETR"]), "letg": np.array(["RETR"]), "off_nom_roll": np.array([off_nom_roll]), "dh_heater": np.array([dh_heater], dtype='int')} # For the focal plane model we need a quaternion. if name == "fptemp_11": for i in range(4): states["q%d" % (i+1)] = np.array([q[i]]) else: mylog.info("Modeling a %d-chip observation concurrent with " % ccd_count + "the %s vehicle loads." % vehicle_load) states = dict((k, state.value) for (k, state) in States.from_load_page(vehicle_load).table.items()) ecs_run_idxs = states["tstart"] < tstop states["ccd_count"][ecs_run_idxs] = ccd_count states["fep_count"][ecs_run_idxs] = fep_count states["clocking"][ecs_run_idxs] = clocking states["vid_board"][ecs_run_idxs] = ccd_count > 0 super(SimulateSingleObs, self).__init__(name, datestart, dateend, states, T_init, model_spec=model_spec, get_msids=False, no_eclipse=True) mylog.info("Run Parameters") mylog.info("--------------") mylog.info("Start Datestring: %s" % datestart) mylog.info("Length of ECS run in hours: %s" % hours) mylog.info("Stop Datestring: %s" % datestop) mylog.info("Initial Temperature: %g degrees C" % T_init) mylog.info("CCD Count: %d" % ccd_count) mylog.info("FEP Count: %d" % fep_count) if vehicle_load is None: disp_pitch = pitch disp_roll = off_nom_roll else: pitches = states["pitch"][ecs_run_idxs] rolls = states["off_nom_roll"][ecs_run_idxs] disp_pitch = "Min: %g, Max: %g" % (pitches.min(), pitches.max()) disp_roll = "Min: %g, Max: %g" % (rolls.min(), rolls.max()) mylog.info("Pitch: %s" % disp_pitch) mylog.info("SIM Position: %g" % simpos) mylog.info("Off-nominal Roll: %s" % disp_roll) mylog.info("Detector Housing Heater: %s" % {0: "OFF", 1: "ON"}[dh_heater]) mylog.info("Model Result") mylog.info("------------") if self.name == "fptemp_11": limit = limits[self.name][instrument] margin = 0.0 else: limit = limits[self.name] margin = margins[self.name] if self.name in low_limits: self.low_limit = Quantity(low_limits[self.name], "deg_C") else: self.low_limit = None self.limit = Quantity(limit, "deg_C") self.margin = Quantity(margin, 'deg_C') self.limit_time = None self.limit_date = None self.duration = None self.violate = False if self.no_limit: return viols = self.mvals.value > self.limit.value if np.any(viols): idx = np.where(viols)[0][0] self.limit_time = self.times('model', self.name)[idx] self.limit_date = secs2date(self.limit_time) self.duration = Quantity((self.limit_time.value-tstart)*0.001, "ks") msg = "The limit of %g degrees C will be reached at %s, " % (self.limit.value, self.limit_date) msg += "after %g ksec." % self.duration.value mylog.info(msg) if self.limit_time < self.tstop: self.violate = True viol_time = "before" else: self.violate = False viol_time = "after" mylog.info("The limit is reached %s the end of the observation." % viol_time) else: mylog.info("The limit of %g degrees C is never reached." % self.limit.value) if self.violate: mylog.warning("This observation is NOT safe from a thermal perspective.") else: mylog.info("This observation is safe from a thermal perspective.")
def __init__(self, name, tstart, tstop, states=None, T_init=None, get_msids=True, dt=328.0, model_spec=None, mask_bad_times=False, ephem_file=None, evolve_method=None, rk4=None, tl_file=None, no_eclipse=False, compute_model=None): self.name = name.lower() self.sname = short_name[name] if self.sname in short_name_rev: self.model_check = importlib.import_module(f"{self.sname}_check") else: self.model_check = None self.model_spec = find_json(name, model_spec) self.ephem_file = ephem_file tstart = get_time(tstart) tstop = get_time(tstop) tstart_secs = DateTime(tstart).secs self.no_earth_heat = getattr(self, "no_earth_heat", False) if states is not None: if isinstance(states, States): states_obj = states states = states.as_array() else: if "tstart" not in states: states["tstart"] = DateTime(states["datestart"]).secs if "tstop" not in states: states["tstop"] = DateTime(states["datestop"]).secs num_states = states["tstart"].size if "letg" not in states: states["letg"] = np.array(["RETR"]*num_states) if "hetg" not in states: states["hetg"] = np.array(["RETR"]*num_states) states_obj = States(states) else: states_obj = EmptyTimeSeries() if T_init is None: T_init = fetch.MSID(self.name, tstart_secs-700., tstart_secs+700.).vals.mean() if compute_model is not None: self.xija_model = compute_model(self.name, tstart, tstop, states, dt, T_init, model_spec, evolve_method, rk4) elif self.name in short_name and states is not None: self.xija_model = self._compute_acis_model(self.name, tstart, tstop, states, dt, T_init, rk4=rk4, no_eclipse=no_eclipse, evolve_method=evolve_method) else: self.xija_model = self._compute_model(name, tstart, tstop, dt, T_init, evolve_method=evolve_method, rk4=rk4) self.bad_times = getattr(self.xija_model, "bad_times", None) self.bad_times_indices = getattr(self.xija_model, "bad_times_indices", None) if isinstance(states, dict): states.pop("dh_heater", None) components = [self.name] if 'dpa_power' in self.xija_model.comp: components.append('dpa_power') if 'earthheat__fptemp' in self.xija_model.comp: components.append('earthheat__fptemp') if states is None: components += ["pitch", "roll", "fep_count", "vid_board", "clocking", "ccd_count", "sim_z"] masks = {} if mask_bad_times and self.bad_times is not None: masks[self.name] = np.ones(self.xija_model.times.shape, dtype='bool') for (left, right) in self.bad_times_indices: masks[self.name][left:right] = False model_obj = Model.from_xija(self.xija_model, components, masks=masks) if get_msids: msids_obj = self._get_msids(model_obj, [self.name], tl_file) else: msids_obj = EmptyTimeSeries() super(ThermalModelRunner, self).__init__(msids_obj, states_obj, model_obj)