Instruction:
Write a Python 3 script for
closing the specified cursor and removes it from the self. cursors dictionary.

Response:
def close_cursor(self, handle):
        '''
        Closes the cursor specified and removes it from the `self.cursors`
        dictionary.

        '''

        if handle in self.cursors:
            self.cursors[handle].close()
        else:
            raise KeyError('cursor with handle %s was not found' % handle)


Instruction:
Can you tell what is the following Python 3 function doing
def tap_query(querystr,
              simbad_mirror='simbad',
              returnformat='csv',
              forcefetch=False,
              cachedir='~/.astrobase/simbad-cache',
              verbose=True,
              timeout=10.0,
              refresh=2.0,
              maxtimeout=90.0,
              maxtries=3,
              complete_query_later=False,
              jitter=5.0):
    '''This queries the SIMBAD TAP service using the ADQL query string provided.

    Parameters
    ----------

    querystr : str
        This is the ADQL query string. See:
        http://www.ivoa.net/documents/ADQL/2.0 for the specification.

    simbad_mirror : str
        This is the key used to select a SIMBAD mirror from the
        `SIMBAD_URLS` dict above. If set, the specified mirror will be used. If
        None, a random mirror chosen from that dict will be used.

    returnformat : {'csv','votable','json'}
        The returned file format to request from the GAIA catalog service.

    forcefetch : bool
        If this is True, the query will be retried even if cached results for
        it exist.

    cachedir : str
        This points to the directory where results will be downloaded.

    verbose : bool
        If True, will indicate progress and warn of any issues.

    timeout : float
        This sets the amount of time in seconds to wait for the service to
        respond to our initial request.

    refresh : float
        This sets the amount of time in seconds to wait before checking if the
        result file is available. If the results file isn't available after
        `refresh` seconds have elapsed, the function will wait for `refresh`
        seconds continuously, until `maxtimeout` is reached or the results file
        becomes available.

    maxtimeout : float
        The maximum amount of time in seconds to wait for a result to become
        available after submitting our query request.

    maxtries : int
        The maximum number of tries (across all mirrors tried) to make to either
        submit the request or download the results, before giving up.

    complete_query_later : bool
        If set to True, a submitted query that does not return a result before
        `maxtimeout` has passed will be cancelled but its input request
        parameters and the result URL provided by the service will be saved. If
        this function is then called later with these same input request
        parameters, it will check if the query finally finished and a result is
        available. If so, will download the results instead of submitting a new
        query. If it's not done yet, will start waiting for results again. To
        force launch a new query with the same request parameters, set the
        `forcefetch` kwarg to True.

    jitter : float
        This is used to control the scale of the random wait in seconds before
        starting the query. Useful in parallelized situations.

    Returns
    -------

    dict
        This returns a dict of the following form::

            {'params':dict of the input params used for the query,
             'provenance':'cache' or 'new download',
             'result':path to the file on disk with the downloaded data table}

    '''

    # get the default params
    inputparams = TAP_PARAMS.copy()

    # update them with our input params

    inputparams['QUERY'] = querystr[::]

    if returnformat in RETURN_FORMATS:
        inputparams['FORMAT'] = returnformat
    else:
        LOGWARNING('unknown result format: %s requested, using CSV' %
                   returnformat)
        inputparams['FORMAT'] = 'csv'

    # see if the cachedir exists
    if '~' in cachedir:
        cachedir = os.path.expanduser(cachedir)
    if not os.path.exists(cachedir):
        os.makedirs(cachedir)

    # generate the cachefname and look for it
    xcachekey = '-'.join([repr(inputparams[x])
                         for x in sorted(inputparams.keys())])
    cachekey = hashlib.sha256(xcachekey.encode()).hexdigest()
    cachefname = os.path.join(
        cachedir,
        '%s.%s' % (cachekey, RETURN_FORMATS[returnformat])
    )
    provenance = 'cache'

    incomplete_qpklf = os.path.join(
        cachedir,
        'incomplete-query-%s' % cachekey
    )

    ##########################################
    ## COMPLETE A QUERY THAT MAY BE RUNNING ##
    ##########################################

    # first, check if this query can be resurrected
    if (not forcefetch and
        complete_query_later and
        os.path.exists(incomplete_qpklf)):

        with open(incomplete_qpklf, 'rb') as infd:
            incomplete_qinfo = pickle.load(infd)

        LOGWARNING('complete_query_later = True, and '
                   'this query was not completed on a '
                   'previous run, will check if it is done now...')

        # get the status URL and go into a loop to see if the query completed
        waitdone = False
        timeelapsed = 0.0

        simbad_mirror = incomplete_qinfo['simbad_mirror']
        status_url = incomplete_qinfo['status_url']
        phasekeyword = incomplete_qinfo['phase_keyword']
        resultkeyword = incomplete_qinfo['result_keyword']

        while not waitdone:

            if timeelapsed > maxtimeout:

                LOGERROR('SIMBAD TAP query still not done '
                         'after waiting %s seconds for results.\n'
                         'status URL is: %s' %
                         (maxtimeout,
                          repr(inputparams),
                          status_url))

                return None

            try:

                resreq = requests.get(status_url,
                                      timeout=timeout)

                resreq.raise_for_status()

                # parse the response XML and get the job status
                resxml = parseString(resreq.text)

                jobstatuselem = (
                    resxml.getElementsByTagName(phasekeyword)[0]
                )
                jobstatus = jobstatuselem.firstChild.toxml()

                if jobstatus == 'COMPLETED':

                    if verbose:

                        LOGINFO('SIMBAD query completed, '
                                'retrieving results...')
                    waitdone = True

                # if we're not done yet, then wait some more
                elif jobstatus != 'ERROR':

                    if verbose:
                        LOGINFO('elapsed time: %.1f, '
                                'current status: %s, '
                                'status URL: %s, waiting...'
                                % (timeelapsed, jobstatus, status_url))

                    time.sleep(refresh)
                    timeelapsed = timeelapsed + refresh

                # if the JOB failed, then bail out immediately
                else:

                    LOGERROR('SIMBAD TAP query failed due to a server error.\n'
                             'status URL: %s\n'
                             'status contents: %s' %
                             (status_url,
                              resreq.text))

                    # since this job failed, remove the incomplete query pickle
                    # so we can try this from scratch
                    os.remove(incomplete_qpklf)

                    return None

            except requests.exceptions.Timeout as e:

                LOGEXCEPTION(
                    'SIMBAD query timed out while waiting for status '
                    'download results.\n'
                    'query: %s\n'
                    'status URL: %s' %
                    (repr(inputparams), status_url)
                )

                return None

            except Exception as e:

                LOGEXCEPTION(
                    'SIMBAD query failed while waiting for status\n'
                    'query: %s\n'
                    'status URL: %s\n'
                    'status contents: %s' %
                    (repr(inputparams),
                     status_url,
                     resreq.text)
                )

                # if the query fails completely, then either the status URL
                # doesn't exist any more or something else went wrong. we'll
                # remove the incomplete query pickle so we can try this from
                # scratch
                os.remove(incomplete_qpklf)

                return None

        #
        # at this point, we should be ready to get the query results
        #
        LOGINFO('query completed, retrieving results...')
        result_url_elem = resxml.getElementsByTagName(resultkeyword)[0]
        result_url = result_url_elem.getAttribute('xlink:href')
        result_nrows = result_url_elem.getAttribute('rows')

        try:

            resreq = requests.get(result_url, timeout=timeout)
            resreq.raise_for_status()

            if cachefname.endswith('.gz'):

                with gzip.open(cachefname,'wb') as outfd:
                    for chunk in resreq.iter_content(chunk_size=65536):
                        outfd.write(chunk)

            else:

                with open(cachefname,'wb') as outfd:
                    for chunk in resreq.iter_content(chunk_size=65536):
                        outfd.write(chunk)

            if verbose:
                LOGINFO('done. rows in result: %s' % result_nrows)
            tablefname = cachefname

            provenance = 'cache'

            # return a dict pointing to the result file
            # we'll parse this later
            resdict = {'params':inputparams,
                       'provenance':provenance,
                       'result':tablefname}

            # all went well, so we'll remove the incomplete query pickle
            os.remove(incomplete_qpklf)

            return resdict

        except requests.exceptions.Timeout as e:

            LOGEXCEPTION(
                'SIMBAD query timed out while trying to '
                'download results.\n'
                'query: %s\n'
                'result URL: %s' %
                (repr(inputparams), result_url)
            )
            return None

        except Exception as e:

            LOGEXCEPTION(
                'SIMBAD query failed because of an error '
                'while trying to download results.\n'
                'query: %s\n'
                'result URL: %s\n'
                'response status code: %s' %
                (repr(inputparams),
                 result_url,
                 resreq.status_code)
            )

            # if the result download fails, then either the result URL doesn't
            # exist any more or something else went wrong. we'll remove the
            # incomplete query pickle so we can try this from scratch
            os.remove(incomplete_qpklf)

            return None

    #####################
    ## RUN A NEW QUERY ##
    #####################

    # otherwise, we check the cache if it's done already, or run it again if not
    if forcefetch or (not os.path.exists(cachefname)):

        provenance = 'new download'
        time.sleep(random.randint(1,jitter))

        # generate a jobid here and update the input params
        jobid = 'ab-simbad-%i' % time.time()
        inputparams['JOBNAME'] = jobid
        inputparams['JOBDESCRIPTION'] = 'astrobase-simbad-tap-ADQL-query'

        try:

            waitdone = False
            timeelapsed = 0.0

            # set the simbad mirror to use
            if simbad_mirror is not None and simbad_mirror in SIMBAD_URLS:

                tapurl = SIMBAD_URLS[simbad_mirror]['url']
                resultkeyword = SIMBAD_URLS[simbad_mirror]['resultkeyword']
                phasekeyword = SIMBAD_URLS[simbad_mirror]['phasekeyword']
                randkey = simbad_mirror

                # sub in a table name if this is left unresolved in the input
                # query
                if '{table}' in querystr:
                    inputparams['QUERY'] = (
                        querystr.format(
                            table=SIMBAD_URLS[simbad_mirror]['table']
                        )
                    )

            else:

                randkey = random.choice(list(SIMBAD_URLS.keys()))
                tapurl = SIMBAD_URLS[randkey]['url']
                resultkeyword = SIMBAD_URLS[randkey]['resultkeyword']
                phasekeyword = SIMBAD_URLS[randkey]['phasekeyword']

                # sub in a table name if this is left unresolved in the input
                # query
                if '{table}' in querystr:
                    inputparams['QUERY'] = (
                        querystr.format(
                            table=SIMBAD_URLS[randkey]['table']
                        )
                    )

                if verbose:
                    LOGINFO('using SIMBAD mirror TAP URL: %s' % tapurl)

            # send the query and get status
            if verbose:
                LOGINFO(
                    'submitting SIMBAD TAP query request for input params: %s'
                    % repr(inputparams)
                )

            # here, we'll make sure the SIMBAD mirror works before doing
            # anything else
            mirrorok = False
            ntries = 1

            while (not mirrorok):

                if ntries > maxtries:

                    LOGERROR('maximum number of allowed SIMBAD query '
                             'submission tries (%s) reached, bailing out...' %
                             maxtries)
                    return None

                try:

                    req = requests.post(tapurl,
                                        data=inputparams,
                                        timeout=timeout)
                    resp_status = req.status_code
                    req.raise_for_status()

                    mirrorok = True

                # this handles immediate 503s
                except requests.exceptions.HTTPError as e:

                    LOGWARNING(
                        'SIMBAD TAP server: %s not responding, '
                        'trying another mirror...'
                        % tapurl
                    )
                    mirrorok = False

                    # for now, we have only one SIMBAD mirror to hit, so we'll
                    # wait a random time between 1 and 5 seconds to hit it again
                    remainingmirrors = list(SIMBAD_URLS.keys())
                    waittime = random.choice(range(1,6))
                    time.sleep(waittime)

                    randkey = remainingmirrors[0]
                    tapurl = SIMBAD_URLS[randkey]['url']
                    resultkeyword = SIMBAD_URLS[randkey]['resultkeyword']
                    phasekeyword = SIMBAD_URLS[randkey]['phasekeyword']
                    if '{table}' in querystr:
                        inputparams['QUERY'] = (
                            querystr.format(
                                table=SIMBAD_URLS[randkey]['table']
                            )
                        )

                # this handles initial query submission timeouts
                except requests.exceptions.Timeout as e:

                    LOGWARNING(
                        'SIMBAD TAP query submission timed out, '
                        'mirror is probably down. Trying another mirror...'
                    )
                    mirrorok = False

                    # for now, we have only one SIMBAD mirror to hit, so we'll
                    # wait a random time between 1 and 5 seconds to hit it again
                    remainingmirrors = list(SIMBAD_URLS.keys())
                    waittime = random.choice(range(1,6))
                    time.sleep(waittime)

                    randkey = remainingmirrors[0]
                    tapurl = SIMBAD_URLS[randkey]['url']
                    resultkeyword = SIMBAD_URLS[randkey]['resultkeyword']
                    phasekeyword = SIMBAD_URLS[randkey]['phasekeyword']
                    if '{table}' in querystr:
                        inputparams['QUERY'] = (
                            querystr.format(
                                table=SIMBAD_URLS[randkey]['table']
                            )
                        )

                # update the number of submission tries
                ntries = ntries + 1

            # NOTE: python-requests follows the "303 See Other" redirect
            # automatically, so we get the XML status doc immediately. We don't
            # need to look up the location of it in the initial response's
            # header as in the SIMBAD example.
            status_url = req.url

            # parse the response XML and get the job status
            resxml = parseString(req.text)

            jobstatuselem = resxml.getElementsByTagName(phasekeyword)

            if jobstatuselem:

                jobstatuselem = jobstatuselem[0]

            else:
                LOGERROR('could not parse job phase using '
                         'keyword %s in result XML' % phasekeyword)
                LOGERROR('%s' % req.txt)

                req.close()
                return None

            jobstatus = jobstatuselem.firstChild.toxml()

            # if the job completed already, jump down to retrieving results
            if jobstatus == 'COMPLETED':

                if verbose:

                    LOGINFO('SIMBAD query completed, '
                            'retrieving results...')

                    waitdone = True

            elif jobstatus == 'ERROR':

                if verbose:

                    LOGERROR(
                        'SIMBAD query failed immediately '
                        '(probably an ADQL error): %s, '
                        'status URL: %s, status contents: %s' %
                        (repr(inputparams),
                         status_url,
                         req.text)
                    )
                    return None

            # we wait for the job to complete if it's not done already
            else:

                if verbose:
                    LOGINFO(
                        'request submitted successfully, '
                        'current status is: %s. '
                        'waiting for results...' % jobstatus
                    )

                while not waitdone:

                    if timeelapsed > maxtimeout:

                        LOGERROR('SIMBAD TAP query timed out '
                                 'after waiting %s seconds for results.\n'
                                 'request was: %s\n'
                                 'status URL is: %s\n'
                                 'last status was: %s' %
                                 (maxtimeout,
                                  repr(inputparams),
                                  status_url,
                                  jobstatus))

                        # here, we'll check if we're allowed to sleep on a query
                        # for a bit and return to it later if the last status
                        # was QUEUED or EXECUTING
                        if complete_query_later and jobstatus in ('EXECUTING',
                                                                  'QUEUED'):

                            # write a pickle with the query params that we can
                            # pick up later to finish this query
                            incomplete_qpklf = os.path.join(
                                cachedir,
                                'incomplete-query-%s' % cachekey
                            )
                            with open(incomplete_qpklf, 'wb') as outfd:

                                savedict = inputparams.copy()

                                savedict['status_url'] = status_url
                                savedict['last_status'] = jobstatus
                                savedict['simbad_mirror'] = simbad_mirror
                                savedict['phase_keyword'] = phasekeyword
                                savedict['result_keyword'] = resultkeyword

                                pickle.dump(savedict,
                                            outfd,
                                            pickle.HIGHEST_PROTOCOL)

                            LOGINFO('complete_query_later = True, '
                                    'last state of query was: %s, '
                                    'will resume later if this function '
                                    'is called again with the same query' %
                                    jobstatus)

                        return None

                    time.sleep(refresh)
                    timeelapsed = timeelapsed + refresh

                    try:

                        resreq = requests.get(status_url, timeout=timeout)
                        resreq.raise_for_status()

                        # parse the response XML and get the job status
                        resxml = parseString(resreq.text)

                        jobstatuselem = (
                            resxml.getElementsByTagName(phasekeyword)[0]
                        )
                        jobstatus = jobstatuselem.firstChild.toxml()

                        if jobstatus == 'COMPLETED':

                            if verbose:

                                LOGINFO('SIMBAD query completed, '
                                        'retrieving results...')
                            waitdone = True

                        else:
                            if verbose:
                                LOGINFO('elapsed time: %.1f, '
                                        'current status: %s, '
                                        'status URL: %s, waiting...'
                                        % (timeelapsed, jobstatus, status_url))
                            continue

                    except requests.exceptions.Timeout as e:

                        LOGEXCEPTION(
                            'SIMBAD query timed out while waiting for results '
                            'download results.\n'
                            'query: %s\n'
                            'status URL: %s' %
                            (repr(inputparams), status_url)
                        )
                        return None

                    except Exception as e:

                        LOGEXCEPTION(
                            'SIMBAD query failed while waiting for results\n'
                            'query: %s\n'
                            'status URL: %s\n'
                            'status contents: %s' %
                            (repr(inputparams),
                             status_url,
                             resreq.text)
                        )
                        return None

            #
            # at this point, we should be ready to get the query results
            #
            result_url_elem = resxml.getElementsByTagName(resultkeyword)[0]
            result_url = result_url_elem.getAttribute('xlink:href')
            result_nrows = result_url_elem.getAttribute('rows')

            try:

                resreq = requests.get(result_url, timeout=timeout)
                resreq.raise_for_status()

                if cachefname.endswith('.gz'):

                    with gzip.open(cachefname,'wb') as outfd:
                        for chunk in resreq.iter_content(chunk_size=65536):
                            outfd.write(chunk)

                else:

                    with open(cachefname,'wb') as outfd:
                        for chunk in resreq.iter_content(chunk_size=65536):
                            outfd.write(chunk)

                if verbose:
                    LOGINFO('done. rows in result: %s' % result_nrows)
                tablefname = cachefname

            except requests.exceptions.Timeout as e:

                LOGEXCEPTION(
                    'SIMBAD query timed out while trying to '
                    'download results.\n'
                    'query: %s\n'
                    'result URL: %s' %
                    (repr(inputparams), result_url)
                )
                return None

            except Exception as e:

                LOGEXCEPTION(
                    'SIMBAD query failed because of an error '
                    'while trying to download results.\n'
                    'query: %s\n'
                    'result URL: %s\n'
                    'response status code: %s' %
                    (repr(inputparams),
                     result_url,
                     resreq.status_code)
                )
                return None

        except requests.exceptions.HTTPError as e:
            LOGEXCEPTION('SIMBAD TAP query failed.\nrequest status was: '
                         '%s.\nquery was: %s' % (resp_status,
                                                 repr(inputparams)))
            return None

        except requests.exceptions.Timeout as e:
            LOGERROR('SIMBAD TAP query submission timed out, '
                     'site is probably down. Request was: '
                     '%s' % repr(inputparams))
            return None

        except Exception as e:
            LOGEXCEPTION('SIMBAD TAP query request failed for '
                         '%s' % repr(inputparams))

            if 'resxml' in locals():
                LOGERROR('HTTP response from service:\n%s' % req.text)

            return None

    ############################
    ## GET RESULTS FROM CACHE ##
    ############################

    else:

        if verbose:
            LOGINFO('getting cached SIMBAD query result for '
                    'request: %s' %
                    (repr(inputparams)))

        tablefname = cachefname

        # try to open the cached file to make sure it's OK
        try:
            infd = gzip.open(cachefname,'rb')
            simbad_objectnames = np.genfromtxt(
                infd,
                names=True,
                delimiter=',',
                dtype='U20,f8,f8,U20,U20,U20,i8,U600,f8',
                usecols=(0,1,2,3,4,5,6,7,8),
                comments='?',  # object names can have '#' in them
            )
            infd.close()

        except Exception as e:

            LOGEXCEPTION('could not read cached SIMBAD result file: %s, '
                         'fetching from server again' % cachefname)

            return tap_query(querystr,
                             simbad_mirror=simbad_mirror,
                             returnformat=returnformat,
                             forcefetch=True,
                             cachedir=cachedir,
                             verbose=verbose,
                             timeout=timeout,
                             refresh=refresh,
                             maxtimeout=maxtimeout)

    #
    # all done with retrieval, now return the result dict
    #

    # return a dict pointing to the result file
    # we'll parse this later
    resdict = {'params':inputparams,
               'provenance':provenance,
               'result':tablefname}

    return resdict

