Ejemplo n.º 1
0
 def test_centerpoint(self):
     """Testing finding a centerpoint from a bounding box of locations"""
     box = [[-93.207783, 44.89076], [-93.003514, 44.89076],
            [-93.003514, 44.992279], [-93.207783, 44.992279]]
     average = utils.centerpoint(box)
     self.assertEqual(average[0], 44.9415195)
     self.assertEqual(average[1], -93.1056485)
Ejemplo n.º 2
0
def get_location_from_user_timeline(username, fallback):
    """
    :param username: the string of the twitter username to follow
    :param fallback: a dict in the form of {'lat': 45.585, 'lng': -95.91, 'name': 'Morris, MN'}
                     containing a fallback in case no location can be found
    :return: a location dict in the form of {'lat': 45.585, 'lng': -95.91, 'name': 'Morris, MN'}
    """
    api = get_tweepy_api()
    # gets the 20 most recent tweets from the given profile
    timeline = api.user_timeline(screen_name=username, include_rts=False, count=20)
    for tweet in timeline:
        # if tweet has coordinates (from a smartphone)
        if tweet.coordinates is not None:
            loc = dict()
            loc['lat'] = tweet.coordinates['coordinates'][1]
            loc['lng'] = tweet.coordinates['coordinates'][0]
            loc['name'] = tweet.place.full_name
            logging.debug('Found {0}: {1}, {2}'.format(loc['name'], loc['lat'], loc['lng']))
            return loc
        # if the location is a place, not coordinates
        elif tweet.place is not None:
            point = utils.centerpoint(tweet.place.bounding_box.coordinates[0])
            loc = dict()
            loc['lat'] = point[0]
            loc['lng'] = point[1]
            loc['name'] = tweet.place.full_name
            logging.debug('Found the center of bounding box at {0}: {1}, {2}'
                          .format(loc['name'], loc['lat'], loc['lng']))
            return loc
    # fallback to hardcoded location if there is no valid data
    logging.warning('Could not find tweet with location, falling back to hardcoded location')
    return fallback
Ejemplo n.º 3
0
def get_location_from_user_timeline(username, fallback):
    """
    Load the 20 most recent tweets of a given twitter handle and return a models.WeatherLocation object of the most
    recent location. This function will find a tweet with coordinates or a place, preferring coordinates. If a location
    is not found in the most recent 20 tweets, the given fallback location will be returned.
    :type username: str
    :param username: twitter username to follow
    :type fallback: models.WeatherLocation
    :param fallback: a fallback in case no location can be found
    :return: models.WeatherLocation
    """
    api = get_tweepy_api()
    # gets the 20 most recent tweets from the given profile
    try:
        timeline = api.user_timeline(screen_name=username,
                                     include_rts=False,
                                     count=20)
        for tweet in timeline:
            # if tweet has coordinates (from a smartphone)
            if tweet.coordinates is not None:
                lat = tweet.coordinates['coordinates'][1]
                lng = tweet.coordinates['coordinates'][0]
                name = CONFIG['variable_location']['unnamed_location_name']
                # sometimes a tweet contains a coordinate, but is not in a Twitter place
                # for example, https://twitter.com/BrianMitchL/status/982664157857271810 has coordinates, but no place
                if tweet.place is not None:
                    name = tweet.place.full_name
                logging.debug('Found %s: %f, %f', name, lat, lng)
                return models.WeatherLocation(lat=lat, lng=lng, name=name)
            # if the location is a place, not coordinates
            if tweet.place is not None:
                point = utils.centerpoint(
                    tweet.place.bounding_box.coordinates[0])
                lat = point[0]
                lng = point[1]
                name = tweet.place.full_name
                logging.debug('Found the center of bounding box at %s: %f, %f',
                              name, lat, lng)
                return models.WeatherLocation(lat=lat, lng=lng, name=name)
        # fallback to hardcoded location if there is no valid data
        logging.warning(
            'Could not find tweet with location, falling back to hardcoded location'
        )
        return fallback
    except tweepy.TweepError as err:
        logging.error(err)
        logging.warning(
            'Could not find tweet with location, falling back to hardcoded location'
        )
        return fallback
