def testExceptions(self):
        """
        Test that errors are produced whenever ObservationMetaData
        parameters are overwritten in an unintentional way
        """

        metadata = {'pointingRA':[1.5], 'pointingDec':[0.5],
                    'Opsim_expmjd':[52000.0],
                    'Opsim_rotskypos':[1.3],
                    'Opsim_filter':[2],
                    'Opsim_rawseeing':[0.7]}

        obs_metadata = ObservationMetaData(phoSimMetaData=metadata,
                                           boundType='circle',
                                           boundLength=0.1)

        with self.assertRaises(RuntimeError):
            obs_metadata.pointingRA=1.2

        with self.assertRaises(RuntimeError):
            obs_metadata.pointingDec=1.2

        with self.assertRaises(RuntimeError):
            obs_metadata.rotSkyPos=1.5

        with self.assertRaises(RuntimeError):
            obs_metadata.seeing=0.5

        with self.assertRaises(RuntimeError):
            obs_metadata.setBandpassM5andSeeing()

        obs_metadata = ObservationMetaData(pointingRA=1.5,
                                           pointingDec=1.5)
Esempio n. 2
0
    def testAssignment(self):
        """
        Test that ObservationMetaData member variables get passed correctly
        """

        mjd = 5120.0
        RA = 1.5
        Dec = -1.1
        rotSkyPos = -10.0
        skyBrightness = 25.0

        testObsMD = ObservationMetaData()
        testObsMD.pointingRA = RA
        testObsMD.pointingDec = Dec
        testObsMD.rotSkyPos = rotSkyPos
        testObsMD.skyBrightness = skyBrightness
        testObsMD.mjd = mjd
        testObsMD.boundType = 'box'
        testObsMD.boundLength = [1.2, 3.0]

        self.assertAlmostEqual(testObsMD.pointingRA, RA, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)
        self.assertEqual(testObsMD.boundType, 'box')
        self.assertAlmostEqual(testObsMD.boundLength[0], 1.2, 10)
        self.assertAlmostEqual(testObsMD.boundLength[1], 3.0, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd, 10)

        # test reassignment

        testObsMD.pointingRA = RA+1.0
        testObsMD.pointingDec = Dec+1.0
        testObsMD.rotSkyPos = rotSkyPos+1.0
        testObsMD.skyBrightness = skyBrightness+1.0
        testObsMD.boundLength = 2.2
        testObsMD.boundType = 'circle'
        testObsMD.mjd = mjd + 10.0

        self.assertAlmostEqual(testObsMD.pointingRA, RA+1.0, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec+1.0, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos+1.0, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness+1.0, 10)
        self.assertEqual(testObsMD.boundType, 'circle')
        self.assertAlmostEqual(testObsMD.boundLength, 2.2, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd+10.0, 10)

        testObsMD = ObservationMetaData(mjd=mjd, pointingRA=RA,
                                        pointingDec=Dec, rotSkyPos=rotSkyPos, bandpassName='z',
                                        skyBrightness=skyBrightness)

        self.assertAlmostEqual(testObsMD.mjd.TAI, 5120.0, 10)
        self.assertAlmostEqual(testObsMD.pointingRA, 1.5, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, -1.1, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, -10.0, 10)
        self.assertEqual(testObsMD.bandpass, 'z')
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)

        # test assigning ModifiedJulianDate
        obs = ObservationMetaData()
        mjd = ModifiedJulianDate(TAI=57388.0)
        obs.mjd = mjd
        self.assertEqual(obs.mjd, mjd)

        mjd2 = ModifiedJulianDate(TAI=45000.0)
        obs.mjd = mjd2
        self.assertEqual(obs.mjd, mjd2)
        self.assertNotEqual(obs.mjd, mjd)