Response: This function queries the SIMBAD TAP service using the provided ADQL query string.


Instruction:
Here you have a function in Python 3, explain what it does
def xmatch_cplist_external_catalogs(cplist,
                                    xmatchpkl,
                                    xmatchradiusarcsec=2.0,
                                    updateexisting=True,
                                    resultstodir=None):
    '''This xmatches external catalogs to a collection of checkplots.

    Parameters
    ----------

    cplist : list of str
        This is the list of checkplot pickle files to process.

    xmatchpkl : str
        The filename of a pickle prepared beforehand with the
        `checkplot.pkl_xmatch.load_xmatch_external_catalogs` function,
        containing collected external catalogs to cross-match the objects in the
        input `cplist` against.

    xmatchradiusarcsec : float
        The match radius to use for the cross-match in arcseconds.

    updateexisting : bool
        If this is True, will only update the `xmatch` dict in each checkplot
        pickle with any new cross-matches to the external catalogs. If False,
        will overwrite the `xmatch` dict with results from the current run.

    resultstodir : str or None
        If this is provided, then it must be a directory to write the resulting
        checkplots to after xmatch is done. This can be used to keep the
        original checkplots in pristine condition for some reason.

    Returns
    -------

    dict
        Returns a dict with keys = input checkplot pickle filenames and vals =
        xmatch status dict for each checkplot pickle.

    '''

    # load the external catalog
    with open(xmatchpkl,'rb') as infd:
        xmd = pickle.load(infd)

    # match each object. this is fairly fast, so this is not parallelized at the
    # moment

    status_dict = {}

    for cpf in cplist:

        cpd = _read_checkplot_picklefile(cpf)

        try:

            # match in place
            xmatch_external_catalogs(cpd, xmd,
                                     xmatchradiusarcsec=xmatchradiusarcsec,
                                     updatexmatch=updateexisting)

            for xmi in cpd['xmatch']:

                if cpd['xmatch'][xmi]['found']:
                    LOGINFO('checkplot %s: %s matched to %s, '
                            'match dist: %s arcsec' %
                            (os.path.basename(cpf),
                             cpd['objectid'],
                             cpd['xmatch'][xmi]['name'],
                             cpd['xmatch'][xmi]['distarcsec']))

                if not resultstodir:
                    outcpf = _write_checkplot_picklefile(cpd,
                                                         outfile=cpf)
                else:
                    xcpf = os.path.join(resultstodir, os.path.basename(cpf))
                    outcpf = _write_checkplot_picklefile(cpd,
                                                         outfile=xcpf)

            status_dict[cpf] = outcpf

        except Exception as e:

            LOGEXCEPTION('failed to match objects for %s' % cpf)
            status_dict[cpf] = None

    return status_dict