Ejemplo n.º 4
0
def get_location_from_user_timeline(username, fallback):
    """
    Load the 20 most recent tweets of a given twitter handle and return a models.WeatherLocation object of the most
    recent location. This function will find a tweet with coordinates or a place, preferring coordinates. If a location
    is not found in the most recent 20 tweets, the given fallback location will be returned.
    :type username: str
    :param username: twitter username to follow
    :type fallback: models.WeatherLocation
    :param fallback: a fallback in case no location can be found
    :return: models.WeatherLocation
    """
    api = get_tweepy_api()
    # gets the 20 most recent tweets from the given profile
    try:
        timeline = api.user_timeline(screen_name=username, include_rts=False, count=20)
        for tweet in timeline:
            # if tweet has coordinates (from a smartphone)
            if tweet.coordinates is not None:
                lat = tweet.coordinates['coordinates'][1]
                lng = tweet.coordinates['coordinates'][0]
                name = CONFIG['variable_location']['unnamed_location_name']
                # sometimes a tweet contains a coordinate, but is not in a Twitter place
                # for example, https://twitter.com/BrianMitchL/status/982664157857271810 has coordinates, but no place
                if tweet.place is not None:
                    name = tweet.place.full_name
                logging.debug('Found %s: %f, %f', name, lat, lng)
                return models.WeatherLocation(lat=lat, lng=lng, name=name)
            # if the location is a place, not coordinates
            elif tweet.place is not None:
                point = utils.centerpoint(tweet.place.bounding_box.coordinates[0])
                lat = point[0]
                lng = point[1]
                name = tweet.place.full_name
                logging.debug('Found the center of bounding box at %s: %f, %f', name, lat, lng)
                return models.WeatherLocation(lat=lat, lng=lng, name=name)
        # fallback to hardcoded location if there is no valid data
        logging.warning('Could not find tweet with location, falling back to hardcoded location')
        return fallback
    except tweepy.TweepError as err:
        logging.error(err)
        logging.warning('Could not find tweet with location, falling back to hardcoded location')
        return fallback