def do_photometry(chunk, obs_lock, obs_metadata_dict, star_lock,
                  star_data_dict, job_lock, job_dict, out_dir):
    """
    -Take a chunk from the stellar database
    -Calculate the light curves for all of the stars in the chunk
    -Write the light curves to disk
    -Save the metadata and static data to dicts to be output later

    Parameters
    ----------
    chunk -- a set of stars returned by sqlite3.fetchmany()
             the columns in chunk are:
             simobjid, hpid, sedFilename, magNorm, ebv, varParamStr,
             parallax, ra, decl
             Note: chunk must be from one hpid (healpix pixel ID)

    obs_lock -- the lock corresponding to the observation metadata
    for this healpixel

    obs_metadata_dict -- the dict where observation metadata is stored

    star_lock -- the lock corresponding to the stellar metadata for this
    healpixel

    star_data_dict -- the dict where stellar metadata (i.e. static data) is
    stored

    job_lock -- the lock used to prevent too many processes from entering
    light curve generation at once

    job_dict -- the dict keeping track of how many jobs are doing light curve
    generation

    out_dir -- directory where light curve files are to be stored

    Returns
    -------
    None
    """
    dummy_sed = sims_photUtils.Sed()

    # find the lookup file associating healpixel with obsHistID;
    # this should probably actually be passed into the method
    data_dir = '/astro/store/pogo4/danielsf/desc_dc2_truth'
    assert os.path.isdir(data_dir)
    hpid_lookup_name = os.path.join(data_dir, 'hpid_to_obsHistID_lookup.h5')
    assert os.path.isfile(hpid_lookup_name)

    metadata_dict = {}
    metadata_keys = ['obsHistID', 'ra', 'dec', 'rotTelPos', 'mjd', 'filter']
    hpid = int(chunk[0][1])
    with h5py.File(hpid_lookup_name, 'r') as in_file:
        valid_obsid = in_file['%d' % hpid][()]
        for k in metadata_keys:
            metadata_dict[k] = in_file[k][()]

    valid_obsid = np.sort(valid_obsid)
    valid_dex = np.searchsorted(metadata_dict['obsHistID'], valid_obsid)
    np.testing.assert_array_equal(metadata_dict['obsHistID'][valid_dex],
                                  valid_obsid)

    for k in metadata_dict.keys():
        metadata_dict[k] = metadata_dict[k][valid_dex]

    # generate delta_magnitude light curves
    has_dmag = False
    while not has_dmag:

        # make sure no more than 5 processes are doing this at once
        with job_lock:
            if job_dict['running_dmag'] >= 5:
                continue
            else:
                job_dict['running_dmag'] += 1
                #print('running dmag %d' % job_dict['running_dmag'])

        t_start = time.time()
        var_gen = VariabilityGenerator(chunk)
        dmag_raw = var_gen.applyVariability(var_gen.varParamStr,
                                            expmjd=metadata_dict['mjd'])

        dmag_raw = dmag_raw.transpose([1, 2, 0])
        # transpose so the columns are (star, mjd, filter)
        assert dmag_raw.shape == (len(chunk), len(metadata_dict['mjd']), 6)

        dmag = np.zeros((len(chunk), len(metadata_dict['mjd'])), dtype=float)

        for i_mjd in range(len(metadata_dict['mjd'])):
            dmag[:, i_mjd] = dmag_raw[:, i_mjd, metadata_dict['filter'][i_mjd]]

        del dmag_raw
        has_dmag = True
        with job_lock:
            job_dict['running_dmag'] -= 1
            #print('running dmag %d' % job_dict['running_dmag'])

    quiescent_fluxes = np.zeros((len(chunk), 6), dtype=float)
    for i_bp, bp in enumerate('ugrizy'):
        quiescent_fluxes[:, i_bp] = dummy_sed.fluxFromMag(
            var_gen.column_by_name('quiescent_%s' % bp))

    t_dmag = time.time() - t_start

    star_ra = np.array([c[7] for c in chunk])
    star_dec = np.array([c[8] for c in chunk])

    # Now figure out which obsHistID are observing which stars
    obs_mask = np.zeros((len(chunk), len(metadata_dict['mjd'])), dtype=bool)

    fov_radius = 2.1  # in degrees
    t_start = time.time()
    for i_obs in range(len(metadata_dict['mjd'])):
        ra = np.degrees(metadata_dict['ra'][i_obs])
        dec = np.degrees(metadata_dict['dec'][i_obs])

        ## back-of-the-envelope implementation
        ## (does not use focal plane model with chip gaps, etc.)
        #ang_dist = angularSeparation(ra, dec, star_ra, star_dec)
        #valid = ang_dist<fov_radius

        rotTel = np.degrees(metadata_dict['rotTelPos'][i_obs])
        obs_md = ObservationMetaData(pointingRA=ra,
                                     pointingDec=dec,
                                     mjd=metadata_dict['mjd'][i_obs])
        rotSky = getRotSkyPos(ra, dec, obs_md, rotTel)
        obs_md.rotSkyPos = rotSky
        chip_name = chipNameFromRaDecLSST(star_ra,
                                          star_dec,
                                          obs_metadata=obs_md).astype(str)
        valid = np.char.find(chip_name, 'None') < 0

        obs_mask[:, i_obs] = valid

    # verify that any stars with empty light curves are, in fact
    # outside of DC2
    region_of_interest = DC2_bounds()
    xyz_offenders = []
    for i_star in range(len(chunk)):
        if obs_mask[i_star].sum() == 0:
            xyz_offenders.append(
                xyz_from_ra_dec(star_ra[i_star], star_dec[i_star]))

    if len(xyz_offenders) > 0:
        xyz_offenders = np.array(xyz_offenders)
        v0 = region_of_interest.hs_list[0].contains_many_pts(xyz_offenders)
        v1 = region_of_interest.hs_list[1].contains_many_pts(xyz_offenders)
        v2 = region_of_interest.hs_list[2].contains_many_pts(xyz_offenders)
        v3 = region_of_interest.hs_list[3].contains_many_pts(xyz_offenders)
        if (v0 & v1 & v2 & v3).sum() > 0:
            msg = "\n\nsome stars in healpixel %d unobserved\n\n" % hpid
            raise RuntimeError(msg)

    t_flux = time.time()
    dflux_arr = []
    for i_star in range(len(chunk)):
        star_filter = metadata_dict['filter'][obs_mask[i_star]]
        q_flux = quiescent_fluxes[i_star][star_filter]
        assert len(q_flux) == obs_mask[i_star].sum()
        q_mag = dummy_sed.magFromFlux(q_flux)
        tot_mag = q_mag + dmag[i_star, obs_mask[i_star]]
        tot_flux = dummy_sed.fluxFromMag(tot_mag)
        dflux = tot_flux - q_flux
        dflux_arr.append(dflux)
        assert len(dflux) == obs_mask[i_star].sum()

    # store metadata in the output dicts
    with obs_lock:
        obs_metadata_dict['mjd'].append(metadata_dict['mjd'])
        obs_metadata_dict['obsHistID'].append(metadata_dict['obsHistID'])
        obs_metadata_dict['filter'].append(metadata_dict['filter'])

    with star_lock:
        local_simobjid = var_gen.column_by_name('simobjid')
        out_file_name = create_out_name(out_dir, hpid)

        # write the light curves to disk now; otherwise, the memory
        # footprint of the job becomes too large
        with h5py.File(out_file_name, 'a') as out_file:
            for i_star in range(len(local_simobjid)):
                if len(dflux_arr[i_star]) == 0:
                    continue
                simobjid = local_simobjid[i_star]
                out_file.create_dataset('%d_flux' % simobjid,
                                        data=dflux_arr[i_star])
                out_file.create_dataset(
                    '%d_obsHistID' % simobjid,
                    data=metadata_dict['obsHistID'][obs_mask[i_star]])

        star_data_dict['simobjid'].append(local_simobjid)
        star_data_dict['ra'].append(star_ra)
        star_data_dict['dec'].append(star_dec)
        for i_bp, bp in enumerate('ugrizy'):
            star_data_dict['quiescent_%s' % bp].append(quiescent_fluxes[:,
                                                                        i_bp])
    def ObservationMetaDataFromPointing(self, OpSimPointingRecord, OpSimColumns=None,
                                        boundLength=1.75, boundType='circle'):
        """
        Return instance of ObservationMetaData for an OpSim Pointing record
        from OpSim.

        Parameters
        ----------
        OpSimPointingRecord : Dictionary, mandatory
            Dictionary of values with keys corresponding to certain columns of
            the Summary table in the OpSim database. The minimal list of keys
            required for catsim to work is 'fiveSigmaDepth',
            'filtSkyBrightness', and at least one of ('finSeeing', 'FWHMeff').
            More keys defined in columnMap may be necessary for PhoSim to work.
        OpSimColumns : tuple of strings, optional, defaults to None
            The columns corresponding to the OpSim records. If None, attempts
            to obtain these from the OpSimRecord as OpSimRecord.dtype.names
        boundType : {'circle', 'box'}, optional, defaults to 'circle'
            Shape of the observation
        boundLength : scalar float, optional, defaults to 1.75
            'characteristic size' of observation field, in units of degrees.
            For boundType='circle', this is a radius, for boundType='box', this
            is a size of the box
        """

        pointing = OpSimPointingRecord
        pointing_column_names = pointing.dtype.names
        # Decide what is the name of the column in the OpSim database
        # corresponding to the Seeing. For older OpSim outputs, this is
        # 'finSeeing'. For later OpSim outputs this is 'FWHMeff'
        if OpSimColumns is None:
            OpSimColumns = pointing_column_names

        self._set_seeing_column(OpSimColumns)

        # check to make sure the OpSim pointings being supplied contain
        # the minimum required information
        for required_column in ('fieldRA', 'fieldDec', 'expMJD', 'filter'):
            if required_column not in OpSimColumns:
                raise RuntimeError("ObservationMetaDataGenerator requires that the database of "
                                   "pointings include the coluns:\nfieldRA (in radians)"
                                   "\nfieldDec (in radians)\nexpMJD\nfilter")

        # construct a raw dict of all of the OpSim columns associated with this pointing
        raw_dict = dict([(col, pointing[col]) for col in pointing_column_names])

        obs = ObservationMetaData(pointingRA=np.degrees(pointing['fieldRA']),
                                  pointingDec=np.degrees(pointing['fieldDec']),
                                  mjd=pointing['expMJD'],
                                  bandpassName=pointing['filter'],
                                  boundType=boundType,
                                  boundLength=boundLength)

        if 'fiveSigmaDepth' in pointing_column_names:
            obs.m5 = pointing['fiveSigmaDepth']
        if 'filtSkyBrightness' in pointing_column_names:
            obs.skyBrightness = pointing['filtSkyBrightness']
        if self._seeing_column in pointing_column_names:
            obs.seeing = pointing[self._seeing_column]
        if 'rotSkyPos' in pointing_column_names:
            obs.rotSkyPos = np.degrees(pointing['rotSkyPos'])

        obs.OpsimMetaData = raw_dict

        return obs
    def testAssignment(self):
        """
        Test that ObservationMetaData member variables get passed correctly
        """

        mjd = 5120.0
        RA = 1.5
        Dec = -1.1
        rotSkyPos = -10.0
        skyBrightness = 25.0

        testObsMD = ObservationMetaData()
        testObsMD.pointingRA = RA
        testObsMD.pointingDec = Dec
        testObsMD.rotSkyPos = rotSkyPos
        testObsMD.skyBrightness = skyBrightness
        testObsMD.mjd = mjd
        testObsMD.boundType = 'box'
        testObsMD.boundLength = [1.2, 3.0]

        self.assertAlmostEqual(testObsMD.pointingRA, RA, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)
        self.assertEqual(testObsMD.boundType, 'box')
        self.assertAlmostEqual(testObsMD.boundLength[0], 1.2, 10)
        self.assertAlmostEqual(testObsMD.boundLength[1], 3.0, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd, 10)

        #test reassignment

        testObsMD.pointingRA = RA+1.0
        testObsMD.pointingDec = Dec+1.0
        testObsMD.rotSkyPos = rotSkyPos+1.0
        testObsMD.skyBrightness = skyBrightness+1.0
        testObsMD.boundLength = 2.2
        testObsMD.boundType = 'circle'
        testObsMD.mjd = mjd + 10.0

        self.assertAlmostEqual(testObsMD.pointingRA, RA+1.0, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec+1.0, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos+1.0, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness+1.0, 10)
        self.assertEqual(testObsMD.boundType, 'circle')
        self.assertAlmostEqual(testObsMD.boundLength,2.2, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd+10.0, 10)

        phosimMD = OrderedDict([('pointingRA', (-2.0,float)),
                                ('pointingDec', (0.9,float)),
                                ('Opsim_rotskypos', (1.1,float)),
                                ('Opsim_expmjd',(4000.0,float)),
                                ('Opsim_filter',('g',str))])


        testObsMD.phoSimMetaData = phosimMD
        self.assertAlmostEqual(testObsMD.pointingRA, numpy.degrees(-2.0), 10)
        self.assertAlmostEqual(testObsMD.pointingDec, numpy.degrees(0.9), 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, numpy.degrees(1.1))
        self.assertAlmostEqual(testObsMD.mjd.TAI, 4000.0, 10)
        self.assertAlmostEqual(testObsMD.bandpass, 'g')

        testObsMD = ObservationMetaData(mjd=mjd, pointingRA=RA,
            pointingDec=Dec, rotSkyPos=rotSkyPos, bandpassName='z',
            skyBrightness=skyBrightness)

        self.assertAlmostEqual(testObsMD.mjd.TAI,5120.0,10)
        self.assertAlmostEqual(testObsMD.pointingRA,1.5,10)
        self.assertAlmostEqual(testObsMD.pointingDec,-1.1,10)
        self.assertAlmostEqual(testObsMD.rotSkyPos,-10.0,10)
        self.assertEqual(testObsMD.bandpass,'z')
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)

        testObsMD = ObservationMetaData()
        testObsMD.phoSimMetaData = phosimMD

        self.assertAlmostEqual(testObsMD.mjd.TAI,4000.0,10)

        #recall that pointingRA/Dec are stored as radians in phoSim metadata
        self.assertAlmostEqual(testObsMD.pointingRA,numpy.degrees(-2.0),10)
        self.assertAlmostEqual(testObsMD.pointingDec,numpy.degrees(0.9),10)
        self.assertAlmostEqual(testObsMD.rotSkyPos,numpy.degrees(1.1),10)
        self.assertEqual(testObsMD.bandpass,'g')

        testObsMD = ObservationMetaData()
        testObsMD.phoSimMetaData = phosimMD

        self.assertAlmostEqual(testObsMD.mjd.TAI,4000.0,10)

        #recall that pointingRA/Dec are stored as radians in phoSim metadata
        self.assertAlmostEqual(testObsMD.pointingRA,numpy.degrees(-2.0),10)
        self.assertAlmostEqual(testObsMD.pointingDec,numpy.degrees(0.9),10)
        self.assertAlmostEqual(testObsMD.rotSkyPos,numpy.degrees(1.1),10)
        self.assertEqual(testObsMD.bandpass,'g')


        # test assigning ModifiedJulianDate
        obs = ObservationMetaData()
        mjd = ModifiedJulianDate(TAI=57388.0)
        obs.mjd = mjd
        self.assertEqual(obs.mjd, mjd)

        mjd2 = ModifiedJulianDate(TAI=45000.0)
        obs.mjd = mjd2
        self.assertEqual(obs.mjd, mjd2)
        self.assertNotEqual(obs.mjd, mjd)