Response: This function matches external catalogs to a collection of checkplots.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def xmatch_cpdir_external_catalogs(cpdir,
                                   xmatchpkl,
                                   cpfileglob='checkplot-*.pkl*',
                                   xmatchradiusarcsec=2.0,
                                   updateexisting=True,
                                   resultstodir=None):
    '''This xmatches external catalogs to all checkplots in a directory.

    Parameters
    -----------

    cpdir : str
        This is the directory to search in for checkplots.

    xmatchpkl : str
        The filename of a pickle prepared beforehand with the
        `checkplot.pkl_xmatch.load_xmatch_external_catalogs` function,
        containing collected external catalogs to cross-match the objects in the
        input `cplist` against.

    cpfileglob : str
        This is the UNIX fileglob to use in searching for checkplots.

    xmatchradiusarcsec : float
        The match radius to use for the cross-match in arcseconds.

    updateexisting : bool
        If this is True, will only update the `xmatch` dict in each checkplot
        pickle with any new cross-matches to the external catalogs. If False,
        will overwrite the `xmatch` dict with results from the current run.

    resultstodir : str or None
        If this is provided, then it must be a directory to write the resulting
        checkplots to after xmatch is done. This can be used to keep the
        original checkplots in pristine condition for some reason.

    Returns
    -------

    dict
        Returns a dict with keys = input checkplot pickle filenames and vals =
        xmatch status dict for each checkplot pickle.

    '''

    cplist = glob.glob(os.path.join(cpdir, cpfileglob))

    return xmatch_cplist_external_catalogs(
        cplist,
        xmatchpkl,
        xmatchradiusarcsec=xmatchradiusarcsec,
        updateexisting=updateexisting,
        resultstodir=resultstodir
    )

Response: This function will load all external catalogs from a directory and return a dict containing the xmatch status of each external catalogs.


Instruction:
Here you have a function in Python 3, explain what it does
def colormagdiagram_cplist(cplist,
                           outpkl,
                           color_mag1=['gaiamag','sdssg'],
                           color_mag2=['kmag','kmag'],
                           yaxis_mag=['gaia_absmag','rpmj']):
    '''This makes color-mag diagrams for all checkplot pickles in the provided
    list.

    Can make an arbitrary number of CMDs given lists of x-axis colors and y-axis
    mags to use.

    Parameters
    ----------

    cplist : list of str
        This is the list of checkplot pickles to process.

    outpkl : str
        The filename of the output pickle that will contain the color-mag
        information for all objects in the checkplots specified in `cplist`.

    color_mag1 : list of str
        This a list of the keys in each checkplot's `objectinfo` dict that will
        be used as color_1 in the equation::

                x-axis color = color_mag1 - color_mag2

    color_mag2 : list of str
        This a list of the keys in each checkplot's `objectinfo` dict that will
        be used as color_2 in the equation::

                x-axis color = color_mag1 - color_mag2

    yaxis_mag : list of str
        This is a list of the keys in each checkplot's `objectinfo` dict that
        will be used as the (absolute) magnitude y-axis of the color-mag
        diagrams.

    Returns
    -------

    str
        The path to the generated CMD pickle file for the collection of objects
        in the input checkplot list.

    Notes
    -----

    This can make many CMDs in one go. For example, the default kwargs for
    `color_mag`, `color_mag2`, and `yaxis_mag` result in two CMDs generated and
    written to the output pickle file:

    - CMD1 -> gaiamag - kmag on the x-axis vs gaia_absmag on the y-axis
    - CMD2 -> sdssg - kmag on the x-axis vs rpmj (J reduced PM) on the y-axis

    '''

    # first, we'll collect all of the info
    cplist_objectids = []
    cplist_mags = []
    cplist_colors = []

    for cpf in cplist:

        cpd = _read_checkplot_picklefile(cpf)
        cplist_objectids.append(cpd['objectid'])

        thiscp_mags = []
        thiscp_colors = []

        for cm1, cm2, ym in zip(color_mag1, color_mag2, yaxis_mag):

            if (ym in cpd['objectinfo'] and
                cpd['objectinfo'][ym] is not None):
                thiscp_mags.append(cpd['objectinfo'][ym])
            else:
                thiscp_mags.append(np.nan)

            if (cm1 in cpd['objectinfo'] and
                cpd['objectinfo'][cm1] is not None and
                cm2 in cpd['objectinfo'] and
                cpd['objectinfo'][cm2] is not None):
                thiscp_colors.append(cpd['objectinfo'][cm1] -
                                     cpd['objectinfo'][cm2])
            else:
                thiscp_colors.append(np.nan)

        cplist_mags.append(thiscp_mags)
        cplist_colors.append(thiscp_colors)

    # convert these to arrays
    cplist_objectids = np.array(cplist_objectids)
    cplist_mags = np.array(cplist_mags)
    cplist_colors = np.array(cplist_colors)

    # prepare the outdict
    cmddict = {'objectids':cplist_objectids,
               'mags':cplist_mags,
               'colors':cplist_colors,
               'color_mag1':color_mag1,
               'color_mag2':color_mag2,
               'yaxis_mag':yaxis_mag}

    # save the pickled figure and dict for fast retrieval later
    with open(outpkl,'wb') as outfd:
        pickle.dump(cmddict, outfd, pickle.HIGHEST_PROTOCOL)

    plt.close('all')

    return cmddict

Response: This function will make a list of checkplot - specific colormag diagrams for all objects in a checkplot.