Ejemplo n.º 5
0
def create_input_datafiles_bumps(rfn=None):
    """
        returns: list of mgsdtasets
        Didactic case, no input parameters: create the pupil & monochromatic image 
        on appropriate pixel scales.  
        Pupil and image arrays of same size, ready to FT into each 
        other losslessly.  For real data you'll need to worry about adjusting
        sizes to satify this sampling relation between the two planes.
        For finite bandwidth data image simulation will loop over wavelengths.
        For coarsely sampled pixels image simulation will need to use finer
        sampling and rebin to detector pixel sizes.
        [email protected] Jan 2019
    """
    mft = matrixDFT.MatrixFourierTransform()

    pupilradius = 50
    pupil = utils.makedisk(250, radius=pupilradius)  # D=100 pix, array 250 pix
    pupilindex = np.where(pupil >= 1)
    pupilfn = rfn + "__input_pup.fits"
    fits.writeto(pupilfn, pupil, overwrite=True)

    mgsdatasets = []

    defocus_list = (-12.0, 12.0, -10.0, 10.0, -8.0, 8.0, -6.0, 6.0, -4.0, 4.0,
                    -2.0, 2.0)
    print(defocus_list)
    number_of_d_across_D = range(
        1, 16)  # different aberrations - dia of bump in pupil

    rippleamp = 1.0  # radians, 1/6.3 waves, about 340nm at 2.12 micron  Bump height.  bad var name

    # for a variety of bumps across the pupil:
    for nwaves in number_of_d_across_D:  # preserve var name nwaves from ripple case - bad varname here.
        pupil = fits.getdata(pupilfn)
        mgsdataset = [pupilfn]  # an mgsdataset starts with the pupil file...

        rbump = 4.0 * float(
            pupilradius) / nwaves  # sigma of gaussian bump in pupil
        #print("{:.1e}".format(rbump))
        bump = utils.makegauss(250, ctr=(145.0, 145.0),
                               sigma=rbump)  # D=100 pix, array 250 pix

        rbump = 0.5 * float(pupilradius) / nwaves  # rad of disk bump in pupil
        #print("{:.1e}".format(rbump))
        bump = utils.makedisk(250, radius=rbump,
                              ctr=(145.0, 145.0))  # D=100 pix, array 250 pix

        bump = (1.0 / np.sqrt(2)) * bump / bump[pupilindex].std(
        )  # 0.5 variance aberration, same SR hit
        ripplephase = rippleamp * bump  # bad var name

        phasefn = rfn + "bump_{0:d}acrossD_peak_{1:.1f}_pha.fits".format(
            int(nwaves), rippleamp)
        fits.PrimaryHDU(ripplephase).writeto(phasefn, overwrite=True)
        mgsdataset.append(
            phasefn)  # an mgsdataset now pupil file, phase abberration file,

        # Now create images for each defocus in the list
        #
        # First a utility array, parabola P2V unity over pupil, zero outside pupil
        prad = pupilradius  # for parabola=unity at edge of active pupil 2% < unity
        center = utils.centerpoint(pupil.shape[0])
        unityP2Vparabola = np.fromfunction(
            utils.parabola2d, pupil.shape, cx=center[0],
            cy=center[1]) / (prad * prad) * pupil
        fits.writeto(rfn + "unityP2Vparabola.fits",
                     unityP2Vparabola,
                     overwrite=True)  # sanity check - write it out

        for defoc in defocus_list:  # defoc units are waves, Peak-to-Valley

            defocusphase = unityP2Vparabola * 2 * np.pi * defoc
            aberfn = "pup__bump_defoc_{:.1f}wav.fits".format(defoc)
            fits.writeto(rfn + aberfn, defocusphase, overwrite=True)

            aberfn = rfn + "pup_bump_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                int(nwaves), rippleamp, defoc)
            imagfn = rfn + "__input_" + "img_bump_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                int(nwaves), rippleamp, defoc)

            aber = defocusphase + ripplephase
            fits.writeto(aberfn, aber, overwrite=True)

            imagefield = mft.perform(pupil * np.exp(1j * aber), pupil.shape[0],
                                     pupil.shape[0])
            image = (imagefield * imagefield.conj()).real

            fits.writeto(imagfn, image / image.sum(), overwrite=True)

            mgsdataset.append((defoc, imagfn, aberfn))

        mgsdatasets.append(mgsdataset)

    # Prepare a quicklook at signal in the pairs of defocussed images:
    side = 2.0  # inches, quicklook at curvatue signal imshow

    ndefoc = len(defocus_list) // 2
    nspatfreq = len(number_of_d_across_D)
    magnif = 2.0
    fig = plt.figure(1, (nspatfreq * magnif, ndefoc * magnif))
    grid = ImageGrid(
        fig,
        111,  # similar to subplot(111)
        nrows_ncols=(ndefoc, nspatfreq),  # creates 2x2 grid of axes
        axes_pad=0.02,  # pad between axes in inch.
    )
    i = 0
    iwav = 0
    for nwaves in number_of_d_across_D:
        ifoc = 0
        maxlist = []
        for defoc in defocus_list:  # defoc units are waves, Peak-to-Valley
            if defoc > 0:
                imagfn_pos = rfn + "__input_" + "img_bump_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                    int(nwaves), rippleamp, defoc)
                imagfn_neg = rfn + "__input_" + "img_bump_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                    int(nwaves), rippleamp, -defoc)
                datapos = fits.getdata(imagfn_pos).flatten()
                dataneg = fits.getdata(imagfn_neg).flatten()
                quicklook = (datapos - dataneg[::-1]).reshape((250, 250)) * (
                    defoc * defoc)  # normalize to equal brightness signal
                maxlist.append(quicklook.max())
                print("max {:.1e}".format(quicklook.max())
                      )  # print me out and fix the limits for imshow by hand.
                #fits.writeto(imagfn_pos.replace("img","qlk"), quicklook, overwrite=True)
                i = iwav + (ndefoc - ifoc - 1) * nspatfreq
                grid[i].imshow(
                    quicklook,
                    origin='lower',
                    cmap=plt.get_cmap(
                        'ocean'),  # RdBu, gist_rainbow, ocean, none
                    vmin=-3.0e-3,
                    vmax=3.0e-3)  # The AxesGrid object work as a list of axes.
                grid[i].set_xticks([])
                grid[i].set_yticks([])
                grid[i].text(20,
                             220,
                             "D/{:d} dia bump".format(int(nwaves + 1)),
                             color='y',
                             weight='bold')
                grid[i].text(20,
                             20,
                             "+/-{:d}w PV".format(int(defoc)),
                             color='w',
                             weight='bold')
                #print('iwav', iwav, 'ifoc', ifoc, 'i:', i)
                ifoc += 1
        iwav += 1
    strtop = "Wavefront signal from piston phase bumps of different diameters vs. defocus either side of focus"
    strbot = "Anand S. 2019, after Dean & Bowers, JOSA A 2003 (figs. 4 & 7)"
    fig.text(0.02, 0.94, strtop, fontsize=18, weight='bold')
    fig.text(0.02, 0.05, strbot, fontsize=14)
    plt.tight_layout()
    plt.savefig("DeanBowers2003_signal_vs_defocus_bump.png",
                dpi=150,
                pad_inches=1.0)
    plt.show()

    #print("Unity P-V parabola:", arraystats(unityP2Vparabola))
    """
    for dataset in mgsdatasets: 
        print("MGS data set: pupilfn, aber, (defoc/PVwaves, imagefn, defoc+aber), (repeats)")
        for thing in dataset: 
            print("\t", thing)
        print("")
    """
    return mgsdatasets
