diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..80504117 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +filterwarnings = + # See https://github.com/numpy/numpy/issues/11788 for why this is benign + ignore:numpy.ufunc size changed:RuntimeWarning + ignore:the imp module is deprecated in favour of importlib:DeprecationWarning + ignore:parse functions are required to provide a named argument:PendingDeprecationWarning +norecursedirs = .* alt_core build ctypes_example dist doc examples GUI_play ipynb models* nmass* pyside tests_no_pytest tmp twodof www diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 755eff61..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -norecursedirs = .* alt_core build ctypes_example dist doc examples GUI_play ipynb models* nmass* pyside tests_no_pytest tmp twodof www diff --git a/xija/component/experimental.py b/xija/component/experimental.py index 60a9badb..7f875bae 100644 --- a/xija/component/experimental.py +++ b/xija/component/experimental.py @@ -95,7 +95,7 @@ class SolarHeatSimZ(SolarHeat): """Solar heating (pitch and SimZ dependent)""" def __init__(self, model, node, simz_comp, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001', + tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001:12:00:00', hrci_bias=0.0, hrcs_bias=0.0, acisi_bias=0.0): SolarHeat.__init__(self, model, node, pitch_comp, eclipse_comp, P_pitches, Ps, dPs, var_func, tau, ampl, bias, diff --git a/xija/component/heat.py b/xija/component/heat.py index 268363ec..3c679311 100644 --- a/xija/component/heat.py +++ b/xija/component/heat.py @@ -117,7 +117,7 @@ class SolarHeat(PrecomputedHeatPower): """ def __init__(self, model, node, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001'): + tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001:12:00:00'): ModelComponent.__init__(self, model) self.node = self.model.get_comp(node) self.pitch_comp = self.model.get_comp(pitch_comp) @@ -283,10 +283,10 @@ def plot_solar_heat__pitch(self, fig, ax): class SolarHeatMulplicative(SolarHeat): def __init__(self, model, node, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.0334, bias=0.0, epoch='2010:001'): + tau=1732.0, ampl=0.0334, bias=0.0, epoch='2010:001:12:00:00'): super(SolarHeatMulplicative, self).__init__( model, node, pitch_comp, eclipse_comp=eclipse_comp, - P_pitches=P_pitches, Ps=Ps, dPs=dPs, var_func=var_func, + P_pitches=P_pitches, Ps=Ps, dPs=dPs, var_func=var_func, tau=tau, ampl=ampl, bias=bias, epoch=epoch) def _compute_dvals(self): @@ -316,7 +316,7 @@ class SolarHeatAcisCameraBody(SolarHeat): """ def __init__(self, model, node, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001', + tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001:12:00:00', dh_heater_comp=None, dh_heater_bias=0.0): super(SolarHeatAcisCameraBody, self).__init__( @@ -353,7 +353,7 @@ class SolarHeatHrc(SolarHeat): """ def __init__(self, model, node, simz_comp, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001', + tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001:12:00:00', hrc_bias=0.0): SolarHeat.__init__(self, model, node, pitch_comp, eclipse_comp, P_pitches, Ps, dPs, var_func, tau, ampl, bias, @@ -391,7 +391,7 @@ class SolarHeatHrcOpts(SolarHeat): """ def __init__(self, model, node, simz_comp, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001', + tau=1732.0, ampl=0.05, bias=0.0, epoch='2010:001:12:00:00', hrci_bias=0.0, hrcs_bias=0.0): SolarHeat.__init__(self, model, node, pitch_comp, eclipse_comp, P_pitches, Ps, dPs, var_func, tau, ampl, bias, @@ -415,12 +415,12 @@ def dvals_post_hook(self): class SolarHeatHrcMult(SolarHeatHrcOpts, SolarHeatMulplicative): def __init__(self, model, node, simz_comp, pitch_comp, eclipse_comp=None, P_pitches=None, Ps=None, dPs=None, var_func='exp', - tau=1732.0, ampl=0.0334, bias=0.0, epoch='2010:001', + tau=1732.0, ampl=0.0334, bias=0.0, epoch='2010:001:12:00:00', hrci_bias=0.0, hrcs_bias=0.0): super(SolarHeatHrcMult, self).__init__( model, node, simz_comp, pitch_comp, eclipse_comp=eclipse_comp, P_pitches=P_pitches, Ps=Ps, dPs=dPs, var_func=var_func, tau=tau, - ampl=ampl, bias=bias, epoch=epoch, hrci_bias=hrci_bias, + ampl=ampl, bias=bias, epoch=epoch, hrci_bias=hrci_bias, hrcs_bias=hrcs_bias) # For back compatibility prior to Xija 0.2 @@ -506,7 +506,10 @@ def dvals(self): for x in ('x', 'y', 'z')] aoattqt_1234s = [getattr(self, 'aoattqt{}'.format(x)) for x in range(1, 5)] - ephems = np.array([x.dvals for x in ephem_xyzs]).transpose() + # Note: the copy() here is so that the array becomes contiguous in + # memory and allows numba to run faster (and avoids NumbaPerformanceWarning: + # np.dot() is faster on contiguous arrays). + ephems = np.array([x.dvals for x in ephem_xyzs]).transpose().copy() q_atts = np.array([x.dvals for x in aoattqt_1234s]).transpose() # Q_atts can have occasional bad values, maybe because the @@ -608,7 +611,7 @@ class AcisPsmcSolarHeat(PrecomputedHeatPower): """Solar heating of PSMC box. This is dependent on SIM-Z""" def __init__(self, model, node, pitch_comp, simz_comp, dh_heater_comp, P_pitches=None, P_vals=None, dPs=None, var_func='linear', - tau=1732.0, ampl=0.05, epoch='2013:001', dh_heater=0.05): + tau=1732.0, ampl=0.05, epoch='2013:001:12:00:00', dh_heater=0.05): ModelComponent.__init__(self, model) self.n_mvals = 1 self.node = self.model.get_comp(node) @@ -833,7 +836,7 @@ class AcisDpaStatePower(PrecomputedHeatPower): state. See dpa/NOTES.power. """ def __init__(self, model, node, mult=1.0, - fep_count=None, ccd_count=None, + fep_count=None, ccd_count=None, vid_board=None, clocking=None, pow_states=None): super(AcisDpaStatePower, self).__init__(model) @@ -927,7 +930,7 @@ def plot_data__time(self, fig, ax): else: plot_cxctime(self.model.times, powers, ls='-', color='#d92121', fig=fig, ax=ax) - plot_cxctime(self.model.times, self.dvals, + plot_cxctime(self.model.times, self.dvals, color='#386cb0', ls='-', fig=fig, ax=ax) ax.grid() ax.set_title('{}: model (red) and data (blue)'.format(self.name)) diff --git a/xija/model.py b/xija/model.py index 2384e5d3..00dc03b4 100644 --- a/xija/model.py +++ b/xija/model.py @@ -18,15 +18,11 @@ from . import component from . import tmal -try: - # Optional packages for model fitting or use on HEAD LAN - from Chandra.Time import DateTime, date2secs - from astropy.io import ascii - import Ska.Numpy - import Chandra.cmd_states - import Ska.DBI -except ImportError: - pass +# Optional packages for model fitting or use on HEAD LAN +from Chandra.Time import DateTime, date2secs +from astropy.io import ascii +import Ska.Numpy +import Ska.DBI import pyyaks.context as pyc from .files import files as xija_files @@ -73,9 +69,9 @@ class XijaModel(object): - ``evolve_method = 1`` uses the original ODE solver which treats every two steps as a full RK2 step. - ``evolve_method = 2`` uses the new ODE solver which treats every step - as a full RK2 step, and optionally allows for RK4 if ``rk4 = 1``. + as a full RK2 step, and optionally allows for RK4 if ``rk4 = 1``. - Otherwise defaults are: ``name='xijamodel'``, ``start = stop - 45 days``, - ``stop = NOW - 30 days``, ``dt = 328 secs``, ``evolve_method = 1``, + ``stop = NOW - 30 days``, ``dt = 328 secs``, ``evolve_method = 1``, ``rk4 = 0`` :param name: model name @@ -158,7 +154,7 @@ def _get_allowed_timestep(self, dt): """ This method ensures that only certain timesteps are chosen, which are integer multiples of 8.2 and where 328.0/dt is an - integer. + integer. """ if dt > DEFAULT_DT: logger.warning("dt = %g s greater than upper " @@ -218,23 +214,11 @@ def _eng_match_times(self, start, stop): def _get_cmd_states(self): if not hasattr(self, '_cmd_states'): - logger.info('Reading commanded states DB over %s to %s' % + import kadi.commands.states as kadi_states + logger.info('Getting kadi commanded states over %s to %s' % (self.datestart, self.datestop)) - for dbi in ('hdf5', 'sybase'): - try: - states = Chandra.cmd_states.fetch_states( - self.datestart, self.datestop, dbi=dbi) - break - except IOError as err: - logger.info('Warning: ' + str(err)) - pass - else: - # Both hdf5 and sybase failed - raise IOError('Could not read commanded states from ' - 'HDF5 or sybase tables') - - self._cmd_states = Chandra.cmd_states.interpolate_states( - states, self.times) + states = kadi_states.get_states(self.datestart, self.datestop) + self._cmd_states = kadi_states.interpolate_states(states, self.times).as_array() return self._cmd_states @@ -591,7 +575,7 @@ def core_2(self): _core_2 = np.ctypeslib.load_library('core_2', loader_path) _core_2.calc_model_2.restype = ctypes.c_int _core_2.calc_model_2.argtypes = [ - ctypes.c_int, ctypes.c_int, ctypes.c_int, + ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), ctypes.POINTER(ctypes.POINTER(ctypes.c_int)), diff --git a/xija/tests/dpa_real.npz b/xija/tests/dpa_real.npz index a796f173..05f65484 100644 Binary files a/xija/tests/dpa_real.npz and b/xija/tests/dpa_real.npz differ diff --git a/xija/tests/dpa_remove_pow.npz b/xija/tests/dpa_remove_pow.npz index 23e3ceaf..cfc4f643 100644 Binary files a/xija/tests/dpa_remove_pow.npz and b/xija/tests/dpa_remove_pow.npz differ diff --git a/xija/tests/test_models.py b/xija/tests/test_models.py index 23e35540..b83f9a62 100644 --- a/xija/tests/test_models.py +++ b/xija/tests/test_models.py @@ -26,15 +26,9 @@ def abs_path(spec): def test_dpa_real(): - mdl = ThermalModel('dpa', start='2012:001', stop='2012:007', + mdl = ThermalModel('dpa', start='2020:001:12:00:00', stop='2020:007:12:00:00', model_spec=abs_path('dpa.json')) - # Check that cmd_states database can be read. Skip if not, probably - # running test on a platform without access. - try: - mdl._get_cmd_states() - except: - pytest.skip('No commanded states access - ' - 'cannot run DPA model with real states') + mdl._get_cmd_states() mdl.make() mdl.calc() @@ -57,13 +51,9 @@ def test_pitch_clip(): has been modified so the solarheat pitch range is from 55 .. 153. Make sure the model still runs with no interpolation error. """ - mdl = ThermalModel('dpa', start='2012:001', stop='2012:007', + mdl = ThermalModel('dpa', start='2012:001:12:00:00', stop='2012:007:12:00:00', model_spec=abs_path('dpa_clip.json')) - try: - mdl._get_cmd_states() - except: - pytest.skip('No commanded states access - ' - 'cannot run DPA model with real states') + mdl._get_cmd_states() mdl.make() mdl.calc() @@ -78,7 +68,7 @@ def test_pitch_range_clip(): Make sure the pitch range stored does not include values outside of the 45 to 180 degree range. """ - mdl = ThermalModel('tank', start='2019:120', stop='2019:122', + mdl = ThermalModel('tank', start='2019:120:12:00:00', stop='2019:122:12:00:00', model_spec=abs_path('pftank2t.json')) mdl.comp['pf0tank2t'].set_data(20.0) @@ -91,15 +81,9 @@ def test_pitch_range_clip(): def test_dpa_remove_pow(): - mdl = ThermalModel('dpa', start='2012:001', stop='2012:007', + mdl = ThermalModel('dpa', start='2019:001:12:00:00', stop='2019:007:12:00:00', model_spec=abs_path('dpa_remove_pow.json')) - # Check that cmd_states database can be read. Skip if not, probably - # running test on a platform without access. - try: - mdl._get_cmd_states() - except: - pytest.skip('No commanded states access - ' - 'cannot run DPA model with real states') + mdl._get_cmd_states() mdl.make() mdl.calc() @@ -116,7 +100,7 @@ def test_dpa_remove_pow(): assert np.allclose(dpa.mvals, regr['mvals']) def get_dpa_model(): - mdl = ThermalModel('dpa', start='2012:001', stop='2012:007', + mdl = ThermalModel('dpa', start='2012:001:12:00:00', stop='2012:007:12:00:00', model_spec=abs_path('dpa.json')) times = (mdl.times - mdl.times[0]) / 10000.0 mdl.comp['1dpamzt'].set_data(30.0) @@ -148,7 +132,7 @@ def test_dpa(): mvals=dpa.mvals) regr = np.load(reffile) - assert np.allclose(mdl.times, regr['times']) + assert np.allclose(mdl.times, regr['times'], rtol=0, atol=1e-3) assert np.allclose(dpa.dvals, regr['dvals']) assert np.allclose(dpa.mvals, regr['mvals']) @@ -162,7 +146,7 @@ def test_plotdate(): def test_data_types(): for data_type in (int, float, np.float32, np.float64, np.int32, np.int64, np.complex64): - mdl = ThermalModel('dpa', start='2012:001', stop='2012:007', + mdl = ThermalModel('dpa', start='2012:001:12:00:00', stop='2012:007:12:00:00', model_spec=abs_path('dpa.json')) dpa = mdl.comp['1dpamzt'] dpa.set_data(data_type(30.0)) @@ -174,7 +158,7 @@ def test_data_types(): def test_minusz(): - mdl = ThermalModel('minusz', start='2012:001', stop='2012:004', + mdl = ThermalModel('minusz', start='2012:001:12:00:00', stop='2012:004:12:00:00', model_spec=abs_path('minusz.json')) times = (mdl.times - mdl.times[0]) / 10000.0 msids = ('tephin', 'tcylaft6', 'tcylfmzm', 'tmzp_my', 'tfssbkt1') @@ -199,7 +183,7 @@ def test_minusz(): def test_pftank2t(): - mdl = ThermalModel('pftank2t', start='2012:001', stop='2012:004', + mdl = ThermalModel('pftank2t', start='2012:001:12:00:00', stop='2012:004:12:00:00', model_spec=abs_path('pftank2t.json')) times = (mdl.times - mdl.times[0]) / 10000.0 msids = ('pftank2t', 'pf0tank2t') @@ -237,7 +221,7 @@ def test_multi_solar_heat_values(): P_pitches2 = [45, 65, 90, 140, 180] P_vals2 = [1.0, 1.0, 0.0, 1.0, 1.0] - model = ThermalModel('test', start='2011:001', stop='2011:005') + model = ThermalModel('test', start='2011:001:12:00:00', stop='2011:005:12:00:00') tephin = model.add(Node, 'tephin') tcylaft6 = model.add(Node, 'tcylaft6') pitch = model.add(Pitch) @@ -273,7 +257,7 @@ def test_multi_solar_heat_values(): temp_name = tempfile.NamedTemporaryFile(delete=False).name model.write(temp_name) model2 = ThermalModel('test', model_spec=temp_name, - start='2011:001', stop='2011:005') + start='2011:001:12:00:00', stop='2011:005:12:00:00') os.unlink(temp_name) model2.get_comp('tephin').set_data(30.0) model2.get_comp('tcylaft6').set_data(30.0)