Instruction:
Can you tell what is the following Python 3 function doing
def add_cmd_to_checkplot(
        cpx,
        cmdpkl,
        require_cmd_magcolor=True,
        save_cmd_pngs=False
):
    '''This adds CMD figures to a checkplot dict or pickle.

    Looks up the CMDs in `cmdpkl`, adds the object from `cpx` as a gold(-ish)
    star in the plot, and then saves the figure to a base64 encoded PNG, which
    can then be read and used by the `checkplotserver`.

    Parameters
    ----------

    cpx : str or dict
        This is the input checkplot pickle or dict to add the CMD to.

    cmdpkl : str or dict
        The CMD pickle generated by the `colormagdiagram_cplist` or
        `colormagdiagram_cpdir` functions above, or the dict produced by reading
        this pickle in.

    require_cmd_magcolor : bool
        If this is True, a CMD plot will not be made if the color and mag keys
        required by the CMD are not present or are nan in this checkplot's
        objectinfo dict.

    save_cmd_png : bool
        If this is True, then will save the CMD plots that were generated and
        added back to the checkplotdict as PNGs to the same directory as
        `cpx`. If `cpx` is a dict, will save them to the current working
        directory.

    Returns
    -------

    str or dict
        If `cpx` was a str filename of checkplot pickle, this will return that
        filename to indicate that the CMD was added to the file. If `cpx` was a
        checkplotdict, this will return the checkplotdict with a new key called
        'colormagdiagram' containing the base64 encoded PNG binary streams of
        all CMDs generated.

    '''

    # get the checkplot
    if isinstance(cpx, str) and os.path.exists(cpx):
        cpdict = _read_checkplot_picklefile(cpx)
    elif isinstance(cpx, dict):
        cpdict = cpx
    else:
        LOGERROR('unknown type of checkplot provided as the cpx arg')
        return None

    # get the CMD
    if isinstance(cmdpkl, str) and os.path.exists(cmdpkl):
        with open(cmdpkl, 'rb') as infd:
            cmd = pickle.load(infd)
    elif isinstance(cmdpkl, dict):
        cmd = cmdpkl

    cpdict['colormagdiagram'] = {}

    # get the mags and colors from the CMD dict
    cplist_mags = cmd['mags']
    cplist_colors = cmd['colors']

    # now make the CMD plots for each color-mag combination in the CMD
    for c1, c2, ym, ind in zip(cmd['color_mag1'],
                               cmd['color_mag2'],
                               cmd['yaxis_mag'],
                               range(len(cmd['color_mag1']))):

        # get these from the checkplot for this object
        if (c1 in cpdict['objectinfo'] and
            cpdict['objectinfo'][c1] is not None):
            c1mag = cpdict['objectinfo'][c1]
        else:
            c1mag = np.nan

        if (c2 in cpdict['objectinfo'] and
            cpdict['objectinfo'][c2] is not None):
            c2mag = cpdict['objectinfo'][c2]
        else:
            c2mag = np.nan

        if (ym in cpdict['objectinfo'] and
            cpdict['objectinfo'][ym] is not None):
            ymmag = cpdict['objectinfo'][ym]
        else:
            ymmag = np.nan

        if (require_cmd_magcolor and
            not (np.isfinite(c1mag) and
                 np.isfinite(c2mag) and
                 np.isfinite(ymmag))):

            LOGWARNING("required color: %s-%s or mag: %s are not "
                       "in this checkplot's objectinfo dict "
                       "(objectid: %s), skipping CMD..." %
                       (c1, c2, ym, cpdict['objectid']))
            continue

        # make the CMD for this color-mag combination
        try:

            thiscmd_title = r'%s-%s/%s' % (CMD_LABELS[c1],
                                           CMD_LABELS[c2],
                                           CMD_LABELS[ym])

            # make the scatter plot
            fig = plt.figure(figsize=(10,8))
            plt.plot(cplist_colors[:,ind],
                     cplist_mags[:,ind],
                     rasterized=True,
                     marker='o',
                     linestyle='none',
                     mew=0,
                     ms=3)

            # put this object on the plot
            plt.plot([c1mag - c2mag], [ymmag],
                     ms=20,
                     color='#b0ff05',
                     marker='*',
                     mew=0)

            plt.xlabel(r'$%s - %s$' % (CMD_LABELS[c1], CMD_LABELS[c2]))
            plt.ylabel(r'$%s$' % CMD_LABELS[ym])
            plt.title('%s - $%s$ CMD' % (cpdict['objectid'], thiscmd_title))
            plt.gca().invert_yaxis()

            # now save the figure to StrIO and put it back in the checkplot
            cmdpng = StrIO()
            plt.savefig(cmdpng, bbox_inches='tight',
                        pad_inches=0.0, format='png')
            cmdpng.seek(0)
            cmdb64 = base64.b64encode(cmdpng.read())
            cmdpng.close()

            plt.close('all')
            plt.gcf().clear()

            cpdict['colormagdiagram']['%s-%s/%s' % (c1,c2,ym)] = cmdb64

            # if we're supposed to export to PNG, do so
            if save_cmd_pngs:

                if isinstance(cpx, str):
                    outpng = os.path.join(os.path.dirname(cpx),
                                          'cmd-%s-%s-%s.%s.png' %
                                          (cpdict['objectid'],
                                           c1,c2,ym))
                else:
                    outpng = 'cmd-%s-%s-%s.%s.png' % (cpdict['objectid'],
                                                      c1,c2,ym)

                _base64_to_file(cmdb64, outpng)

        except Exception as e:
            LOGEXCEPTION('CMD for %s-%s/%s does not exist in %s, skipping...' %
                         (c1, c2, ym, cmdpkl))
            continue

    #
    # end of making CMDs
    #

    if isinstance(cpx, str):
        cpf = _write_checkplot_picklefile(cpdict, outfile=cpx, protocol=4)
        return cpf
    elif isinstance(cpx, dict):
        return cpdict

Response: This function adds a CMD to a checkplot dict or pickle.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def add_cmds_cplist(cplist, cmdpkl,
                    require_cmd_magcolor=True,
                    save_cmd_pngs=False):
    '''This adds CMDs for each object in cplist.

    Parameters
    ----------

    cplist : list of str
        This is the input list of checkplot pickles to add the CMDs to.

    cmdpkl : str
        This is the filename of the CMD pickle created previously.

    require_cmd_magcolor : bool
        If this is True, a CMD plot will not be made if the color and mag keys
        required by the CMD are not present or are nan in each checkplot's
        objectinfo dict.

    save_cmd_pngs : bool
        If this is True, then will save the CMD plots that were generated and
        added back to the checkplotdict as PNGs to the same directory as
        `cpx`.

    Returns
    -------

    Nothing.

    '''

    # load the CMD first to save on IO
    with open(cmdpkl,'rb') as infd:
        cmd = pickle.load(infd)

    for cpf in cplist:

        add_cmd_to_checkplot(cpf, cmd,
                             require_cmd_magcolor=require_cmd_magcolor,
                             save_cmd_pngs=save_cmd_pngs)

Response: This function adds the CMDs for each object in cplist to the checkplotdict.


Instruction:
Can you tell what is the following Python 3 function doing
def add_cmds_cpdir(cpdir,
                   cmdpkl,
                   cpfileglob='checkplot*.pkl*',
                   require_cmd_magcolor=True,
                   save_cmd_pngs=False):
    '''This adds CMDs for each object in cpdir.

    Parameters
    ----------

    cpdir : list of str
        This is the directory to search for checkplot pickles.

    cmdpkl : str
        This is the filename of the CMD pickle created previously.

    cpfileglob : str
        The UNIX fileglob to use when searching for checkplot pickles to operate
        on.

    require_cmd_magcolor : bool
        If this is True, a CMD plot will not be made if the color and mag keys
        required by the CMD are not present or are nan in each checkplot's
        objectinfo dict.

    save_cmd_pngs : bool
        If this is True, then will save the CMD plots that were generated and
        added back to the checkplotdict as PNGs to the same directory as
        `cpx`.

    Returns
    -------

    Nothing.

    '''

    cplist = glob.glob(os.path.join(cpdir, cpfileglob))

    return add_cmds_cplist(cplist,
                           cmdpkl,
                           require_cmd_magcolor=require_cmd_magcolor,
                           save_cmd_pngs=save_cmd_pngs)

Response: This function adds CMDs for each object in cpdir.