Esempio n. 6
0
    def ObservationMetaDataFromPointing(self,
                                        OpSimPointingRecord,
                                        OpSimColumns=None,
                                        boundLength=1.75,
                                        boundType='circle'):
        """
        Return instance of ObservationMetaData for an OpSim Pointing record
        from OpSim.

        Parameters
        ----------
        OpSimPointingRecord : Dictionary, mandatory
            Dictionary of values with keys corresponding to certain columns of
            the Summary table in the OpSim database. The minimal list of keys
            required for catsim to work is 'fiveSigmaDepth',
            'filtSkyBrightness', and at least one of ('finSeeing', 'FWHMeff').
            More keys defined in columnMap may be necessary for PhoSim to work.
        OpSimColumns : tuple of strings, optional, defaults to None
            The columns corresponding to the OpSim records. If None, attempts
            to obtain these from the OpSimRecord as OpSimRecord.dtype.names
        boundType : {'circle', 'box'}, optional, defaults to 'circle'
            Shape of the observation
        boundLength : scalar float, optional, defaults to 1.75
            'characteristic size' of observation field, in units of degrees.
            For boundType='circle', this is a radius, for boundType='box', this
            is a size of the box
        """

        pointing = OpSimPointingRecord
        pointing_column_names = pointing.dtype.names
        # Decide what is the name of the column in the OpSim database
        # corresponding to the Seeing. For older OpSim outputs, this is
        # 'finSeeing'. For later OpSim outputs this is 'FWHMeff'
        if OpSimColumns is None:
            OpSimColumns = pointing_column_names

        self._set_seeing_column(OpSimColumns)

        # get the names of the columns that contain the minimal schema
        # for an ObservationMetaData
        mjd_name = self.user_interface_to_opsim['expMJD'][0]
        ra_name = self.user_interface_to_opsim['fieldRA'][0]
        dec_name = self.user_interface_to_opsim['fieldDec'][0]
        filter_name = self.user_interface_to_opsim['telescopeFilter'][0]

        # check to see if angles are in degrees or radians
        if self.user_interface_to_opsim['fieldRA'][1] is None:
            in_degrees = True
        else:
            in_degrees = False

        # check to make sure the OpSim pointings being supplied contain
        # the minimum required information

        for required_column in (ra_name, dec_name, mjd_name, filter_name):
            if required_column not in OpSimColumns:
                raise RuntimeError(
                    "ObservationMetaDataGenerator requires that the database of "
                    "pointings include data for:\nfieldRA"
                    "\nfieldDec\nexpMJD\nfilter")

        # construct a raw dict of all of the OpSim columns associated with this pointing
        raw_dict = dict([(col, pointing[col])
                         for col in pointing_column_names])
        raw_dict['opsim_version'] = self.opsim_version

        if in_degrees:
            ra_val = pointing[ra_name]
            dec_val = pointing[dec_name]
        else:
            ra_val = np.degrees(pointing[ra_name])
            dec_val = np.degrees(pointing[dec_name])
        mjd_val = pointing[mjd_name]
        filter_val = pointing[filter_name]

        obs = ObservationMetaData(pointingRA=ra_val,
                                  pointingDec=dec_val,
                                  mjd=mjd_val,
                                  bandpassName=filter_val,
                                  boundType=boundType,
                                  boundLength=boundLength)

        m5_name = self.user_interface_to_opsim['m5'][0]
        rotSky_name = self.user_interface_to_opsim['rotSkyPos'][0]

        if m5_name in pointing_column_names:
            obs.m5 = pointing[m5_name]
        if 'filtSkyBrightness' in pointing_column_names:
            obs.skyBrightness = pointing['filtSkyBrightness']
        if self._seeing_column in pointing_column_names:
            obs.seeing = pointing[self._seeing_column]
        if rotSky_name in pointing_column_names:
            if in_degrees:
                obs.rotSkyPos = pointing[rotSky_name]
            else:
                obs.rotSkyPos = np.degrees(pointing[rotSky_name])

        obs.OpsimMetaData = raw_dict

        return obs
