def _update_energy(self, chunk): """Create SpectralWCS information using FITS headers, if available. If the WLEN and BANDPASS keyword values are set to the defaults, there is no energy information.""" self._logger.debug('Begin _update_energy') mc.check_param(chunk, Chunk) wlen = self._headers[0].get('WLEN') bandpass = self._headers[0].get('BANDPASS') if wlen is None or wlen < 0 or bandpass is None or bandpass < 0: chunk.energy = None chunk.energy_axis = None self._logger.debug( f'Setting chunk energy to None because WLEN {wlen} and ' f'BANDPASS {bandpass}' ) else: naxis = CoordAxis1D(Axis('WAVE', 'um')) start_ref_coord = RefCoord(0.5, self.get_start_ref_coord_val(0)) end_ref_coord = RefCoord(1.5, self.get_end_ref_coord_val(0)) naxis.range = CoordRange1D(start_ref_coord, end_ref_coord) chunk.energy = SpectralWCS( naxis, specsys='TOPOCENT', ssysobs='TOPOCENT', ssyssrc='TOPOCENT', bandpass_name=self._headers[0].get('FILTER'), ) chunk.energy_axis = None self._logger.debug('Setting chunk energy range (CoordRange1D).')
def build_chunk_energy_range(chunk, filter_name, filter_md): """ Set a range axis for chunk energy using central wavelength and FWHM. Units are Angstroms. Axis is set to 4. :param chunk: Chunk to add a CoordRange1D Axis to :param filter_name: string to set to bandpassName :param filter_md: dict with a 'cw' and 'fwhm' value """ # If n_axis=1 (as I guess it will be for all but processes GRACES # spectra now?) that means crpix=0.5 and the corresponding crval would # central_wl - bandpass/2.0 (i.e. the minimum wavelength). It is fine # if you instead change crpix to 1.0. I guess since the ‘range’ of # one pixel is 0.5 to 1.5. cw = ac.FilterMetadataCache.get_central_wavelength(filter_md) fwhm = ac.FilterMetadataCache.get_fwhm(filter_md) if cw is not None and fwhm is not None: resolving_power = ac.FilterMetadataCache.get_resolving_power(filter_md) axis = CoordAxis1D(axis=Axis(ctype='WAVE', cunit='Angstrom')) ref_coord1 = RefCoord(0.5, cw - fwhm / 2.0) ref_coord2 = RefCoord(1.5, cw + fwhm / 2.0) axis.range = CoordRange1D(ref_coord1, ref_coord2) energy = SpectralWCS(axis=axis, specsys='TOPOCENT', ssyssrc=None, ssysobs=None, bandpass_name=filter_name, resolving_power=resolving_power) chunk.energy = energy
def _build_chunk_energy(chunk, headers): # DB 18-09-19 # NEOSSat folks wanted the min/max wavelengths in the BANDPASS keyword to # be used as the upper/lower wavelengths. BANDPASS = ‘4000,9000’ so # ref_coord1 = RefCoord(0.5, 4000) and ref_coord2 = RefCoord(1.5, 9000). # The WAVELENG value is not used for anything since they opted to do it # this way. They interpret WAVELENG as being the wavelengh of peak # throughput of the system I think. min_wl, max_wl = _get_energy(headers[0]) axis = CoordAxis1D(axis=Axis(ctype='WAVE', cunit='um')) if min_wl is not None and max_wl is not None: ref_coord1 = RefCoord(0.5, min_wl) ref_coord2 = RefCoord(1.5, max_wl) axis.range = CoordRange1D(ref_coord1, ref_coord2) # DB 24-09-19 # If FILTER not in header, set filter_name = ‘CLEAR’ filter_name = headers[0].get('FILTER', 'CLEAR') # DB 24-09-19 # if wavelength IS None, wl = 0.6 microns, and resolving_power is # always determined. resolving_power = None wavelength = headers[0].get('WAVELENG', 6000) wl = wavelength / 1e4 # everything in microns resolving_power = wl / (max_wl - min_wl) energy = SpectralWCS(axis=axis, specsys='TOPOCENT', ssyssrc='TOPOCENT', ssysobs='TOPOCENT', bandpass_name=filter_name, resolving_power=resolving_power) chunk.energy = energy
def _build_chunk_position(chunk, headers, obs_id): # DB 18-08-19 # Ignoring rotation for now: Use CRVAL1 = RA from header, CRVAL2 = DEC # from header. NAXIS1/NAXIS2 values gives number of pixels along RA/DEC # axes (again, ignoring rotation) and assume CRPIX1 = NAXIS1/2.0 and # CRPIX2 = NAXIS2/2.0 (i.e. in centre of image). XBINNING/YBINNING # give binning values along RA/DEC axes. CDELT1 (scale in # degrees/pixel; it’s 3 arcsec/unbinned pixel)= 3.0*XBINNING/3600.0 # CDELT2 = 3.0*YBINNING/3600.0. Set CROTA2=0.0 header = headers[0] ra = get_ra(header) dec = get_dec(header) if ra is None or dec is None: logging.warning(f'No position information for {obs_id}') chunk.naxis = None else: header['CTYPE1'] = 'RA---TAN' header['CTYPE2'] = 'DEC--TAN' header['CUNIT1'] = 'deg' header['CUNIT2'] = 'deg' header['CRVAL1'] = ra header['CRVAL2'] = dec header['CRPIX1'] = get_position_axis_function_naxis1(header) header['CRPIX2'] = get_position_axis_function_naxis2(header) wcs_parser = WcsParser(header, obs_id, 0) if chunk is None: chunk = Chunk() wcs_parser.augment_position(chunk) x_binning = header.get('XBINNING') if x_binning is None: x_binning = 1.0 cdelt1 = 3.0 * x_binning / 3600.0 y_binning = header.get('YBINNING') if y_binning is None: y_binning = 1.0 cdelt2 = 3.0 * y_binning / 3600.0 objct_rol = header.get('OBJCTROL') if objct_rol is None: objct_rol = 0.0 crota2 = 90.0 - objct_rol crota2_rad = math.radians(crota2) cd11 = cdelt1 * math.cos(crota2_rad) cd12 = abs(cdelt2) * _sign(cdelt1) * math.sin(crota2_rad) cd21 = -abs(cdelt1) * _sign(cdelt2) * math.sin(crota2_rad) cd22 = cdelt2 * math.cos(crota2_rad) dimension = Dimension2D(header.get('NAXIS1'), header.get('NAXIS2')) x = RefCoord(get_position_axis_function_naxis1(header), get_ra(header)) y = RefCoord(get_position_axis_function_naxis2(header), get_dec(header)) ref_coord = Coord2D(x, y) function = CoordFunction2D(dimension, ref_coord, cd11, cd12, cd21, cd22) chunk.position.axis.function = function chunk.position_axis_1 = 1 chunk.position_axis_2 = 2
def build_chunk_energy_bounds(wave, axis): import numpy as np # limit the effect on container content # caom2IngestEspadons.py, l698 x = np.arange(1, wave.size + 1, dtype='float32') wavegrade = np.gradient(wave) waveinflect = wave[np.where(abs(wavegrade) > 0.01)] xinflect = x[np.where(abs(wavegrade) > 0.01)] # add start and finish pixels onto waveinflect and xinflect and these are # our list of sub-bounds. allxinflect = np.append(x[0], xinflect) allxinflect = np.append(allxinflect, x[-1]) allwaveinflect = np.append(wave[0], waveinflect) allwaveinflect = np.append(allwaveinflect, wave[-1]) numwaveschunk = int(len(allxinflect) / 2.0) bounds = CoordBounds1D() for jj in range(numwaveschunk): indexlo = jj * 2 + 0 indexhi = indexlo + 1 x1 = float(allxinflect[indexlo]) x2 = float(allxinflect[indexhi]) w1 = float(allwaveinflect[indexlo]) w2 = float(allwaveinflect[indexhi]) coord_range = CoordRange1D(RefCoord(x1, w1), RefCoord(x2, w2)) bounds.samples.append(coord_range) return bounds
def _update_time(chunk, headers): """Create TemporalWCS information using FITS header information. This information should always be available from the file.""" logging.debug('Begin _update_time.') mc.check_param(chunk, Chunk) mjd_start = headers[0].get('MJD_STAR') mjd_end = headers[0].get('MJD_END') if mjd_start is None or mjd_end is None: mjd_start, mjd_end = ac.find_time_bounds(headers) if mjd_start is None or mjd_end is None: chunk.time = None logging.debug('Cannot calculate mjd_start {} or mjd_end {}'.format( mjd_start, mjd_end)) else: logging.debug('Calculating range with start {} and end {}.'.format( mjd_start, mjd_start)) start = RefCoord(0.5, mjd_start) end = RefCoord(1.5, mjd_end) time_cf = CoordFunction1D(1, headers[0].get('TEFF'), start) time_axis = CoordAxis1D(Axis('TIME', 'd'), function=time_cf) time_axis.range = CoordRange1D(start, end) chunk.time = TemporalWCS(time_axis) chunk.time.exposure = headers[0].get('TEFF') chunk.time.resolution = 0.1 chunk.time.timesys = 'UTC' chunk.time.trefpos = 'TOPOCENTER' chunk.time_axis = 4 logging.debug('Done _update_time.')
def bad_range_wcs(): ctype = "RM" unit = "rad/m^2" error = None start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(1.1)) range = CoordRange1D(start, end) axis_1d = CoordAxis1D(wcs.Axis(ctype, unit), error, range) return chunk.CustomWCS(axis_1d)
def get_test_function_with_range(ctype, unit, px, sx, nx, ds): error = None start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(11.1)) range = CoordRange1D(start, end) axis_1d = CoordAxis1D(wcs.Axis(ctype, unit), error, range) ref_coord = RefCoord(px, sx) axis_1d.function = CoordFunction1D(nx, ds, ref_coord) custom_wcs = chunk.CustomWCS(axis_1d) return custom_wcs
def test_range(self): # Polarization range is None, should not produce an error axis = Axis("STOKES", "cunit") axis_1d = CoordAxis1D(axis) polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis range contains valid positive values start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(9.9), float(10.1)) p_range = CoordRange1D(start, end) axis_1d.range = p_range polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis range contains valid negative values start = RefCoord(float(-8.1), float(-7.9)) end = RefCoord(float(-1.1), float(-0.9)) n_range = CoordRange1D(start, end) axis_1d.range = n_range polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis range contains invalid positive values start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(11.1)) p_range = CoordRange1D(start, end) axis_1d.range = p_range polarization = PolarizationWCS(axis_1d) with pytest.raises(InvalidWCSError) as ex: wcsvalidator._validate_polarization_wcs(polarization) assert ('Invalid Polarization WCS' in str(ex.value)) assert ('11' in str(ex.value)) # Polarization axis range contains invalid negative values start = RefCoord(float(-9.1), float(-8.9)) end = RefCoord(float(-1.1), float(-0.9)) n_range = CoordRange1D(start, end) axis_1d.range = n_range polarization = PolarizationWCS(axis_1d) with pytest.raises(InvalidWCSError) as ex: wcsvalidator._validate_polarization_wcs(polarization) assert ('Invalid Polarization WCS' in str(ex.value)) assert ('-9' in str(ex.value)) # Polarization axis range contains an invalid value (0) within a range start = RefCoord(float(-8.1), float(-7.9)) end = RefCoord(float(9.9), float(10.1)) range = CoordRange1D(start, end) axis_1d.range = range polarization = PolarizationWCS(axis_1d) with pytest.raises(InvalidWCSError) as ex: wcsvalidator._validate_polarization_wcs(polarization) assert ('Invalid Polarization WCS' in str(ex.value)) assert ('0' in str(ex.value))
def bad_bounds_wcs(): ctype = "RM" unit = "rad/m^2" error = None range = None c1 = RefCoord(float(0.9), float(1.1)) c2 = RefCoord(float(10.9), float(1.1)) bounds = CoordBounds1D() bounds.samples.append(CoordRange1D(c1, c2)) axis_1d = CoordAxis1D(wcs.Axis(ctype, unit), error, range, bounds) return chunk.CustomWCS(axis_1d)
def _build_time(row): bounds = CoordBounds1D() start_date = ac.get_datetime(row[3].strip()) end_date = ac.get_datetime(row[4].strip()) start_date.format = 'mjd' end_date.format = 'mjd' exposure = float(ac.get_timedelta_in_s(row[5].strip())) start_ref_coord = RefCoord(0.5, start_date.value) end_ref_coord = RefCoord(1.5, end_date.value) bounds.samples.append(CoordRange1D(start_ref_coord, end_ref_coord)) return bounds, exposure
def _update_time_bounds(self, observation, storage_name): """Add chunk time bounds to the chunk from the first part, by referencing information from the second header.""" lower_values = '' upper_values = '' with fits.open(storage_name.sources_names[0]) as fits_data: xtension = fits_data[1].header['XTENSION'] extname = fits_data[1].header['EXTNAME'] if 'BINTABLE' in xtension and 'PROVENANCE' in extname: for ii in fits_data[1].data[0]['STARTTIME']: lower_values = f'{ii} {lower_values}' for ii in fits_data[1].data[0]['DURATION']: upper_values = f'{ii} {upper_values} ' else: raise mc.CadcException( f'Opened a composite file that does not match the ' f'expected profile ' f'(XTENSION=BINTABLE/EXTNAME=PROVENANCE). ' f'{xtension} {extname}' ) for plane in observation.planes: for artifact in observation.planes[plane].artifacts: parts = observation.planes[plane].artifacts[artifact].parts for p in parts: if p == '0': lower = lower_values.split() upper = upper_values.split() if len(lower) != len(upper): raise mc.CadcException( 'Cannot make RefCoords with inconsistent ' 'values.' ) chunk = parts[p].chunks[0] bounds = CoordBounds1D() chunk.time.axis.bounds = bounds for ii in range(len(lower)): mjd_start, mjd_end = ac.convert_time( mc.to_float(lower[ii]), mc.to_float(upper[ii]) ) lower_refcoord = RefCoord(0.5, mjd_start) upper_refcoord = RefCoord(1.5, mjd_end) r = CoordRange1D(lower_refcoord, upper_refcoord) bounds.samples.append(r) # if execution has gotten to this point, remove range # if it exists, since only one of bounds or range # should be provided, and bounds is more specific. PD, # slack, 2018-07-16 if chunk.time.axis.range is not None: chunk.time.axis.range = None
def _build_time(start, end, tos): bounds = CoordBounds1D() if start is not None and end is not None: start_date = ac.get_datetime(start) start_date.format = 'mjd' end_date = ac.get_datetime(end) end_date.format = 'mjd' start_ref_coord = RefCoord(0.5, start_date.value) end_ref_coord = RefCoord(1.5, end_date.value) bounds.samples.append(CoordRange1D(start_ref_coord, end_ref_coord)) exposure = None if tos is not None: exposure = float(ac.get_timedelta_in_s(tos)) return bounds, exposure
def build_temporal_wcs_append_sample(temporal_wcs, lower, upper): """All the CAOM entities for building a TemporalWCS instance with a bounds definition, or appending a sample, in one function. """ if temporal_wcs is None: samples = TypedList(CoordRange1D, ) bounds = CoordBounds1D(samples=samples) temporal_wcs = TemporalWCS(axis=CoordAxis1D(axis=Axis('TIME', 'd'), bounds=bounds), timesys='UTC') start_ref_coord = RefCoord(pix=0.5, val=lower) end_ref_coord = RefCoord(pix=1.5, val=upper) sample = CoordRange1D(start_ref_coord, end_ref_coord) temporal_wcs.axis.bounds.samples.append(sample) return temporal_wcs
def build_energy(): # units are nm min = 400 max = 800 central_wl = (min + max) / 2.0 # = 1200.0 / 2.0 == 600.0 nm fwhm = (max - min) ref_coord1 = RefCoord(0.5, central_wl - fwhm / 2.0) # == 100.0 nm ref_coord2 = RefCoord(1.5, central_wl + fwhm / 2.0) # == 500.0 nm axis = CoordAxis1D(axis=Axis(ctype='WAVE', cunit='nm')) axis.range = CoordRange1D(ref_coord1, ref_coord2) energy = SpectralWCS(axis=axis, specsys='TOPOCENT', ssyssrc='TOPOCENT', ssysobs='TOPOCENT', bandpass_name='CLEAR') return energy
def build_chunk_time(chunk, header, name): """ :param chunk: CAOM2 Chunk instance for which to set time. :param header: FITS header with the keywords for value extraction. :param name: str for logging information only. :return: """ logging.debug(f'Begin build_chunk_time for {name}.') # DB 02-07-20 # time metadata comes from MJD_OBS and EXPTIME, it's not # an axis requiring cutout support exp_time = header.get('EXPTIME') mjd_obs = header.get('MJD-OBS') if exp_time is None or mjd_obs is None: chunk.time = None else: if chunk.time is None: coord_error = CoordError(syser=1e-07, rnder=1e-07) time_axis = CoordAxis1D(axis=Axis('TIME', 'd'), error=coord_error) chunk.time = TemporalWCS(axis=time_axis, timesys='UTC') ref_coord = RefCoord(pix=0.5, val=mjd_obs) chunk.time.axis.function = CoordFunction1D( naxis=1, delta=mc.convert_to_days(exp_time), ref_coord=ref_coord) chunk.time.exposure = exp_time chunk.time.resolution = mc.convert_to_days(exp_time) logging.debug(f'End build_chunk_time.')
def bad_delta(): axis_1d = CoordAxis1D(wcs.Axis("RM", "rad/m**2")) # delta < 0.0 is bad ref_coord = RefCoord(float(1.0), float(2.0)) axis_1d.function = CoordFunction1D(int(100), -0.01, ref_coord) return chunk.CustomWCS(axis_1d)
def _update_energy(chunk, header, filter_name, obs_id): logging.debug(f'Begin _update_energy for {obs_id}.') # because the type for the axes are 'LINEAR', which isn't an energy type, # so can't use the WcsParser from caom2utils. disp_axis = header.get('DISPAXIS') naxis = header.get('NAXIS') if disp_axis is not None and naxis is not None and disp_axis <= naxis: axis = Axis(ctype='WAVE', cunit='Angstrom') coord_axis_1d = CoordAxis1D(axis) ref_coord = RefCoord( pix=header.get(f'CRPIX{disp_axis}'), val=header.get(f'CRVAL{disp_axis}'), ) fn = CoordFunction1D( naxis=header.get(f'NAXIS{disp_axis}'), delta=header.get(f'CD{disp_axis}_{disp_axis}'), ref_coord=ref_coord, ) coord_axis_1d.function = fn energy = SpectralWCS(axis=coord_axis_1d, specsys='TOPOCENT') energy.bandpass_name = filter_name # DB 07-08-20 # I think the best we can do is assume that a resolution element is 2 # pixels wide. So resolving power is the absolute value of # approximately CRVAL3/(2 * CD3_3) energy.resolving_power = abs( header.get(f'CRVAL{disp_axis}') / (2 * header.get(f'CD{disp_axis}_{disp_axis}'))) chunk.energy = energy chunk.energy_axis = disp_axis logging.debug('End _update_energy.')
def bad_function_wcs(): ctype = "RM" unit = "rad/m^2" error = None range = None c1 = RefCoord(float(0.9), float(1.1)) c2 = RefCoord(float(10.9), float(1.1)) bounds = CoordBounds1D() bounds.samples.append(CoordRange1D(c1, c2)) naxis = 1 delta = 0.0 ref_coord = RefCoord(float(0.9), float(1.1)) func = CoordFunction1D(naxis, delta, ref_coord) axis_1d = CoordAxis1D(wcs.Axis(ctype, unit), error, range, bounds, func) return chunk.CustomWCS(axis_1d)
def bad_range_wcs(): px = float(0.5) sx = float(54321.0) nx = 200 ds = float(0.01) axis_1d = wcs.CoordAxis1D(wcs.Axis("RM", "rad/m**2")) # divide into 2 samples with a gap between c1 = RefCoord(px, sx) c2 = RefCoord(0.0, 0.0) c3 = RefCoord(px + nx * 0.66, sx + nx * ds * 0.66) c4 = RefCoord(px + nx, sx + nx * ds) axis_1d.bounds = CoordBounds1D() axis_1d.bounds.samples.append(CoordRange1D(c1, c3)) axis_1d.bounds.samples.append(CoordRange1D(c4, c2)) return chunk.CustomWCS(axis_1d)
def build_time(seconds, microseconds): mjd_start = AstroTime(seconds, format='unix') mjd_start.format = 'mjd' start = RefCoord(0.5, mjd_start.value) end_ms = seconds + (microseconds / 1e7) mjd_end = AstroTime(end_ms, format='unix') mjd_end.format = 'mjd' end = RefCoord(1.5, mjd_end.value) axis = CoordAxis1D(axis=Axis(ctype='TIME', cunit='d')) axis.range = CoordRange1D(start, end) return TemporalWCS(axis=axis, timesys='UTC', trefpos=None, mjdref=None, exposure=(microseconds / 1e7), resolution=None)
def test_range1d_to_interval(self): # happy path wcs = CustomTestUtil.good_wcs() start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(11.1)) range_1d = CoordRange1D(start, end) actual_interval = wcs_util.CustomAxisUtil.range1d_to_interval( wcs, range_1d) expected_interval = Interval(1.1, 11.1) self.assertEqual(expected_interval.lower, actual_interval.lower) self.assertEqual(expected_interval.upper, actual_interval.upper) self.assertEqual(None, actual_interval.samples) # function_1d.delta == 0.0 && function_1d.naxis > 1 start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(1.1)) range_1d = CoordRange1D(start, end) with pytest.raises(ValueError) as ex: wcs_util.CustomAxisUtil.range1d_to_interval(wcs, range_1d) assert ('Invalid CoordRange1D:' in str(ex.value))
def test_val2pix(self): # happy path wcs = CustomTestUtil.good_wcs() naxis = int(100) delta = -0.01 ref_coord = RefCoord(0.0, 0.0) func = CoordFunction1D(naxis, delta, ref_coord) val = 0.1 pix = wcs_util.CustomAxisUtil.val2pix(wcs, func, val) expected_pix = -10.0 self.assertEqual(pix, expected_pix)
def _update_ngvs_time(chunk, provenance, obs_id): logging.debug(f'Begin _update_ngvs_time for {obs_id}') if (chunk is not None and provenance is not None and len(provenance.inputs) > 0): # bounds = ctor config = mc.Config() config.get_executors() subject = mc.define_subject(config) client = CAOM2RepoClient( subject, config.logging_level, 'ivo://cadc.nrc.ca/ams') metrics = mc.Metrics(config) bounds = CoordBounds1D() min_date = 0 max_date = sys.float_info.max exposure = 0 for entry in provenance.inputs: ip_obs_id, ip_product_id = mc.CaomName.decompose_provenance_input( entry.uri) logging.info(f'Retrieving provenance metadata for {ip_obs_id}.') ip_obs = mc.repo_get(client, 'CFHT', ip_obs_id, metrics) if ip_obs is not None: ip_plane = ip_obs.planes.get(ip_product_id) if (ip_plane is not None and ip_plane.time is not None and ip_plane.time.bounds is not None): bounds.samples.append(CoordRange1D( RefCoord(pix=0.5, val=ip_plane.time.bounds.lower), RefCoord(pix=1.5, val=ip_plane.time.bounds.upper))) min_date = min(ip_plane.time.bounds.lower, min_date) max_date = max(ip_plane.time.bounds.upper, max_date) exposure += ip_plane.time.exposure axis = Axis(ctype='TIME', cunit='d') time_axis = CoordAxis1D(axis=axis, error=None, range=None, bounds=bounds, function=None) temporal_wcs = TemporalWCS(axis=time_axis, timesys=None, trefpos=None, mjdref=None, exposure=mc.to_float(exposure), resolution=None) chunk.time = temporal_wcs logging.debug(f'End _update_ngvs_time.')
def test_function1d_to_interval_happy_path(self): # happy path wcs = CustomTestUtil.good_wcs() naxis = int(100) delta = -0.2 ref_coord = RefCoord(0.0, 0.0) function_1d = CoordFunction1D(naxis, delta, ref_coord) actual_interval = wcs_util.CustomAxisUtil.function1d_to_interval( wcs, function_1d) expected_interval = Interval(-502.5, -2.5) self.assertEqual(expected_interval.lower, actual_interval.lower) self.assertEqual(expected_interval.upper, actual_interval.upper) self.assertEqual(None, actual_interval.samples) # function_1d.delta == 0.0 && function_1d.naxis > 1 naxis = int(100) delta = 0.0 ref_coord = RefCoord(0.0, 0.0) function_1d = CoordFunction1D(naxis, delta, ref_coord) with pytest.raises(ValueError) as ex: wcs_util.CustomAxisUtil.function1d_to_interval(wcs, function_1d) assert ('Invalid CoordFunction1D:' in str(ex.value))
def get_test_function_with_function(ctype, unit, px, sx, nx, ds): error = None range = None bounds = None naxis = int(1) delta = float(2.5) ref_coord = wcs.RefCoord(float(1.0), float(2.0)) function = CoordFunction1D(naxis, delta, ref_coord) axis_1d = CoordAxis1D(wcs.Axis(ctype, unit), error, range, bounds, function) ref_coord = RefCoord(px, sx) axis_1d.function = CoordFunction1D(nx, ds, ref_coord) custom_wcs = chunk.CustomWCS(axis_1d) return custom_wcs
def _update_time(self, chunk, obs_id): """Create TemporalWCS information using FITS header information. This information should always be available from the file.""" self._logger.debug('Begin _update_time.') mc.check_param(chunk, Chunk) mjd_start = self._headers[0].get('MJD_STAR') mjd_end = self._headers[0].get('MJD_END') if mjd_start is None or mjd_end is None: mjd_start, mjd_end = ac.find_time_bounds(self._headers) if mjd_start is None or mjd_end is None: chunk.time = None self._logger.debug( f'Cannot calculate MJD_STAR {mjd_start} or ' f'MDJ_END' f' {mjd_end}' ) elif mjd_start == 'NaN' or mjd_end == 'NaN': raise mc.CadcException( f'Invalid time values MJD_STAR {mjd_start} or MJD_END ' f'{mjd_end} for {obs_id}, stopping ingestion.' ) else: self._logger.debug( f'Calculating range with start {mjd_start} and end {mjd_end}.' ) start = RefCoord(0.5, mjd_start) end = RefCoord(1.5, mjd_end) time_cf = CoordFunction1D(1, self._headers[0].get('TEFF'), start) time_axis = CoordAxis1D(Axis('TIME', 'd'), function=time_cf) time_axis.range = CoordRange1D(start, end) chunk.time = TemporalWCS(time_axis) chunk.time.exposure = self._headers[0].get('TEFF') chunk.time.resolution = 0.1 chunk.time.timesys = 'UTC' chunk.time.trefpos = 'TOPOCENTER' chunk.time_axis = None self._logger.debug('Done _update_time.')
def _update_time(part, chunk, header, obs_id): logging.debug(f'Begin _update_time for {obs_id} part {part.name}.') # DB 02-07-20 # time metadata comes from MJD_OBS and EXPTIME, it's not # an axis requiring cutout support exp_time = header.get('EXPTIME') mjd_obs = header.get('MJD_OBS') if exp_time is None or mjd_obs is None: chunk.time = None else: if chunk.time is None: coord_error = CoordError(syser=1e-07, rnder=1e-07) time_axis = CoordAxis1D(axis=Axis('TIME', 'd'), error=coord_error) chunk.time = TemporalWCS(axis=time_axis, timesys='UTC') ref_coord = RefCoord(pix=0.5, val=mjd_obs) chunk.time.axis.function = CoordFunction1D( naxis=1, delta=mc.convert_to_days(exp_time), ref_coord=ref_coord) chunk.time.exposure = float(exp_time) chunk.time.resolution = mc.convert_to_days(exp_time) logging.debug(f'End _update_time.')
def get_test_function_with_bounds_3_samples(ctype, unit, px, sx, nx, ds): error = None range = None start = RefCoord(float(0.8), float(1.1)) end = RefCoord(float(10.8), float(11.1)) b_range_1 = CoordRange1D(start, end) start = RefCoord(float(0.9), float(1.2)) end = RefCoord(float(10.9), float(11.2)) b_range_2 = CoordRange1D(start, end) start = RefCoord(float(-0.9), float(-1.2)) end = RefCoord(float(0.6), float(0.2)) b_range_3 = CoordRange1D(start, end) samples = caom_util.TypedList(CoordRange1D, b_range_1, b_range_2, b_range_3) bounds = CoordBounds1D(samples) axis_1d = wcs.CoordAxis1D(wcs.Axis(ctype, unit), error, range, bounds) ref_coord = wcs.RefCoord(px, sx) axis_1d.function = wcs.CoordFunction1D(nx, ds, ref_coord) custom_wcs = chunk.CustomWCS(axis_1d) return custom_wcs
def test_bounds(self): # Polarization bounds is None, should not produce an error axis = Axis("STOKES", "cunit") axis_1d = CoordAxis1D(axis) polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis bounds contains one valid range start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(9.9), float(10.1)) p_range = CoordRange1D(start, end) samples = caom_util.TypedList(CoordRange1D, p_range) axis_1d.bounds = CoordBounds1D(samples) polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis bounds contains more than one valid range start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(9.9), float(10.1)) p_range = CoordRange1D(start, end) start = RefCoord(float(-8.1), float(-7.9)) end = RefCoord(float(-1.1), float(-0.9)) n_range = CoordRange1D(start, end) samples = caom_util.TypedList(CoordRange1D, p_range, n_range) axis_1d.bounds = CoordBounds1D(samples) polarization = PolarizationWCS(axis_1d) wcsvalidator._validate_polarization_wcs(polarization) # Polarization axis bounds contains one invalid range start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(10.9), float(11.1)) p_range = CoordRange1D(start, end) samples = caom_util.TypedList(CoordRange1D, p_range) axis_1d.bounds = CoordBounds1D(samples) polarization = PolarizationWCS(axis_1d) with pytest.raises(InvalidWCSError) as ex: wcsvalidator._validate_polarization_wcs(polarization) assert ('Invalid Polarization WCS' in str(ex.value)) assert ('11' in str(ex.value)) # Polarization axis bounds contains more than one invalid range start = RefCoord(float(0.9), float(1.1)) end = RefCoord(float(9.9), float(10.1)) p_range = CoordRange1D(start, end) start = RefCoord(float(-9.1), float(-8.9)) end = RefCoord(float(-1.1), float(-0.9)) n_range = CoordRange1D(start, end) samples = caom_util.TypedList(CoordRange1D, p_range, n_range) axis_1d.bounds = CoordBounds1D(samples) polarization = PolarizationWCS(axis_1d) with pytest.raises(InvalidWCSError) as ex: wcsvalidator._validate_polarization_wcs(polarization) assert ('Invalid Polarization WCS' in str(ex.value)) assert ('-9' in str(ex.value))