Instruction:
Here you have a function in Python 3, explain what it does
def parallel_update_objectinfo_cplist(
        cplist,
        liststartindex=None,
        maxobjects=None,
        nworkers=NCPUS,
        fast_mode=False,
        findercmap='gray_r',
        finderconvolve=None,
        deredden_object=True,
        custom_bandpasses=None,
        gaia_submit_timeout=10.0,
        gaia_submit_tries=3,
        gaia_max_timeout=180.0,
        gaia_mirror=None,
        complete_query_later=True,
        lclistpkl=None,
        nbrradiusarcsec=60.0,
        maxnumneighbors=5,
        plotdpi=100,
        findercachedir='~/.astrobase/stamp-cache',
        verbose=True
):
    '''
    This updates objectinfo for a list of checkplots.

    Useful in cases where a previous round of GAIA/finderchart/external catalog
    acquisition failed. This will preserve the following keys in the checkplots
    if they exist:

    comments
    varinfo
    objectinfo.objecttags

    Parameters
    ----------

    cplist : list of str
        A list of checkplot pickle file names to update.

    liststartindex : int
        The index of the input list to start working at.

    maxobjects : int
        The maximum number of objects to process in this run. Use this with
        `liststartindex` to effectively distribute working on a large list of
        input checkplot pickles over several sessions or machines.

    nworkers : int
        The number of parallel workers that will work on the checkplot
        update process.

    fast_mode : bool or float
        This runs the external catalog operations in a "fast" mode, with short
        timeouts and not trying to hit external catalogs that take a long time
        to respond. See the docstring for
        `checkplot.pkl_utils._pkl_finder_objectinfo` for details on how this
        works. If this is True, will run in "fast" mode with default timeouts (5
        seconds in most cases). If this is a float, will run in "fast" mode with
        the provided timeout value in seconds.

    findercmap : str or matplotlib.cm.Colormap object

    findercmap : str or matplotlib.cm.ColorMap object
        The Colormap object to use for the finder chart image.

    finderconvolve : astropy.convolution.Kernel object or None
        If not None, the Kernel object to use for convolving the finder image.

    deredden_objects : bool
        If this is True, will use the 2MASS DUST service to get extinction
        coefficients in various bands, and then try to deredden the magnitudes
        and colors of the object already present in the checkplot's objectinfo
        dict.

    custom_bandpasses : dict
        This is a dict used to provide custom bandpass definitions for any
        magnitude measurements in the objectinfo dict that are not automatically
        recognized by the `varclass.starfeatures.color_features` function. See
        its docstring for details on the required format.

    gaia_submit_timeout : float
        Sets the timeout in seconds to use when submitting a request to look up
        the object's information to the GAIA service. Note that if `fast_mode`
        is set, this is ignored.

    gaia_submit_tries : int
        Sets the maximum number of times the GAIA services will be contacted to
        obtain this object's information. If `fast_mode` is set, this is
        ignored, and the services will be contacted only once (meaning that a
        failure to respond will be silently ignored and no GAIA data will be
        added to the checkplot's objectinfo dict).

    gaia_max_timeout : float
        Sets the timeout in seconds to use when waiting for the GAIA service to
        respond to our request for the object's information. Note that if
        `fast_mode` is set, this is ignored.

    gaia_mirror : str
        This sets the GAIA mirror to use. This is a key in the
        `services.gaia.GAIA_URLS` dict which defines the URLs to hit for each
        mirror.

    complete_query_later : bool
        If this is True, saves the state of GAIA queries that are not yet
        complete when `gaia_max_timeout` is reached while waiting for the GAIA
        service to respond to our request. A later call for GAIA info on the
        same object will attempt to pick up the results from the existing query
        if it's completed. If `fast_mode` is True, this is ignored.

    lclistpkl : dict or str
        If this is provided, must be a dict resulting from reading a catalog
        produced by the `lcproc.catalogs.make_lclist` function or a str path
        pointing to the pickle file produced by that function. This catalog is
        used to find neighbors of the current object in the current light curve
        collection. Looking at neighbors of the object within the radius
        specified by `nbrradiusarcsec` is useful for light curves produced by
        instruments that have a large pixel scale, so are susceptible to
        blending of variability and potential confusion of neighbor variability
        with that of the actual object being looked at. If this is None, no
        neighbor lookups will be performed.

    nbrradiusarcsec : float
        The radius in arcseconds to use for a search conducted around the
        coordinates of this object to look for any potential confusion and
        blending of variability amplitude caused by their proximity.

    maxnumneighbors : int
        The maximum number of neighbors that will have their light curves and
        magnitudes noted in this checkplot as potential blends with the target
        object.

    plotdpi : int
        The resolution in DPI of the plots to generate in this function
        (e.g. the finder chart, etc.)

    findercachedir : str
        The path to the astrobase cache directory for finder chart downloads
        from the NASA SkyView service.

    verbose : bool
        If True, will indicate progress and warn about potential problems.

    Returns
    -------

    list of str
        Paths to the updated checkplot pickle file.

    '''

    # work around the Darwin segfault after fork if no network activity in
    # main thread bug: https://bugs.python.org/issue30385#msg293958
    if sys.platform == 'darwin':
        import requests
        requests.get('http://captive.apple.com/hotspot-detect.html')

    # handle the start and end indices
    if (liststartindex is not None) and (maxobjects is None):
        cplist = cplist[liststartindex:]

    elif (liststartindex is None) and (maxobjects is not None):
        cplist = cplist[:maxobjects]

    elif (liststartindex is not None) and (maxobjects is not None):
        cplist = (
            cplist[liststartindex:liststartindex+maxobjects]
        )

    tasks = [(x, {'fast_mode':fast_mode,
                  'findercmap':findercmap,
                  'finderconvolve':finderconvolve,
                  'deredden_object':deredden_object,
                  'custom_bandpasses':custom_bandpasses,
                  'gaia_submit_timeout':gaia_submit_timeout,
                  'gaia_submit_tries':gaia_submit_tries,
                  'gaia_max_timeout':gaia_max_timeout,
                  'gaia_mirror':gaia_mirror,
                  'complete_query_later':complete_query_later,
                  'lclistpkl':lclistpkl,
                  'nbrradiusarcsec':nbrradiusarcsec,
                  'maxnumneighbors':maxnumneighbors,
                  'plotdpi':plotdpi,
                  'findercachedir':findercachedir,
                  'verbose':verbose}) for x in cplist]

    resultfutures = []
    results = []

    with ProcessPoolExecutor(max_workers=nworkers) as executor:
        resultfutures = executor.map(cp_objectinfo_worker, tasks)

    results = [x for x in resultfutures]

    executor.shutdown()
    return results

Response: This function updates objectinfo for a list of checkplot pickle files.


Instruction:
Given the following Python 3 function, write the documentation
def parallel_update_objectinfo_cpdir(cpdir,
                                     cpglob='checkplot-*.pkl*',
                                     liststartindex=None,
                                     maxobjects=None,
                                     nworkers=NCPUS,
                                     fast_mode=False,
                                     findercmap='gray_r',
                                     finderconvolve=None,
                                     deredden_object=True,
                                     custom_bandpasses=None,
                                     gaia_submit_timeout=10.0,
                                     gaia_submit_tries=3,
                                     gaia_max_timeout=180.0,
                                     gaia_mirror=None,
                                     complete_query_later=True,
                                     lclistpkl=None,
                                     nbrradiusarcsec=60.0,
                                     maxnumneighbors=5,
                                     plotdpi=100,
                                     findercachedir='~/.astrobase/stamp-cache',
                                     verbose=True):
    '''This updates the objectinfo for a directory of checkplot pickles.

    Useful in cases where a previous round of GAIA/finderchart/external catalog
    acquisition failed. This will preserve the following keys in the checkplots
    if they exist:

    comments
    varinfo
    objectinfo.objecttags

    Parameters
    ----------

    cpdir : str
        The directory to look for checkplot pickles in.

    cpglob : str
        The UNIX fileglob to use when searching for checkplot pickle files.

    liststartindex : int
        The index of the input list to start working at.

    maxobjects : int
        The maximum number of objects to process in this run. Use this with
        `liststartindex` to effectively distribute working on a large list of
        input checkplot pickles over several sessions or machines.

    nworkers : int
        The number of parallel workers that will work on the checkplot
        update process.

    fast_mode : bool or float
        This runs the external catalog operations in a "fast" mode, with short
        timeouts and not trying to hit external catalogs that take a long time
        to respond. See the docstring for
        `checkplot.pkl_utils._pkl_finder_objectinfo` for details on how this
        works. If this is True, will run in "fast" mode with default timeouts (5
        seconds in most cases). If this is a float, will run in "fast" mode with
        the provided timeout value in seconds.

    findercmap : str or matplotlib.cm.Colormap object

    findercmap : str or matplotlib.cm.ColorMap object
        The Colormap object to use for the finder chart image.

    finderconvolve : astropy.convolution.Kernel object or None
        If not None, the Kernel object to use for convolving the finder image.

    deredden_objects : bool
        If this is True, will use the 2MASS DUST service to get extinction
        coefficients in various bands, and then try to deredden the magnitudes
        and colors of the object already present in the checkplot's objectinfo
        dict.

    custom_bandpasses : dict
        This is a dict used to provide custom bandpass definitions for any
        magnitude measurements in the objectinfo dict that are not automatically
        recognized by the `varclass.starfeatures.color_features` function. See
        its docstring for details on the required format.

    gaia_submit_timeout : float
        Sets the timeout in seconds to use when submitting a request to look up
        the object's information to the GAIA service. Note that if `fast_mode`
        is set, this is ignored.

    gaia_submit_tries : int
        Sets the maximum number of times the GAIA services will be contacted to
        obtain this object's information. If `fast_mode` is set, this is
        ignored, and the services will be contacted only once (meaning that a
        failure to respond will be silently ignored and no GAIA data will be
        added to the checkplot's objectinfo dict).

    gaia_max_timeout : float
        Sets the timeout in seconds to use when waiting for the GAIA service to
        respond to our request for the object's information. Note that if
        `fast_mode` is set, this is ignored.

    gaia_mirror : str
        This sets the GAIA mirror to use. This is a key in the
        `services.gaia.GAIA_URLS` dict which defines the URLs to hit for each
        mirror.

    complete_query_later : bool
        If this is True, saves the state of GAIA queries that are not yet
        complete when `gaia_max_timeout` is reached while waiting for the GAIA
        service to respond to our request. A later call for GAIA info on the
        same object will attempt to pick up the results from the existing query
        if it's completed. If `fast_mode` is True, this is ignored.

    lclistpkl : dict or str
        If this is provided, must be a dict resulting from reading a catalog
        produced by the `lcproc.catalogs.make_lclist` function or a str path
        pointing to the pickle file produced by that function. This catalog is
        used to find neighbors of the current object in the current light curve
        collection. Looking at neighbors of the object within the radius
        specified by `nbrradiusarcsec` is useful for light curves produced by
        instruments that have a large pixel scale, so are susceptible to
        blending of variability and potential confusion of neighbor variability
        with that of the actual object being looked at. If this is None, no
        neighbor lookups will be performed.

    nbrradiusarcsec : float
        The radius in arcseconds to use for a search conducted around the
        coordinates of this object to look for any potential confusion and
        blending of variability amplitude caused by their proximity.

    maxnumneighbors : int
        The maximum number of neighbors that will have their light curves and
        magnitudes noted in this checkplot as potential blends with the target
        object.

    plotdpi : int
        The resolution in DPI of the plots to generate in this function
        (e.g. the finder chart, etc.)

    findercachedir : str
        The path to the astrobase cache directory for finder chart downloads
        from the NASA SkyView service.

    verbose : bool
        If True, will indicate progress and warn about potential problems.

    Returns
    -------

    list of str
        Paths to the updated checkplot pickle file.

    '''

    cplist = sorted(glob.glob(os.path.join(cpdir, cpglob)))

    return parallel_update_objectinfo_cplist(
        cplist,
        liststartindex=liststartindex,
        maxobjects=maxobjects,
        nworkers=nworkers,
        fast_mode=fast_mode,
        findercmap=findercmap,
        finderconvolve=finderconvolve,
        deredden_object=deredden_object,
        custom_bandpasses=custom_bandpasses,
        gaia_submit_timeout=gaia_submit_timeout,
        gaia_submit_tries=gaia_submit_tries,
        gaia_max_timeout=gaia_max_timeout,
        gaia_mirror=gaia_mirror,
        complete_query_later=complete_query_later,
        lclistpkl=lclistpkl,
        nbrradiusarcsec=nbrradiusarcsec,
        maxnumneighbors=maxnumneighbors,
        plotdpi=plotdpi,
        findercachedir=findercachedir,
        verbose=verbose
    )

