def verify_unit(quantity, unit): """Verify unit of passed quantity and return it. Parameters: quantity: Quantity to be verified. unit: Equivalent unit, or string parsable by :py:class:`astropy.units.Unit` Raises: ValueError: Units are not equivalent. Returns: quantity parameter, unchanged. Example: .. code-block:: python def __init__(self, a): self.a = verify_unit(a, astropy.units.m) :type quantity: :py:class:`astropy.units.Quantity` :type unit: :py:class:`astropy.units.UnitBase` """ if not isinstance(unit, Unit): unit = Unit(unit) if unit.is_equivalent((quantity * u.one).unit): return quantity else: raise ValueError( "Unit '{}' not equivalent to quantity '{}'.".format(unit, quantity))
def __init__(self, physical_unit=None, function_unit=None): if physical_unit is None: self._physical_unit = dimensionless_unscaled else: self._physical_unit = Unit(physical_unit) if (not isinstance(self._physical_unit, UnitBase) or self._physical_unit.is_equivalent( self._default_function_unit)): raise ValueError("Unit {0} is not a physical unit." .format(self._physical_unit)) if function_unit is None: self._function_unit = self._default_function_unit else: # any function unit should be equivalent to subclass default function_unit = Unit(getattr(function_unit, 'function_unit', function_unit)) if function_unit.is_equivalent(self._default_function_unit): self._function_unit = function_unit else: raise ValueError("Cannot initialize '{0}' instance with " "function unit '{1}', as it is not " "equivalent to default function unit '{2}'." .format(self.__class__.__name__, function_unit, self._default_function_unit))
def verify_unit(quantity, unit): """Verify unit of passed quantity and return it. Parameters: quantity: :py:class:`~astropy.units.Quantity` to be verified. Bare numbers are valid if the unit is dimensionless. unit: Equivalent unit, or string parsable by :py:class:`astropy.units.Unit` Raises: ValueError: Units are not equivalent. Returns: ``quantity`` unchanged. Bare numbers will be converted to a dimensionless :py:class:`~astropy.units.Quantity`. Example: .. code-block:: python def __init__(self, a): self.a = verify_unit(a, astropy.units.m) """ if not isinstance(unit, UnitBase): unit = Unit(unit) q = quantity * u.one if unit.is_equivalent(q.unit): return q else: raise ValueError("Unit '{}' not equivalent to quantity '{}'.".format(unit, quantity))
def _create(wlk, root, session): query = session.query(DatabaseEntry) for key, value in root.attrs.iteritems(): typ = key[0] if typ == 'tag': criterion = TableTag.name.in_([value]) # `key[1]` is here the `inverted` attribute of the tag. That means # that if it is True, the given tag must not be included in the # resulting entries. if key[1]: query = query.filter(~DatabaseEntry.tags.any(criterion)) else: query = query.filter(DatabaseEntry.tags.any(criterion)) elif typ == 'fitsheaderentry': key, val, inverted = value key_criterion = TableFitsHeaderEntry.key == key value_criterion = TableFitsHeaderEntry.value == val if inverted: query = query.filter(not_(and_( DatabaseEntry.fits_header_entries.any(key_criterion), DatabaseEntry.fits_header_entries.any(value_criterion)))) else: query = query.filter(and_( DatabaseEntry.fits_header_entries.any(key_criterion), DatabaseEntry.fits_header_entries.any(value_criterion))) elif typ == 'download time': start, end, inverted = value if inverted: query = query.filter( ~DatabaseEntry.download_time.between(start, end)) else: query = query.filter( DatabaseEntry.download_time.between(start, end)) elif typ == 'path': path, inverted = value if inverted: query = query.filter(or_( DatabaseEntry.path != path, DatabaseEntry.path == None)) else: query = query.filter(DatabaseEntry.path == path) elif typ == 'wave': min_, max_, unit = value waveunit = Unit(unit) # convert min_ and max_ to nm from the unit `waveunit` wavemin = waveunit.to(nm, min_, equivalencies.spectral()) wavemax = waveunit.to(nm, max_, equivalencies.spectral()) query = query.filter(and_( DatabaseEntry.wavemin >= wavemin, DatabaseEntry.wavemax <= wavemax)) elif typ == 'time': start, end, near = value query = query.filter(and_( DatabaseEntry.observation_time_start < end, DatabaseEntry.observation_time_end > start)) else: query = query.filter_by(**{typ: value}) return query.all()
def test_gps_scale(scale): u = Unit(scale[:-1]) fig = pyplot.figure() ax = fig.gca(xscale=scale) if scale == 'years': x = numpy.arange(50) else: x = numpy.arange(1e2) ax.plot(x * u.decompose().scale, x) fig.canvas.draw() xscale = ax.get_xaxis()._scale assert xscale.get_unit() == Unit(scale[:-1]) pyplot.close(fig)
def units_to_fits(unit): """ Convert an astropy unit to a FITS format string. uses the to_string() method built-in to astropy Unit() Notes ----- The output will be the format defined in the FITS standard: http://fits.gsfc.nasa.gov/fits_standard.html A roundtrip from fits_to_units -> units_to_fits may not return the original string, as people often don't follow the standard. """ if unit is None: unit = Unit('') return unit.to_string("fits").upper()
def aspcapStar_loader(file_obj, **kwargs): """ Loader for APOGEE aspcapStar files. Parameters ---------- file_obj: str or file-like FITS file name or object (provided from name by Astropy I/O Registry). Returns ------- data: Spectrum1D The spectrum that is represented by the data in this table. """ if isinstance(file_obj, fits.hdu.hdulist.HDUList): close_hdulist = False hdulist = file_obj else: close_hdulist = True hdulist = fits.open(file_obj, **kwargs) header = hdulist[0].header meta = {'header': header} wcs = WCS(hdulist[1].header) data = hdulist[1].data # spectrum in the first extension unit = def_unit('arbitrary units') uncertainty = StdDevUncertainty(hdulist[2].data) # dispersion from the WCS but convert out of logspace dispersion = 10**wcs.all_pix2world(np.arange(data.shape[0]), 0)[0] dispersion_unit = Unit('Angstrom') if close_hdulist: hdulist.close() return Spectrum1D(data=data * unit, uncertainty=uncertainty, spectral_axis=dispersion * dispersion_unit, meta=meta, wcs=wcs)
def __init__(self, value, uncertainty, unit=None): if isinstance(value, self.__class__): self._uncertainty = value.uncertainty else: if isinstance(value, Quantity): if unit is not None: raise ValueError('cannot use the unit argument when ' '`value` is a Quantity') else: self._unit = (dimensionless_unscaled if unit is None else Unit(unit)) if hasattr(uncertainty, 'unit'): if (uncertainty.unit != dimensionless_unscaled and self._unit != dimensionless_unscaled): try: uncertainty = uncertainty.to(self._unit) except: raise UnitsError('cannot convert unit of uncertainty ' 'to unit of the `Data` instance') if (self._unit == dimensionless_unscaled and uncertainty.unit != dimensionless_unscaled): raise UnitsError('cannot assign an uncertainty with units ' 'to a `Data` instance without a unit') uncertainty = Uncertainty(uncertainty) else: uncertainty = Uncertainty(uncertainty, self._unit) if uncertainty.size != self.size: raise ValueError('uncertainty must have the same shape as ' 'the `Data` instance') self._uncertainty = uncertainty self._uncertainty.parent_data = self
def crab_flux(energy=1, reference=CRAB_DEFAULT_REFERENCE): """Differential Crab flux. See the ``gammapy.spectrum.crab`` module docstring for a description of the available reference spectra. Parameters ---------- energy : array_like Energy (TeV) reference : {{'hegra', 'hess_pl', 'hess_ecpl', 'meyer'}} Published Crab reference spectrum Returns ------- flux : array Differential flux (cm^-2 s^-1 TeV^-1) at ``energy`` """ if reference == 'hegra': f = hegra['diff_flux'] g = hegra['index'] return f * energy**(-g) elif reference == 'hess_pl': f = hess_pl['diff_flux'] g = hess_pl['index'] return f * energy**(-g) elif reference == 'hess_ecpl': f = hess_ecpl['diff_flux'] g = hess_ecpl['index'] e_c = hess_ecpl['cutoff'] return f * energy**(-g) * np.exp(-energy / e_c) elif reference == 'meyer': # Meyer et al., 2010arXiv1008.4524M, Appendix D p = np.array( [-0.00449161, 0, 0.0473174, -0.179475, -0.53616, -10.2708]) log_energy = np.log10(np.asarray(energy)) log_flux = np.poly1d(p)(log_energy) flux = 10**log_flux return Unit('erg').to('TeV') * flux / energy**2 else: raise ValueError('Unknown reference: {0}'.format(reference))
def get_wave_unit(tag, hdulist, idx=None): """ Attempt to pull wavelength unit from the Table Parameters ---------- tag : str Tag used for wavelengths hdulist : fits header data unit list idx : int, optional Index of list for Table input Returns ------- unit : astropy Unit Defaults to None """ from astropy.units import Unit if idx is None: idx = 1 # Use Table if isinstance(hdulist[idx], BinTableHDU): tab = Table(hdulist[idx].data) header = hdulist[idx].header else: # NEED HEADER INFO return None # Try table header (following VLT/X-Shooter here) keys = list(header) # Python 3 values = list(itervalues(header)) # Python 3 hidx = values.index(tag) if keys[hidx][0:5] == 'TTYPE': try: tunit = header[keys[hidx].replace('TYPE', 'UNIT')] except KeyError: return None else: if tunit in ['Angstroem', 'Angstroms', 'ANGSTROMS']: tunit = 'Angstrom' unit = Unit(tunit) return unit else: return None
def fwhms(self): """ This function ... :return: """ # Initialize a list to contain the fwhm of the fitted stars fwhms = [] # Loop over all stars for star in self.stars: # If the star contains a model, add the fwhm of that model to the list if star.has_model: fwhm_pix = star.fwhm * Unit("pix") fwhm_arcsec = fwhm_pix * self.frame.average_pixelscale.to( "arcsec/pix") fwhms.append(fwhm_arcsec) # Return the list return fwhms
def generic_spectrum1d_loader(file_name, **kwargs): name = os.path.basename(file_name.rstrip(os.sep)).rsplit('.', 1)[0] hdulist = fits.open(file_name, **kwargs) header = hdulist[0].header meta = {'header': header} wcs = WCS(hdulist[0].header) unit = Unit('Jy') uncertainty = StdDevUncertainty(hdulist[3].data) data = hdulist[1].data mask = hdulist[2].data hdulist.close() return MOSSpectrum2D(data=data, name=name, wcs=wcs, uncertainty=uncertainty, unit=unit, meta=meta, mask=mask)
def project_azimuth_to_pa(azimuth, inclination): """ This function ... :param azimuth: :param inclination: """ # Get the azimuth angle and inclination in radians azimuth_radian = azimuth.to("radian").value i_radian = inclination.to("radian").value denominator = math.sqrt( math.cos(azimuth_radian)**2 * math.cos(i_radian)**2 + math.sin(azimuth_radian)**2) cos_pa = math.cos(azimuth_radian) * math.cos(i_radian) / denominator sin_pa = math.sin(azimuth_radian) / denominator pa_radian = math.atan2(sin_pa, cos_pa) * Unit("radian") return pa_radian.to("deg")
def _summary_flux_points(self): """Print flux point results""" d = self.data ss = '\n*** Flux points info ***\n\n' ss += 'Number of flux points: {}\n'.format(d['N_Flux_Points']) ss += 'Flux points table: \n\n\t' flux_points = self.flux_points['ENERGY', 'DIFF_FLUX', 'DIFF_FLUX_ERR_HI', 'DIFF_FLUX_ERR_LO'][:int(d['N_Flux_Points'])] flux_points['ENERGY'].format = '.3f' flux_unit = Unit('1E-12 cm^-2 s^-1 TeV^-1') for _ in ['DIFF_FLUX', 'DIFF_FLUX_ERR_HI', 'DIFF_FLUX_ERR_LO']: flux_points[_] = flux_points[_].to(flux_unit) flux_points[_].format = '.3f' flux_points[_].unit = flux_unit # convert table to string ss += '\n\t'.join(flux_points.pformat(-1)) return ss + '\n'
def __init__(self): path = os.path.dirname(pyfoxsi.__file__) for i in np.arange(3): path = os.path.dirname(path) path = os.path.join(path, 'data/') filename = 'shell_parameters.csv' params_file = os.path.join(path, filename) self.shell_params = pd.read_csv(params_file, index_col=0) the_units = [ Unit(this_unit) for this_unit in self.shell_params.loc[np.nan].values ] self.units = {} for i, col in enumerate(self.shell_params): self.units.update({col: the_units[i]}) self.shell_params.drop(self.shell_params.index[0], inplace=True) missing_shells = np.setdiff1d(self.shell_params.index, pyfoxsi.shell_ids) self.shell_params.drop(missing_shells) for col in self.shell_params.columns: self.shell_params[col] = self.shell_params[col].astype(float)
def project_tilt_to_pa(tilt, inclination): """ This function ... :param tilt: :param inclination: :return: """ # Get the tilt angle and inclination in radians tilt_radian = tilt.to("radian").value i_radian = inclination.to("radian").value denominator = math.sqrt( math.sin(tilt_radian)**2 * math.sin(i_radian)**2 + math.cos(tilt_radian)**2) cos_pa = math.sin(tilt_radian) * math.sin(i_radian) / denominator sin_pa = math.cos(tilt_radian) / denominator pa_radian = math.atan2(sin_pa, cos_pa) * Unit("radian") return pa_radian.to("deg")
def __init__( self, default=None, description="", unit=None, ucd=None, dtype=None, type=None, ndim=None, allow_none=True, max_length=None, ): self.default = default self.description = description self.unit = Unit(unit) if unit is not None else None self.ucd = ucd self.dtype = np.dtype(dtype) if dtype is not None else None self.type = type self.ndim = ndim self.allow_none = allow_none self.max_length = max_length
def deproject_pa_to_tilt(pa, inclination): """ This function ... :param pa: :param inclination: :return: """ # Get the PA and inclination in radians pa_radian = pa.to("radian").value i_radian = inclination.to("radian").value denominator = math.sqrt( math.sin(pa_radian)**2 * math.sin(i_radian)**2 + math.cos(pa_radian)**2) cos_tilt = math.sin(pa_radian) * math.sin(i_radian) / denominator sin_tilt = math.cos(pa_radian) / denominator tilt_radian = math.atan2(sin_tilt, cos_tilt) * Unit("radian") return tilt_radian.to("deg")
def test_make_psf_map(): psf = fake_psf3d(0.3 * u.deg) pointing = SkyCoord(0, 0, unit="deg") energy_axis = MapAxis(nodes=[0.2, 0.7, 1.5, 2.0, 10.0], unit="TeV", name="energy") rad_axis = MapAxis(nodes=np.linspace(0.0, 1.0, 51), unit="deg", name="theta") geom = WcsGeom.create(skydir=pointing, binsz=0.2, width=5, axes=[rad_axis, energy_axis]) psfmap = make_psf_map(psf, pointing, geom, 3 * u.deg) assert psfmap.psf_map.geom.axes[0] == rad_axis assert psfmap.psf_map.geom.axes[1] == energy_axis assert psfmap.psf_map.unit == Unit("sr-1") assert psfmap.psf_map.data.shape == (4, 50, 25, 25)
def to_image_hdu(self): """ Convert image to a `~astropy.io.fits.PrimaryHDU`. Returns ------- hdu : `~astropy.io.fits.PrimaryHDU` Primary image hdu object. """ if self.wcs is not None: header = self.wcs.to_header() else: header = fits.Header() # Add meta data header.update(self.meta) if self.unit is not None: header['BUNIT'] = Unit(self.unit).to_string('fits') if self.name is not None: header['EXTNAME'] = self.name header['HDUNAME'] = self.name return fits.PrimaryHDU(data=self.data, header=header)
def simple_generic_loader(file_name, **kwargs): """ Basic FITS file loader Presumption is the primary data is a table with columns 'flux' and 'err'. The dispersion information is encoded in the FITS header keywords. Parameters ---------- file_name: str The path to the FITS file Returns ------- data: Spectrum1DRef The data. """ name = os.path.basename(file_name.rstrip(os.sep)).rsplit('.', 1)[0] hdulist = fits.open(file_name, **kwargs) header = hdulist[0].header tab = Table.read(file_name) meta = {'header': header} wcs = WCS(hdulist[0].header) unit = Unit('Jy') uncertainty = StdDevUncertainty(tab["err"]) data = tab["flux"] hdulist.close() return Spectrum1DRef(data=data, name=name, wcs=wcs, uncertainty=uncertainty, unit=unit, meta=meta)
def unit(self, value): """ The unit should be set to a value consistent with the parent NDData unit and the uncertainty type. """ if value is not None: # Check the hidden attribute below, not the property. The property # raises an exception if there is no parent_nddata. if self._parent_nddata is not None: parent_unit = self.parent_nddata.unit try: # Check for consistency with the unit of the parent_nddata self._data_unit_to_uncertainty_unit(parent_unit).to(value) except UnitConversionError: raise UnitConversionError("Unit {} is incompatible " "with unit {} of parent " "nddata".format( value, parent_unit)) self._unit = Unit(value) else: self._unit = value
def calculate_attenuation(self): """ This function ... :return: """ # Inform the user log.info("Calculating the attenuations ...") # The wavelength of the V band v_band_wavelength = 0.55 * Unit("micron") # Calculate the attenuations of the dust in star-forming regions #self.attenuation_sfr = AttenuationCurve() # Find the V-band attenuation for the SFR dust #v_band_attenuation_sfr = self.attenuation_sfr.attenuation_at(v_band_wavelength) # Calculate the attenuations of the diffuse dust component self.attenuation_diffuse = AttenuationCurve.from_seds( self.total_sed, self.transparent_sed) # Find the V-band attenuation for the diffuse dust component v_band_attenuation_diffuse = self.attenuation_diffuse.attenuation_at( v_band_wavelength) # Normalize the attenuation curve to the V-band attenuation self.attenuation_diffuse.normalize_at(v_band_wavelength) # SED of transparent + delta flux mappings #transparent_sfr_sed = self.transparent_sed + delta_flux_mappings # Calculate the attenuations for all the dust #self.attenuation_total = AttenuationCurve.from_seds(self.total_sed, transparent_sfr_sed) # Find the total V-band attenuation #v_band_attenuation_total = self.attenuation_total.attenuation_at(v_band_wavelength) #print(' V band attenuation from star-forming regions: ' + str(att_mappings_V)) print(' V band attenuation from diffuse dust: ' + str(v_band_attenuation_diffuse))
def to_psf3d(self, rad): """Create a PSF3D from an analytical PSF. Parameters ---------- rad : `~astropy.units.Quantity` or `~astropy.coordinates.Angle` the array of position errors (rad) on which the PSF3D will be defined Returns ------- psf3d : `~gammapy.irf.PSF3D` the PSF3D. It will be defined on the same energy and offset values than the input psf. """ offsets = self.theta energy = self.energy energy_lo = self.energy_lo energy_hi = self.energy_hi rad_lo = rad[:-1] rad_hi = rad[1:] psf_values = np.zeros((rad_lo.shape[0], offsets.shape[0], energy_lo.shape[0])) * Unit("sr-1") for i, offset in enumerate(offsets): psftable = self.to_energy_dependent_table_psf(offset) psf_values[:, i, :] = psftable.evaluate(energy, 0.5 * (rad_lo + rad_hi)).T return PSF3D( energy_lo, energy_hi, offsets, rad_lo, rad_hi, psf_values, self.energy_thresh_lo, self.energy_thresh_hi, )
def from_image_hdu(cls, image_hdu): """ Create image from ImageHDU. Parameters ---------- image_hdu : `astropy.io.fits.ImageHDU` Source image HDU. Examples -------- >>> from astropy.io import fits >>> from gammapy.image import SkyImage >>> hdu_list = fits.open('data.fits') >>> image = SkyImage.from_image_hdu(hdu_list['myimage']) """ data = image_hdu.data header = image_hdu.header wcs = WCS(image_hdu.header) name = header.get('HDUNAME') if name is None: name = header.get('EXTNAME') try: # Validate unit string unit = Unit(header['BUNIT'], format='fits').to_string() except (KeyError, ValueError): unit = None meta = OrderedDict(header) # parse astropy.io.fits.header._HeaderCommentaryCards as strings if 'HISTORY' in meta: meta['HISTORY'] = str(meta['HISTORY']) if 'COMMENT' in meta: meta['COMMENT'] = str(meta['COMMENT']) return cls(name, data, wcs, unit, meta)
def lightcurve(self): """Lightcurve (`~gammapy.time.LightCurve`). """ flux = self.data['Flux_History'] # Flux error is given as asymmetric high/low flux_err_lo = self.data['Unc_Flux_History'][:, 0] flux_err_hi = self.data['Unc_Flux_History'][:, 1] # TODO: Change lightcurve class to support this, # then fill appropriately here # for now, we just use the mean flux_err = 0.5 * (-flux_err_lo + flux_err_hi) flux_unit = Unit('cm-2 s-1') # Really the time binning is stored in a separate HDU in the FITS # catalog file called `Hist_Start`, with a single column `Hist_Start` # giving the time binning in MET (mission elapsed time) # This is not available here for now. # TODO: read that info in `SourceCatalog3FGL` and pass it down to the # `SourceCatalogObject3FGL` object somehow. # For now, we just hard-code the start and stop time and assume # equally-spaced time intervals. This is roughly correct, # for plotting the difference doesn't matter, only for analysis time_start = Time('2008-08-02T00:33:19') time_end = Time('2012-07-31T22:45:47') n_points = len(flux) time_step = (time_end - time_start) / n_points time_bounds = time_start + np.arange(n_points + 1) * time_step table = QTable() table['TIME_MIN'] = time_bounds[:-1] table['TIME_MAX'] = time_bounds[1:] table['FLUX'] = flux * flux_unit table['FLUX_ERR'] = flux_err * flux_unit lc = LightCurve(table) return lc
def _map_transforms_from_table_header(self, table_name): """ create any transforms needed to "undo" ones in the writer """ tab = self._tables[table_name] attrs = tab.attrs._f_list() for attr in attrs: if attr.endswith("_UNIT"): colname = attr[:-5] tr = QuantityColumnTransform(unit=Unit(tab.attrs[attr])) self.add_column_transform(table_name, colname, tr) elif attr.endswith("_ENUM"): colname = attr[:-5] tr = EnumColumnTransform(tab.attrs[attr]) self.add_column_transform(table_name, colname, tr) elif attr.endswith("_TIME_SCALE"): colname, _, _ = attr.rpartition("_TIME_SCALE") scale = tab.attrs[attr] time_format = get_hdf5_attr(tab.attrs, colname + "_TIME_FORMAT", "mjd") transform = TimeColumnTransform(scale=scale, format=time_format) self.add_column_transform(table_name, colname, transform) elif attr.endswith("_TRANSFORM_SCALE"): colname, _, _ = attr.rpartition("_TRANSFORM_SCALE") tr = FixedPointColumnTransform( scale=tab.attrs[attr], offset=get_hdf5_attr(tab.attrs, colname + "_TRANSFORM_OFFSET", 0), source_dtype=get_hdf5_attr(tab.attrs, colname + "_TRANSFORM_DTYPE", "float32"), target_dtype=tab.dtype[colname].base, ) self.add_column_transform(table_name, colname, tr)
def integrate_psf(self, integration_time, hdu=None): """Integrates psf frames over the input time. Args: integration_time (Quantity): This is used for computing the number of frames 'n_frames', via floor division by the 'timestep' attribute. hdu (int, str, optional): Specifier of the HD Default is None for the first HD """ # Check input parameters if isinstance(integration_time, (int, float)): logger.warning(f"Interpreting scalar type integration_time as {integration_time} s") integration_time = integration_time * Unit('s') elif not isinstance(integration_time, Quantity): raise SpecklepyTypeError('integrate_psf', 'integration_time', type(integration_time), 'Quantity') if integration_time < self.timestep: raise ValueError(f"integrate_psf received integration time {integration_time} shorter than the time " f"resolution of the psf source ({self.timestep})!") n_frames = int(integration_time / self.timestep) # Read PSF frames from source file data = fits.getdata(self.psf_source, hdu) self.psf_frame += 1 if self.psf_frame + n_frames < data.shape[0]: self.psf = np.sum(data[self.psf_frame : self.psf_frame+n_frames], axis=0) else: self.psf = np.sum(data[self.psf_frame : ], axis=0) self.psf += np.sum(data[ : (self.psf_frame+n_frames) % data.shape[0]], axis=0) self.psf_frame += n_frames - 1 self.psf_frame = self.psf_frame % data.shape[0] # Normalize the integrated PSF self.psf = self.normalize(self.psf)
def spectral_axis(self): """ Returns a Quantity array with the values of the spectral axis. *PROBLEM*: THIS IS EXPENSIVE! How do we make this not evaluate each time? Cache? """ spectral_wcs = self.spectral_wcs # Lim: What if I have wavelength arrays and I don't want WCS # conversion? # Tom: this is beyond the scope of the prototype work, your question is # more how to make a WCS object that contains a wavelength array. The # mixin is for NDData which assumes a WCS has been created (not # necessarily a *FITS* WCS, just some transformation object). # Adam: We are now assuming that lookup tables ARE WCSes and WCSes # can be generated from arrays if spectral_wcs.naxis == 0: raise TypeError('WCS has no spectral axis') # TODO: make pix_to_world wrapper that does this # (i.e., make sure fits-wcs and gwcs have same API) spectral_axis = spectral_wcs.pix2world( np.arange(self._spectral_axis_len), 0)[0] # Try to get the dispersion unit information try: # Where does gwcs store this? spectral_unit = self.wcs.wcs.cunit[self.wcs.wcs.spec] except AttributeError: logging.warning("No spectral_axis unit information in WCS.") spectral_unit = Unit("") spectral_axis = spectral_axis * spectral_unit return spectral_axis
def update_missing_channel_params(channel, **kwargs): """Update empty channel parameters using the given input This method will only set parameters in the channel if the target parameter is `None`. Parameters ---------- channel : `~gwpy.detector.Channel` channel to update **kwargs `(key, value)` pairs to set """ target = get_channel(str(channel)) if isinstance(channel, Channel): for param in ['unit', 'sample_rate', 'frametype']: if getattr(target, param) is None or (param == 'unit' and getattr( target, param) is Unit('undef')): setattr(target, param, getattr(channel, param)) for param in kwargs: if getattr(target, param) is None: setattr(target, param, kwargs[param]) return target
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0): if unit is not None: # Convert possible string input to a (function) unit. unit = Unit(unit) if not isinstance(unit, FunctionUnitBase): # By default, use value's physical unit. value_unit = getattr(value, 'unit', None) if value_unit is None: # if iterable, see if first item has a unit # (mixed lists fail in super call below). try: value_unit = getattr(value[0], 'unit') except Exception: pass physical_unit = getattr(value_unit, 'physical_unit', value_unit) unit = cls._unit_class(physical_unit, function_unit=unit) # initialise! return super().__new__(cls, value, unit, dtype=dtype, copy=copy, order=order, subok=subok, ndmin=ndmin)
def sixdfgs_tabular_fits_loader(file_obj, **kwargs): """ Load the tabular variant of a 6dF Galaxy Survey (6dFGS) file. 6dFGS used the Six-degree Field instrument on the UK Schmidt Telescope (UKST) at the Siding Spring Observatory (SSO) near Coonabarabran, Australia. Further details can be found at http://www.6dfgs.net/, or https://docs.datacentral.org.au/6dfgs/. Catalogues and spectra were produced, with the spectra being provided as both fits tables and as fits images. This loads the tabular variant of the spectra. Note that this does not include uncertainties - uncertainties are only provided in the image variants. Parameters ---------- file_obj: str, file-like or HDUList FITS file name, object (provided from name by Astropy I/O Registry), or HDUList (as resulting from astropy.io.fits.open()). Returns ------- data: Spectrum1D The 6dF spectrum that is represented by the data in this table. """ with read_fileobj_or_hdulist(file_obj, **kwargs) as hdulist: header = hdulist[0].header table = Table.read(hdulist) flux = Quantity(table["FLUX"]) wavelength = Quantity(table["WAVE"]) if flux.unit == COUNTS_PER_SECOND: flux._unit = Unit("count/s") meta = {"header": header} return Spectrum1D(flux=flux, spectral_axis=wavelength, meta=meta)
def loudest_event_metric_factory(column): column = column.lower() @_use_dqflag def loudest_event(segments, before, after=None): """Percentage reduction in the amplitude of the loudest event by %s Parameters ---------- segments : `DataQualityFlag`, `~glue.segments.segmentlist` the set of segments to test before : `~glue.ligolw.table.Table` the event trigger table to test after : `~glue.ligolw.table.Table`, optional the remaining triggers after vetoes. This is calculated is not given Returns ------ reduction : `float` the percentage reduction in the amplitude of the loudest event """ if after is None: after = before.veto(segments.active) try: brank = before[column].max() except ValueError: # no triggers to start with return 0 try: arank = after[column].max() except ValueError: # no triggers after veto return 100 return (brank - arank) / brank * 100 loudest_event.__doc__ %= column return register_metric( Metric(loudest_event, 'Loudest event by %s' % column, unit=Unit('%')))
def __ilshift__(self, other): try: other = Unit(other) except UnitTypeError: return NotImplemented if not isinstance(other, self._unit_class): return NotImplemented try: factor = self.unit.physical_unit._to(other.physical_unit) except UnitConversionError: # Maybe via equivalencies? Now we do make a temporary copy. try: value = self._to_value(other) except UnitConversionError: return NotImplemented self.view(np.ndarray)[...] = value else: self.view(np.ndarray)[...] += self.unit.from_physical(factor) self._set_unit(other) return self
def calculate_residuals(self): """Calculate residuals and residual errors Based on `~gammapy.spectrum.results.SpectrumFitResult` and `~gammapy.spectrum.results.FluxPoints` Returns ------- residuals : `~astropy.units.Quantity` Residuals residuals_err : `~astropy.units.Quantity` Residual errors """ func = self.fit.to_sherpa_model() x = self.points['energy'].quantity y = self.points['flux'].quantity y_err = self.points['flux_err_hi'].quantity func_y = func(x.to('keV').value) * Unit('s-1 cm-2 keV-1') residuals = (y - func_y) / y # Todo: add correct formular (butterfly) residuals_err = y_err / y return residuals.decompose(), residuals_err.decompose()
def __init__(self, array=None, copy=True, unit=None): if isinstance(array, NDUncertainty): # Given an NDUncertainty class or subclass check that the type # is the same. if array.uncertainty_type != self.uncertainty_type: raise IncompatibleUncertaintiesException # Check if two units are given and take the explicit one then. if (unit is not None and unit != array._unit): # TODO : Clarify it (see NDData.init for same problem)? log.info("overwriting Uncertainty's current " "unit with specified unit.") elif array._unit is not None: unit = array.unit array = array.array elif isinstance(array, Quantity): # Check if two units are given and take the explicit one then. if (unit is not None and array.unit is not None and unit != array.unit): log.info("overwriting Quantity's current " "unit with specified unit.") elif array.unit is not None: unit = array.unit array = array.value if unit is None: self._unit = None else: self._unit = Unit(unit) if copy: array = deepcopy(array) unit = deepcopy(unit) self.array = array self.parent_nddata = None # no associated NDData - until it is set!
def entries_from_file(file, default_waveunit=None): """Use the headers of a FITS file to generate an iterator of :class:`sunpy.database.tables.DatabaseEntry` instances. Gathered information will be saved in the attribute `fits_header_entries`. If the key INSTRUME, WAVELNTH or DATE-OBS / DATE_OBS is available, the attribute `instrument`, `wavemin` and `wavemax` or `observation_time_start` is set, respectively. If the wavelength unit can be read, the values of `wavemin` and `wavemax` are converted to nm (nanometres). The value of the `file` parameter is used to set the attribute `path` of each generated database entry. Parameters ---------- file : str or file-like object Either a path pointing to a FITS file or a an opened file-like object. If an opened file object, its mode must be one of the following rb, rb+, or ab+. default_waveunit : str, optional The wavelength unit that is used for a header if it cannot be found. Raises ------ sunpy.database.WaveunitNotFoundError If `default_waveunit` is not given and the wavelength unit cannot be found in one of the FITS headers sunpy.WaveunitNotConvertibleError If a wavelength unit could be found but cannot be used to create an instance of the type ``astropy.units.Unit``. This can be the case for example if a FITS header has the key `WAVEUNIT` with the value `nonsense`. Examples -------- >>> entries = list(entries_from_file(sunpy.data.sample.SWAP_LEVEL1_IMAGE)) >>> len(entries) 1 >>> entry = entries.pop() >>> entry.instrument 'SWAP' >>> entry.observation_time_start, entry.observation_time_end (datetime.datetime(2012, 1, 1, 0, 16, 7, 836000), None) >>> entry.wavemin, entry.wavemax (17.400000000000002, 17.400000000000002) >>> len(entry.fits_header_entries) 112 """ headers = fits.get_header(file) if isinstance(file, (str, unicode)): filename = file else: filename = getattr(file, "name", None) for header in headers: entry = DatabaseEntry(path=filename) for key, value in header.iteritems(): # Yes, it is possible to have an empty key in a FITS file. # Example: sunpy.data.sample.EIT_195_IMAGE # Don't ask me why this could be a good idea. if key == "": value = str(value) elif key == "KEYCOMMENTS": for k, v in value.iteritems(): entry.fits_key_comments.append(FitsKeyComment(k, v)) continue entry.fits_header_entries.append(FitsHeaderEntry(key, value)) waveunit = fits.extract_waveunit(header) if waveunit is None: waveunit = default_waveunit unit = None if waveunit is not None: try: unit = Unit(waveunit) except ValueError: raise WaveunitNotConvertibleError(waveunit) for header_entry in entry.fits_header_entries: key, value = header_entry.key, header_entry.value if key == "INSTRUME": entry.instrument = value elif key == "WAVELNTH": if unit is None: raise WaveunitNotFoundError(file) # use the value of `unit` to convert the wavelength to nm entry.wavemin = entry.wavemax = unit.to(nm, value, equivalencies.spectral()) # NOTE: the key DATE-END or DATE_END is not part of the official # FITS standard, but many FITS files use it in their header elif key in ("DATE-END", "DATE_END"): entry.observation_time_end = parse_time(value) elif key in ("DATE-OBS", "DATE_OBS"): entry.observation_time_start = parse_time(value) yield entry
def _from_query_result_block(cls, qr_block, default_waveunit=None): """Make a new :class:`DatabaseEntry` instance from a VSO query result block. The values of :attr:`wavemin` and :attr:`wavemax` are converted to nm (nanometres). Parameters ---------- qr_block : suds.sudsobject.QueryResponseBlock A query result block is usually not created directly; instead, one gets instances of ``suds.sudsobject.QueryResponseBlock`` by iterating over a VSO query result. default_waveunit : str, optional The wavelength unit that is used if it cannot be found in the `qr_block`. Examples -------- >>> from sunpy.net import vso >>> client = vso.VSOClient() >>> qr = client.query( ... vso.attrs.Time('2001/1/1', '2001/1/2'), ... vso.attrs.Instrument('eit')) >>> entry = DatabaseEntry.from_query_result_block(qr[0]) >>> entry.source 'SOHO' >>> entry.provider 'SDAC' >>> entry.physobs 'intensity' >>> entry.fileid '/archive/soho/private/data/processed/eit/lz/2001/01/efz20010101.010014' >>> entry.observation_time_start, entry.observation_time_end (datetime.datetime(2001, 1, 1, 1, 0, 14), datetime.datetime(2001, 1, 1, 1, 0, 21)) >>> entry.instrument 'EIT' >>> entry.size 2059.0 >>> entry.wavemin, entry.wavemax (17.1, 17.1) """ time_start = timestamp2datetime("%Y%m%d%H%M%S", qr_block.time.start) time_end = timestamp2datetime("%Y%m%d%H%M%S", qr_block.time.end) wave = qr_block.wave unit = None if wave.waveunit is None: if default_waveunit is not None: unit = Unit(default_waveunit) else: # some query response blocks store the unit "kev", # but AstroPy only understands "keV". See issue #766. waveunit = wave.waveunit if waveunit == "kev": waveunit = "keV" unit = Unit(waveunit) if wave.wavemin is None: wavemin = None else: if unit is None: raise WaveunitNotFoundError(qr_block) wavemin = unit.to(nm, float(wave.wavemin), equivalencies.spectral()) if wave.wavemax is None: wavemax = None else: if unit is None: raise WaveunitNotFoundError(qr_block) wavemax = unit.to(nm, float(wave.wavemax), equivalencies.spectral()) source = str(qr_block.source) if qr_block.source is not None else None provider = str(qr_block.provider) if qr_block.provider is not None else None fileid = str(qr_block.fileid) if qr_block.fileid is not None else None instrument = str(qr_block.instrument) if qr_block.instrument is not None else None physobs = getattr(qr_block, "physobs", None) if physobs is not None: physobs = str(physobs) return cls( source=source, provider=provider, physobs=physobs, fileid=fileid, observation_time_start=time_start, observation_time_end=time_end, instrument=instrument, size=qr_block.size, wavemin=wavemin, wavemax=wavemax, )
def to_tree(cls, node, ctx): if isinstance(node, six.string_types): node = Unit(node, format='vounit', parse_strict='warn') if isinstance(node, UnitBase): return node.to_string(format='vounit') raise TypeError("'{0}' is not a valid unit".format(node))
class FunctionUnitBase(metaclass=ABCMeta): """Abstract base class for function units. Function units are functions containing a physical unit, such as dB(mW). Most of the arithmetic operations on function units are defined in this base class. While instantiation is defined, this class should not be used directly. Rather, subclasses should be used that override the abstract properties `_default_function_unit` and `_quantity_class`, and the abstract methods `from_physical`, and `to_physical`. Parameters ---------- physical_unit : `~astropy.units.Unit` or `string` Unit that is encapsulated within the function unit. If not given, dimensionless. function_unit : `~astropy.units.Unit` or `string` By default, the same as the function unit set by the subclass. """ # ↓↓↓ the following four need to be set by subclasses # Make this a property so we can ensure subclasses define it. @property @abstractmethod def _default_function_unit(self): """Default function unit corresponding to the function. This property should be overridden by subclasses, with, e.g., `~astropy.unit.MagUnit` returning `~astropy.unit.mag`. """ # This has to be a property because the function quantity will not be # known at unit definition time, as it gets defined after. @property @abstractmethod def _quantity_class(self): """Function quantity class corresponding to this function unit. This property should be overridden by subclasses, with, e.g., `~astropy.unit.MagUnit` returning `~astropy.unit.Magnitude`. """ @abstractmethod def from_physical(self, x): """Transformation from value in physical to value in function units. This method should be overridden by subclasses. It is used to provide automatic transformations using an equivalency. """ @abstractmethod def to_physical(self, x): """Transformation from value in function to value in physical units. This method should be overridden by subclasses. It is used to provide automatic transformations using an equivalency. """ # ↑↑↑ the above four need to be set by subclasses # have priority over arrays, regular units, and regular quantities __array_priority__ = 30000 def __init__(self, physical_unit=None, function_unit=None): if physical_unit is None: self._physical_unit = dimensionless_unscaled else: self._physical_unit = Unit(physical_unit) if (not isinstance(self._physical_unit, UnitBase) or self._physical_unit.is_equivalent( self._default_function_unit)): raise ValueError("Unit {0} is not a physical unit." .format(self._physical_unit)) if function_unit is None: self._function_unit = self._default_function_unit else: # any function unit should be equivalent to subclass default function_unit = Unit(getattr(function_unit, 'function_unit', function_unit)) if function_unit.is_equivalent(self._default_function_unit): self._function_unit = function_unit else: raise ValueError("Cannot initialize '{0}' instance with " "function unit '{1}', as it is not " "equivalent to default function unit '{2}'." .format(self.__class__.__name__, function_unit, self._default_function_unit)) def _copy(self, physical_unit=None): """Copy oneself, possibly with a different physical unit.""" if physical_unit is None: physical_unit = self.physical_unit return self.__class__(physical_unit, self.function_unit) @property def physical_unit(self): return self._physical_unit @property def function_unit(self): return self._function_unit @property def equivalencies(self): """List of equivalencies between function and physical units. Uses the `from_physical` and `to_physical` methods. """ return [(self, self.physical_unit, self.to_physical, self.from_physical)] # ↓↓↓ properties/methods required to behave like a unit def decompose(self, bases=set()): """Copy the current unit with the physical unit decomposed. For details, see `~astropy.units.UnitBase.decompose`. """ return self._copy(self.physical_unit.decompose(bases)) @property def si(self): """Copy the current function unit with the physical unit in SI.""" return self._copy(self.physical_unit.si) @property def cgs(self): """Copy the current function unit with the physical unit in CGS.""" return self._copy(self.physical_unit.cgs) def _get_physical_type_id(self): """Get physical type corresponding to physical unit.""" return self.physical_unit._get_physical_type_id() @property def physical_type(self): """Return the physical type of the physical unit (e.g., 'length').""" return self.physical_unit.physical_type def is_equivalent(self, other, equivalencies=[]): """ Returns `True` if this unit is equivalent to ``other``. Parameters ---------- other : unit object or string or tuple The unit to convert to. If a tuple of units is specified, this method returns true if the unit matches any of those in the tuple. equivalencies : list of equivalence pairs, optional A list of equivalence pairs to try if the units are not directly convertible. See :ref:`unit_equivalencies`. This list is in addition to the built-in equivalencies between the function unit and the physical one, as well as possible global defaults set by, e.g., `~astropy.units.set_enabled_equivalencies`. Use `None` to turn off any global equivalencies. Returns ------- bool """ if isinstance(other, tuple): return any(self.is_equivalent(u, equivalencies=equivalencies) for u in other) other_physical_unit = getattr(other, 'physical_unit', ( dimensionless_unscaled if self.function_unit.is_equivalent(other) else other)) return self.physical_unit.is_equivalent(other_physical_unit, equivalencies) def to(self, other, value=1., equivalencies=[]): """ Return the converted values in the specified unit. Parameters ---------- other : `~astropy.units.Unit` object, `~astropy.units.function.FunctionUnitBase` object or string The unit to convert to. value : scalar int or float, or sequence convertible to array, optional Value(s) in the current unit to be converted to the specified unit. If not provided, defaults to 1.0. equivalencies : list of equivalence pairs, optional A list of equivalence pairs to try if the units are not directly convertible. See :ref:`unit_equivalencies`. This list is in meant to treat only equivalencies between different physical units; the build-in equivalency between the function unit and the physical one is automatically taken into account. Returns ------- values : scalar or array Converted value(s). Input value sequences are returned as numpy arrays. Raises ------ UnitsError If units are inconsistent. """ # conversion to one's own physical unit should be fastest if other is self.physical_unit: return self.to_physical(value) other_function_unit = getattr(other, 'function_unit', other) if self.function_unit.is_equivalent(other_function_unit): # when other is an equivalent function unit: # first convert physical units to other's physical units other_physical_unit = getattr(other, 'physical_unit', dimensionless_unscaled) if self.physical_unit != other_physical_unit: value_other_physical = self.physical_unit.to( other_physical_unit, self.to_physical(value), equivalencies) # make function unit again, in own system value = self.from_physical(value_other_physical) # convert possible difference in function unit (e.g., dex->dB) return self.function_unit.to(other_function_unit, value) else: # when other is not a function unit return self.physical_unit.to(other, self.to_physical(value), equivalencies) def is_unity(self): return False def __eq__(self, other): return (self.physical_unit == getattr(other, 'physical_unit', dimensionless_unscaled) and self.function_unit == getattr(other, 'function_unit', other)) def __ne__(self, other): return not self.__eq__(other) def __mul__(self, other): if isinstance(other, (str, UnitBase, FunctionUnitBase)): if self.physical_unit == dimensionless_unscaled: # If dimensionless, drop back to normal unit and retry. return self.function_unit * other else: raise UnitsError("Cannot multiply a function unit " "with a physical dimension with any unit.") else: # Anything not like a unit, try initialising as a function quantity. try: return self._quantity_class(other, unit=self) except Exception: return NotImplemented def __rmul__(self, other): return self.__mul__(other) def __div__(self, other): if isinstance(other, (str, UnitBase, FunctionUnitBase)): if self.physical_unit == dimensionless_unscaled: # If dimensionless, drop back to normal unit and retry. return self.function_unit / other else: raise UnitsError("Cannot divide a function unit " "with a physical dimension by any unit.") else: # Anything not like a unit, try initialising as a function quantity. try: return self._quantity_class(1./other, unit=self) except Exception: return NotImplemented def __rdiv__(self, other): if isinstance(other, (str, UnitBase, FunctionUnitBase)): if self.physical_unit == dimensionless_unscaled: # If dimensionless, drop back to normal unit and retry. return other / self.function_unit else: raise UnitsError("Cannot divide a function unit " "with a physical dimension into any unit") else: # Don't know what to do with anything not like a unit. return NotImplemented __truediv__ = __div__ __rtruediv__ = __rdiv__ def __pow__(self, power): if power == 0: return dimensionless_unscaled elif power == 1: return self._copy() if self.physical_unit == dimensionless_unscaled: return self.function_unit ** power raise UnitsError("Cannot raise a function unit " "with a physical dimension to any power but 0 or 1.") def __pos__(self): return self._copy() def to_string(self, format='generic'): """ Output the unit in the given format as a string. The physical unit is appended, within parentheses, to the function unit, as in "dB(mW)", with both units set using the given format Parameters ---------- format : `astropy.units.format.Base` instance or str The name of a format or a formatter object. If not provided, defaults to the generic format. """ if format not in ('generic', 'unscaled', 'latex'): raise ValueError("Function units cannot be written in {0} format. " "Only 'generic', 'unscaled' and 'latex' are " "supported.".format(format)) self_str = self.function_unit.to_string(format) pu_str = self.physical_unit.to_string(format) if pu_str == '': pu_str = '1' if format == 'latex': self_str += r'$\mathrm{{\left( {0} \right)}}$'.format( pu_str[1:-1]) # need to strip leading and trailing "$" else: self_str += '({0})'.format(pu_str) return self_str def __str__(self): """Return string representation for unit.""" self_str = str(self.function_unit) pu_str = str(self.physical_unit) if pu_str: self_str += '({0})'.format(pu_str) return self_str def __repr__(self): # By default, try to give a representation using `Unit(<string>)`, # with string such that parsing it would give the correct FunctionUnit. if callable(self.function_unit): return 'Unit("{0}")'.format(self.to_string()) else: return '{0}("{1}"{2})'.format( self.__class__.__name__, self.physical_unit, "" if self.function_unit is self._default_function_unit else ', unit="{0}"'.format(self.function_unit)) def _repr_latex_(self): """ Generate latex representation of unit name. This is used by the IPython notebook to print a unit with a nice layout. Returns ------- Latex string """ return self.to_string('latex') def __hash__(self): return hash((self.function_unit, self.physical_unit))
def _from_fido_search_result_block(cls, sr_block, default_waveunit=None): """ Make a new :class:`DatabaseEntry` instance from a Fido search result block. Parameters ---------- sr_block : `sunpy.net.dataretriever.client.QueryResponseBlock` A query result block is usually not created directly; instead, one gets instances of ``sunpy.net.dataretriever.client.QueryResponseBlock`` by iterating over each element of a Fido search result. default_waveunit : `str`, optional The wavelength unit that is used if it cannot be found in the `sr_block`. """ # All attributes of DatabaseEntry that are not in QueryResponseBlock # are set as None for now. source = getattr(sr_block, 'source', None) provider = getattr(sr_block, 'provider', None) physobs = getattr(sr_block, 'physobs', None) if physobs is not None: physobs = str(physobs) instrument = getattr(sr_block, 'instrument', None) time_start = sr_block.time.start time_end = sr_block.time.end wavelengths = getattr(sr_block, 'wave', None) wavelength_temp = {} if isinstance(wavelength_temp, tuple): # Tuple of values wavelength_temp['wavemin'] = wavelengths[0] wavelength_temp['wavemax'] = wavelengths[1] else: # Single Value wavelength_temp['wavemin'] = wavelength_temp['wavemax'] = wavelengths final_values = {} for key, val in wavelength_temp.items(): if isinstance(val, quantity.Quantity): unit = getattr(val, 'unit', None) if unit is None: if default_waveunit is not None: unit = Unit(default_waveunit) else: raise WaveunitNotFoundError(sr_block) final_values[key] = unit.to(nm, float(val.value), equivalencies.spectral()) elif val is None or np.isnan(val): final_values[key] = val wavemin = final_values['wavemin'] wavemax = final_values['wavemax'] # sr_block.url of a QueryResponseBlock attribute is stored in fileid fileid = str(sr_block.url) if sr_block.url is not None else None size = None return cls( source=source, provider=provider, physobs=physobs, fileid=fileid, observation_time_start=time_start, observation_time_end=time_end, instrument=instrument, size=size, wavemin=wavemin, wavemax=wavemax)
def _from_fido_search_result_block(cls, sr_block, default_waveunit=None): """ Make a new :class:`DatabaseEntry` instance from a Fido search result block. Parameters ---------- sr_block : `sunpy.net.dataretriever.client.QueryResponseBlock` A query result block is usually not created directly; instead, one gets instances of ``sunpy.net.dataretriever.client.QueryResponseBlock`` by iterating over each element of a Fido search result. default_waveunit : `str`, optional The wavelength unit that is used if it cannot be found in the `sr_block`. Examples -------- >>> from sunpy.net import Fido, attrs >>> from sunpy.database.tables import DatabaseEntry >>> sr = Fido.search(attrs.Time("2012/1/1", "2012/1/2"), ... attrs.Instrument('lyra')) >>> entry = DatabaseEntry._from_fido_search_result_block(sr[0][0]) >>> entry.source 'Proba2' >>> entry.provider 'esa' >>> entry.physobs 'irradiance' >>> entry.fileid 'http://proba2.oma.be/lyra/data/bsd/2012/01/01/lyra_20120101-000000_lev2_std.fits' >>> entry.observation_time_start, entry.observation_time_end (datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 2, 0, 0)) >>> entry.instrument 'lyra' """ # All attributes of DatabaseEntry that are not in QueryResponseBlock # are set as None for now. source = getattr(sr_block, 'source', None) provider = getattr(sr_block, 'provider', None) physobs = getattr(sr_block, 'physobs', None) if physobs is not None: physobs = str(physobs) instrument = getattr(sr_block, 'instrument', None) time_start = sr_block.time.start time_end = sr_block.time.end wavelengths = getattr(sr_block, 'wave', None) wavelength_temp = {} if isinstance(wavelength_temp, tuple): # Tuple of values wavelength_temp['wavemin'] = wavelengths[0] wavelength_temp['wavemax'] = wavelengths[1] else: # Single Value wavelength_temp['wavemin'] = wavelength_temp['wavemax'] = wavelengths final_values = {} for key, val in wavelength_temp.items(): if isinstance(val, quantity.Quantity): unit = getattr(val, 'unit', None) if unit is None: if default_waveunit is not None: unit = Unit(default_waveunit) else: raise WaveunitNotFoundError(sr_block) final_values[key] = unit.to(nm, float(val.value), equivalencies.spectral()) elif val is None or np.isnan(val): final_values[key] = val wavemin = final_values['wavemin'] wavemax = final_values['wavemax'] # sr_block.url of a QueryResponseBlock attribute is stored in fileid fileid = str(sr_block.url) if sr_block.url is not None else None size = None return cls( source=source, provider=provider, physobs=physobs, fileid=fileid, observation_time_start=time_start, observation_time_end=time_end, instrument=instrument, size=size, wavemin=wavemin, wavemax=wavemax)
def _create(wlk, root, session): query = session.query(DatabaseEntry) for key, value in root.attrs.iteritems(): typ = key[0] if typ == "tag": criterion = TableTag.name.in_([value]) # `key[1]` is here the `inverted` attribute of the tag. That means # that if it is True, the given tag must not be included in the # resulting entries. if key[1]: query = query.filter(~DatabaseEntry.tags.any(criterion)) else: query = query.filter(DatabaseEntry.tags.any(criterion)) elif typ == "fitsheaderentry": key, val, inverted = value key_criterion = TableFitsHeaderEntry.key == key value_criterion = TableFitsHeaderEntry.value == val if inverted: query = query.filter( not_( and_( DatabaseEntry.fits_header_entries.any(key_criterion), DatabaseEntry.fits_header_entries.any(value_criterion), ) ) ) else: query = query.filter( and_( DatabaseEntry.fits_header_entries.any(key_criterion), DatabaseEntry.fits_header_entries.any(value_criterion), ) ) elif typ == "download time": start, end, inverted = value if inverted: query = query.filter(~DatabaseEntry.download_time.between(start, end)) else: query = query.filter(DatabaseEntry.download_time.between(start, end)) elif typ == "path": path, inverted = value if inverted: query = query.filter(or_(DatabaseEntry.path != path, DatabaseEntry.path == None)) else: query = query.filter(DatabaseEntry.path == path) elif typ == "wave": min_, max_, unit = value waveunit = Unit(unit) # convert min_ and max_ to nm from the unit `waveunit` wavemin = waveunit.to(nm, min_, equivalencies.spectral()) wavemax = waveunit.to(nm, max_, equivalencies.spectral()) query = query.filter(and_(DatabaseEntry.wavemin >= wavemin, DatabaseEntry.wavemax <= wavemax)) elif typ == "time": start, end, near = value query = query.filter( and_(DatabaseEntry.observation_time_start < end, DatabaseEntry.observation_time_end > start) ) else: if typ.lower() not in SUPPORTED_SIMPLE_VSO_ATTRS.union(SUPPORTED_NONVSO_ATTRS): raise NotImplementedError("The attribute {0!r} is not yet supported to query a database.".format(typ)) query = query.filter_by(**{typ: value}) return query.all()
def entries_from_file(file, default_waveunit=None, time_string_parse_format=''): # Note: time_string_parse_format='' so that None won't be passed to Time.strptime # (which would make strptime freak out, if I remember correctly). """Use the headers of a FITS file to generate an iterator of :class:`sunpy.database.tables.DatabaseEntry` instances. Gathered information will be saved in the attribute `fits_header_entries`. If the key INSTRUME, WAVELNTH or DATE-OBS / DATE_OBS is available, the attribute `instrument`, `wavemin` and `wavemax` or `observation_time_start` is set, respectively. If the wavelength unit can be read, the values of `wavemin` and `wavemax` are converted to nm (nanometres). The value of the `file` parameter is used to set the attribute `path` of each generated database entry. Parameters ---------- file : str or file-like object Either a path pointing to a FITS file or a an opened file-like object. If an opened file object, its mode must be one of the following rb, rb+, or ab+. default_waveunit : str, optional The wavelength unit that is used for a header if it cannot be found. time_string_parse_format : str, optional Fallback timestamp format which will be passed to `~astropy.time.Time.strptime` if `sunpy.time.parse_time` is unable to automatically read the `date-obs` metadata. Raises ------ sunpy.database.WaveunitNotFoundError If `default_waveunit` is not given and the wavelength unit cannot be found in one of the FITS headers sunpy.WaveunitNotConvertibleError If a wavelength unit could be found but cannot be used to create an instance of the type ``astropy.units.Unit``. This can be the case for example if a FITS header has the key `WAVEUNIT` with the value `nonsense`. Examples -------- >>> from sunpy.database.tables import entries_from_file >>> import sunpy.data.sample # doctest: +REMOTE_DATA >>> entries = list(entries_from_file(sunpy.data.sample.SWAP_LEVEL1_IMAGE)) # doctest: +REMOTE_DATA >>> len(entries) # doctest: +REMOTE_DATA 1 >>> entry = entries.pop() # doctest: +REMOTE_DATA >>> entry.instrument # doctest: +REMOTE_DATA 'SWAP' >>> entry.observation_time_start, entry.observation_time_end # doctest: +REMOTE_DATA (datetime.datetime(2011, 6, 7, 6, 33, 29, 759000), None) >>> entry.wavemin, entry.wavemax # doctest: +REMOTE_DATA (17.400000000000002, 17.400000000000002) >>> len(entry.fits_header_entries) # doctest: +REMOTE_DATA 111 """ headers = fits.get_header(file) # This just checks for blank default headers # due to compression. for header in headers: if header == DEFAULT_HEADER: headers.remove(header) if isinstance(file, str): filename = file else: filename = getattr(file, 'name', None) for header in headers: entry = DatabaseEntry(path=filename) for key, value in header.items(): # Yes, it is possible to have an empty key in a FITS file. # Example: sunpy.data.sample.EIT_195_IMAGE # Don't ask me why this could be a good idea. if key == '': value = str(value) elif key == 'KEYCOMMENTS': for k, v in value.items(): entry.fits_key_comments.append(FitsKeyComment(k, v)) continue entry.fits_header_entries.append(FitsHeaderEntry(key, value)) waveunit = fits.extract_waveunit(header) entry.hdu_index = headers.index(header) if waveunit is None: waveunit = default_waveunit unit = None if waveunit is not None: try: unit = Unit(waveunit) except ValueError: raise WaveunitNotConvertibleError(waveunit) for header_entry in entry.fits_header_entries: key, value = header_entry.key, header_entry.value if key == 'INSTRUME': entry.instrument = value elif key == 'WAVELNTH': if unit is None: raise WaveunitNotFoundError(file) # use the value of `unit` to convert the wavelength to nm entry.wavemin = entry.wavemax = unit.to( nm, value, equivalencies.spectral()) # NOTE: the key DATE-END or DATE_END is not part of the official # FITS standard, but many FITS files use it in their header elif key in ('DATE-END', 'DATE_END'): try: dt = parse_time(value).datetime except ValueError: dt = Time.strptime(value, time_string_parse_format).datetime entry.observation_time_end = dt elif key in ('DATE-OBS', 'DATE_OBS'): try: dt = parse_time(value).datetime except ValueError: dt = Time.strptime(value, time_string_parse_format).datetime entry.observation_time_start = dt yield entry
def _from_query_result_block(cls, qr_block, default_waveunit=None): """Make a new :class:`DatabaseEntry` instance from a VSO query result block. The values of :attr:`wavemin` and :attr:`wavemax` are converted to nm (nanometres). Parameters ---------- qr_block : suds.sudsobject.QueryResponseBlock A query result block is usually not created directly; instead, one gets instances of ``suds.sudsobject.QueryResponseBlock`` by iterating over a VSO query result. default_waveunit : str, optional The wavelength unit that is used if it cannot be found in the `qr_block`. Examples -------- >>> from sunpy.net import vso >>> from sunpy.database.tables import DatabaseEntry >>> client = vso.VSOClient() # doctest: +REMOTE_DATA >>> qr = client.search( ... vso.attrs.Time('2001/1/1', '2001/1/2'), ... vso.attrs.Instrument('eit')) # doctest: +REMOTE_DATA >>> entry = DatabaseEntry._from_query_result_block(qr[0]) # doctest: +REMOTE_DATA >>> entry.source # doctest: +REMOTE_DATA SOHO >>> entry.provider # doctest: +REMOTE_DATA SDAC >>> entry.physobs # doctest: +REMOTE_DATA 'intensity' >>> entry.fileid # doctest: +REMOTE_DATA /archive/soho/private/data/processed/eit/lz/2001/01/efz20010101.000042 >>> entry.observation_time_start, entry.observation_time_end # doctest: +REMOTE_DATA (datetime.datetime(2001, 1, 1, 0, 0, 42), datetime.datetime(2001, 1, 1, 0, 0, 54)) >>> entry.instrument # doctest: +REMOTE_DATA EIT >>> entry.size # doctest: +REMOTE_DATA 2059.0 >>> entry.wavemin, entry.wavemax # doctest: +REMOTE_DATA (19.5, 19.5) """ time_start = timestamp2datetime('%Y%m%d%H%M%S', qr_block.time.start) if not qr_block.time.end: qr_block.time.end = qr_block.time.start time_end = timestamp2datetime('%Y%m%d%H%M%S', qr_block.time.end) wave = qr_block.wave unit = None if wave.waveunit is None: if default_waveunit is not None: unit = Unit(default_waveunit) else: # some query response blocks store the unit "kev", # but Astropy only understands "keV". See issue #766. waveunit = wave.waveunit if waveunit == "kev": waveunit = "keV" unit = Unit(waveunit) if wave.wavemin is None: wavemin = None else: if unit is None: raise WaveunitNotFoundError(qr_block) wavemin = unit.to(nm, float(wave.wavemin), equivalencies.spectral()) if wave.wavemax is None: wavemax = None else: if unit is None: raise WaveunitNotFoundError(qr_block) wavemax = unit.to(nm, float(wave.wavemax), equivalencies.spectral()) source = getattr(qr_block, 'source', None) provider = getattr(qr_block, 'provider', None) fileid = getattr(qr_block, 'fileid', None) instrument = getattr(qr_block, 'instrument', None) size = getattr(qr_block, 'size', -1) physobs = getattr(qr_block, 'physobs', None) if physobs is not None: physobs = str(physobs) return cls( source=source, provider=provider, physobs=physobs, fileid=fileid, observation_time_start=time_start, observation_time_end=time_end, instrument=instrument, size=size, wavemin=wavemin, wavemax=wavemax)