Ejemplo n.º 6
0
def create_input_datafiles(rfn=None):
    """
        returns: list of mgsdtasets
            pupilfn: name of pupil file
        Pupil and image data, true (zero mean) phase map (rad) 
        No de-tilting of the thase done.
        Didactic case, no input parameters: create the pupil & monochromatic image 
        on appropriate pixel scales.  
        Pupil and image arrays of same size, ready to FT into each 
        other losslessly.  For real data you'll need to worry about adjusting
        sizes to satify this sampling relation between the two planes.
        For finite bandwidth data image simulation will loop over wavelengths.
        For coarsely sampled pixels image simulation will need to use finer
        sampling and rebin to detector pixel sizes.
        [email protected] Jan 2019
    """
    mft = matrixDFT.MatrixFourierTransform()

    pupilradius = 50
    pupil = utils.makedisk(250, radius=pupilradius)  # D=100 pix, array 250 pix
    pupilfn = rfn + "__input_pup.fits"
    fits.writeto(pupilfn, pupil, overwrite=True)

    mgsdatasets = []

    dfoc_max = 12
    nfoci = 8  # number of defocus steps, in geo prog
    ffac = pow(10, np.log10(dfoc_max) / nfoci)
    defocus_list = []
    for i in range(nfoci):
        defocus_list.append(ffac)
        defocus_list.append(-ffac)
        ffac *= pow(10, np.log10(dfoc_max) / nfoci)
    defocus_list.reverse()
    defocus_list = (-12.0, 12.0, -10.0, 10.0, -8.0, 8.0, -6.0, 6.0, -4.0, 4.0,
                    -2.0, 2.0)
    print(defocus_list)
    number_of_waves_across_D = range(
        1, 16)  # different aberrations - number of waves across pupil
    print(number_of_waves_across_D)

    rippleamp = 1.0  # radians, 1/6.3 waves, about 340nm at 2.12 micron
    ripplepha = 30.0  # degrees, just for fun
    rippleangle = 15.0  # degrees

    # for a variety of ripples across the pupil:
    for nwaves in number_of_waves_across_D:
        pupil = fits.getdata(pupilfn)
        mgsdataset = [pupilfn]  # an mgsdataset starts with the pupil file...
        spatialwavelen = 2.0 * pupilradius / nwaves
        khat = np.array((np.sin(rippleangle * np.pi / 180.0),
                         np.cos(rippleangle * np.pi / 180.0)))  # unit vector
        kwavedata = np.fromfunction(utils.kwave2d,
                                    pupil.shape,
                                    spatialwavelen=spatialwavelen,
                                    center=utils.centerpoint(pupil.shape[0]),
                                    offset=ripplepha,
                                    khat=khat)
        ripplephase = pupil * rippleamp * kwavedata
        #imagefield = ft.perform(pupilarray, fp_size_reselt, npup)  # remove this
        #image_intensity = (imagefield*imagefield.conj()).real  # remove this
        #psf = image_intensity / image_intensity.sum()  # total intensity unity  # remove this

        phasefn = rfn + "ripple_{0:d}acrossD_peak_{1:.1f}_pha.fits".format(
            int(nwaves), rippleamp)
        fits.PrimaryHDU(ripplephase).writeto(phasefn, overwrite=True)
        mgsdataset.append(
            phasefn)  # an mgsdataset now pupil file, phase abberration file,

        # Now create images for each defocus in the list
        #
        # First a utility array, parabola P2V unity over pupil, zero outside pupil
        prad = pupilradius  # for parabola=unity at edge of active pupil 2% < unity
        center = utils.centerpoint(pupil.shape[0])
        unityP2Vparabola = np.fromfunction(
            utils.parabola2d, pupil.shape, cx=center[0],
            cy=center[1]) / (prad * prad) * pupil
        fits.writeto(rfn + "unityP2Vparabola.fits",
                     unityP2Vparabola,
                     overwrite=True)  # sanity check - write it out

        for defoc in defocus_list:  # defoc units are waves, Peak-to-Valley

            defocusphase = unityP2Vparabola * 2 * np.pi * defoc
            aberfn = "pup_defoc_{:.1f}wav.fits".format(defoc)
            fits.writeto(rfn + aberfn, defocusphase, overwrite=True)

            aberfn = rfn + "pup_ripple_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                int(nwaves), rippleamp, defoc)
            imagfn = rfn + "__input_" + "img_ripple_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                int(nwaves), rippleamp, defoc)

            aber = defocusphase + ripplephase
            #fits.writeto(aberfn, aber, overwrite=True)

            imagefield = mft.perform(pupil * np.exp(1j * aber), pupil.shape[0],
                                     pupil.shape[0])
            image = (imagefield * imagefield.conj()).real

            fits.writeto(imagfn, image / image.sum(), overwrite=True)

            mgsdataset.append((defoc, imagfn, aberfn))

        mgsdatasets.append(mgsdataset)
        """
        phase = de_mean(phase, pupil) # zero mean phase - doesn't change image
        fits.writeto(rfn+"{:1d}__input_truepha.fits".format(pnum), phase, overwrite=True)
        mft = matrixDFT.MatrixFourierTransform()
        imagefield = mft.perform(pupil * np.exp(1j*phase), pupil.shape[0], pupil.shape[0])
        image =  (imagefield*imagefield.conj()).real 
        fits.writeto(rfn+"{:1d}__input_img.fits".format(pnum), image/image.sum(), overwrite=True)
        del mft
        """

    # Prepare a quicklook at signal in the pairs of defocussed images:
    side = 2.0  # inches, quicklook at curvatue signal imshow

    ndefoc = len(defocus_list) // 2
    nspatfreq = len(number_of_waves_across_D)
    magnif = 2.0
    fig = plt.figure(1, (nspatfreq * magnif, ndefoc * magnif))
    grid = ImageGrid(
        fig,
        111,  # similar to subplot(111)
        nrows_ncols=(ndefoc, nspatfreq),  # creates 2x2 grid of axes
        axes_pad=0.02,  # pad between axes in inch.
    )
    i = 0
    iwav = 0
    for nwaves in number_of_waves_across_D:
        ifoc = 0
        maxlist = []
        for defoc in defocus_list:  # defoc units are waves, Peak-to-Valley
            if defoc > 0:
                imagfn_pos = rfn + "__input_" + "img_ripple_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                    int(nwaves), rippleamp, defoc)
                imagfn_neg = rfn + "__input_" + "img_ripple_{0:d}acrossD_peak_{1:.1f}_defoc_{2:.1f}wav.fits".format(
                    int(nwaves), rippleamp, -defoc)
                datapos = fits.getdata(imagfn_pos).flatten()
                dataneg = fits.getdata(imagfn_neg).flatten()
                quicklook = (datapos - dataneg[::-1]).reshape((250, 250)) * (
                    defoc * defoc)  # normalize to equal brightness signal
                maxlist.append(quicklook.max())
                #print("max {:.1e}".format(quicklook.max()))
                #fits.writeto(imagfn_pos.replace("img","qlk"), quicklook, overwrite=True)
                i = iwav + (ndefoc - ifoc - 1) * nspatfreq
                grid[i].imshow(
                    quicklook,
                    origin='lower',
                    cmap=plt.get_cmap(
                        'ocean'),  # RdBu, gist_rainbow, ocean, none
                    vmin=-1.3e-2,
                    vmax=1.3e-2)  # The AxesGrid object work as a list of axes.
                grid[i].set_xticks([])
                grid[i].set_yticks([])
                grid[i].text(20,
                             220,
                             "{:d} ripples ax D".format(int(nwaves)),
                             color='y',
                             weight='bold')
                grid[i].text(20,
                             20,
                             "+/-{:d}w PV".format(int(defoc)),
                             color='w',
                             weight='bold')

                ifoc += 1
        iwav += 1
    strtop = "Wavefront signal from phase ripples across the pupil vs. defocus either side of focus"
    strbot = "Anand S. 2019, illustrating Dean & Bowers, JOSA A 2003 (figs. 4 & 7)"
    fig.text(0.02, 0.94, strtop, fontsize=18, weight='bold')
    fig.text(0.02, 0.05, strbot, fontsize=14)
    plt.tight_layout()
    plt.savefig("DeanBowers2003_signal_vs_defocus_ripple.pdf",
                dpi=150,
                pad_inches=1.0)
    plt.show()

    #print("Unity P-V parabola:", arraystats(unityP2Vparabola))
    """
    for dataset in mgsdatasets: 
        print("MGS data set: pupilfn, aber, (defoc/PVwaves, imagefn, defoc+aber), (repeats)")
        for thing in dataset: 
            print("\t", thing)
        print("")
    """
    return mgsdatasets