Response: This function updates the objectinfo for a directory of checkplot pickles.


Instruction:
Can you generate the documentation for the following Python 3 function
def _gaussian(x, amp, loc, std):
    '''This is a simple gaussian.

    Parameters
    ----------

    x : np.array
        The items at which the Gaussian is evaluated.

    amp : float
        The amplitude of the Gaussian.

    loc : float
        The central value of the Gaussian.

    std : float
        The standard deviation of the Gaussian.

    Returns
    -------

    np.array
        Returns the Gaussian evaluated at the items in `x`, using the provided
        parameters of `amp`, `loc`, and `std`.

    '''

    return amp * np.exp(-((x - loc)*(x - loc))/(2.0*std*std))

Response: This function is a simple gaussian. It is a simple gaussian.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def invgauss_eclipses_func(ebparams, times, mags, errs):
    '''This returns a double eclipse shaped function.

    Suitable for first order modeling of eclipsing binaries.

    Parameters
    ----------

    ebparams : list of float
        This contains the parameters for the eclipsing binary::

            ebparams = [period (time),
                        epoch (time),
                        pdepth: primary eclipse depth (mags),
                        pduration: primary eclipse duration (phase),
                        psdepthratio: primary-secondary eclipse depth ratio,
                        secondaryphase: center phase of the secondary eclipse]

        `period` is the period in days.

        `epoch` is the time of minimum in JD.

        `pdepth` is the depth of the primary eclipse.

        - for magnitudes -> pdepth should be < 0
        - for fluxes     -> pdepth should be > 0

        `pduration` is the length of the primary eclipse in phase.

        `psdepthratio` is the ratio in the eclipse depths:
        `depth_secondary/depth_primary`. This is generally the same as the ratio
        of the `T_effs` of the two stars.

        `secondaryphase` is the phase at which the minimum of the secondary
        eclipse is located. This effectively parameterizes eccentricity.

        All of these will then have fitted values after the fit is done.

    times,mags,errs : np.array
        The input time-series of measurements and associated errors for which
        the eclipse model will be generated. The times will be used to generate
        model mags, and the input `times`, `mags`, and `errs` will be resorted
        by model phase and returned.

    Returns
    -------

    (modelmags, phase, ptimes, pmags, perrs) : tuple
        Returns the model mags and phase values. Also returns the input `times`,
        `mags`, and `errs` sorted by the model's phase.

    '''

    (period, epoch, pdepth, pduration, depthratio, secondaryphase) = ebparams

    # generate the phases
    iphase = (times - epoch)/period
    iphase = iphase - np.floor(iphase)

    phasesortind = np.argsort(iphase)
    phase = iphase[phasesortind]
    ptimes = times[phasesortind]
    pmags = mags[phasesortind]
    perrs = errs[phasesortind]

    zerolevel = np.median(pmags)
    modelmags = np.full_like(phase, zerolevel)

    primaryecl_amp = -pdepth
    secondaryecl_amp = -pdepth * depthratio

    primaryecl_std = pduration/5.0    # we use 5-sigma as full-width -> duration
    secondaryecl_std = pduration/5.0  # secondary eclipse has the same duration

    halfduration = pduration/2.0

    # phase indices
    primary_eclipse_ingress = (
        (phase >= (1.0 - halfduration)) & (phase <= 1.0)
    )
    primary_eclipse_egress = (
        (phase >= 0.0) & (phase <= halfduration)
    )

    secondary_eclipse_phase = (
        (phase >= (secondaryphase - halfduration)) &
        (phase <= (secondaryphase + halfduration))
    )

    # put in the eclipses
    modelmags[primary_eclipse_ingress] = (
        zerolevel + _gaussian(phase[primary_eclipse_ingress],
                              primaryecl_amp,
                              1.0,
                              primaryecl_std)
    )
    modelmags[primary_eclipse_egress] = (
        zerolevel + _gaussian(phase[primary_eclipse_egress],
                              primaryecl_amp,
                              0.0,
                              primaryecl_std)
    )
    modelmags[secondary_eclipse_phase] = (
        zerolevel + _gaussian(phase[secondary_eclipse_phase],
                              secondaryecl_amp,
                              secondaryphase,
                              secondaryecl_std)
    )

    return modelmags, phase, ptimes, pmags, perrs

Response: This function returns a double eclipse shaped function that can be used to generate the model mags and phase values for the eclipsing binaries.


Instruction:
Given the following Python 3 function, write the documentation
def invgauss_eclipses_residual(ebparams, times, mags, errs):
    '''This returns the residual between the modelmags and the actual mags.

    Parameters
    ----------

    ebparams : list of float
        This contains the parameters for the eclipsing binary::

            ebparams = [period (time),
                        epoch (time),
                        pdepth: primary eclipse depth (mags),
                        pduration: primary eclipse duration (phase),
                        psdepthratio: primary-secondary eclipse depth ratio,
                        secondaryphase: center phase of the secondary eclipse]

        `period` is the period in days.

        `epoch` is the time of minimum in JD.

        `pdepth` is the depth of the primary eclipse.

        - for magnitudes -> `pdepth` should be < 0
        - for fluxes     -> `pdepth` should be > 0

        `pduration` is the length of the primary eclipse in phase.

        `psdepthratio` is the ratio in the eclipse depths:
        `depth_secondary/depth_primary`. This is generally the same as the ratio
        of the `T_effs` of the two stars.

        `secondaryphase` is the phase at which the minimum of the secondary
        eclipse is located. This effectively parameterizes eccentricity.

        All of these will then have fitted values after the fit is done.

    times,mags,errs : np.array
        The input time-series of measurements and associated errors for which
        the eclipse model will be generated. The times will be used to generate
        model mags, and the input `times`, `mags`, and `errs` will be resorted
        by model phase and returned.

    Returns
    -------

    np.array
        The residuals between the input `mags` and generated `modelmags`,
        weighted by the measurement errors in `errs`.

    '''

    modelmags, phase, ptimes, pmags, perrs = (
        invgauss_eclipses_func(ebparams, times, mags, errs)
    )

    # this is now a weighted residual taking into account the measurement err
    return (pmags - modelmags)/perrs

Response: This function returns the residual between the modelmags and the actual mags.