Esempio n. 7
0
    def testAssignment(self):
        """
        Test that ObservationMetaData member variables get passed correctly
        """

        mjd = 5120.0
        RA = 1.5
        Dec = -1.1
        rotSkyPos = -10.0
        skyBrightness = 25.0

        testObsMD = ObservationMetaData()
        testObsMD.pointingRA = RA
        testObsMD.pointingDec = Dec
        testObsMD.rotSkyPos = rotSkyPos
        testObsMD.skyBrightness = skyBrightness
        testObsMD.mjd = mjd
        testObsMD.boundType = 'box'
        testObsMD.boundLength = [1.2, 3.0]

        self.assertAlmostEqual(testObsMD.pointingRA, RA, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)
        self.assertEqual(testObsMD.boundType, 'box')
        self.assertAlmostEqual(testObsMD.boundLength[0], 1.2, 10)
        self.assertAlmostEqual(testObsMD.boundLength[1], 3.0, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd, 10)

        # test reassignment

        testObsMD.pointingRA = RA + 1.0
        testObsMD.pointingDec = Dec + 1.0
        testObsMD.rotSkyPos = rotSkyPos + 1.0
        testObsMD.skyBrightness = skyBrightness + 1.0
        testObsMD.boundLength = 2.2
        testObsMD.boundType = 'circle'
        testObsMD.mjd = mjd + 10.0

        self.assertAlmostEqual(testObsMD.pointingRA, RA + 1.0, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, Dec + 1.0, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, rotSkyPos + 1.0, 10)
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness + 1.0,
                               10)
        self.assertEqual(testObsMD.boundType, 'circle')
        self.assertAlmostEqual(testObsMD.boundLength, 2.2, 10)
        self.assertAlmostEqual(testObsMD.mjd.TAI, mjd + 10.0, 10)

        testObsMD = ObservationMetaData(mjd=mjd,
                                        pointingRA=RA,
                                        pointingDec=Dec,
                                        rotSkyPos=rotSkyPos,
                                        bandpassName='z',
                                        skyBrightness=skyBrightness)

        self.assertAlmostEqual(testObsMD.mjd.TAI, 5120.0, 10)
        self.assertAlmostEqual(testObsMD.pointingRA, 1.5, 10)
        self.assertAlmostEqual(testObsMD.pointingDec, -1.1, 10)
        self.assertAlmostEqual(testObsMD.rotSkyPos, -10.0, 10)
        self.assertEqual(testObsMD.bandpass, 'z')
        self.assertAlmostEqual(testObsMD.skyBrightness, skyBrightness, 10)

        # test assigning ModifiedJulianDate
        obs = ObservationMetaData()
        mjd = ModifiedJulianDate(TAI=57388.0)
        obs.mjd = mjd
        self.assertEqual(obs.mjd, mjd)

        mjd2 = ModifiedJulianDate(TAI=45000.0)
        obs.mjd = mjd2
        self.assertEqual(obs.mjd, mjd2)
        self.assertNotEqual(obs.mjd, mjd)
    def ObservationMetaDataFromPointing(self, OpSimPointingRecord, OpSimColumns=None,
                                        boundLength=1.75, boundType='circle'):
        """
        Return instance of ObservationMetaData for an OpSim Pointing record
        from OpSim.

        Parameters
        ----------
        OpSimPointingRecord : Dictionary, mandatory
            Dictionary of values with keys corresponding to certain columns of
            the Summary table in the OpSim database. The minimal list of keys
            required for catsim to work is 'fiveSigmaDepth',
            'filtSkyBrightness', and at least one of ('finSeeing', 'FWHMeff').
            More keys defined in columnMap may be necessary for PhoSim to work.
        OpSimColumns : tuple of strings, optional, defaults to None
            The columns corresponding to the OpSim records. If None, attempts
            to obtain these from the OpSimRecord as OpSimRecord.dtype.names
        boundType : {'circle', 'box'}, optional, defaults to 'circle'
            Shape of the observation
        boundLength : scalar float, optional, defaults to 1.75
            'characteristic size' of observation field, in units of degrees.
            For boundType='circle', this is a radius, for boundType='box', this
            is a size of the box
        """

        pointing = OpSimPointingRecord
        pointing_column_names = pointing.dtype.names
        # Decide what is the name of the column in the OpSim database
        # corresponding to the Seeing. For older OpSim outputs, this is
        # 'finSeeing'. For later OpSim outputs this is 'FWHMeff'
        if OpSimColumns is None:
            OpSimColumns = pointing_column_names

        self._set_seeing_column(OpSimColumns)

        # get the names of the columns that contain the minimal schema
        # for an ObservationMetaData
        mjd_name = self.user_interface_to_opsim['expMJD'][0]
        ra_name = self.user_interface_to_opsim['fieldRA'][0]
        dec_name = self.user_interface_to_opsim['fieldDec'][0]
        filter_name = self.user_interface_to_opsim['telescopeFilter'][0]

        # check to see if angles are in degrees or radians
        if self.user_interface_to_opsim['fieldRA'][1] is None:
            in_degrees = True
        else:
            in_degrees = False

        # check to make sure the OpSim pointings being supplied contain
        # the minimum required information

        for required_column in (ra_name, dec_name, mjd_name, filter_name):
            if required_column not in OpSimColumns:
                raise RuntimeError("ObservationMetaDataGenerator requires that the database of "
                                   "pointings include data for:\nfieldRA"
                                   "\nfieldDec\nexpMJD\nfilter")

        # construct a raw dict of all of the OpSim columns associated with this pointing
        raw_dict = dict([(col, pointing[col]) for col in pointing_column_names])
        raw_dict['opsim_version'] = self.opsim_version

        if in_degrees:
            ra_val = pointing[ra_name]
            dec_val = pointing[dec_name]
        else:
            ra_val = np.degrees(pointing[ra_name])
            dec_val = np.degrees(pointing[dec_name])
        mjd_val = pointing[mjd_name]
        filter_val = pointing[filter_name]

        obs = ObservationMetaData(pointingRA=ra_val,
                                  pointingDec=dec_val,
                                  mjd=mjd_val,
                                  bandpassName=filter_val,
                                  boundType=boundType,
                                  boundLength=boundLength)

        m5_name = self.user_interface_to_opsim['m5'][0]
        rotSky_name = self.user_interface_to_opsim['rotSkyPos'][0]

        if m5_name in pointing_column_names:
            obs.m5 = pointing[m5_name]
        if 'filtSkyBrightness' in pointing_column_names:
            obs.skyBrightness = pointing['filtSkyBrightness']
        if self._seeing_column in pointing_column_names:
            obs.seeing = pointing[self._seeing_column]
        if rotSky_name in pointing_column_names:
            if in_degrees:
                obs.rotSkyPos = pointing[rotSky_name]
            else:
                obs.rotSkyPos = np.degrees(pointing[rotSky_name])

        obs.OpsimMetaData = raw_dict

        return obs