Ejemplo n.º 7
0
def exer6(odir):
    """ Coronagraph train, no optimization for speed.  
    2nd order BLC, didactic example, fftlike """
    # instantiate an mft object:
    ft = matrixDFT.MatrixFourierTransform()

    npup = 250 # Size of all arrays
    radius = 50.0

    # Numerical reselts in DFT setup cf telescope reselts:
    # reselts of telescope - here its 0.4 reselts per DFT output image pixel if npup=250,radius=50.
    dftpixel = 2.0 * radius / npup
    # Jinc first zero in reselts of telescope...
    firstzero_optical_reselts = 10.0
    firstzero_numericalpixels = firstzero_optical_reselts / dftpixel
    print("Jinc firstzero_numericalpixels", firstzero_numericalpixels)

    jinc = np.fromfunction(utils.Jinc, (npup,npup),
                           c=utils.centerpoint(npup),
                           scale=firstzero_numericalpixels)
    fpm_blc2ndorder = 1 - jinc*jinc
    print("Jinc fpm min = ", fpm_blc2ndorder.min(), 
          "Jinc fpm max = ", fpm_blc2ndorder.max())

    # Pupil, Pupilphase, Apodizer, FP intensity, Intensity after FPM, 
    # Lyot intensity, Lyot Stop, Post-Lyot Stop Intensity, Final image.
    #
    # Set up optical train for a typical Lyot style or phase mask coronagraph:
    Cordict = {
        "Pupil": utils.makedisk(npup, radius=radius),
        "Pupilphase": None,
        "Apodizer": None,
        "FPintensity": None,
        "FPM": fpm_blc2ndorder,
        "LyotIntensity": None,
        "LyotStop":  utils.makedisk(npup, radius=41),
        "PostLyotStopIntensity": None,
        "FinalImage": None,
        "ContrastImage": None}


    # Propagate through the coronagraph train...
    # Start with perfect incoming plane wave, no aberrations
    efield = Cordict["Pupil"]
    # Put in phase aberrations:
    if Cordict["Pupilphase"] is not None:
        efield *= np.exp(1j*Cordict["Pupilphase"])
    # Apodize the entrance pupil:
    if Cordict["Apodizer"] is not None:
        efield *= Cordict["Apodizer"]
    # PROPAGATE TO FIRST FOCAL PLANE:
    efield = ft.perform(efield, npup, npup)
    # Store FPM intensity:
    Cordict["FPintensity"] = (efield * efield.conj()).real

    # Save no-Cor efield for normalization of cor image by peak of no-FPM image
    efield_NC = efield.copy()
    # Multiply by FPM transmission function
    # Lyot style - zero in center, phase mask style: zero integral over domain
    efield *=  Cordict["FPM"]

    # PROPAGATE TO LYOT PLANE:
    efield_NC = ft.perform(efield_NC, npup, npup)
    efield = ft.perform(efield, npup, npup)
    # Save Cor Lyot intensity;
    Cordict["LyotIntensity"] = (efield * efield.conj()).real
    # Apply Lyot stop:
    if Cordict["LyotStop"] is not None: efield_NC *= Cordict["LyotStop"]
    if Cordict["LyotStop"] is not None: efield *= Cordict["LyotStop"]
    # Save Cor Lyot intensity after applying Lyot stop;
    Cordict["PostLyotStopIntensity"] = (efield * efield.conj()).real

    # PROPAGATE TO FINAL IMAGE PLANE:
    efield_NC = ft.perform(efield_NC, npup, npup)
    efield = ft.perform(efield, npup, npup)
    final_image_intensity_NC = (efield_NC * efield_NC.conj()).real
    final_image_intensity = (efield * efield.conj()).real
    Cordict["FinalImage"] = (efield * efield.conj()).real
    Cordict["ContrastImage"] = (efield * efield.conj()).real / final_image_intensity_NC.max()

    # Write our coronagraph planes:
    planenames, cube = corcube(Cordict)
    # write planemames as fits keywords
    print(odir+"/ex6_BLC_2ndOrder.fits")
    fits.PrimaryHDU(cube).writeto(odir+"/ex6_BLC_2ndOrder.fits", overwrite=True)
    fobj = fits.open(odir+"/ex6_BLC_2ndOrder.fits")
    fobj[0].header["Pupil"] = 1
    fobj[0].header["FPI"] = (2, "focal plane Intensity")
    fobj[0].header["FPM"] = (3, "focal plane mask")
    fobj[0].header["LyotIntn"] = (4, "Lyot Intensity")
    fobj[0].header["LyotStop"] = 5
    fobj[0].header["PostLyot"] = (6, "Post Lyot Stop Intensity")
    fobj[0].header["CorIm"] = (7, "Raw cor image")
    fobj[0].header["Contrast"] = (8, "Cor image in contrast units")
    fobj.writeto(odir+"/ex6_BLC_2ndOrder.fits", overwrite=True)