Instruction:
Given the following Python 3 function, write the documentation
def jhk_to_bmag(jmag, hmag, kmag):
    '''Converts given J, H, Ks mags to a B magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted B band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             BJHK,
                             BJH, BJK, BHK,
                             BJ, BH, BK)

Response: Converts given J H Ks mags to a B magnitude value.


Instruction:
Can you generate the documentation for the following Python 3 function
def jhk_to_vmag(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to a V magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted V band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             VJHK,
                             VJH, VJK, VHK,
                             VJ, VH, VK)

Response: Converts given J H Ks mags to V magnitude value.


Instruction:
Create a Python 3 function for
converting given J H Ks mags to an R band magnitude value.

Response:
def jhk_to_rmag(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an R magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted R band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             RJHK,
                             RJH, RJK, RHK,
                             RJ, RH, RK)


Instruction:
Write a Python 3 script to
convert given J H Ks mags to an I band magnitude value.

Response:
def jhk_to_imag(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an I magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted I band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             IJHK,
                             IJH, IJK, IHK,
                             IJ, IH, IK)


Instruction:
Can you implement a function in Python 3 that
converts given J H Ks mags to SDSS u band magnitude value.

Response:
def jhk_to_sdssu(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an SDSS u magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted SDSS u band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             SDSSU_JHK,
                             SDSSU_JH, SDSSU_JK, SDSSU_HK,
                             SDSSU_J, SDSSU_H, SDSSU_K)


Instruction:
How would you implement a function in Python 3 that
converts given J H Ks mags to SDSS g magnitude value.

Response:
def jhk_to_sdssg(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an SDSS g magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted SDSS g band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             SDSSG_JHK,
                             SDSSG_JH, SDSSG_JK, SDSSG_HK,
                             SDSSG_J, SDSSG_H, SDSSG_K)


Instruction:
Given the following Python 3 function, write the documentation
def jhk_to_sdssr(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an SDSS r magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted SDSS r band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             SDSSR_JHK,
                             SDSSR_JH, SDSSR_JK, SDSSR_HK,
                             SDSSR_J, SDSSR_H, SDSSR_K)

Response: Converts given J H Ks mags to SDSS r magnitude value.


Instruction:
Can you generate the documentation for the following Python 3 function
def jhk_to_sdssi(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an SDSS i magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted SDSS i band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             SDSSI_JHK,
                             SDSSI_JH, SDSSI_JK, SDSSI_HK,
                             SDSSI_J, SDSSI_H, SDSSI_K)

Response: Converts given J H Ks mags to SDSS i band magnitude value.


Instruction:
Write a Python 3 script for
converting given J H Ks mags to SDSS z magnitude value.

Response:
def jhk_to_sdssz(jmag,hmag,kmag):
    '''Converts given J, H, Ks mags to an SDSS z magnitude value.

    Parameters
    ----------

    jmag,hmag,kmag : float
        2MASS J, H, Ks mags of the object.

    Returns
    -------

    float
        The converted SDSS z band magnitude.

    '''

    return convert_constants(jmag,hmag,kmag,
                             SDSSZ_JHK,
                             SDSSZ_JH, SDSSZ_JK, SDSSZ_HK,
                             SDSSZ_J, SDSSZ_H, SDSSZ_K)


Instruction:
Write a Python 3 function that can
calculate the absolute magnitude for a GAIA object.

Response:
def absolute_gaia_magnitude(gaia_mag,
                            gaia_parallax_mas,
                            gaia_mag_err=None,
                            gaia_parallax_err_mas=None):
    '''Calculates the GAIA absolute magnitude for object (or array of objects).

    Given a G mag and the parallax measured by GAIA, gets the absolute mag using
    the usual equation::

        G - M_G = 5 x log10(d_pc) - 5
        M_G = 5 - 5log10(d_pc) + G

    Parameters
    ----------

    gaia_mag : float or array-like
        The measured GAIA G magnitude.

    gaia_parallax_max : float or array-like
        The measured parallax of the object in mas.

    gaia_mag_err : float or array-like or None
        The measurement error in GAIA G magnitude.

    gaia_parallax_err_mas : float or array-like or None
        The measurement error in GAIA parallax in mas.

    Returns
    -------

    float or array-like
        The absolute magnitude M_G of the object(s).

    If both `_err` input kwargs are provided, will return a tuple of the form::

        (M_G float or array-like, M_G_err float or array-like)

    '''

    # get the distance
    # we're using the naive calculation of d. this is inaccurate as stated in
    # Bailer-Jones 2015 (http://arxiv.org/abs/1507.02105) if the error in
    # parallax is a significant fraction of parallax
    d_pc = np.abs(1000.0/gaia_parallax_mas)

    # get the distance error
    if gaia_parallax_err_mas is not None:
        d_pc_err = (
            (1000.0/(gaia_parallax_mas*gaia_parallax_mas)) *
            gaia_parallax_err_mas
        )
    else:
        d_pc_err = None

    # calculate the absolute mag from the relation
    # FIXME: this is NOT corrected for extinction in G mag. see Jordi+ 2010
    # (http://adsabs.harvard.edu/abs/2010A%26A...523A..48J) to figure out
    # A_G/A_V as a function of (V-I)_0, then apply it here
    M_G = 5 - 5.0*np.log10(d_pc) + gaia_mag

    # calculate the err in M_G
    if d_pc_err is not None and gaia_mag_err is not None:

        M_G_err = np.sqrt(
            ((5.0/(d_pc * np.log(10.0)))**2 * (d_pc_err)**2) +
            gaia_mag_err*gaia_mag_err
        )
    else:
        M_G_err = None

    if M_G_err is not None:
        return M_G, M_G_err
    else:
        return M_G


Instruction:
Implement a Python 3 function for
calculating the Schwarzenberg - Czerny AoV statistic at a test frequency.

Response:
def aov_theta(times, mags, errs, frequency,
              binsize=0.05, minbin=9):
    '''Calculates the Schwarzenberg-Czerny AoV statistic at a test frequency.

    Parameters
    ----------

    times,mags,errs : np.array
        The input time-series and associated errors.

    frequency : float
        The test frequency to calculate the theta statistic at.

    binsize : float
        The phase bin size to use.

    minbin : int
        The minimum number of items in a phase bin to consider in the
        calculation of the statistic.

    Returns
    -------

    theta_aov : float
        The value of the AoV statistic at the specified `frequency`.

    '''

    period = 1.0/frequency
    fold_time = times[0]

    phased = phase_magseries(times,
                             mags,
                             period,
                             fold_time,
                             wrap=False,
                             sort=True)

    phases = phased['phase']
    pmags = phased['mags']
    bins = nparange(0.0, 1.0, binsize)
    ndets = phases.size

    binnedphaseinds = npdigitize(phases, bins)

    bin_s1_tops = []
    bin_s2_tops = []
    binndets = []
    goodbins = 0

    all_xbar = npmedian(pmags)

    for x in npunique(binnedphaseinds):

        thisbin_inds = binnedphaseinds == x
        thisbin_mags = pmags[thisbin_inds]

        if thisbin_mags.size > minbin:

            thisbin_ndet = thisbin_mags.size
            thisbin_xbar = npmedian(thisbin_mags)

            # get s1
            thisbin_s1_top = (
                thisbin_ndet *
                (thisbin_xbar - all_xbar) *
                (thisbin_xbar - all_xbar)
            )

            # get s2
            thisbin_s2_top = npsum((thisbin_mags - all_xbar) *
                                   (thisbin_mags - all_xbar))

            bin_s1_tops.append(thisbin_s1_top)
            bin_s2_tops.append(thisbin_s2_top)
            binndets.append(thisbin_ndet)
            goodbins = goodbins + 1

    # turn the quantities into arrays
    bin_s1_tops = nparray(bin_s1_tops)
    bin_s2_tops = nparray(bin_s2_tops)
    binndets = nparray(binndets)

    # calculate s1 first
    s1 = npsum(bin_s1_tops)/(goodbins - 1.0)

    # then calculate s2
    s2 = npsum(bin_s2_tops)/(ndets - goodbins)

    theta_aov = s1/s2

    return theta_aov


Instruction:
How would you explain what the following Python 3 function does
def _aov_worker(task):
    '''This is a parallel worker for the function below.

    Parameters
    ----------

    task : tuple
        This is of the form below::

            task[0] = times
            task[1] = mags
            task[2] = errs
            task[3] = frequency
            task[4] = binsize
            task[5] = minbin

    Returns
    -------

    theta_aov : float
        The theta value at the specified frequency. nan if the calculation
        fails.

    '''

    times, mags, errs, frequency, binsize, minbin = task

    try:

        theta = aov_theta(times, mags, errs, frequency,
                          binsize=binsize, minbin=minbin)

        return theta

    except Exception as e:

        return npnan

Response: This is a parallel worker for the function below.


Instruction:
Here you have a function in Python 3, explain what it does
def get_frequency_grid(times,
                       samplesperpeak=5,
                       nyquistfactor=5,
                       minfreq=None,
                       maxfreq=None,
                       returnf0dfnf=False):
    '''This calculates a frequency grid for the period finding functions in this
    module.

    Based on the autofrequency function in astropy.stats.lombscargle.

    http://docs.astropy.org/en/stable/_modules/astropy/stats/lombscargle/core.html#LombScargle.autofrequency

    Parameters
    ----------

    times : np.array
        The times to use to generate the frequency grid over.

    samplesperpeak : int
        The minimum sample coverage each frequency point in the grid will get.

    nyquistfactor : int
        The multiplier over the Nyquist rate to use.

    minfreq,maxfreq : float or None
        If not None, these will be the limits of the frequency grid generated.

    returnf0dfnf : bool
        If this is True, will return the values of `f0`, `df`, and `Nf`
        generated for this grid.

    Returns
    -------

    np.array
        A grid of frequencies.

    '''

    baseline = times.max() - times.min()
    nsamples = times.size

    df = 1. / baseline / samplesperpeak

    if minfreq is not None:
        f0 = minfreq
    else:
        f0 = 0.5 * df

    if maxfreq is not None:
        Nf = int(np.ceil((maxfreq - f0) / df))
    else:
        Nf = int(0.5 * samplesperpeak * nyquistfactor * nsamples)

    if returnf0dfnf:
        return f0, df, Nf, f0 + df * np.arange(Nf)
    else:
        return f0 + df * np.arange(Nf)

Response: This function calculates a frequency grid for the period finding functions in this module.


Instruction:
Here you have a function in Python 3, explain what it does
def bootstrap_falsealarmprob(lspinfo,
                             times,
                             mags,
                             errs,
                             nbootstrap=250,
                             magsarefluxes=False,
                             sigclip=10.0,
                             npeaks=None):
    '''Calculates the false alarm probabilities of periodogram peaks using
    bootstrap resampling of the magnitude time series.

    The false alarm probability here is defined as::

        (1.0 + sum(trialbestpeaks[i] > peak[j]))/(ntrialbestpeaks + 1)

    for each best periodogram peak j. The index i is for each bootstrap
    trial. This effectively gives us a significance for the peak. Smaller FAP
    means a better chance that the peak is real.

    The basic idea is to get the number of trial best peaks that are larger than
    the current best peak and divide this by the total number of trials. The
    distribution of these trial best peaks is obtained after scrambling the mag
    values and rerunning the specified periodogram method for a bunch of trials.

    `lspinfo` is the output dict from a periodbase periodogram function and MUST
    contain a 'method' key that corresponds to one of the keys in the LSPMETHODS
    dict above. This will let this function know which periodogram function to
    run to generate the bootstrap samples. The lspinfo SHOULD also have a
    'kwargs' key that corresponds to the input keyword arguments for the
    periodogram function as it was run originally, to keep everything the same
    during the bootstrap runs. If this is missing, default values will be used.

    FIXME: this may not be strictly correct; must look more into bootstrap
    significance testing. Also look into if we're doing resampling correctly for
    time series because the samples are not iid. Look into moving block
    bootstrap.

    Parameters
    ----------

    lspinfo : dict
        A dict of period-finder results from one of the period-finders in
        periodbase, or your own functions, provided it's of the form and
        contains at least the keys listed below::

            {'periods': np.array of all periods searched by the period-finder,
             'lspvals': np.array of periodogram power value for each period,
             'bestperiod': a float value that is the period with the highest
                           peak in the periodogram, i.e. the most-likely actual
                           period,
             'method': a three-letter code naming the period-finder used; must
                       be one of the keys in the
                       `astrobase.periodbase.METHODLABELS` dict,
             'nbestperiods': a list of the periods corresponding to periodogram
                             peaks (`nbestlspvals` below) to annotate on the
                             periodogram plot so they can be called out
                             visually,
             'nbestlspvals': a list of the power values associated with
                             periodogram peaks to annotate on the periodogram
                             plot so they can be called out visually; should be
                             the same length as `nbestperiods` above,
             'kwargs': dict of kwargs passed to your own period-finder function}

        If you provide your own function's period-finder results, you should add
        a corresponding key for it to the LSPMETHODS dict above so the bootstrap
        function can use it correctly. Your period-finder function should take
        `times`, `mags`, errs and any extra parameters as kwargs and return a
        dict of the form described above. A small worked example::

            from your_module import your_periodfinder_func
            from astrobase import periodbase

            periodbase.LSPMETHODS['your-finder'] = your_periodfinder_func

            # run a period-finder session
            your_pfresults = your_periodfinder_func(times, mags, errs,
                                                    **extra_kwargs)

            # run bootstrap to find FAP
            falsealarm_info = periodbase.bootstrap_falsealarmprob(
                your_pfresults,
                times, mags, errs,
                nbootstrap=250,
                magsarefluxes=False,
            )

    times,mags,errs : np.arrays
        The magnitude/flux time-series to process along with their associated
        measurement errors.

    nbootstrap : int
        The total number of bootstrap trials to run. This is set to 250 by
        default, but should probably be around 1000 for realistic results.

    magsarefluxes : bool
        If True, indicates the input time-series is fluxes and not mags.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    npeaks : int or None
        The number of peaks from the list of 'nbestlspvals' in the period-finder
        result dict to run the bootstrap for. If None, all of the peaks in this
        list will have their FAP calculated.

    Returns
    -------

    dict
        Returns a dict of the form::

            {'peaks':allpeaks,
             'periods':allperiods,
             'probabilities':allfaps,
             'alltrialbestpeaks':alltrialbestpeaks}

    '''

    # figure out how many periods to work on
    if (npeaks and (0 < npeaks < len(lspinfo['nbestperiods']))):
        nperiods = npeaks
    else:
        LOGWARNING('npeaks not specified or invalid, '
                   'getting FAP for all %s periodogram peaks' %
                   len(lspinfo['nbestperiods']))
        nperiods = len(lspinfo['nbestperiods'])

    nbestperiods = lspinfo['nbestperiods'][:nperiods]
    nbestpeaks = lspinfo['nbestlspvals'][:nperiods]

    # get rid of nans first and sigclip
    stimes, smags, serrs = sigclip_magseries(times,
                                             mags,
                                             errs,
                                             magsarefluxes=magsarefluxes,
                                             sigclip=sigclip)

    allpeaks = []
    allperiods = []
    allfaps = []
    alltrialbestpeaks = []

    # make sure there are enough points to calculate a spectrum
    if len(stimes) > 9 and len(smags) > 9 and len(serrs) > 9:

        for ind, period, peak in zip(range(len(nbestperiods)),
                                     nbestperiods,
                                     nbestpeaks):

            LOGINFO('peak %s: running %s trials...' % (ind+1, nbootstrap))

            trialbestpeaks = []

            for _trial in range(nbootstrap):

                # get a scrambled index
                tindex = np.random.randint(0,
                                           high=mags.size,
                                           size=mags.size)

                # get the kwargs dict out of the lspinfo
                if 'kwargs' in lspinfo:

                    kwargs = lspinfo['kwargs']

                    # update the kwargs with some local stuff
                    kwargs.update({'magsarefluxes':magsarefluxes,
                                   'sigclip':sigclip,
                                   'verbose':False})
                else:
                    kwargs = {'magsarefluxes':magsarefluxes,
                              'sigclip':sigclip,
                              'verbose':False}

                # run the periodogram with scrambled mags and errs
                # and the appropriate keyword arguments
                lspres = LSPMETHODS[lspinfo['method']](
                    times, mags[tindex], errs[tindex],
                    **kwargs
                )
                trialbestpeaks.append(lspres['bestlspval'])

            trialbestpeaks = np.array(trialbestpeaks)
            alltrialbestpeaks.append(trialbestpeaks)

            # calculate the FAP for a trial peak j = FAP[j] =
            # (1.0 + sum(trialbestpeaks[i] > peak[j]))/(ntrialbestpeaks + 1)
            if lspinfo['method'] != 'pdm':
                falsealarmprob = (
                    (1.0 + trialbestpeaks[trialbestpeaks > peak].size) /
                    (trialbestpeaks.size + 1.0)
                )
            # for PDM, we're looking for a peak smaller than the best peak
            # because values closer to 0.0 are more significant
            else:
                falsealarmprob = (
                    (1.0 + trialbestpeaks[trialbestpeaks < peak].size) /
                    (trialbestpeaks.size + 1.0)
                )

            LOGINFO('FAP for peak %s, period: %.6f = %.3g' % (ind+1,
                                                              period,
                                                              falsealarmprob))

            allpeaks.append(peak)
            allperiods.append(period)
            allfaps.append(falsealarmprob)

        return {'peaks':allpeaks,
                'periods':allperiods,
                'probabilities':allfaps,
                'alltrialbestpeaks':alltrialbestpeaks}

    else:
        LOGERROR('not enough mag series points to calculate periodogram')
        return None

Response: Calculates the false alarm probabilities of periodogram peaks using the bootstrap resampling of the magnitude time series.


Instruction:
Explain what the following Python 3 code does
def read_original_textlc(lcpath):
    '''
    Read .epdlc, and .tfalc light curves and return a corresponding labelled
    dict (if LC from <2012) or astropy table (if >=2012). Each has different
    keys that can be accessed via .keys()

    Input:
    lcpath: path (string) to light curve data, which is a textfile with HAT
    LC data.

    Example:
    dat = read_original_textlc('HAT-115-0003266.epdlc')
    '''

    LOGINFO('reading original HAT text LC: {:s}'.format(lcpath))

    N_lines_to_parse_comments = 50
    with open(lcpath, 'rb') as file:
        head = [next(file) for ind in range(N_lines_to_parse_comments)]

    N_comment_lines = len([l for l in head if l.decode('UTF-8')[0] == '#'])

    # if there are too many comment lines, fail out
    if N_comment_lines < N_lines_to_parse_comments:
        LOGERROR(
            'LC file {fpath} has too many comment lines'.format(fpath=lcpath)
        )
        return None

    first_data_line = list(
        filter(None, head[N_comment_lines].decode('UTF-8').split())
    )
    N_cols = len(first_data_line)

    # There are different column formats depending on when HAT pipeline was run
    # also different formats for different types of LCs:
    # pre-2012: .epdlc -> 17 columns
    # pre-2012: .tfalc -> 20 columns
    # post-2012: .epdlc or .tfalc -> 32 columns

    if N_cols == 17:
        colformat = 'pre2012-epdlc'
    elif N_cols == 20:
        colformat = 'pre2012-tfalc'
    elif N_cols == 32:
        colformat = 'post2012-hatlc'
    else:
        LOGERROR("can't handle this column format yet, "
                 "file: {fpath}, ncols: {ncols}".format(fpath=lcpath,
                                                        ncols=N_cols))
        return None

    # deal with pre-2012 column format
    if colformat == 'pre2012-epdlc':

        col_names = ['framekey','rjd',
                     'aim_000','aie_000','aiq_000',
                     'aim_001','aie_001','aiq_001',
                     'aim_002','aie_002','aiq_002',
                     'arm_000','arm_001','arm_002',
                     'aep_000','aep_001','aep_002']
        col_dtypes = ['U8',float,
                      float,float,'U1',
                      float,float,'U1',
                      float,float,'U1',
                      float,float,float,
                      float,float,float]
        dtype_pairs = [el for el in zip(col_names, col_dtypes)]
        data = np.genfromtxt(lcpath, names=col_names, dtype=col_dtypes,
                             skip_header=N_comment_lines, delimiter=None)
        out = {}
        for ix in range(len(data.dtype.names)):
            out[data.dtype.names[ix]] = data[data.dtype.names[ix]]

    elif colformat == 'pre2012-tfalc':

        col_names = ['framekey','rjd',
                     'aim_000','aie_000','aiq_000',
                     'aim_001','aie_001','aiq_001',
                     'aim_002','aie_002','aiq_002',
                     'arm_000','arm_001','arm_002',
                     'aep_000','aep_001','aep_002',
                     'atf_000','atf_001','atf_002']
        col_dtypes = ['U8',float,
                      float,float,'U1',
                      float,float,'U1',
                      float,float,'U1',
                      float,float,float,
                      float,float,float,
                      float,float,float]
        dtype_pairs = [el for el in zip(col_names, col_dtypes)]
        data = np.genfromtxt(lcpath, names=col_names, dtype=col_dtypes,
                             skip_header=N_comment_lines, delimiter=None)
        out = {}
        for ix in range(len(data.dtype.names)):
            out[data.dtype.names[ix]] = data[data.dtype.names[ix]]

    elif colformat == 'post2012-hatlc':

        col_names = ['hatid', 'framekey', 'fld', 'bjd',
                     'aim_000', 'aie_000', 'aiq_000',
                     'aim_001', 'aie_001', 'aiq_001',
                     'aim_002', 'aie_002', 'aiq_002',
                     'arm_000', 'arm_001', 'arm_002',
                     'aep_000', 'aep_001', 'aep_002',
                     'atf_000', 'atf_001', 'atf_002',
                     'xcc', 'ycc', 'bgv', 'bge',
                     'fsv', 'fdv', 'fkv',
                     'iha', 'izd', 'rjd']

        out = astascii.read(lcpath, names=col_names, comment='#')

    return out

Response:
Read original text file with HAT - 111 - 0003266. epdlc and. tfalc light curves and return a corresponding labelled
    dict.